[TOMOYO #9 (2.6.27-rc7-mm1) 1/6] LSM adapter functions.

Kentaro Takeda
2008-09-24T09:05:53Z

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
security/tomoyo/tomoyo.c | 341 +++++++++++++++++++++++++++++++++++++++++++++++
security/tomoyo/tomoyo.h | 97 +++++++++++++
2 files changed, 438 insertions(+)

--- /dev/null
+++ linux-2.6.27-rc7-mm1/security/tomoyo/tomoyo.c
@@ -0,0 +1,341 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2008 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2008/09/24
+ *
+ */
+
+#include <linux/security.h>
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/audit.h>
+#include <linux/device_cgroup.h>
+
+static int tmy_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp)
+{
+ new->security = old->security;
+ return 0;
+}
+
+static int tmy_bprm_check_security(struct linux_binprm *bprm)
+{
+ struct domain_info *next_domain = NULL;
+ int retval;
+ /*
+ * If called by do_execve() (i.e. bprm->sh_bang == 0),
+ * I do execute permission check.
+ */
+ if (bprm->sh_bang)
+ return 0;
+ tmy_load_policy(bprm->filename);
+ retval = tmy_find_next_domain(bprm, &next_domain);
+ if (!retval)
+ bprm->cred->security = next_domain;
+ return retval;
+}
+
+static int tmy_sysctl(struct ctl_table *table, int op)
+{
+ int error;
+ char *name;
+
+ if ((op & 6) == 0)
+ return 0;
+
+ name = sysctlpath_from_table(table);
+ if (!name)
+ return -ENOMEM;
+
+ error = tmy_check_file_perm(name, op & 6, "sysctl");
+ tmy_free(name);
+
+ return error;
+}
+
+/* Copied from fs/namei.c */
+static inline int may_create(struct inode *dir, struct dentry *child)
+{
+ if (child->d_inode)
+ return -EEXIST;
+ if (IS_DEADDIR(dir))
+ return -ENOENT;
+ return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+}
+
+/* Copied from fs/namei.c */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+ uid_t fsuid = current_fsuid();
+
+ if (!(dir->i_mode & S_ISVTX))
+ return 0;
+ if (inode->i_uid == fsuid)
+ return 0;
+ if (dir->i_uid == fsuid)
+ return 0;
+ return !capable(CAP_FOWNER);
+}
+
+/* Copied from fs/namei.c */
+static int may_delete(struct inode *dir, struct dentry *victim, int isdir)
+{
+ int error;
+
+ if (!victim->d_inode)
+ return -ENOENT;
+
+ BUG_ON(victim->d_parent->d_inode != dir);
+ audit_inode_child(victim->d_name.name, victim, dir);
+
+ error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ if (error)
+ return error;
+ if (IS_APPEND(dir))
+ return -EPERM;
+ if (check_sticky(dir, victim->d_inode) || IS_APPEND(victim->d_inode) ||
+ IS_IMMUTABLE(victim->d_inode))
+ return -EPERM;
+ if (isdir) {
+ if (!S_ISDIR(victim->d_inode->i_mode))
+ return -ENOTDIR;
+ if (IS_ROOT(victim))
+ return -EBUSY;
+ } else if (S_ISDIR(victim->d_inode->i_mode))
+ return -EISDIR;
+ if (IS_DEADDIR(dir))
+ return -ENOENT;
+ if (victim->d_flags & DCACHE_NFSFS_RENAMED)
+ return -EBUSY;
+ return 0;
+}
+
+static int tmy_path_truncate(struct path *path, loff_t length,
+ unsigned int time_attrs, struct file *filp)
+{
+ struct dentry *dentry = path->dentry;
+ int error;
+ mutex_lock(&dentry->d_inode->i_mutex);
+ error = tmy_check_1path_perm(TMY_TYPE_TRUNCATE_ACL,
+ path->dentry, path->mnt);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ return error;
+}
+
+static int tmy_path_unlink(struct path *parent, struct dentry *dentry)
+{
+ struct inode *dir = parent->dentry->d_inode;
+ int error = may_delete(dir, dentry, 0);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->unlink)
+ return -EPERM;
+ mutex_lock(&dentry->d_inode->i_mutex);
+ error = tmy_check_1path_perm(TMY_TYPE_UNLINK_ACL, dentry, parent->mnt);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ return error;
+}
+
+static int tmy_path_mkdir(struct path *parent, struct dentry *dentry, int mode)
+{
+ struct inode *dir = parent->dentry->d_inode;
+ int error = may_create(dir, dentry);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->mkdir)
+ return -EPERM;
+ return tmy_check_1path_perm(TMY_TYPE_MKDIR_ACL, dentry, parent->mnt);
+}
+
+static int tmy_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+ struct inode *dir = parent->dentry->d_inode;
+ int error = may_delete(dir, dentry, 1);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->rmdir)
+ return -EPERM;
+ mutex_lock(&dentry->d_inode->i_mutex);
+ error = tmy_check_1path_perm(TMY_TYPE_RMDIR_ACL, dentry, parent->mnt);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ return error;
+}
+
+static int tmy_path_symlink(struct path *parent, struct dentry *dentry,
+ const char *old_name)
+{
+ struct inode *dir = parent->dentry->d_inode;
+ int error = may_create(dir, dentry);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->symlink)
+ return -EPERM;
+ return tmy_check_1path_perm(TMY_TYPE_SYMLINK_ACL, dentry, parent->mnt);
+}
+
+static int tmy_path_mknod(struct path *parent, struct dentry *dentry, int mode,
+ unsigned int dev)
+{
+ struct vfsmount *mnt = parent->mnt;
+ struct inode *dir = parent->dentry->d_inode;
+ int error = 0;
+
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ case 0:
+ error = may_create(dir, dentry);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->create)
+ return -EACCES; /* shouldn't it be ENOSYS? */
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ error = may_create(dir, dentry);
+ if (error)
+ return error;
+ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+ return -EPERM;
+ if (!dir->i_op || !dir->i_op->mknod)
+ return -EPERM;
+ error = devcgroup_inode_mknod(mode, dev);
+ if (error)
+ return error;
+ break;
+ default:
+ return 0;
+ }
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ case 0:
+ error = tmy_check_1path_perm(TMY_TYPE_CREATE_ACL, dentry, mnt);
+ break;
+ case S_IFCHR:
+ error = tmy_check_1path_perm(TMY_TYPE_MKCHAR_ACL, dentry, mnt);
+ break;
+ case S_IFBLK:
+ error = tmy_check_1path_perm(TMY_TYPE_MKBLOCK_ACL, dentry, mnt);
+ break;
+ case S_IFIFO:
+ error = tmy_check_1path_perm(TMY_TYPE_MKFIFO_ACL, dentry, mnt);
+ break;
+ case S_IFSOCK:
+ error = tmy_check_1path_perm(TMY_TYPE_MKSOCK_ACL, dentry, mnt);
+ break;
+ }
+ return error;
+}
+
+static int tmy_path_link(struct dentry *old_dentry, struct path *new_dir,
+ struct dentry *new_dentry)
+{
+ struct inode *dir = new_dir->dentry->d_inode;
+
+ struct inode *inode = old_dentry->d_inode;
+ int error;
+ if (!inode)
+ return -ENOENT;
+ error = may_create(dir, new_dentry);
+ if (error)
+ return error;
+ if (dir->i_sb != inode->i_sb)
+ return -EXDEV;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return -EPERM;
+ if (!dir->i_op || !dir->i_op->link)
+ return -EPERM;
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ return tmy_check_2path_perm(TMY_TYPE_LINK_ACL,
+ old_dentry, new_dir->mnt,
+ new_dentry, new_dir->mnt);
+}
+
+static int tmy_path_rename(struct path *old_parent, struct dentry *old_dentry,
+ struct path *new_parent, struct dentry *new_dentry)
+{
+ struct inode *old_dir = old_parent->dentry->d_inode;
+ struct inode *new_dir = new_parent->dentry->d_inode;
+
+ int error;
+ int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+ if (old_dentry->d_inode == new_dentry->d_inode)
+ return 0;
+ error = may_delete(old_dir, old_dentry, is_dir);
+ if (error)
+ return error;
+ if (!new_dentry->d_inode)
+ error = may_create(new_dir, new_dentry);
+ else
+ error = may_delete(new_dir, new_dentry, is_dir);
+ if (error)
+ return error;
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+
+ if (is_dir && new_dir != old_dir) {
+ error = inode_permission(old_dentry->d_inode, MAY_WRITE);
+ if (error)
+ return error;
+ }
+
+ return tmy_check_2path_perm(TMY_TYPE_RENAME_ACL,
+ old_dentry, old_parent->mnt,
+ new_dentry, new_parent->mnt);
+}
+
+static int tmy_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+ return tmy_check_rewrite_permission(file);
+ return 0;
+}
+
+static int tmy_dentry_open(struct file *f, const struct cred *cred)
+{
+ int flags = f->f_flags;
+ if ((flags + 1) & O_ACCMODE)
+ flags++;
+ flags |= f->f_flags & (O_APPEND | O_TRUNC);
+ return tmy_check_open_permission(f->f_dentry, f->f_vfsmnt, flags);
+}
+
+static struct security_operations tomoyo_security_ops = {
+ .name = "tomoyo",
+ .cred_prepare = tmy_cred_prepare,
+ .bprm_check_security = tmy_bprm_check_security,
+ .sysctl = tmy_sysctl,
+ .file_fcntl = tmy_file_fcntl,
+ .dentry_open = tmy_dentry_open,
+ .path_truncate = tmy_path_truncate,
+ .path_unlink = tmy_path_unlink,
+ .path_mkdir = tmy_path_mkdir,
+ .path_rmdir = tmy_path_rmdir,
+ .path_symlink = tmy_path_symlink,
+ .path_mknod = tmy_path_mknod,
+ .path_link = tmy_path_link,
+ .path_rename = tmy_path_rename,
+};
+
+static int __init tmy_init(void)
+{
+ struct cred *cred = (struct cred *) current->cred;
+ if (!security_module_enable(&tomoyo_security_ops))
+ return 0;
+ /* register ourselves with the security framework */
+ if (register_security(&tomoyo_security_ops))
+ panic("Failure registering TOMOYO Linux");
+ printk(KERN_INFO "TOMOYO Linux initialized\n");
+ cred->security = &KERNEL_DOMAIN;
+ return 0;
+}
+
+security_initcall(tmy_init);
--- /dev/null
+++ linux-2.6.27-rc7-mm1/security/tomoyo/tomoyo.h
@@ -0,0 +1,97 @@
+/*
+ * security/tomoyo/tomoyo.h
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2008 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2008/09/24
+ *
+ */
+
+#ifndef _LINUX_TOMOYO_H
+#define _LINUX_TOMOYO_H
+
+struct path_info;
+struct dentry;
+struct vfsmount;
+struct inode;
+struct linux_binprm;
+struct pt_regs;
+struct tmy_page_buffer;
+
+char *sysctlpath_from_table(struct ctl_table *table);
+int tmy_check_file_perm(const char *filename, const u8 perm,
+ const char *operation);
+int tmy_check_exec_perm(const struct path_info *filename,
+ struct tmy_page_buffer *buf);
+int tmy_check_open_permission(struct dentry *dentry, struct vfsmount *mnt,
+ const int flag);
+int tmy_check_1path_perm(const u8 operation,
+ struct dentry *dentry,
+ struct vfsmount *mnt);
+int tmy_check_2path_perm(const u8 operation,
+ struct dentry *dentry1,
+ struct vfsmount *mnt1,
+ struct dentry *dentry2,
+ struct vfsmount *mnt2);
+int tmy_check_rewrite_permission(struct file *filp);
+int tmy_find_next_domain(struct linux_binprm *bprm,
+ struct domain_info **next_domain);
+
+/* Index numbers for Access Controls. */
+
+#define TYPE_SINGLE_PATH_ACL 0
+#define TYPE_DOUBLE_PATH_ACL 1
+
+/* Index numbers for File Controls. */
+
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+#define TMY_TYPE_READ_WRITE_ACL 0
+#define TMY_TYPE_EXECUTE_ACL 1
+#define TMY_TYPE_READ_ACL 2
+#define TMY_TYPE_WRITE_ACL 3
+#define TMY_TYPE_CREATE_ACL 4
+#define TMY_TYPE_UNLINK_ACL 5
+#define TMY_TYPE_MKDIR_ACL 6
+#define TMY_TYPE_RMDIR_ACL 7
+#define TMY_TYPE_MKFIFO_ACL 8
+#define TMY_TYPE_MKSOCK_ACL 9
+#define TMY_TYPE_MKBLOCK_ACL 10
+#define TMY_TYPE_MKCHAR_ACL 11
+#define TMY_TYPE_TRUNCATE_ACL 12
+#define TMY_TYPE_SYMLINK_ACL 13
+#define TMY_TYPE_REWRITE_ACL 14
+#define MAX_SINGLE_PATH_OPERATION 15
+
+#define TMY_TYPE_LINK_ACL 0
+#define TMY_TYPE_RENAME_ACL 1
+#define MAX_DOUBLE_PATH_OPERATION 2
+
+#define TMY_DOMAINPOLICY 0
+#define TMY_EXCEPTIONPOLICY 1
+#define TMY_DOMAIN_STATUS 2
+#define TMY_PROCESS_STATUS 3
+#define TMY_MEMINFO 4
+#define TMY_SELFDOMAIN 5
+#define TMY_VERSION 6
+#define TMY_PROFILE 7
+#define TMY_MANAGER 8
+#define TMY_UPDATESCOUNTER 9
+
+extern struct domain_info KERNEL_DOMAIN;
+
+static inline struct domain_info *tmy_domain(struct task_struct *task)
+{
+ return (struct domain_info *) task->cred->security;
+}
+
+#endif

--