From 57c2590fb7fd38bd52708ff2716a577d0c2b3c5a Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:38:44 +0900 Subject: TOMOYO: Update profile structure. This patch allows users to change access control mode for per-operation basis. This feature comes from non LSM version of TOMOYO which is designed for permitting users to use SELinux and TOMOYO at the same time. SELinux does not care filename in a directory whereas TOMOYO does. Change of filename can change how the file is used. For example, renaming index.txt to .htaccess will change how the file is used. Thus, letting SELinux to enforce read()/write()/mmap() etc. restriction and letting TOMOYO to enforce rename() restriction is an example usage of this feature. What is unfortunate for me is that currently LSM does not allow users to use SELinux and LSM version of TOMOYO at the same time... Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 431 ++++++++++++++++++++++++++++------------------- security/tomoyo/common.h | 68 ++++++-- security/tomoyo/domain.c | 9 +- security/tomoyo/file.c | 131 +++++++++----- security/tomoyo/mount.c | 3 +- security/tomoyo/util.c | 92 +++++++--- 6 files changed, 486 insertions(+), 248 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index bdf1ed7..811adb5 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -9,53 +9,74 @@ #include #include #include -#include #include "common.h" +static struct tomoyo_profile tomoyo_default_profile = { + .learning = &tomoyo_default_profile.preference, + .permissive = &tomoyo_default_profile.preference, + .enforcing = &tomoyo_default_profile.preference, + .preference.enforcing_verbose = true, + .preference.learning_max_entry = 2048, + .preference.learning_verbose = false, + .preference.permissive_verbose = true +}; + +/* Profile version. Currently only 20090903 is defined. */ +static unsigned int tomoyo_profile_version; + +/* Profile table. Memory is allocated as needed. */ +static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; + /* String table for functionality that takes 4 modes. */ static const char *tomoyo_mode_4[4] = { "disabled", "learning", "permissive", "enforcing" }; -/* String table for functionality that takes 2 modes. */ -static const char *tomoyo_mode_2[4] = { - "disabled", "enabled", "enabled", "enabled" -}; -/* - * tomoyo_control_array is a static data which contains - * - * (1) functionality name used by /sys/kernel/security/tomoyo/profile . - * (2) initial values for "struct tomoyo_profile". - * (3) max values for "struct tomoyo_profile". - */ -static struct { - const char *keyword; - unsigned int current_value; - const unsigned int max_value; -} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = { - [TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 }, - [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX }, - [TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 }, +/* String table for /sys/kernel/security/tomoyo/profile */ +static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_FILE_EXECUTE] = "file::execute", + [TOMOYO_MAC_FILE_OPEN] = "file::open", + [TOMOYO_MAC_FILE_CREATE] = "file::create", + [TOMOYO_MAC_FILE_UNLINK] = "file::unlink", + [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir", + [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir", + [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo", + [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock", + [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate", + [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink", + [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite", + [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock", + [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar", + [TOMOYO_MAC_FILE_LINK] = "file::link", + [TOMOYO_MAC_FILE_RENAME] = "file::rename", + [TOMOYO_MAC_FILE_CHMOD] = "file::chmod", + [TOMOYO_MAC_FILE_CHOWN] = "file::chown", + [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp", + [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl", + [TOMOYO_MAC_FILE_CHROOT] = "file::chroot", + [TOMOYO_MAC_FILE_MOUNT] = "file::mount", + [TOMOYO_MAC_FILE_UMOUNT] = "file::umount", + [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", }; -/* - * tomoyo_profile is a structure which is used for holding the mode of access - * controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing. - * An administrator can define up to 256 profiles. - * The ->profile of "struct tomoyo_domain_info" is used for remembering - * the profile's number (0 - 255) assigned to that domain. - */ -static struct tomoyo_profile { - unsigned int value[TOMOYO_MAX_CONTROL_INDEX]; - const struct tomoyo_path_info *comment; -} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; - /* Permit policy management by non-root user? */ static bool tomoyo_manage_by_non_root; /* Utility functions. */ /** + * tomoyo_yesno - Return "yes" or "no". + * + * @value: Bool value. + */ +static const char *tomoyo_yesno(const unsigned int value) +{ + return value ? "yes" : "no"; +} + +/** * tomoyo_print_name_union - Print a tomoyo_name_union. * * @head: Pointer to "struct tomoyo_io_buffer". @@ -154,80 +175,62 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) } /** - * tomoyo_check_flags - Check mode for specified functionality. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * @index: The functionality to check mode. - * - * TOMOYO checks only process context. - * This code disables TOMOYO's enforcement in case the function is called from - * interrupt context. - */ -unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, - const u8 index) -{ - const u8 profile = domain->profile; - - if (WARN_ON(in_interrupt())) - return 0; - return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX -#if TOMOYO_MAX_PROFILES != 256 - && profile < TOMOYO_MAX_PROFILES -#endif - && tomoyo_profile_ptr[profile] ? - tomoyo_profile_ptr[profile]->value[index] : 0; -} - -/** - * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * - * Returns true if domain policy violation warning should be printed to - * console. - */ -bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) -{ - return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0; -} - -/** * tomoyo_find_or_assign_new_profile - Create a new profile. * * @profile: Profile number to create. * * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. */ -static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned - int profile) +static struct tomoyo_profile *tomoyo_find_or_assign_new_profile +(const unsigned int profile) { - struct tomoyo_profile *ptr = NULL; - int i; - + struct tomoyo_profile *ptr; + struct tomoyo_profile *entry; if (profile >= TOMOYO_MAX_PROFILES) return NULL; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - return NULL; ptr = tomoyo_profile_ptr[profile]; if (ptr) - goto ok; - ptr = kmalloc(sizeof(*ptr), GFP_NOFS); - if (!tomoyo_memory_ok(ptr)) { - kfree(ptr); - ptr = NULL; - goto ok; + return ptr; + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + ptr = tomoyo_profile_ptr[profile]; + if (!ptr && tomoyo_memory_ok(entry)) { + ptr = entry; + ptr->learning = &tomoyo_default_profile.preference; + ptr->permissive = &tomoyo_default_profile.preference; + ptr->enforcing = &tomoyo_default_profile.preference; + ptr->default_config = TOMOYO_CONFIG_DISABLED; + memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, + sizeof(ptr->config)); + mb(); /* Avoid out-of-order execution. */ + tomoyo_profile_ptr[profile] = ptr; + entry = NULL; } - for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) - ptr->value[i] = tomoyo_control_array[i].current_value; - mb(); /* Avoid out-of-order execution. */ - tomoyo_profile_ptr[profile] = ptr; - ok: mutex_unlock(&tomoyo_policy_lock); + out: + kfree(entry); return ptr; } /** - * tomoyo_write_profile - Write to profile table. + * tomoyo_profile - Find a profile. + * + * @profile: Profile number to find. + * + * Returns pointer to "struct tomoyo_profile". + */ +struct tomoyo_profile *tomoyo_profile(const u8 profile) +{ + struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; + if (!tomoyo_policy_loaded) + return &tomoyo_default_profile; + BUG_ON(!ptr); + return ptr; +} + +/** + * tomoyo_write_profile - Write profile table. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -237,64 +240,116 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) { char *data = head->write_buf; unsigned int i; - unsigned int value; + int value; + int mode; + u8 config; + bool use_default = false; char *cp; struct tomoyo_profile *profile; - unsigned long num; - - cp = strchr(data, '-'); - if (cp) - *cp = '\0'; - if (strict_strtoul(data, 10, &num)) - return -EINVAL; - if (cp) + if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) + return 0; + i = simple_strtoul(data, &cp, 10); + if (data == cp) { + profile = &tomoyo_default_profile; + } else { + if (*cp != '-') + return -EINVAL; data = cp + 1; - profile = tomoyo_find_or_assign_new_profile(num); - if (!profile) - return -EINVAL; + profile = tomoyo_find_or_assign_new_profile(i); + if (!profile) + return -EINVAL; + } cp = strchr(data, '='); if (!cp) return -EINVAL; - *cp = '\0'; + *cp++ = '\0'; + if (profile != &tomoyo_default_profile) + use_default = strstr(cp, "use_default") != NULL; + if (strstr(cp, "verbose=yes")) + value = 1; + else if (strstr(cp, "verbose=no")) + value = 0; + else + value = -1; + if (!strcmp(data, "PREFERENCE::enforcing")) { + if (use_default) { + profile->enforcing = &tomoyo_default_profile.preference; + return 0; + } + profile->enforcing = &profile->preference; + if (value >= 0) + profile->preference.enforcing_verbose = value; + return 0; + } + if (!strcmp(data, "PREFERENCE::permissive")) { + if (use_default) { + profile->permissive = &tomoyo_default_profile.preference; + return 0; + } + profile->permissive = &profile->preference; + if (value >= 0) + profile->preference.permissive_verbose = value; + return 0; + } + if (!strcmp(data, "PREFERENCE::learning")) { + char *cp2; + if (use_default) { + profile->learning = &tomoyo_default_profile.preference; + return 0; + } + profile->learning = &profile->preference; + if (value >= 0) + profile->preference.learning_verbose = value; + cp2 = strstr(cp, "max_entry="); + if (cp2) + sscanf(cp2 + 10, "%u", + &profile->preference.learning_max_entry); + return 0; + } + if (profile == &tomoyo_default_profile) + return -EINVAL; if (!strcmp(data, "COMMENT")) { const struct tomoyo_path_info *old_comment = profile->comment; - profile->comment = tomoyo_get_name(cp + 1); + profile->comment = tomoyo_get_name(cp); tomoyo_put_name(old_comment); return 0; } - for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) { - if (strcmp(data, tomoyo_control_array[i].keyword)) - continue; - if (sscanf(cp + 1, "%u", &value) != 1) { - int j; - const char **modes; - switch (i) { - case TOMOYO_VERBOSE: - modes = tomoyo_mode_2; - break; - default: - modes = tomoyo_mode_4; - break; - } - for (j = 0; j < 4; j++) { - if (strcmp(cp + 1, modes[j])) - continue; - value = j; - break; - } - if (j == 4) - return -EINVAL; - } else if (value > tomoyo_control_array[i].max_value) { - value = tomoyo_control_array[i].max_value; + if (!strcmp(data, "CONFIG")) { + i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; + config = profile->default_config; + } else if (tomoyo_str_starts(&data, "CONFIG::")) { + config = 0; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + if (strcmp(data, tomoyo_mac_keywords[i])) + continue; + config = profile->config[i]; + break; } - profile->value[i] = value; - return 0; + if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + return -EINVAL; + } else { + return -EINVAL; } - return -EINVAL; + if (use_default) { + config = TOMOYO_CONFIG_USE_DEFAULT; + } else { + for (mode = 3; mode >= 0; mode--) + if (strstr(cp, tomoyo_mode_4[mode])) + /* + * Update lower 3 bits in order to distinguish + * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. + */ + config = (config & ~7) | mode; + } + if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + profile->config[i] = config; + else if (config != TOMOYO_CONFIG_USE_DEFAULT) + profile->default_config = config; + return 0; } /** - * tomoyo_read_profile - Read from profile table. + * tomoyo_read_profile - Read profile table. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -302,53 +357,82 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) */ static int tomoyo_read_profile(struct tomoyo_io_buffer *head) { - static const int total = TOMOYO_MAX_CONTROL_INDEX + 1; - int step; - + int index; if (head->read_eof) return 0; - for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total; - step++) { - const u8 index = step / total; - u8 type = step % total; + if (head->read_bit) + goto body; + tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); + tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s " + "max_entry=%u }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + learning_verbose), + tomoyo_default_profile.preference.learning_max_entry); + tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + permissive_verbose)); + tomoyo_io_printf(head, "PREFERENCE::enforcing={ verbose=%s }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + enforcing_verbose)); + head->read_bit = 1; + body: + for (index = head->read_step; index < TOMOYO_MAX_PROFILES; index++) { + bool done; + u8 config; + int i; + int pos; const struct tomoyo_profile *profile = tomoyo_profile_ptr[index]; - head->read_step = step; + const struct tomoyo_path_info *comment; + head->read_step = index; if (!profile) continue; - if (!type) { /* Print profile' comment tag. */ - if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n", - index, profile->comment ? - profile->comment->name : "")) - break; - continue; - } - type--; - if (type < TOMOYO_MAX_CONTROL_INDEX) { - const unsigned int value = profile->value[type]; - const char **modes = NULL; - const char *keyword - = tomoyo_control_array[type].keyword; - switch (tomoyo_control_array[type].max_value) { - case 3: - modes = tomoyo_mode_4; - break; - case 1: - modes = tomoyo_mode_2; - break; - } - if (modes) { - if (!tomoyo_io_printf(head, "%u-%s=%s\n", index, - keyword, modes[value])) - break; - } else { - if (!tomoyo_io_printf(head, "%u-%s=%u\n", index, - keyword, value)) - break; - } + pos = head->read_avail; + comment = profile->comment; + done = tomoyo_io_printf(head, "%u-COMMENT=%s\n", index, + comment ? comment->name : ""); + if (!done) + goto out; + config = profile->default_config; + if (!tomoyo_io_printf(head, "%u-CONFIG={ mode=%s }\n", index, + tomoyo_mode_4[config & 3])) + goto out; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + config = profile->config[i]; + if (config == TOMOYO_CONFIG_USE_DEFAULT) + continue; + if (!tomoyo_io_printf(head, + "%u-CONFIG::%s={ mode=%s }\n", + index, tomoyo_mac_keywords[i], + tomoyo_mode_4[config & 3])) + goto out; } + if (profile->learning != &tomoyo_default_profile.preference && + !tomoyo_io_printf(head, "%u-PREFERENCE::learning={ " + "verbose=%s max_entry=%u }\n", index, + tomoyo_yesno(profile->preference. + learning_verbose), + profile->preference.learning_max_entry)) + goto out; + if (profile->permissive != &tomoyo_default_profile.preference + && !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ " + "verbose=%s }\n", index, + tomoyo_yesno(profile->preference. + permissive_verbose))) + goto out; + if (profile->enforcing != &tomoyo_default_profile.preference && + !tomoyo_io_printf(head, "%u-PREFERENCE::enforcing={ " + "verbose=%s }\n", index, + tomoyo_yesno(profile->preference. + enforcing_verbose))) + goto out; + continue; + out: + head->read_avail = pos; + break; } - if (step == TOMOYO_MAX_PROFILES * total) + if (index == TOMOYO_MAX_PROFILES) head->read_eof = true; return 0; } @@ -1595,7 +1679,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) static int tomoyo_read_version(struct tomoyo_io_buffer *head) { if (!head->read_eof) { - tomoyo_io_printf(head, "2.2.0"); + tomoyo_io_printf(head, "2.3.0-pre"); head->read_eof = true; } return 0; @@ -1915,6 +1999,9 @@ void tomoyo_check_profile(void) profile, domain->domainname->name); } tomoyo_read_unlock(idx); - printk(KERN_INFO "TOMOYO: 2.2.0 2009/04/01\n"); + if (tomoyo_profile_version != 20090903) + panic("Profile version %u is not supported.\n", + tomoyo_profile_version); + printk(KERN_INFO "TOMOYO: 2.3.0-pre 2010/06/03\n"); printk(KERN_INFO "Mandatory Access Control activated.\n"); } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 54db39a..c777c59 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -42,7 +42,8 @@ enum tomoyo_mode_index { TOMOYO_CONFIG_DISABLED, TOMOYO_CONFIG_LEARNING, TOMOYO_CONFIG_PERMISSIVE, - TOMOYO_CONFIG_ENFORCING + TOMOYO_CONFIG_ENFORCING, + TOMOYO_CONFIG_USE_DEFAULT = 255 }; /* Keywords for ACLs. */ @@ -75,14 +76,6 @@ enum tomoyo_mode_index { #define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 /* Index numbers for Access Controls. */ -enum tomoyo_mac_index { - TOMOYO_MAC_FOR_FILE, /* domain_policy.conf */ - TOMOYO_MAX_ACCEPT_ENTRY, - TOMOYO_VERBOSE, - TOMOYO_MAX_CONTROL_INDEX -}; - -/* Index numbers for Access Controls. */ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_ACL, TOMOYO_TYPE_PATH2_ACL, @@ -157,6 +150,38 @@ enum tomoyo_securityfs_interface_index { TOMOYO_MANAGER }; +enum tomoyo_mac_index { + TOMOYO_MAC_FILE_EXECUTE, + TOMOYO_MAC_FILE_OPEN, + TOMOYO_MAC_FILE_CREATE, + TOMOYO_MAC_FILE_UNLINK, + TOMOYO_MAC_FILE_MKDIR, + TOMOYO_MAC_FILE_RMDIR, + TOMOYO_MAC_FILE_MKFIFO, + TOMOYO_MAC_FILE_MKSOCK, + TOMOYO_MAC_FILE_TRUNCATE, + TOMOYO_MAC_FILE_SYMLINK, + TOMOYO_MAC_FILE_REWRITE, + TOMOYO_MAC_FILE_MKBLOCK, + TOMOYO_MAC_FILE_MKCHAR, + TOMOYO_MAC_FILE_LINK, + TOMOYO_MAC_FILE_RENAME, + TOMOYO_MAC_FILE_CHMOD, + TOMOYO_MAC_FILE_CHOWN, + TOMOYO_MAC_FILE_CHGRP, + TOMOYO_MAC_FILE_IOCTL, + TOMOYO_MAC_FILE_CHROOT, + TOMOYO_MAC_FILE_MOUNT, + TOMOYO_MAC_FILE_UMOUNT, + TOMOYO_MAC_FILE_PIVOT_ROOT, + TOMOYO_MAX_MAC_INDEX +}; + +enum tomoyo_mac_category_index { + TOMOYO_MAC_CATEGORY_FILE, + TOMOYO_MAX_MAC_CATEGORY_INDEX +}; + #define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ /********** Structure definitions. **********/ @@ -174,6 +199,7 @@ struct tomoyo_request_info { u8 retry; u8 profile; u8 mode; /* One of tomoyo_mode_index . */ + u8 type; }; /* @@ -649,6 +675,23 @@ struct tomoyo_policy_manager_entry { bool is_deleted; /* True if this entry is deleted. */ }; +struct tomoyo_preference { + unsigned int learning_max_entry; + bool enforcing_verbose; + bool learning_verbose; + bool permissive_verbose; +}; + +struct tomoyo_profile { + const struct tomoyo_path_info *comment; + struct tomoyo_preference *learning; + struct tomoyo_preference *permissive; + struct tomoyo_preference *enforcing; + struct tomoyo_preference preference; + u8 default_config; + u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX]; +}; + /********** Function prototypes. **********/ extern asmlinkage long sys_getpid(void); @@ -685,6 +728,7 @@ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, /* Check whether the given number matches the given number_union. */ bool tomoyo_compare_number_union(const unsigned long value, const struct tomoyo_number_union *ptr); +int tomoyo_get_mode(const u8 profile, const u8 index); /* Transactional sprintf() for policy dump. */ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); @@ -747,7 +791,8 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); const char *tomoyo_path2keyword(const u8 operation); /* Fill "struct tomoyo_request_info". */ int tomoyo_init_request_info(struct tomoyo_request_info *r, - struct tomoyo_domain_info *domain); + struct tomoyo_domain_info *domain, + const u8 index); /* Check permission for mount operation. */ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, unsigned long flags, void *data_page); @@ -794,6 +839,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * domainname, const u8 profile); +struct tomoyo_profile *tomoyo_profile(const u8 profile); /* Allocate memory for "struct tomoyo_path_group". */ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name); @@ -844,7 +890,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); /* Initialize mm related code. */ void __init tomoyo_mm_init(void); -int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, +int tomoyo_check_exec_perm(struct tomoyo_request_info *r, const struct tomoyo_path_info *filename); int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index a07ca6d..09ec37c 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -812,8 +812,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) struct tomoyo_domain_info *domain = NULL; const char *old_domain_name = old_domain->domainname->name; const char *original_name = bprm->filename; - const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); - const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); + u8 mode; + bool is_enforce; int retval = -ENOMEM; bool need_kfree = false; struct tomoyo_path_info rn = { }; /* real name */ @@ -822,7 +822,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ln.name = tomoyo_get_last_name(old_domain); tomoyo_fill_path_info(&ln); - tomoyo_init_request_info(&r, NULL); + mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); + is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); if (!tmp) goto out; @@ -880,7 +881,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } /* Check execute permission. */ - retval = tomoyo_check_exec_perm(old_domain, &rn); + retval = tomoyo_check_exec_perm(&r, &rn); if (retval == TOMOYO_RETRY_REQUEST) goto retry; if (retval < 0) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 83fa17a..8e51348 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -51,6 +51,42 @@ static const char *tomoyo_path_number_keyword [TOMOYO_TYPE_CHGRP] = "chgrp", }; +static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = { + [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE, + [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK, + [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR, + [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE, + [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK, + [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE, + [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT, + [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT, +}; + +static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { + [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK, + [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR, +}; + +static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = { + [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK, + [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME, + [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT, +}; + +static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { + [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE, + [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR, + [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO, + [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK, + [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL, + [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD, + [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN, + [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP, +}; + void tomoyo_put_name_union(struct tomoyo_name_union *ptr) { if (!ptr) @@ -1057,6 +1093,10 @@ static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, int error; next: + r->type = tomoyo_p2mac[operation]; + r->mode = tomoyo_get_mode(r->profile, r->type); + if (r->mode == TOMOYO_CONFIG_DISABLED) + return 0; do { error = tomoyo_path_acl(r, filename, 1 << operation); if (!error) @@ -1249,8 +1289,8 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, struct tomoyo_path_info buf; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path->mnt || !path->dentry) + if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) + == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry) return 0; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) @@ -1269,21 +1309,19 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, /** * tomoyo_check_exec_perm - Check permission for "execute". * - * @domain: Pointer to "struct tomoyo_domain_info". + * @r: Pointer to "struct tomoyo_request_info". * @filename: Check permission for "execute". * * Returns 0 on success, negativevalue otherwise. * * Caller holds tomoyo_read_lock(). */ -int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, +int tomoyo_check_exec_perm(struct tomoyo_request_info *r, const struct tomoyo_path_info *filename) { - struct tomoyo_request_info r; - - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) + if (r->mode == TOMOYO_CONFIG_DISABLED) return 0; - return tomoyo_file_perm(&r, filename, 1); + return tomoyo_file_perm(r, filename, 1); } /** @@ -1304,17 +1342,11 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct tomoyo_request_info r; int idx; - if (tomoyo_init_request_info(&r, domain) == TOMOYO_CONFIG_DISABLED || - !path->mnt) - return 0; - if (acc_mode == 0) - return 0; - if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)) - /* - * I don't check directories here because mkdir() and rmdir() - * don't call me. - */ + if (!path->mnt || + (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))) return 0; + buf.name = NULL; + r.mode = TOMOYO_CONFIG_DISABLED; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) goto out; @@ -1324,15 +1356,26 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, * we need to check "allow_rewrite" permission when the filename is not * opened for append mode or the filename is truncated at open time. */ - if ((acc_mode & MAY_WRITE) && - ((flag & O_TRUNC) || !(flag & O_APPEND)) && - (tomoyo_is_no_rewrite_file(&buf))) { - error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf); + if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND) + && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE) + != TOMOYO_CONFIG_DISABLED) { + if (!tomoyo_get_realpath(&buf, path)) { + error = -ENOMEM; + goto out; + } + if (tomoyo_is_no_rewrite_file(&buf)) + error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, + &buf); } - if (!error) + if (!error && acc_mode && + tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) + != TOMOYO_CONFIG_DISABLED) { + if (!buf.name && !tomoyo_get_realpath(&buf, path)) { + error = -ENOMEM; + goto out; + } error = tomoyo_file_perm(&r, &buf, acc_mode); - if (!error && (flag & O_TRUNC)) - error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, &buf); + } out: kfree(buf.name); tomoyo_read_unlock(idx); @@ -1356,9 +1399,12 @@ int tomoyo_path_perm(const u8 operation, struct path *path) struct tomoyo_request_info r; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path->mnt) + if (!path->mnt) + return 0; + if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation]) + == TOMOYO_CONFIG_DISABLED) return 0; + buf.name = NULL; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) goto out; @@ -1371,6 +1417,7 @@ int tomoyo_path_perm(const u8 operation, struct path *path) break; case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: + case TOMOYO_TYPE_UMOUNT: tomoyo_add_slash(&buf); break; } @@ -1442,8 +1489,9 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path, struct tomoyo_path_info buf; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path->mnt) + if (!path->mnt || + tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation]) + == TOMOYO_CONFIG_DISABLED) return 0; idx = tomoyo_read_lock(); error = -ENOMEM; @@ -1477,8 +1525,9 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, struct tomoyo_request_info r; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path1->mnt || !path2->mnt) + if (!path1->mnt || !path2->mnt || + tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation]) + == TOMOYO_CONFIG_DISABLED) return 0; buf1.name = NULL; buf2.name = NULL; @@ -1486,13 +1535,19 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, if (!tomoyo_get_realpath(&buf1, path1) || !tomoyo_get_realpath(&buf2, path2)) goto out; - { - struct dentry *dentry = path1->dentry; - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { - tomoyo_add_slash(&buf1); - tomoyo_add_slash(&buf2); - } - } + switch (operation) { + struct dentry *dentry; + case TOMOYO_TYPE_RENAME: + case TOMOYO_TYPE_LINK: + dentry = path1->dentry; + if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode)) + break; + /* fall through */ + case TOMOYO_TYPE_PIVOT_ROOT: + tomoyo_add_slash(&buf1); + tomoyo_add_slash(&buf2); + break; + } do { error = tomoyo_path2_acl(&r, operation, &buf1, &buf2); if (!error) diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 7c1c7fd..77ee8bf 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -248,7 +248,8 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, int error; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) + if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) + == TOMOYO_CONFIG_DISABLED) return 0; if (!type) type = ""; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 592b76a..307793e 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -792,25 +792,67 @@ const char *tomoyo_get_exe(void) } /** + * tomoyo_get_mode - Get MAC mode. + * + * @profile: Profile number. + * @index: Index number of functionality. + * + * Returns mode. + */ +int tomoyo_get_mode(const u8 profile, const u8 index) +{ + u8 mode; + const u8 category = TOMOYO_MAC_CATEGORY_FILE; + if (!tomoyo_policy_loaded) + return TOMOYO_CONFIG_DISABLED; + mode = tomoyo_profile(profile)->config[index]; + if (mode == TOMOYO_CONFIG_USE_DEFAULT) + mode = tomoyo_profile(profile)->config[category]; + if (mode == TOMOYO_CONFIG_USE_DEFAULT) + mode = tomoyo_profile(profile)->default_config; + return mode & 3; +} + +/** * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. * * @r: Pointer to "struct tomoyo_request_info" to initialize. * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). + * @index: Index number of functionality. * * Returns mode. */ int tomoyo_init_request_info(struct tomoyo_request_info *r, - struct tomoyo_domain_info *domain) + struct tomoyo_domain_info *domain, const u8 index) { + u8 profile; memset(r, 0, sizeof(*r)); if (!domain) domain = tomoyo_domain(); r->domain = domain; - r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); + profile = domain->profile; + r->profile = profile; + r->type = index; + r->mode = tomoyo_get_mode(profile, index); return r->mode; } /** + * tomoyo_last_word - Get last component of a line. + * + * @line: A line. + * + * Returns the last word of a line. + */ +static const char *tomoyo_last_word(const char *name) +{ + const char *cp = strrchr(name, ' '); + if (cp) + return cp + 1; + return name; +} + +/** * tomoyo_warn_log - Print warning or error message on console. * * @r: Pointer to "struct tomoyo_request_info". @@ -818,29 +860,34 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, */ void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) { - int len = PAGE_SIZE; va_list args; char *buffer; - if (!tomoyo_verbose_mode(r->domain)) - return; - while (1) { - int len2; - buffer = kmalloc(len, GFP_NOFS); - if (!buffer) + const struct tomoyo_domain_info * const domain = r->domain; + const struct tomoyo_profile *profile = tomoyo_profile(domain->profile); + switch (r->mode) { + case TOMOYO_CONFIG_ENFORCING: + if (!profile->enforcing->enforcing_verbose) return; - va_start(args, fmt); - len2 = vsnprintf(buffer, len - 1, fmt, args); - va_end(args); - if (len2 <= len - 1) { - buffer[len2] = '\0'; - break; - } - len = len2 + 1; - kfree(buffer); + break; + case TOMOYO_CONFIG_PERMISSIVE: + if (!profile->permissive->permissive_verbose) + return; + break; + case TOMOYO_CONFIG_LEARNING: + if (!profile->learning->learning_verbose) + return; + break; } - printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n", - r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", - buffer, tomoyo_get_last_name(r->domain)); + buffer = kmalloc(4096, GFP_NOFS); + if (!buffer) + return; + va_start(args, fmt); + vsnprintf(buffer, 4095, fmt, args); + va_end(args); + buffer[4095] = '\0'; + printk(KERN_WARNING "%s: Access %s denied for %s\n", + r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer, + tomoyo_last_word(domain->domainname->name)); kfree(buffer); } @@ -903,7 +950,8 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) count++; } } - if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) + if (count < tomoyo_profile(domain->profile)->learning-> + learning_max_entry) return true; if (!domain->quota_warned) { domain->quota_warned = true; -- cgit v1.1