diff options
Diffstat (limited to 'security/tomoyo')
-rw-r--r-- | security/tomoyo/Makefile | 2 | ||||
-rw-r--r-- | security/tomoyo/common.c | 157 | ||||
-rw-r--r-- | security/tomoyo/common.h | 121 | ||||
-rw-r--r-- | security/tomoyo/domain.c | 143 | ||||
-rw-r--r-- | security/tomoyo/file.c | 250 | ||||
-rw-r--r-- | security/tomoyo/gc.c | 55 | ||||
-rw-r--r-- | security/tomoyo/path_group.c | 172 | ||||
-rw-r--r-- | security/tomoyo/realpath.c | 30 |
8 files changed, 658 insertions, 272 deletions
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 60a9e2002da..4fb39030f6b 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o +obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 975c45d88ba..b5dbdc9ff73 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -76,6 +76,49 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, const int buffer_len); /** + * tomoyo_parse_name_union - Parse a tomoyo_name_union. + * + * @filename: Name or name group. + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_name_union(const char *filename, + struct tomoyo_name_union *ptr) +{ + if (!tomoyo_is_correct_path(filename, 0, 0, 0)) + return false; + if (filename[0] == '@') { + ptr->group = tomoyo_get_path_group(filename + 1); + ptr->is_group = true; + return ptr->group != NULL; + } + ptr->filename = tomoyo_get_name(filename); + ptr->is_group = false; + return ptr->filename != NULL; +} + +/** + * tomoyo_print_name_union - Print a tomoyo_name_union. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head, + const struct tomoyo_name_union *ptr) +{ + int pos = head->read_avail; + if (pos && head->read_buf[pos - 1] == ' ') + head->read_avail--; + if (ptr->is_group) + return tomoyo_io_printf(head, " @%s", + ptr->group->group_name->name); + return tomoyo_io_printf(head, " %s", ptr->filename->name); +} + +/** * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value. * * @str: Pointer to the string. @@ -172,6 +215,33 @@ static void tomoyo_normalize_line(unsigned char *buffer) } /** + * tomoyo_tokenize - Tokenize string. + * + * @buffer: The line to tokenize. + * @w: Pointer to "char *". + * @size: Sizeof @w . + * + * Returns true on success, false otherwise. + */ +bool tomoyo_tokenize(char *buffer, char *w[], size_t size) +{ + int count = size / sizeof(char *); + int i; + for (i = 0; i < count; i++) + w[i] = ""; + for (i = 0; i < count; i++) { + char *cp = strchr(buffer, ' '); + if (cp) + *cp = '\0'; + w[i] = buffer; + if (!cp) + break; + buffer = cp + 1; + } + return i < count || !*buffer; +} + +/** * tomoyo_is_correct_path - Validate a pathname. * @filename: The pathname to check. * @start_type: Should the pathname start with '/'? @@ -874,17 +944,17 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned int profile) { - static DEFINE_MUTEX(lock); struct tomoyo_profile *ptr = NULL; int i; if (profile >= TOMOYO_MAX_PROFILES) return NULL; - mutex_lock(&lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return NULL; ptr = tomoyo_profile_ptr[profile]; if (ptr) goto ok; - ptr = kmalloc(sizeof(*ptr), GFP_KERNEL); + ptr = kmalloc(sizeof(*ptr), GFP_NOFS); if (!tomoyo_memory_ok(ptr)) { kfree(ptr); ptr = NULL; @@ -895,7 +965,7 @@ static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned mb(); /* Avoid out-of-order execution. */ tomoyo_profile_ptr[profile] = ptr; ok: - mutex_unlock(&lock); + mutex_unlock(&tomoyo_policy_lock); return ptr; } @@ -1071,44 +1141,42 @@ LIST_HEAD(tomoyo_policy_manager_list); static int tomoyo_update_manager_entry(const char *manager, const bool is_delete) { - struct tomoyo_policy_manager_entry *entry = NULL; struct tomoyo_policy_manager_entry *ptr; - const struct tomoyo_path_info *saved_manager; + struct tomoyo_policy_manager_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - bool is_domain = false; if (tomoyo_is_domain_def(manager)) { if (!tomoyo_is_correct_domain(manager)) return -EINVAL; - is_domain = true; + e.is_domain = true; } else { if (!tomoyo_is_correct_path(manager, 1, -1, -1)) return -EINVAL; } - saved_manager = tomoyo_get_name(manager); - if (!saved_manager) + e.manager = tomoyo_get_name(manager); + if (!e.manager) return -ENOMEM; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { - if (ptr->manager != saved_manager) + if (ptr->manager != e.manager) continue; ptr->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->manager = saved_manager; - saved_manager = NULL; - entry->is_domain = is_domain; - list_add_tail_rcu(&entry->list, &tomoyo_policy_manager_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_policy_manager_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, + &tomoyo_policy_manager_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); - tomoyo_put_name(saved_manager); - kfree(entry); + out: + tomoyo_put_name(e.manager); return error; } @@ -1287,7 +1355,8 @@ static int tomoyo_delete_domain(char *domainname) name.name = domainname; tomoyo_fill_path_info(&name); - mutex_lock(&tomoyo_policy_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return 0; /* Is there an active domain? */ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { /* Never delete tomoyo_kernel_domain */ @@ -1369,23 +1438,20 @@ static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, { int pos; u8 bit; - const char *atmark = ""; - const char *filename; const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16); - filename = ptr->filename->name; for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { - const char *msg; if (!(perm & (1 << bit))) continue; /* Print "read/write" instead of "read" and "write". */ if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE) && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) continue; - msg = tomoyo_path2keyword(bit); pos = head->read_avail; - if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg, - atmark, filename)) + if (!tomoyo_io_printf(head, "allow_%s ", + tomoyo_path2keyword(bit)) || + !tomoyo_print_name_union(head, &ptr->name) || + !tomoyo_io_printf(head, "\n")) goto out; } head->read_bit = 0; @@ -1408,23 +1474,18 @@ static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, struct tomoyo_path2_acl *ptr) { int pos; - const char *atmark1 = ""; - const char *atmark2 = ""; - const char *filename1; - const char *filename2; const u8 perm = ptr->perm; u8 bit; - filename1 = ptr->filename1->name; - filename2 = ptr->filename2->name; for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { - const char *msg; if (!(perm & (1 << bit))) continue; - msg = tomoyo_path22keyword(bit); pos = head->read_avail; - if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg, - atmark1, filename1, atmark2, filename2)) + if (!tomoyo_io_printf(head, "allow_%s ", + tomoyo_path22keyword(bit)) || + !tomoyo_print_name_union(head, &ptr->name1) || + !tomoyo_print_name_union(head, &ptr->name2) || + !tomoyo_io_printf(head, "\n")) goto out; } head->read_bit = 0; @@ -1687,6 +1748,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) return tomoyo_write_pattern_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE)) return tomoyo_write_no_rewrite_policy(data, is_delete); + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP)) + return tomoyo_write_path_group_policy(data, is_delete); return -EINVAL; } @@ -1743,6 +1806,12 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) head->read_var2 = NULL; head->read_step = 9; case 9: + if (!tomoyo_read_path_group_policy(head)) + break; + head->read_var1 = NULL; + head->read_var2 = NULL; + head->read_step = 10; + case 10: head->read_eof = true; break; default: @@ -1886,7 +1955,7 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) */ static int tomoyo_open_control(const u8 type, struct file *file) { - struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_KERNEL); + struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); if (!head) return -ENOMEM; @@ -1947,7 +2016,7 @@ static int tomoyo_open_control(const u8 type, struct file *file) } else { if (!head->readbuf_size) head->readbuf_size = 4096 * 2; - head->read_buf = kzalloc(head->readbuf_size, GFP_KERNEL); + head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); if (!head->read_buf) { kfree(head); return -ENOMEM; @@ -1961,7 +2030,7 @@ static int tomoyo_open_control(const u8 type, struct file *file) head->write = NULL; } else if (head->write) { head->writebuf_size = 4096 * 2; - head->write_buf = kzalloc(head->writebuf_size, GFP_KERNEL); + head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS); if (!head->write_buf) { kfree(head->read_buf); kfree(head); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 67bd22dd3e6..9f1ae5e3ba5 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -54,6 +54,7 @@ struct linux_binprm; #define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain " #define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " #define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " +#define TOMOYO_KEYWORD_PATH_GROUP "path_group " #define TOMOYO_KEYWORD_SELECT "select " #define TOMOYO_KEYWORD_USE_PROFILE "use_profile " #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" @@ -204,6 +205,27 @@ struct tomoyo_path_info_with_data { char barrier2[16]; /* Safeguard for overrun. */ }; +struct tomoyo_name_union { + const struct tomoyo_path_info *filename; + struct tomoyo_path_group *group; + u8 is_group; +}; + +/* Structure for "path_group" directive. */ +struct tomoyo_path_group { + struct list_head list; + const struct tomoyo_path_info *group_name; + struct list_head member_list; + atomic_t users; +}; + +/* Structure for "path_group" directive. */ +struct tomoyo_path_group_member { + struct list_head list; + bool is_deleted; + const struct tomoyo_path_info *member_name; +}; + /* * tomoyo_acl_info is a structure which is used for holding * @@ -274,7 +296,7 @@ struct tomoyo_domain_info { * * (1) "head" which is a "struct tomoyo_acl_info". * (2) "perm" which is a bitmask of permitted operations. - * (3) "filename" is the pathname. + * (3) "name" is the pathname. * * Directives held by this structure are "allow_read/write", "allow_execute", * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir", @@ -287,8 +309,7 @@ struct tomoyo_path_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ u8 perm_high; u16 perm; - /* Pointer to single pathname. */ - const struct tomoyo_path_info *filename; + struct tomoyo_name_union name; }; /* @@ -298,8 +319,8 @@ struct tomoyo_path_acl { * * (1) "head" which is a "struct tomoyo_acl_info". * (2) "perm" which is a bitmask of permitted operations. - * (3) "filename1" is the source/old pathname. - * (4) "filename2" is the destination/new pathname. + * (3) "name1" is the source/old pathname. + * (4) "name2" is the destination/new pathname. * * Directives held by this structure are "allow_rename", "allow_link" and * "allow_pivot_root". @@ -307,10 +328,8 @@ struct tomoyo_path_acl { struct tomoyo_path2_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */ u8 perm; - /* Pointer to single pathname. */ - const struct tomoyo_path_info *filename1; - /* Pointer to single pathname. */ - const struct tomoyo_path_info *filename2; + struct tomoyo_name_union name1; + struct tomoyo_name_union name2; }; /* @@ -514,6 +533,9 @@ struct tomoyo_policy_manager_entry { /********** Function prototypes. **********/ +/* Check whether the given name matches the given name_union. */ +bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, + const struct tomoyo_name_union *ptr); /* Check whether the domain has too many ACL entries to hold. */ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain); /* Transactional sprintf() for policy dump. */ @@ -526,6 +548,12 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, const s8 pattern_type, const s8 end_type); /* Check whether the token can be a domainname. */ bool tomoyo_is_domain_def(const unsigned char *buffer); +bool tomoyo_parse_name_union(const char *filename, + struct tomoyo_name_union *ptr); +/* Check whether the given filename matches the given path_group. */ +bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, + const struct tomoyo_path_group *group, + const bool may_use_pattern); /* Check whether the given filename matches the given pattern. */ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, const struct tomoyo_path_info *pattern); @@ -540,10 +568,14 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head); bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head); /* Read "file_pattern" entry in exception policy. */ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head); +/* Read "path_group" entry in exception policy. */ +bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head); /* Read "allow_read" entry in exception policy. */ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head); /* Read "deny_rewrite" entry in exception policy. */ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head); +/* Tokenize a line. */ +bool tomoyo_tokenize(char *buffer, char *w[], size_t size); /* Write domain policy violation warning message to console? */ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); /* Convert double path operation to operation name. */ @@ -580,12 +612,18 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete); int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); /* Create "file_pattern" entry in exception policy. */ int tomoyo_write_pattern_policy(char *data, const bool is_delete); +/* Create "path_group" entry in exception policy. */ +int tomoyo_write_path_group_policy(char *data, const bool is_delete); /* Find a domain by the given name. */ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); /* Find or create a domain by the given name. */ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * domainname, const u8 profile); + +/* Allocate memory for "struct tomoyo_path_group". */ +struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); + /* Check mode for specified functionality. */ unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, const u8 index); @@ -616,6 +654,7 @@ char *tomoyo_realpath_from_path(struct path *path); /* Check memory quota. */ bool tomoyo_memory_ok(void *ptr); +void *tomoyo_commit_ok(void *data, const unsigned int size); /* * Keep the given name on the RAM. @@ -641,6 +680,9 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, int tomoyo_check_rewrite_permission(struct file *filp); int tomoyo_find_next_domain(struct linux_binprm *bprm); +/* Drop refcount on tomoyo_name_union. */ +void tomoyo_put_name_union(struct tomoyo_name_union *ptr); + /* Run garbage collector. */ void tomoyo_run_gc(void); @@ -654,6 +696,7 @@ extern struct srcu_struct tomoyo_ss; /* The list for "struct tomoyo_domain_info". */ extern struct list_head tomoyo_domain_list; +extern struct list_head tomoyo_path_group_list; extern struct list_head tomoyo_domain_initializer_list; extern struct list_head tomoyo_domain_keeper_list; extern struct list_head tomoyo_alias_list; @@ -662,7 +705,6 @@ extern struct list_head tomoyo_pattern_list; extern struct list_head tomoyo_no_rewrite_list; extern struct list_head tomoyo_policy_manager_list; extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; -extern struct mutex tomoyo_name_list_lock; /* Lock for protecting policy. */ extern struct mutex tomoyo_policy_lock; @@ -725,6 +767,12 @@ static inline void tomoyo_put_name(const struct tomoyo_path_info *name) } } +static inline void tomoyo_put_path_group(struct tomoyo_path_group *group) +{ + if (group) + atomic_dec(&group->users); +} + static inline struct tomoyo_domain_info *tomoyo_domain(void) { return current_cred()->security; @@ -736,6 +784,59 @@ static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct return task_cred_xxx(task, security); } +static inline bool tomoyo_is_same_acl_head(const struct tomoyo_acl_info *p1, + const struct tomoyo_acl_info *p2) +{ + return p1->type == p2->type; +} + +static inline bool tomoyo_is_same_name_union +(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) +{ + return p1->filename == p2->filename && p1->group == p2->group && + p1->is_group == p2->is_group; +} + +static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1, + const struct tomoyo_path_acl *p2) +{ + return tomoyo_is_same_acl_head(&p1->head, &p2->head) && + tomoyo_is_same_name_union(&p1->name, &p2->name); +} + +static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, + const struct tomoyo_path2_acl *p2) +{ + return tomoyo_is_same_acl_head(&p1->head, &p2->head) && + tomoyo_is_same_name_union(&p1->name1, &p2->name1) && + tomoyo_is_same_name_union(&p1->name2, &p2->name2); +} + +static inline bool tomoyo_is_same_domain_initializer_entry +(const struct tomoyo_domain_initializer_entry *p1, + const struct tomoyo_domain_initializer_entry *p2) +{ + return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name + && p1->domainname == p2->domainname + && p1->program == p2->program; +} + +static inline bool tomoyo_is_same_domain_keeper_entry +(const struct tomoyo_domain_keeper_entry *p1, + const struct tomoyo_domain_keeper_entry *p2) +{ + return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name + && p1->domainname == p2->domainname + && p1->program == p2->program; +} + +static inline bool tomoyo_is_same_alias_entry +(const struct tomoyo_alias_entry *p1, const struct tomoyo_alias_entry *p2) +{ + return p1->original_name == p2->original_name && + p1->aliased_name == p2->aliased_name; +} + /** * list_for_each_cookie - iterate over a list with cookie. * @pos: the &struct list_head to use as a loop cursor. diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index acb8c397d5c..cd8ba444676 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -130,57 +130,47 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, const bool is_not, const bool is_delete) { - struct tomoyo_domain_initializer_entry *entry = NULL; struct tomoyo_domain_initializer_entry *ptr; - const struct tomoyo_path_info *saved_program = NULL; - const struct tomoyo_path_info *saved_domainname = NULL; + struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; - bool is_last_name = false; if (!tomoyo_is_correct_path(program, 1, -1, -1)) return -EINVAL; /* No patterns allowed. */ if (domainname) { if (!tomoyo_is_domain_def(domainname) && tomoyo_is_correct_path(domainname, 1, -1, -1)) - is_last_name = true; + e.is_last_name = true; else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; - saved_domainname = tomoyo_get_name(domainname); - if (!saved_domainname) + e.domainname = tomoyo_get_name(domainname); + if (!e.domainname) goto out; } - saved_program = tomoyo_get_name(program); - if (!saved_program) + e.program = tomoyo_get_name(program); + if (!e.program) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { - if (ptr->is_not != is_not || - ptr->domainname != saved_domainname || - ptr->program != saved_program) + if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) continue; ptr->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->domainname = saved_domainname; - saved_domainname = NULL; - entry->program = saved_program; - saved_program = NULL; - entry->is_not = is_not; - entry->is_last_name = is_last_name; - list_add_tail_rcu(&entry->list, - &tomoyo_domain_initializer_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_domain_initializer_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, + &tomoyo_domain_initializer_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); out: - tomoyo_put_name(saved_domainname); - tomoyo_put_name(saved_program); - kfree(entry); + tomoyo_put_name(e.domainname); + tomoyo_put_name(e.program); return error; } @@ -350,56 +340,47 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, const bool is_not, const bool is_delete) { - struct tomoyo_domain_keeper_entry *entry = NULL; struct tomoyo_domain_keeper_entry *ptr; - const struct tomoyo_path_info *saved_domainname = NULL; - const struct tomoyo_path_info *saved_program = NULL; + struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; - bool is_last_name = false; if (!tomoyo_is_domain_def(domainname) && tomoyo_is_correct_path(domainname, 1, -1, -1)) - is_last_name = true; + e.is_last_name = true; else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; if (program) { if (!tomoyo_is_correct_path(program, 1, -1, -1)) return -EINVAL; - saved_program = tomoyo_get_name(program); - if (!saved_program) + e.program = tomoyo_get_name(program); + if (!e.program) goto out; } - saved_domainname = tomoyo_get_name(domainname); - if (!saved_domainname) + e.domainname = tomoyo_get_name(domainname); + if (!e.domainname) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { - if (ptr->is_not != is_not || - ptr->domainname != saved_domainname || - ptr->program != saved_program) + if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) continue; ptr->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->domainname = saved_domainname; - saved_domainname = NULL; - entry->program = saved_program; - saved_program = NULL; - entry->is_not = is_not; - entry->is_last_name = is_last_name; - list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_domain_keeper_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, + &tomoyo_domain_keeper_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); out: - tomoyo_put_name(saved_domainname); - tomoyo_put_name(saved_program); - kfree(entry); + tomoyo_put_name(e.domainname); + tomoyo_put_name(e.program); return error; } @@ -551,44 +532,38 @@ static int tomoyo_update_alias_entry(const char *original_name, const char *aliased_name, const bool is_delete) { - struct tomoyo_alias_entry *entry = NULL; struct tomoyo_alias_entry *ptr; - const struct tomoyo_path_info *saved_original_name; - const struct tomoyo_path_info *saved_aliased_name; + struct tomoyo_alias_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; if (!tomoyo_is_correct_path(original_name, 1, -1, -1) || !tomoyo_is_correct_path(aliased_name, 1, -1, -1)) return -EINVAL; /* No patterns allowed. */ - saved_original_name = tomoyo_get_name(original_name); - saved_aliased_name = tomoyo_get_name(aliased_name); - if (!saved_original_name || !saved_aliased_name) + e.original_name = tomoyo_get_name(original_name); + e.aliased_name = tomoyo_get_name(aliased_name); + if (!e.original_name || !e.aliased_name) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { - if (ptr->original_name != saved_original_name || - ptr->aliased_name != saved_aliased_name) + if (!tomoyo_is_same_alias_entry(ptr, &e)) continue; ptr->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->original_name = saved_original_name; - saved_original_name = NULL; - entry->aliased_name = saved_aliased_name; - saved_aliased_name = NULL; - list_add_tail_rcu(&entry->list, &tomoyo_alias_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_alias_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, &tomoyo_alias_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); out: - tomoyo_put_name(saved_original_name); - tomoyo_put_name(saved_aliased_name); - kfree(entry); + tomoyo_put_name(e.original_name); + tomoyo_put_name(e.aliased_name); return error; } @@ -656,7 +631,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * const u8 profile) { struct tomoyo_domain_info *entry; - struct tomoyo_domain_info *domain; + struct tomoyo_domain_info *domain = NULL; const struct tomoyo_path_info *saved_domainname; bool found = false; @@ -665,8 +640,9 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * saved_domainname = tomoyo_get_name(domainname); if (!saved_domainname) return NULL; - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { if (domain->is_deleted || tomoyo_pathcmp(saved_domainname, domain->domainname)) @@ -685,6 +661,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * found = true; } mutex_unlock(&tomoyo_policy_lock); + out: tomoyo_put_name(saved_domainname); kfree(entry); return found ? domain : NULL; @@ -705,7 +682,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) * This function assumes that the size of buffer returned by * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. */ - struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_NOFS); struct tomoyo_domain_info *old_domain = tomoyo_domain(); struct tomoyo_domain_info *domain = NULL; const char *old_domain_name = old_domain->domainname->name; diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 6f3fe76a1fd..1c6f8238ec4 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -45,6 +45,37 @@ static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", }; +void tomoyo_put_name_union(struct tomoyo_name_union *ptr) +{ + if (!ptr) + return; + if (ptr->is_group) + tomoyo_put_path_group(ptr->group); + else + tomoyo_put_name(ptr->filename); +} + +bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, + const struct tomoyo_name_union *ptr) +{ + if (ptr->is_group) + return tomoyo_path_matches_group(name, ptr->group, 1); + return tomoyo_path_matches_pattern(name, ptr->filename); +} + +static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info + *name, + const struct tomoyo_name_union + *ptr, const bool may_use_pattern) +{ + if (ptr->is_group) + return tomoyo_path_matches_group(name, ptr->group, + may_use_pattern); + if (may_use_pattern || !ptr->filename->is_patterned) + return tomoyo_path_matches_pattern(name, ptr->filename); + return false; +} + /** * tomoyo_path2keyword - Get the name of single path operation. * @@ -100,7 +131,7 @@ static struct tomoyo_path_info *tomoyo_get_path(struct path *path) { int error; struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf), - GFP_KERNEL); + GFP_NOFS); if (!buf) return NULL; @@ -164,36 +195,36 @@ LIST_HEAD(tomoyo_globally_readable_list); static int tomoyo_update_globally_readable_entry(const char *filename, const bool is_delete) { - struct tomoyo_globally_readable_file_entry *entry = NULL; struct tomoyo_globally_readable_file_entry *ptr; - const struct tomoyo_path_info *saved_filename; + struct tomoyo_globally_readable_file_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; if (!tomoyo_is_correct_path(filename, 1, 0, -1)) return -EINVAL; - saved_filename = tomoyo_get_name(filename); - if (!saved_filename) + e.filename = tomoyo_get_name(filename); + if (!e.filename) return -ENOMEM; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { - if (ptr->filename != saved_filename) + if (ptr->filename != e.filename) continue; ptr->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->filename = saved_filename; - saved_filename = NULL; - list_add_tail_rcu(&entry->list, &tomoyo_globally_readable_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_globally_readable_file_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, + &tomoyo_globally_readable_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); - tomoyo_put_name(saved_filename); - kfree(entry); + out: + tomoyo_put_name(e.filename); return error; } @@ -311,37 +342,34 @@ LIST_HEAD(tomoyo_pattern_list); static int tomoyo_update_file_pattern_entry(const char *pattern, const bool is_delete) { - struct tomoyo_pattern_entry *entry = NULL; struct tomoyo_pattern_entry *ptr; - const struct tomoyo_path_info *saved_pattern; + struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) }; int error = is_delete ? -ENOENT : -ENOMEM; - saved_pattern = tomoyo_get_name(pattern); - if (!saved_pattern) + if (!e.pattern) return error; - if (!saved_pattern->is_patterned) + if (!e.pattern->is_patterned) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { - if (saved_pattern != ptr->pattern) + if (e.pattern != ptr->pattern) continue; ptr->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->pattern = saved_pattern; - saved_pattern = NULL; - list_add_tail_rcu(&entry->list, &tomoyo_pattern_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_pattern_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, &tomoyo_pattern_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); out: - kfree(entry); - tomoyo_put_name(saved_pattern); + tomoyo_put_name(e.pattern); return error; } @@ -464,36 +492,36 @@ LIST_HEAD(tomoyo_no_rewrite_list); static int tomoyo_update_no_rewrite_entry(const char *pattern, const bool is_delete) { - struct tomoyo_no_rewrite_entry *entry = NULL; struct tomoyo_no_rewrite_entry *ptr; - const struct tomoyo_path_info *saved_pattern; + struct tomoyo_no_rewrite_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; if (!tomoyo_is_correct_path(pattern, 0, 0, 0)) return -EINVAL; - saved_pattern = tomoyo_get_name(pattern); - if (!saved_pattern) + e.pattern = tomoyo_get_name(pattern); + if (!e.pattern) return error; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { - if (ptr->pattern != saved_pattern) + if (ptr->pattern != e.pattern) continue; ptr->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->pattern = saved_pattern; - saved_pattern = NULL; - list_add_tail_rcu(&entry->list, &tomoyo_no_rewrite_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_no_rewrite_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, + &tomoyo_no_rewrite_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); - tomoyo_put_name(saved_pattern); - kfree(entry); + out: + tomoyo_put_name(e.pattern); return error; } @@ -640,13 +668,9 @@ static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain, if (!(acl->perm_high & (perm >> 16))) continue; } - if (may_use_pattern || !acl->filename->is_patterned) { - if (!tomoyo_path_matches_pattern(filename, - acl->filename)) - continue; - } else { + if (!tomoyo_compare_name_union_pattern(filename, &acl->name, + may_use_pattern)) continue; - } error = 0; break; } @@ -805,70 +829,64 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, struct tomoyo_domain_info *const domain, const bool is_delete) { - static const u32 rw_mask = + static const u32 tomoyo_rw_mask = (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); - const struct tomoyo_path_info *saved_filename; + const u32 perm = 1 << type; struct tomoyo_acl_info *ptr; - struct tomoyo_path_acl *entry = NULL; + struct tomoyo_path_acl e = { + .head.type = TOMOYO_TYPE_PATH_ACL, + .perm_high = perm >> 16, + .perm = perm + }; int error = is_delete ? -ENOENT : -ENOMEM; - const u32 perm = 1 << type; + if (type == TOMOYO_TYPE_READ_WRITE) + e.perm |= tomoyo_rw_mask; if (!domain) return -EINVAL; - if (!tomoyo_is_correct_path(filename, 0, 0, 0)) + if (!tomoyo_parse_name_union(filename, &e.name)) return -EINVAL; - saved_filename = tomoyo_get_name(filename); - if (!saved_filename) - return -ENOMEM; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { struct tomoyo_path_acl *acl = container_of(ptr, struct tomoyo_path_acl, head); - if (ptr->type != TOMOYO_TYPE_PATH_ACL) - continue; - if (acl->filename != saved_filename) + if (!tomoyo_is_same_path_acl(acl, &e)) continue; if (is_delete) { if (perm <= 0xFFFF) acl->perm &= ~perm; else acl->perm_high &= ~(perm >> 16); - if ((acl->perm & rw_mask) != rw_mask) + if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask) acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))) - acl->perm &= ~rw_mask; + acl->perm &= ~tomoyo_rw_mask; } else { if (perm <= 0xFFFF) acl->perm |= perm; else acl->perm_high |= (perm >> 16); - if ((acl->perm & rw_mask) == rw_mask) + if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask) acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE; else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)) - acl->perm |= rw_mask; + acl->perm |= tomoyo_rw_mask; } error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->head.type = TOMOYO_TYPE_PATH_ACL; - if (perm <= 0xFFFF) - entry->perm = perm; - else - entry->perm_high = (perm >> 16); - if (perm == (1 << TOMOYO_TYPE_READ_WRITE)) - entry->perm |= rw_mask; - entry->filename = saved_filename; - saved_filename = NULL; - list_add_tail_rcu(&entry->head.list, &domain->acl_info_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_path_acl *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->head.list, + &domain->acl_info_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); - kfree(entry); - tomoyo_put_name(saved_filename); + out: + tomoyo_put_name_union(&e.name); return error; } @@ -890,32 +908,25 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, struct tomoyo_domain_info *const domain, const bool is_delete) { - const struct tomoyo_path_info *saved_filename1; - const struct tomoyo_path_info *saved_filename2; + const u8 perm = 1 << type; + struct tomoyo_path2_acl e = { + .head.type = TOMOYO_TYPE_PATH2_ACL, + .perm = perm + }; struct tomoyo_acl_info *ptr; - struct tomoyo_path2_acl *entry = NULL; int error = is_delete ? -ENOENT : -ENOMEM; - const u8 perm = 1 << type; if (!domain) return -EINVAL; - if (!tomoyo_is_correct_path(filename1, 0, 0, 0) || - !tomoyo_is_correct_path(filename2, 0, 0, 0)) - return -EINVAL; - saved_filename1 = tomoyo_get_name(filename1); - saved_filename2 = tomoyo_get_name(filename2); - if (!saved_filename1 || !saved_filename2) + if (!tomoyo_parse_name_union(filename1, &e.name1) || + !tomoyo_parse_name_union(filename2, &e.name2)) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - if (!is_delete) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - mutex_lock(&tomoyo_policy_lock); list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { struct tomoyo_path2_acl *acl = container_of(ptr, struct tomoyo_path2_acl, head); - if (ptr->type != TOMOYO_TYPE_PATH2_ACL) - continue; - if (acl->filename1 != saved_filename1 || - acl->filename2 != saved_filename2) + if (!tomoyo_is_same_path2_acl(acl, &e)) continue; if (is_delete) acl->perm &= ~perm; @@ -924,22 +935,19 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, error = 0; break; } - if (!is_delete && error && tomoyo_memory_ok(entry)) { - entry->head.type = TOMOYO_TYPE_PATH2_ACL; - entry->perm = perm; - entry->filename1 = saved_filename1; - saved_filename1 = NULL; - entry->filename2 = saved_filename2; - saved_filename2 = NULL; - list_add_tail_rcu(&entry->head.list, &domain->acl_info_list); - entry = NULL; - error = 0; + if (!is_delete && error) { + struct tomoyo_path2_acl *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->head.list, + &domain->acl_info_list); + error = 0; + } } mutex_unlock(&tomoyo_policy_lock); out: - tomoyo_put_name(saved_filename1); - tomoyo_put_name(saved_filename2); - kfree(entry); + tomoyo_put_name_union(&e.name1); + tomoyo_put_name_union(&e.name2); return error; } @@ -992,9 +1000,9 @@ static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain, acl = container_of(ptr, struct tomoyo_path2_acl, head); if (!(acl->perm & perm)) continue; - if (!tomoyo_path_matches_pattern(filename1, acl->filename1)) + if (!tomoyo_compare_name_union(filename1, &acl->name1)) continue; - if (!tomoyo_path_matches_pattern(filename2, acl->filename2)) + if (!tomoyo_compare_name_union(filename2, &acl->name2)) continue; error = 0; break; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index d9ad35bc7fa..b9cc71b0431 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -12,6 +12,8 @@ #include <linux/slab.h> enum tomoyo_gc_id { + TOMOYO_ID_PATH_GROUP, + TOMOYO_ID_PATH_GROUP_MEMBER, TOMOYO_ID_DOMAIN_INITIALIZER, TOMOYO_ID_DOMAIN_KEEPER, TOMOYO_ID_ALIAS, @@ -91,15 +93,15 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) { struct tomoyo_path_acl *entry = container_of(acl, typeof(*entry), head); - tomoyo_put_name(entry->filename); + tomoyo_put_name_union(&entry->name); } break; case TOMOYO_TYPE_PATH2_ACL: { struct tomoyo_path2_acl *entry = container_of(acl, typeof(*entry), head); - tomoyo_put_name(entry->filename1); - tomoyo_put_name(entry->filename2); + tomoyo_put_name_union(&entry->name1); + tomoyo_put_name_union(&entry->name2); } break; default: @@ -149,9 +151,21 @@ static void tomoyo_del_name(const struct tomoyo_name_entry *ptr) { } +static void tomoyo_del_path_group_member(struct tomoyo_path_group_member + *member) +{ + tomoyo_put_name(member->member_name); +} + +static void tomoyo_del_path_group(struct tomoyo_path_group *group) +{ + tomoyo_put_name(group->group_name); +} + static void tomoyo_collect_entry(void) { - mutex_lock(&tomoyo_policy_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return; { struct tomoyo_globally_readable_file_entry *ptr; list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, @@ -275,8 +289,6 @@ static void tomoyo_collect_entry(void) break; } } - mutex_unlock(&tomoyo_policy_lock); - mutex_lock(&tomoyo_name_list_lock); { int i; for (i = 0; i < TOMOYO_MAX_HASH; i++) { @@ -294,7 +306,30 @@ static void tomoyo_collect_entry(void) } } } - mutex_unlock(&tomoyo_name_list_lock); + { + struct tomoyo_path_group *group; + list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { + struct tomoyo_path_group_member *member; + list_for_each_entry_rcu(member, &group->member_list, + list) { + if (!member->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP_MEMBER, + member)) + list_del_rcu(&member->list); + else + break; + } + if (!list_empty(&group->member_list) || + atomic_read(&group->users)) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group)) + list_del_rcu(&group->list); + else + break; + } + } + mutex_unlock(&tomoyo_policy_lock); } static void tomoyo_kfree_entry(void) @@ -335,6 +370,12 @@ static void tomoyo_kfree_entry(void) if (!tomoyo_del_domain(p->element)) continue; break; + case TOMOYO_ID_PATH_GROUP_MEMBER: + tomoyo_del_path_group_member(p->element); + break; + case TOMOYO_ID_PATH_GROUP: + tomoyo_del_path_group(p->element); + break; default: printk(KERN_WARNING "Unknown type\n"); break; diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c new file mode 100644 index 00000000000..c988041c8e1 --- /dev/null +++ b/security/tomoyo/path_group.c @@ -0,0 +1,172 @@ +/* + * security/tomoyo/path_group.c + * + * Copyright (C) 2005-2009 NTT DATA CORPORATION + */ + +#include <linux/slab.h> +#include "common.h" +/* The list for "struct ccs_path_group". */ +LIST_HEAD(tomoyo_path_group_list); + +/** + * tomoyo_get_path_group - Allocate memory for "struct tomoyo_path_group". + * + * @group_name: The name of pathname group. + * + * Returns pointer to "struct tomoyo_path_group" on success, NULL otherwise. + */ +struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) +{ + struct tomoyo_path_group *entry = NULL; + struct tomoyo_path_group *group = NULL; + const struct tomoyo_path_info *saved_group_name; + int error = -ENOMEM; + if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || + !group_name[0]) + return NULL; + saved_group_name = tomoyo_get_name(group_name); + if (!saved_group_name) + return NULL; + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { + if (saved_group_name != group->group_name) + continue; + atomic_inc(&group->users); + error = 0; + break; + } + if (error && tomoyo_memory_ok(entry)) { + INIT_LIST_HEAD(&entry->member_list); + entry->group_name = saved_group_name; + saved_group_name = NULL; + atomic_set(&entry->users, 1); + list_add_tail_rcu(&entry->list, &tomoyo_path_group_list); + group = entry; + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name(saved_group_name); + kfree(entry); + return !error ? group : NULL; +} + +/** + * tomoyo_write_path_group_policy - Write "struct tomoyo_path_group" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, nagative value otherwise. + */ +int tomoyo_write_path_group_policy(char *data, const bool is_delete) +{ + struct tomoyo_path_group *group; + struct tomoyo_path_group_member *member; + struct tomoyo_path_group_member e = { }; + int error = is_delete ? -ENOENT : -ENOMEM; + char *w[2]; + if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) + return -EINVAL; + group = tomoyo_get_path_group(w[0]); + if (!group) + return -ENOMEM; + e.member_name = tomoyo_get_name(w[1]); + if (!e.member_name) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(member, &group->member_list, list) { + if (member->member_name != e.member_name) + continue; + member->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_path_group_member *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, &group->member_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name(e.member_name); + tomoyo_put_path_group(group); + return error; +} + +/** + * tomoyo_read_path_group_policy - Read "struct tomoyo_path_group" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) +{ + struct list_head *gpos; + struct list_head *mpos; + list_for_each_cookie(gpos, head->read_var1, &tomoyo_path_group_list) { + struct tomoyo_path_group *group; + group = list_entry(gpos, struct tomoyo_path_group, list); + list_for_each_cookie(mpos, head->read_var2, + &group->member_list) { + struct tomoyo_path_group_member *member; + member = list_entry(mpos, + struct tomoyo_path_group_member, + list); + if (member->is_deleted) + continue; + if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_PATH_GROUP + "%s %s\n", + group->group_name->name, + member->member_name->name)) + return false; + } + } + return true; +} + +/** + * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. + * + * @pathname: The name of pathname. + * @group: Pointer to "struct tomoyo_path_group". + * @may_use_pattern: True if wild card is permitted. + * + * Returns true if @pathname matches pathnames in @group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, + const struct tomoyo_path_group *group, + const bool may_use_pattern) +{ + struct tomoyo_path_group_member *member; + bool matched = false; + list_for_each_entry_rcu(member, &group->member_list, list) { + if (member->is_deleted) + continue; + if (!member->member_name->is_patterned) { + if (tomoyo_pathcmp(pathname, member->member_name)) + continue; + } else if (may_use_pattern) { + if (!tomoyo_path_matches_pattern(pathname, + member->member_name)) + continue; + } else + continue; + matched = true; + break; + } + return matched; +} diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index c225c65ce42..d1b96f01962 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -139,7 +139,7 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname, */ char *tomoyo_realpath_from_path(struct path *path) { - char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_KERNEL); + char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS); BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) <= TOMOYO_MAX_PATHNAME_LEN - 1); @@ -223,6 +223,25 @@ bool tomoyo_memory_ok(void *ptr) } /** + * tomoyo_commit_ok - Check memory quota. + * + * @data: Data to copy from. + * @size: Size in byte. + * + * Returns pointer to allocated memory on success, NULL otherwise. + */ +void *tomoyo_commit_ok(void *data, const unsigned int size) +{ + void *ptr = kzalloc(size, GFP_NOFS); + if (tomoyo_memory_ok(ptr)) { + memmove(ptr, data, size); + memset(data, 0, size); + return ptr; + } + return NULL; +} + +/** * tomoyo_memory_free - Free memory for elements. * * @ptr: Pointer to allocated memory. @@ -240,8 +259,6 @@ void tomoyo_memory_free(void *ptr) * "const struct tomoyo_path_info *". */ struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; -/* Lock for protecting tomoyo_name_list . */ -DEFINE_MUTEX(tomoyo_name_list_lock); /** * tomoyo_get_name - Allocate permanent memory for string data. @@ -263,14 +280,15 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) len = strlen(name) + 1; hash = full_name_hash((const unsigned char *) name, len - 1); head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; - mutex_lock(&tomoyo_name_list_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return NULL; list_for_each_entry(ptr, head, list) { if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) continue; atomic_inc(&ptr->users); goto out; } - ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL); + ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); allocated_len = ptr ? ksize(ptr) : 0; if (!ptr || (tomoyo_quota_for_policy && atomic_read(&tomoyo_policy_memory_size) + allocated_len @@ -290,7 +308,7 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) tomoyo_fill_path_info(&ptr->entry); list_add_tail(&ptr->list, head); out: - mutex_unlock(&tomoyo_name_list_lock); + mutex_unlock(&tomoyo_policy_lock); return ptr ? &ptr->entry : NULL; } |