TOMOYO Linux uses per-a-domain capability, an approach that associate capabilities with each domain, and assign a domain for each process. The advantages of this approach are that (1) Minimum capabilities required for a process is directly given compared to setting/clearing/inheriting bit fields of a process's capability value. (2) It can support as many types of capabilities as you want. Regarding per-a-task capability, you need to allocate memory for individual "task_struct". Thus, the number of supportable capabilities is bound by the width of variable in bits (i.e. 32 or 64). Regarding per-a-domain capability, you need to allocate memory for individual domains. But you can allocate memory dynamically. Thus, the number of supportable capabilities is not bounded to 32 or 64. I can support up to 65536 types of capability, but currently there are not so many capability hooks inserted. Adding more capability hooks is a future work. Signed-off-by: Kentaro Takeda Signed-off-by: Tetsuo Handa Signed-off-by: Toshiharu Harada --- fs/tomoyo_capability.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) --- /dev/null +++ linux-2.6.25-rc8-mm1/fs/tomoyo_capability.c @@ -0,0 +1,223 @@ +/* + * fs/tomoyo_capability.c + * + * Implementation of the Domain-Based Mandatory Access Control. + * + * Copyright (C) 2005-2008 NTT DATA CORPORATION + * + * Version: 1.6.0 2008/04/01 + * + */ + +#include +#include +#include + +/** + * cap_operation2name - Convert capability operation to capability message. + * + * @operation: Type of operation. + * + * Returns the name of capability. + */ +static const char *cap_operation2name(const u8 operation) +{ + static const char *capability_name[TOMOYO_MAX_CAPABILITY_INDEX] = { + [TOMOYO_INET_STREAM_SOCKET_CREATE] = + "socket(PF_INET, SOCK_STREAM)", + [TOMOYO_INET_STREAM_SOCKET_LISTEN] = + "listen(PF_INET, SOCK_STREAM)", + [TOMOYO_INET_STREAM_SOCKET_CONNECT] = + "connect(PF_INET, SOCK_STREAM)", + [TOMOYO_USE_INET_DGRAM_SOCKET] = + "socket(PF_INET, SOCK_DGRAM)", + [TOMOYO_USE_INET_RAW_SOCKET] = + "socket(PF_INET, SOCK_RAW)", + [TOMOYO_USE_ROUTE_SOCKET] = "socket(PF_ROUTE)", + [TOMOYO_USE_PACKET_SOCKET] = "socket(PF_PACKET)", + [TOMOYO_SYS_MOUNT] = "sys_mount()", + [TOMOYO_SYS_UMOUNT] = "sys_umount()", + [TOMOYO_SYS_REBOOT] = "sys_reboot()", + [TOMOYO_SYS_CHROOT] = "sys_chroot()", + [TOMOYO_SYS_KILL] = "sys_kill()", + [TOMOYO_SYS_VHANGUP] = "sys_vhangup()", + [TOMOYO_SYS_SETTIME] = "sys_settimeofday()", + [TOMOYO_SYS_NICE] = "sys_nice()", + [TOMOYO_SYS_SETHOSTNAME] = "sys_sethostname()", + [TOMOYO_USE_KERNEL_MODULE] = "kernel_module", + [TOMOYO_CREATE_FIFO] = "mknod(FIFO)", + [TOMOYO_CREATE_BLOCK_DEV] = "mknod(BDEV)", + [TOMOYO_CREATE_CHAR_DEV] = "mknod(CDEV)", + [TOMOYO_CREATE_UNIX_SOCKET] = "mknod(SOCKET)", + [TOMOYO_SYS_LINK] = "sys_link()", + [TOMOYO_SYS_SYMLINK] = "sys_symlink()", + [TOMOYO_SYS_RENAME] = "sys_rename()", + [TOMOYO_SYS_UNLINK] = "sys_unlink()", + [TOMOYO_SYS_CHMOD] = "sys_chmod()", + [TOMOYO_SYS_CHOWN] = "sys_chown()", + [TOMOYO_SYS_IOCTL] = "sys_ioctl()", + [TOMOYO_SYS_KEXEC_LOAD] = "sys_kexec_load()", + [TOMOYO_SYS_PIVOT_ROOT] = "sys_pivot_root()", + [TOMOYO_SYS_PTRACE] = "sys_ptrace()", + }; + if (operation < TOMOYO_MAX_CAPABILITY_INDEX) + return capability_name[operation]; + return NULL; +} + +/** + * audit_capability_log - Audit capability log. + * + * @operation: Type of operation. + * @is_granted: True if this is a granted log. + * @profile: Profile number used. + * @mode: Access control mode used. + * + * Returns 0 on success, negative value otherwise. + */ +static int audit_capability_log(const u8 operation, const bool is_granted, + const u8 profile, const u8 mode) +{ + char *buf; + int len = 64; + int len2; + if (ccs_can_save_audit_log(is_granted) < 0) + return -ENOMEM; + buf = ccs_init_audit_log(&len, profile, mode, NULL); + if (!buf) + return -ENOMEM; + len2 = strlen(buf); + snprintf(buf + len2, len - len2 - 1, KEYWORD_ALLOW_CAPABILITY "%s\n", + ccs_cap2keyword(operation)); + return ccs_write_audit_log(buf, is_granted); +} + +/** + * update_capability_acl - Update "struct capability_acl_record" list. + * + * @operation: Type of operation. + * @domain: Pointer to "struct domain_info". + * @condition: Pointer to "struct condition_list". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static int update_capability_acl(const u8 operation, struct domain_info *domain, + const struct condition_list *condition, + const bool is_delete) +{ + struct acl_info *ptr; + struct capability_acl_record *acl; + int error = -ENOMEM; + if (!domain) + return -EINVAL; + mutex_lock(&domain_acl_lock); + if (is_delete) + goto delete; + list1_for_each_entry(ptr, &domain->acl_info_list, list) { + if (ccs_acl_type1(ptr) != TYPE_CAPABILITY_ACL) + continue; + if (ccs_get_condition_part(ptr) != condition) + continue; + acl = container_of(ptr, struct capability_acl_record, head); + if (acl->operation != operation) + continue; + error = ccs_add_domain_acl(NULL, ptr); + goto out; + } + /* Not found. Append it to the tail. */ + acl = ccs_alloc_acl_element(TYPE_CAPABILITY_ACL, condition); + if (!acl) + goto out; + acl->operation = operation; + error = ccs_add_domain_acl(domain, &acl->head); + goto out; + delete: + error = -ENOENT; + list1_for_each_entry(ptr, &domain->acl_info_list, list) { + if (ccs_acl_type2(ptr) != TYPE_CAPABILITY_ACL) + continue; + if (ccs_get_condition_part(ptr) != condition) + continue; + acl = container_of(ptr, struct capability_acl_record, head); + if (acl->operation != operation) + continue; + error = ccs_del_domain_acl(ptr); + break; + } + out: + mutex_unlock(&domain_acl_lock); + return error; +} + +/** + * ccs_capable - Check permission for capability. + * + * @operation: Type of operation. + * + * Returns true on success, false otherwise. + */ +bool ccs_capable(const u8 operation) +{ + struct domain_info * const domain = current->domain_info; + struct acl_info *ptr; + const u8 profile = current->domain_info->profile; + const u8 mode = ccs_check_capability_flags(operation); + const bool is_enforce = (mode == 3); + bool found = false; + if (!mode) + return true; + list1_for_each_entry(ptr, &domain->acl_info_list, list) { + struct capability_acl_record *acl; + if (ccs_acl_type2(ptr) != TYPE_CAPABILITY_ACL) + continue; + acl = container_of(ptr, struct capability_acl_record, head); + if (acl->operation != operation || + !ccs_check_condition(ptr, NULL)) + continue; + ccs_update_condition(ptr); + found = true; + break; + } + audit_capability_log(operation, found, profile, mode); + if (found) + return true; + if (ccs_verbose_mode()) + printk(KERN_WARNING "TOMOYO-%s: %s denied for %s\n", + ccs_get_msg(is_enforce), cap_operation2name(operation), + ccs_get_last_name(domain)); + if (is_enforce) + return !ccs_check_supervisor("%s\n" + KEYWORD_ALLOW_CAPABILITY "%s\n", + domain->domainname->name, + ccs_cap2keyword(operation)); + if (mode == 1 && ccs_check_domain_quota(domain)) + update_capability_acl(operation, domain, NULL, false); + return true; +} +EXPORT_SYMBOL(ccs_capable); /* for net/unix/af_unix.c */ + +/** + * ccs_write_capability_policy - Write "struct capability_acl_record" list. + * + * @data: String to parse. + * @domain: Pointer to "struct domain_info". + * @condition: Pointer to "struct condition_list". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int ccs_write_capability_policy(char *data, struct domain_info *domain, + const struct condition_list *condition, + const bool is_delete) +{ + u8 capability; + for (capability = 0; capability < TOMOYO_MAX_CAPABILITY_INDEX; + capability++) { + if (strcmp(data, ccs_cap2keyword(capability))) + continue; + return update_capability_acl(capability, domain, condition, + is_delete); + } + return -EINVAL; +} -- -- 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/