Messages in this thread Patch in this message | | | Date | Wed, 24 Sep 2008 18:03:18 +0900 | From | Kentaro Takeda <> | Subject | [TOMOYO #9 (2.6.27-rc7-mm1) 1/6] LSM adapter functions. |
| |
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 --
| |