This is LSM adapter for TOMOYO. This version of TOMOYO assigns only one variable "struct tomoyo_domain_info *" to "current->cred->security". Future version will require two variables "struct tomoyo_domain_info *" and "u32". http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/include/linux/sched.h#L1311 Well, how to implement this while "current->cred->security" cannot be modified freely? To be honest, the only object which TOMOYO needs to assign variables is "struct task_struct". TOMOYO's "T" means "task". TOMOYO utilizes characteristics of "struct task_struct". For TOMOYO, use of per task variables is vital. Removal of security_task_alloc() and security_task_free() by introduction of COW credentials will become a nightmare when TOMOYO tries to add "u32". Of cource, if TOMOYO is allowed to add "u32" to "struct task_struct", COW credentials won't become a nightmare. Signed-off-by: Kentaro Takeda Signed-off-by: Tetsuo Handa Signed-off-by: Toshiharu Harada --- security/tomoyo/tomoyo.c | 453 +++++++++++++++++++++++++++++++++++++++++++++++ security/tomoyo/tomoyo.h | 107 +++++++++++ 2 files changed, 560 insertions(+) --- /dev/null +++ linux-2.6.28-rc5-mm1/security/tomoyo/tomoyo.c @@ -0,0 +1,453 @@ +/* + * security/tomoyo/tomoyo.c + * + * LSM hooks for TOMOYO Linux. + * + * Copyright (C) 2005-2008 NTT DATA CORPORATION + * + * Version: 2.2.0-pre 2008/11/11 + * + */ + +#include +#include "common.h" +#include "tomoyo.h" +#include "realpath.h" + +static int tomoyo_cred_prepare(struct cred *new, const struct cred *old, + gfp_t gfp) +{ + /* + * Since "struct tomoyo_domain_info *" is a sharable pointer, + * we don't need to duplicate. + */ + new->security = old->security; + return 0; +} + +static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) +{ + /* + * Do only if this function is called for the first time of an execve + * operation. + */ + if (bprm->cred_prepared) + return 0; + /* + * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested + * for the first time. + */ + if (!tomoyo_policy_loaded) + tomoyo_load_policy(bprm->filename); + /* + * Tell tomoyo_bprm_check_security() is called for the first time of an + * execve operation. + */ + bprm->cred->security = NULL; + return 0; +} + +static int tomoyo_bprm_check_security(struct linux_binprm *bprm) +{ + struct tomoyo_domain_info *domain = bprm->cred->security; + + /* + * Execute permission is checked against pathname passed to do_execve() + * using current domain. + */ + if (!domain) { + struct tomoyo_domain_info *next_domain = NULL; + int retval = tomoyo_find_next_domain(bprm, &next_domain); + + if (!retval) + bprm->cred->security = next_domain; + return retval; + } + /* + * Read permission is checked against interpreters using next domain. + * '1' is the result of open_to_namei_flags(O_RDONLY). + */ + return tomoyo_check_open_permission(domain, &bprm->file->f_path, 1); +} + +#ifdef CONFIG_SYSCTL + +static int tomoyo_prepend(char **buffer, int *buflen, const char *str) +{ + int namelen = strlen(str); + + if (*buflen < namelen) + return -ENOMEM; + *buflen -= namelen; + *buffer -= namelen; + memcpy(*buffer, str, namelen); + return 0; +} + +/** + * sysctlpath_from_table - return the realpath of a ctl_table. + * @table: pointer to "struct ctl_table". + * + * Returns realpath(3) of the @table on success. + * Returns NULL on failure. + * + * This function uses tomoyo_alloc(), so the caller must call tomoyo_free() + * if this function didn't return NULL. + */ +static char *sysctl_path(struct ctl_table *table) +{ + int buflen = TOMOYO_MAX_PATHNAME_LEN; + char *buf = tomoyo_alloc(buflen); + char *end = buf + buflen; + int error = -ENOMEM; + + if (!buf) + return NULL; + + *--end = '\0'; + buflen--; + while (table) { + char buf[32]; + const char *sp = table->procname; + + if (!sp) { + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "=%d=", table->ctl_name); + sp = buf; + } + if (tomoyo_prepend(&end, &buflen, sp) || + tomoyo_prepend(&end, &buflen, "/")) + goto out; + table = table->parent; + } + if (tomoyo_prepend(&end, &buflen, "/proc/sys")) + goto out; + error = tomoyo_encode(buf, end - buf, end); + out: + if (!error) + return buf; + tomoyo_free(buf); + return NULL; +} + +static int tomoyo_sysctl(struct ctl_table *table, int op) +{ + int error; + char *name; + + op &= MAY_READ | MAY_WRITE; + if (!op) + return 0; + name = sysctl_path(table); + if (!name) + return -ENOMEM; + error = tomoyo_check_file_perm(tomoyo_domain(), name, op); + tomoyo_free(name); + return error; +} +#endif + +/** + * tomoyo_update_result - Update error code. + * + * @error: Return code from security_path_*(). + * + * To be able to return DAC's error (if any) to the caller instead of + * MAC's error, we don't return MAC's error at security_path_*(). + * + * We remember MAC's error only if security_path_*() returned an error. + * + * Returns 0 on success, -ENOMEM otherwise if @error != 0. + * Returns previously saved error code and clears it if @error == 0. + */ +static int tomoyo_update_result(int error) +{ + /* Structure for holding the result of security_path_*(). */ + struct tomoyo_check_result_entry { + struct list_head list; + struct task_struct *task; /* = current */ + int error; /* != 0 */ + }; + static LIST_HEAD(list); + static DEFINE_SPINLOCK(lock); + struct task_struct *task = current; + struct tomoyo_check_result_entry *entry; + if (!error) { + if (!list_empty(&list)) { + struct tomoyo_check_result_entry *p; + entry = NULL; + /***** CRITICAL SECTION START *****/ + spin_lock(&lock); + list_for_each_entry(p, &list, list) { + if (p->task != task) + continue; + list_del(&p->list); + entry = p; + break; + } + spin_unlock(&lock); + /***** CRITICAL SECTION END *****/ + if (entry) { + error = entry->error; + kfree(entry); + } + } + return error; + } + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + entry->task = task; + entry->error = error; + /***** CRITICAL SECTION START *****/ + spin_lock(&lock); + list_add(&entry->list, &list); + spin_unlock(&lock); + /***** CRITICAL SECTION END *****/ + return 0; +} + +/** + * tomoyo_save_result - Remember error code for security_inode_*() if any. + * + * @error: Return code from security_path_*(). + * + * Returns 0 on success, -ENOMEM otherwise. + * + * We don't save if @error == 0. + */ +static int tomoyo_save_result(const int error) +{ + return error ? tomoyo_update_result(error) : 0; +} + +/** + * tomoyo_load_result - Fetch error code for security_inode_*(). + * + * Returns error code saved by security_path_*(). + */ +static int tomoyo_load_result(void) +{ + return tomoyo_update_result(0); +} + +/* Clear error code in case security_inode_*() was not called. */ +static void tomoyo_path_clear(void) +{ + tomoyo_load_result(); +} + +static int tomoyo_path_truncate(struct path *path, loff_t length, + unsigned int time_attrs, struct file *filp) +{ + const int ret = tomoyo_check_1path_perm(tomoyo_domain(), + TOMOYO_TYPE_TRUNCATE_ACL, + path); + return tomoyo_save_result(ret); +} + +static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry) +{ + struct path path = { parent->mnt, dentry }; + const int ret = tomoyo_check_1path_perm(tomoyo_domain(), + TOMOYO_TYPE_UNLINK_ACL, + &path); + return tomoyo_save_result(ret); +} + +static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, + int mode) +{ + struct path path = { parent->mnt, dentry }; + const int ret = tomoyo_check_1path_perm(tomoyo_domain(), + TOMOYO_TYPE_MKDIR_ACL, + &path); + return tomoyo_save_result(ret); +} + +static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) +{ + struct path path = { parent->mnt, dentry }; + const int ret = tomoyo_check_1path_perm(tomoyo_domain(), + TOMOYO_TYPE_RMDIR_ACL, + &path); + return tomoyo_save_result(ret); +} + +static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry, + const char *old_name) +{ + struct path path = { parent->mnt, dentry }; + const int ret = tomoyo_check_1path_perm(tomoyo_domain(), + TOMOYO_TYPE_SYMLINK_ACL, + &path); + return tomoyo_save_result(ret); +} + +static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, + int mode, unsigned int dev) +{ + struct path path = { parent->mnt, dentry }; + int type = TOMOYO_TYPE_CREATE_ACL; + + switch (mode & S_IFMT) { + case S_IFCHR: + type = TOMOYO_TYPE_MKCHAR_ACL; + break; + case S_IFBLK: + type = TOMOYO_TYPE_MKBLOCK_ACL; + break; + case S_IFIFO: + type = TOMOYO_TYPE_MKFIFO_ACL; + break; + case S_IFSOCK: + type = TOMOYO_TYPE_MKSOCK_ACL; + break; + } + return tomoyo_save_result(tomoyo_check_1path_perm(tomoyo_domain(), + type, &path)); +} + +static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, + struct dentry *new_dentry) +{ + struct path path1 = { new_dir->mnt, old_dentry }; + struct path path2 = { new_dir->mnt, new_dentry }; + const int ret = tomoyo_check_2path_perm(tomoyo_domain(), + TOMOYO_TYPE_LINK_ACL, + &path1, &path2); + return tomoyo_save_result(ret); +} + +static int tomoyo_path_rename(struct path *old_parent, + struct dentry *old_dentry, + struct path *new_parent, + struct dentry *new_dentry) +{ + struct path path1 = { old_parent->mnt, old_dentry }; + struct path path2 = { new_parent->mnt, new_dentry }; + const int ret = tomoyo_check_2path_perm(tomoyo_domain(), + TOMOYO_TYPE_RENAME_ACL, + &path1, &path2); + return tomoyo_save_result(ret); +} + +static int tomoyo_inode_link(struct dentry *old_dentry, struct inode *inode, + struct dentry *new_dentry) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_unlink(struct inode *inode, struct dentry *dentry) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_symlink(struct inode *inode, struct dentry *dentry, + const char *name) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_mkdir(struct inode *inode, struct dentry *dentry, + int mask) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_rmdir(struct inode *inode, struct dentry *dentry) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_create(struct inode *dir, struct dentry *dentry, + int mode) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_mknod(struct inode *inode, struct dentry *dentry, + int mode, dev_t dev) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_rename(struct inode *old_inode, + struct dentry *old_dentry, + struct inode *new_inode, + struct dentry *new_dentry) +{ + return tomoyo_load_result(); +} + +static int tomoyo_inode_setattr(struct dentry *dentry, struct iattr *iattr) +{ + return tomoyo_load_result(); +} + +static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) + return tomoyo_check_rewrite_permission(tomoyo_domain(), file); + return 0; +} + +static int tomoyo_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); + /* Don't check read permission here if called from do_execve(). */ + if (current->in_execve) + return 0; + return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags); +} + +static struct security_operations tomoyo_security_ops = { + .name = "tomoyo", + .cred_prepare = tomoyo_cred_prepare, + .bprm_set_creds = tomoyo_bprm_set_creds, + .bprm_check_security = tomoyo_bprm_check_security, +#ifdef CONFIG_SYSCTL + .sysctl = tomoyo_sysctl, +#endif + .file_fcntl = tomoyo_file_fcntl, + .dentry_open = tomoyo_dentry_open, + .path_truncate = tomoyo_path_truncate, + .path_unlink = tomoyo_path_unlink, + .path_mkdir = tomoyo_path_mkdir, + .path_rmdir = tomoyo_path_rmdir, + .path_symlink = tomoyo_path_symlink, + .path_mknod = tomoyo_path_mknod, + .path_link = tomoyo_path_link, + .path_rename = tomoyo_path_rename, + .inode_create = tomoyo_inode_create, + .inode_setattr = tomoyo_inode_setattr, + .inode_unlink = tomoyo_inode_unlink, + .inode_mkdir = tomoyo_inode_mkdir, + .inode_rmdir = tomoyo_inode_rmdir, + .inode_symlink = tomoyo_inode_symlink, + .inode_mknod = tomoyo_inode_mknod, + .inode_link = tomoyo_inode_link, + .inode_rename = tomoyo_inode_rename, + .path_clear = tomoyo_path_clear, +}; + +static int __init tomoyo_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 = &tomoyo_kernel_domain; + return 0; +} + +security_initcall(tomoyo_init); --- /dev/null +++ linux-2.6.28-rc5-mm1/security/tomoyo/tomoyo.h @@ -0,0 +1,107 @@ +/* + * 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/11/11 + * + */ + +#ifndef _SECURITY_TOMOYO_TOMOYO_H +#define _SECURITY_TOMOYO_TOMOYO_H + +struct tomoyo_path_info; +struct path; +struct inode; +struct linux_binprm; +struct pt_regs; +struct tomoyo_page_buffer; + +int tomoyo_check_file_perm(struct tomoyo_domain_info *domain, + const char *filename, const u8 perm); +int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, + const struct tomoyo_path_info *filename, + struct tomoyo_page_buffer *buf); +int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, + struct path *path, const int flag); +int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain, + const u8 operation, struct path *path); +int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain, + const u8 operation, struct path *path1, + struct path *path2); +int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain, + struct file *filp); +int tomoyo_find_next_domain(struct linux_binprm *bprm, + struct tomoyo_domain_info **next_domain); + +/* Index numbers for Access Controls. */ + +#define TOMOYO_TYPE_SINGLE_PATH_ACL 0 +#define TOMOYO_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 TOMOYO_TYPE_READ_WRITE_ACL 0 +#define TOMOYO_TYPE_EXECUTE_ACL 1 +#define TOMOYO_TYPE_READ_ACL 2 +#define TOMOYO_TYPE_WRITE_ACL 3 +#define TOMOYO_TYPE_CREATE_ACL 4 +#define TOMOYO_TYPE_UNLINK_ACL 5 +#define TOMOYO_TYPE_MKDIR_ACL 6 +#define TOMOYO_TYPE_RMDIR_ACL 7 +#define TOMOYO_TYPE_MKFIFO_ACL 8 +#define TOMOYO_TYPE_MKSOCK_ACL 9 +#define TOMOYO_TYPE_MKBLOCK_ACL 10 +#define TOMOYO_TYPE_MKCHAR_ACL 11 +#define TOMOYO_TYPE_TRUNCATE_ACL 12 +#define TOMOYO_TYPE_SYMLINK_ACL 13 +#define TOMOYO_TYPE_REWRITE_ACL 14 +#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15 + +#define TOMOYO_TYPE_LINK_ACL 0 +#define TOMOYO_TYPE_RENAME_ACL 1 +#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2 + +#define TOMOYO_DOMAINPOLICY 0 +#define TOMOYO_EXCEPTIONPOLICY 1 +#define TOMOYO_DOMAIN_STATUS 2 +#define TOMOYO_PROCESS_STATUS 3 +#define TOMOYO_MEMINFO 4 +#define TOMOYO_SELFDOMAIN 5 +#define TOMOYO_VERSION 6 +#define TOMOYO_PROFILE 7 +#define TOMOYO_MANAGER 8 +#define TOMOYO_UPDATESCOUNTER 9 + +extern struct tomoyo_domain_info tomoyo_kernel_domain; + +static inline struct tomoyo_domain_info *tomoyo_domain(void) +{ + return current_cred()->security; +} + +/* Caller holds tasklist_lock spinlock. */ +static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct + *task) +{ + /***** CRITICAL SECTION START *****/ + const struct cred *cred = get_task_cred(task); + struct tomoyo_domain_info *domain = cred->security; + + put_cred(cred); + return domain; + /***** CRITICAL SECTION END *****/ +} + +#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */ -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/