Signed-off-by: Kentaro Takeda <takedakn@xxxxxxxxxxxxx> Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx> Signed-off-by: Toshiharu Harada <haradats@xxxxxxxxxxxxx> --- security/tomoyo/tomoyo.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++ security/tomoyo/tomoyo.h | 102 +++++++++++++++++ 2 files changed, 381 insertions(+) --- /dev/null +++ mm/security/tomoyo/tomoyo.h @@ -0,0 +1,102 @@ +/* + * 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/04/30 + * + */ + +#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); + +#define TMY_CHECK_READ_FOR_OPEN_EXEC 1 + +/* 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 + +struct tmy_security { + struct domain_info *domain; + struct domain_info *prev_domain; + u32 flags; +}; + +#define TMY_SECURITY ((struct tmy_security *) current->security) + +#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; + +#endif --- /dev/null +++ mm/security/tomoyo/tomoyo.c @@ -0,0 +1,279 @@ +/* + * security/tomoyo/tomoyo.c + * + * LSM hooks for TOMOYO Linux. + */ + +#include <linux/security.h> +#include "common.h" +#include "tomoyo.h" +#include "realpath.h" + +static struct kmem_cache *tmy_cachep; + +static int tmy_task_alloc_security(struct task_struct *p) +{ + struct tmy_security *ptr = kmem_cache_alloc(tmy_cachep, GFP_KERNEL); + + if (!ptr) + return -ENOMEM; + memcpy(ptr, TMY_SECURITY, sizeof(*ptr)); + p->security = ptr; + return 0; +} + +static void tmy_task_free_security(struct task_struct *p) +{ + kmem_cache_free(tmy_cachep, p->security); +} + +static int tmy_bprm_alloc_security(struct linux_binprm *bprm) +{ + TMY_SECURITY->prev_domain = TMY_SECURITY->domain; + return 0; +} + +static int tmy_bprm_check_security(struct linux_binprm *bprm) +{ + struct domain_info *next_domain = NULL; + int retval = 0; + + tmy_load_policy(bprm->filename); + + /* + * TMY_CHECK_READ_FOR_OPEN_EXEC bit indicates whether this function is + * called by do_execve() or not. + * If called by do_execve(), I do domain transition. + */ + if ((TMY_SECURITY->flags & TMY_CHECK_READ_FOR_OPEN_EXEC)) + goto out; + retval = tmy_find_next_domain(bprm, &next_domain); + if (retval) + goto out; + TMY_SECURITY->domain = next_domain; + TMY_SECURITY->flags |= TMY_CHECK_READ_FOR_OPEN_EXEC; +out: + return retval; +} + +static void tmy_bprm_post_apply_creds(struct linux_binprm *bprm) +{ + TMY_SECURITY->prev_domain = TMY_SECURITY->domain; +} + +static void tmy_bprm_free_security(struct linux_binprm *bprm) +{ + TMY_SECURITY->domain = TMY_SECURITY->prev_domain; + TMY_SECURITY->flags &= ~TMY_CHECK_READ_FOR_OPEN_EXEC; +} + +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; +} + +static int tmy_inode_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + if (S_ISDIR(inode->i_mode)) /* ignore because inode is directory */ + return 0; + if (!nd || !nd->path.dentry || !nd->path.mnt) + return 0; + + /* + * If called by other than do_execve(), I check for read permission of + * interpreter. + * Unlike DAC, I don't check for read permission of pathname passed to + * do_execve(). + * TOMOYO Linux checks for program's execute permission and + * interpreter's read permission. + */ + if ((mask != MAY_EXEC) || + !(TMY_SECURITY->flags & TMY_CHECK_READ_FOR_OPEN_EXEC)) + return 0; + + /* called from open_exec() other than do_execve() */; + return tmy_check_open_permission(nd->path.dentry, nd->path.mnt, + O_RDONLY + 1); +} + +static int tmy_path_open(struct dentry *dentry, struct vfsmount *mnt, int flags) +{ + return tmy_check_open_permission(dentry, mnt, flags); +} + +static int tmy_path_truncate(struct path *path, loff_t length, + unsigned int time_attrs, struct file *filp) +{ + return tmy_check_1path_perm(TMY_TYPE_TRUNCATE_ACL, path->dentry, + path->mnt); +} + +static int tmy_path_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + if (!nd || !nd->path.mnt) + return 0; + return tmy_check_1path_perm(TMY_TYPE_CREATE_ACL, dentry, nd->path.mnt); +} + +static int tmy_path_unlink(struct path *dir, struct dentry *dentry) +{ + const int err = pre_vfs_unlink(dir->dentry->d_inode, dentry); + if (err) + return err; + return tmy_check_1path_perm(TMY_TYPE_UNLINK_ACL, dentry, dir->mnt); +} + +static int tmy_path_mkdir(struct path *dir, struct dentry *dentry, int mode) +{ + const int err = pre_vfs_mkdir(dir->dentry->d_inode, dentry); + if (err) + return err; + return tmy_check_1path_perm(TMY_TYPE_MKDIR_ACL, dentry, dir->mnt); +} + +static int tmy_path_rmdir(struct path *dir, struct dentry *dentry) +{ + const int err = pre_vfs_rmdir(dir->dentry->d_inode, dentry); + if (err) + return err; + return tmy_check_1path_perm(TMY_TYPE_RMDIR_ACL, dentry, dir->mnt); +} + +static int tmy_path_symlink(struct path *dir, struct dentry *dentry, + const char *old_name) +{ + const int err = pre_vfs_symlink(dir->dentry->d_inode, dentry); + if (err) + return err; + return tmy_check_1path_perm(TMY_TYPE_SYMLINK_ACL, dentry, dir->mnt); +} + +static int tmy_path_mknod(struct path *dir, struct dentry *dentry, int mode, + unsigned int dev) +{ + struct vfsmount *mnt = dir->mnt; + int err = 0; + switch (mode & S_IFMT) { + case S_IFCHR: + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: + break; + default: + return 0; + } + err = pre_vfs_mknod(dir->dentry->d_inode, dentry, mode); + if (err) + return err; + if (S_ISCHR(mode)) + return tmy_check_1path_perm(TMY_TYPE_MKCHAR_ACL, dentry, mnt); + if (S_ISBLK(mode)) + return tmy_check_1path_perm(TMY_TYPE_MKBLOCK_ACL, dentry, mnt); + if (S_ISFIFO(mode)) + return tmy_check_1path_perm(TMY_TYPE_MKFIFO_ACL, dentry, mnt); + if (S_ISSOCK(mode)) + return tmy_check_1path_perm(TMY_TYPE_MKSOCK_ACL, dentry, mnt); + return 0; +} + +static int tmy_path_link(struct path *old_path, struct path *new_dir, + struct dentry *new_dentry) +{ + const int err = pre_vfs_link(old_path->dentry, new_dir->dentry->d_inode, + new_dentry); + if (err) + return err; + return tmy_check_2path_perm(TMY_TYPE_LINK_ACL, + old_path->dentry, old_path->mnt, + new_dentry, new_dir->mnt); +} + +static int tmy_path_rename(struct path *old_dir, struct dentry *old_dentry, + struct path *new_dir, struct dentry *new_dentry) +{ + const int err = pre_vfs_rename(old_dir->dentry->d_inode, old_dentry, + new_dir->dentry->d_inode, new_dentry); + if (err) + return err; + return tmy_check_2path_perm(TMY_TYPE_RENAME_ACL, + old_dentry, old_dir->mnt, + new_dentry, new_dir->mnt); +} + +static int tmy_path_uselib(struct nameidata *nd) +{ + return tmy_check_open_permission(nd->path.dentry, nd->path.mnt, + O_RDONLY + 1); +} + +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 struct security_operations tomoyo_security_ops = { + .name = "tomoyo", + + .task_alloc_security = tmy_task_alloc_security, + .task_free_security = tmy_task_free_security, + .bprm_alloc_security = tmy_bprm_alloc_security, + .bprm_check_security = tmy_bprm_check_security, + .bprm_post_apply_creds = tmy_bprm_post_apply_creds, + .bprm_free_security = tmy_bprm_free_security, + .sysctl = tmy_sysctl, + .inode_permission = tmy_inode_permission, + .path_truncate = tmy_path_truncate, + .path_create = tmy_path_create, + .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, + .path_open = tmy_path_open, + .path_uselib = tmy_path_uselib, + .file_fcntl = tmy_file_fcntl, +}; + +static int __init tmy_init(void) +{ + struct tmy_security *tmy_security; + 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"); + tmy_cachep = kmem_cache_create("tomoyo_security", + sizeof(struct tmy_security), + 0, SLAB_PANIC, NULL); + tmy_security = kmem_cache_alloc(tmy_cachep, GFP_KERNEL); + BUG_ON(!tmy_security); + memset(tmy_security, 0, sizeof(*tmy_security)); + tmy_security->domain = &KERNEL_DOMAIN; + init_task.security = tmy_security; + return 0; +} + +security_initcall(tmy_init); -- -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html