From 17fcfbd9d45b57f38d40e31f9d28db53f4af5c88 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:11:36 +0900 Subject: [PATCH] TOMOYO: Add interactive enforcing mode. Since the behavior of the system is restricted by policy, we may need to update policy when you update packages. We need to update policy in the following cases. * The pathname of files has changed. * The dependency of files has changed. * The access permissions required has increased. The ideal way to update policy is to rebuild from the scratch using learning mode. But it is not desirable to change from enforcing mode to other mode if the system has once entered in production state. Suppose MAC could support per-application enforcing mode, the MAC becomes useless if an application that is not running in enforcing mode was cracked. For example, the whole system becomes vulnerable if only HTTP server application is running in learning mode to rebuild policy for the application. So, in TOMOYO Linux, updating policy is done while the system is running in enforcing mode. This patch implements "interactive enforcing mode" which allows administrators to judge whether to accept policy violation in enforcing mode or not. A demo movie is available at http://www.youtube.com/watch?v=b9q1Jo25LPA . Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 426 ++++++++++++++++++++++++++++++++++++- security/tomoyo/common.h | 28 ++- security/tomoyo/domain.c | 47 ++-- security/tomoyo/file.c | 140 ++++++------ security/tomoyo/mount.c | 24 +-- security/tomoyo/realpath.c | 19 +- 6 files changed, 564 insertions(+), 120 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 0c6f9a5c37a5..ee46aaa3566f 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -74,6 +74,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, /* Write operation for /sys/kernel/security/tomoyo/ interface. */ static int tomoyo_write_control(struct file *file, const char __user *buffer, const int buffer_len); +/* Check whether the domain has too many ACL entries to hold. */ +static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); /** * tomoyo_parse_name_union - Parse a tomoyo_name_union. @@ -1031,7 +1033,7 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) * * Caller holds tomoyo_read_lock(). */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) +static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) { unsigned int count = 0; struct tomoyo_domain_info *domain = r->domain; @@ -1530,6 +1532,24 @@ static int tomoyo_delete_domain(char *domainname) return 0; } +/** + * tomoyo_write_domain_policy2 - Write domain policy. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_domain_policy2(char *data, + struct tomoyo_domain_info *domain, + const bool is_delete) +{ + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) + return tomoyo_write_mount_policy(data, domain, is_delete); + return tomoyo_write_file_policy(data, domain, is_delete); +} + /** * tomoyo_write_domain_policy - Write domain policy. * @@ -1580,9 +1600,7 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) domain->ignore_global_allow_read = !is_delete; return 0; } - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) - return tomoyo_write_mount_policy(data, domain, is_delete); - return tomoyo_write_file_policy(data, domain, is_delete); + return tomoyo_write_domain_policy2(data, domain, is_delete); } /** @@ -2185,6 +2203,357 @@ void tomoyo_load_policy(const char *filename) } } +/** + * tomoyo_print_header - Get header line of audit log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns string representation. + * + * This function uses kmalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +static char *tomoyo_print_header(struct tomoyo_request_info *r) +{ + static const char *tomoyo_mode_4[4] = { + "disabled", "learning", "permissive", "enforcing" + }; + struct timeval tv; + const pid_t gpid = task_pid_nr(current); + static const int tomoyo_buffer_len = 4096; + char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); + if (!buffer) + return NULL; + do_gettimeofday(&tv); + snprintf(buffer, tomoyo_buffer_len - 1, + "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" + " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" + " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", + tv.tv_sec, r->profile, tomoyo_mode_4[r->mode], gpid, + (pid_t) sys_getpid(), (pid_t) sys_getppid(), + current_uid(), current_gid(), current_euid(), + current_egid(), current_suid(), current_sgid(), + current_fsuid(), current_fsgid()); + return buffer; +} + +/** + * tomoyo_init_audit_log - Allocate buffer for audit logs. + * + * @len: Required size. + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns pointer to allocated memory. + * + * The @len is updated to add the header lines' size on success. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) +{ + char *buf = NULL; + const char *header; + const char *domainname; + if (!r->domain) + r->domain = tomoyo_domain(); + domainname = r->domain->domainname->name; + header = tomoyo_print_header(r); + if (!header) + return NULL; + *len += strlen(domainname) + strlen(header) + 10; + buf = kzalloc(*len, GFP_NOFS); + if (buf) + snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname); + kfree(header); + return buf; +} + +/* Wait queue for tomoyo_query_list. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); + +/* Lock for manipulating tomoyo_query_list. */ +static DEFINE_SPINLOCK(tomoyo_query_list_lock); + +/* Structure for query. */ +struct tomoyo_query_entry { + struct list_head list; + char *query; + int query_len; + unsigned int serial; + int timer; + int answer; +}; + +/* The list for "struct tomoyo_query_entry". */ +static LIST_HEAD(tomoyo_query_list); + +/* + * Number of "struct file" referring /sys/kernel/security/tomoyo/query + * interface. + */ +static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); + +/** + * tomoyo_supervisor - Ask for the supervisor's decision. + * + * @r: Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. + * + * Returns 0 if the supervisor decided to permit the access request which + * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the + * supervisor decided to retry the access request which violated the policy in + * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise. + */ +int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) +{ + va_list args; + int error = -EPERM; + int pos; + int len; + static unsigned int tomoyo_serial; + struct tomoyo_query_entry *tomoyo_query_entry = NULL; + bool quota_exceeded = false; + char *header; + switch (r->mode) { + char *buffer; + case TOMOYO_CONFIG_LEARNING: + if (!tomoyo_domain_quota_is_ok(r)) + return 0; + va_start(args, fmt); + len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4; + va_end(args); + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return 0; + va_start(args, fmt); + vsnprintf(buffer, len - 1, fmt, args); + va_end(args); + tomoyo_normalize_line(buffer); + tomoyo_write_domain_policy2(buffer, r->domain, false); + kfree(buffer); + /* fall through */ + case TOMOYO_CONFIG_PERMISSIVE: + return 0; + } + if (!r->domain) + r->domain = tomoyo_domain(); + if (!atomic_read(&tomoyo_query_observers)) + return -EPERM; + va_start(args, fmt); + len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; + va_end(args); + header = tomoyo_init_audit_log(&len, r); + if (!header) + goto out; + tomoyo_query_entry = kzalloc(sizeof(*tomoyo_query_entry), GFP_NOFS); + if (!tomoyo_query_entry) + goto out; + tomoyo_query_entry->query = kzalloc(len, GFP_NOFS); + if (!tomoyo_query_entry->query) + goto out; + len = ksize(tomoyo_query_entry->query); + INIT_LIST_HEAD(&tomoyo_query_entry->list); + spin_lock(&tomoyo_query_list_lock); + if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + + sizeof(*tomoyo_query_entry) >= tomoyo_quota_for_query) { + quota_exceeded = true; + } else { + tomoyo_query_memory_size += len + sizeof(*tomoyo_query_entry); + tomoyo_query_entry->serial = tomoyo_serial++; + } + spin_unlock(&tomoyo_query_list_lock); + if (quota_exceeded) + goto out; + pos = snprintf(tomoyo_query_entry->query, len - 1, "Q%u-%hu\n%s", + tomoyo_query_entry->serial, r->retry, header); + kfree(header); + header = NULL; + va_start(args, fmt); + vsnprintf(tomoyo_query_entry->query + pos, len - 1 - pos, fmt, args); + tomoyo_query_entry->query_len = strlen(tomoyo_query_entry->query) + 1; + va_end(args); + spin_lock(&tomoyo_query_list_lock); + list_add_tail(&tomoyo_query_entry->list, &tomoyo_query_list); + spin_unlock(&tomoyo_query_list_lock); + /* Give 10 seconds for supervisor's opinion. */ + for (tomoyo_query_entry->timer = 0; + atomic_read(&tomoyo_query_observers) && tomoyo_query_entry->timer < 100; + tomoyo_query_entry->timer++) { + wake_up(&tomoyo_query_wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + if (tomoyo_query_entry->answer) + break; + } + spin_lock(&tomoyo_query_list_lock); + list_del(&tomoyo_query_entry->list); + tomoyo_query_memory_size -= len + sizeof(*tomoyo_query_entry); + spin_unlock(&tomoyo_query_list_lock); + switch (tomoyo_query_entry->answer) { + case 3: /* Asked to retry by administrator. */ + error = TOMOYO_RETRY_REQUEST; + r->retry++; + break; + case 1: + /* Granted by administrator. */ + error = 0; + break; + case 0: + /* Timed out. */ + break; + default: + /* Rejected by administrator. */ + break; + } + out: + if (tomoyo_query_entry) + kfree(tomoyo_query_entry->query); + kfree(tomoyo_query_entry); + kfree(header); + return error; +} + +/** + * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise. + * + * Waits for access requests which violated policy in enforcing mode. + */ +static int tomoyo_poll_query(struct file *file, poll_table *wait) +{ + struct list_head *tmp; + bool found = false; + u8 i; + for (i = 0; i < 2; i++) { + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, + list); + if (ptr->answer) + continue; + found = true; + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (found) + return POLLIN | POLLRDNORM; + if (i) + break; + poll_wait(file, &tomoyo_query_wait, wait); + } + return 0; +} + +/** + * tomoyo_read_query - Read access requests which violated policy in enforcing mode. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +static int tomoyo_read_query(struct tomoyo_io_buffer *head) +{ + struct list_head *tmp; + int pos = 0; + int len = 0; + char *buf; + if (head->read_avail) + return 0; + if (head->read_buf) { + kfree(head->read_buf); + head->read_buf = NULL; + head->readbuf_size = 0; + } + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->answer) + continue; + if (pos++ != head->read_step) + continue; + len = ptr->query_len; + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (!len) { + head->read_step = 0; + return 0; + } + buf = kzalloc(len, GFP_NOFS); + if (!buf) + return 0; + pos = 0; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->answer) + continue; + if (pos++ != head->read_step) + continue; + /* + * Some query can be skipped because tomoyo_query_list + * can change, but I don't care. + */ + if (len == ptr->query_len) + memmove(buf, ptr->query, len); + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (buf[0]) { + head->read_avail = len; + head->readbuf_size = head->read_avail; + head->read_buf = buf; + head->read_step++; + } else { + kfree(buf); + } + return 0; +} + +/** + * tomoyo_write_answer - Write the supervisor's decision. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0 on success, -EINVAL otherwise. + */ +static int tomoyo_write_answer(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + struct list_head *tmp; + unsigned int serial; + unsigned int answer; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + ptr->timer = 0; + } + spin_unlock(&tomoyo_query_list_lock); + if (sscanf(data, "A%u=%u", &serial, &answer) != 2) + return -EINVAL; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->serial != serial) + continue; + if (!ptr->answer) + ptr->answer = answer; + break; + } + spin_unlock(&tomoyo_query_list_lock); + return 0; +} + /** * tomoyo_read_version: Get version. * @@ -2239,6 +2608,7 @@ static int tomoyo_open_control(const u8 type, struct file *file) if (!head) return -ENOMEM; mutex_init(&head->io_sem); + head->type = type; switch (type) { case TOMOYO_DOMAINPOLICY: /* /sys/kernel/security/tomoyo/domain_policy */ @@ -2280,6 +2650,11 @@ static int tomoyo_open_control(const u8 type, struct file *file) head->write = tomoyo_write_profile; head->read = tomoyo_read_profile; break; + case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */ + head->poll = tomoyo_poll_query; + head->write = tomoyo_write_answer; + head->read = tomoyo_read_query; + break; case TOMOYO_MANAGER: /* /sys/kernel/security/tomoyo/manager */ head->write = tomoyo_write_manager_policy; @@ -2292,7 +2667,9 @@ static int tomoyo_open_control(const u8 type, struct file *file) * for reading. */ head->read = NULL; - } else { + head->poll = NULL; + } else if (!head->poll) { + /* Don't allocate read_buf for poll() access. */ if (!head->readbuf_size) head->readbuf_size = 4096 * 2; head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); @@ -2316,7 +2693,8 @@ static int tomoyo_open_control(const u8 type, struct file *file) return -ENOMEM; } } - head->reader_idx = tomoyo_read_lock(); + if (type != TOMOYO_QUERY) + head->reader_idx = tomoyo_read_lock(); file->private_data = head; /* * Call the handler now if the file is @@ -2327,9 +2705,34 @@ static int tomoyo_open_control(const u8 type, struct file *file) */ if (type == TOMOYO_SELFDOMAIN) tomoyo_read_control(file, NULL, 0); + /* + * If the file is /sys/kernel/security/tomoyo/query , increment the + * observer counter. + * The obserber counter is used by tomoyo_supervisor() to see if + * there is some process monitoring /sys/kernel/security/tomoyo/query. + */ + else if (type == TOMOYO_QUERY) + atomic_inc(&tomoyo_query_observers); return 0; } +/** + * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Waits for read readiness. + * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + */ +int tomoyo_poll_control(struct file *file, poll_table *wait) +{ + struct tomoyo_io_buffer *head = file->private_data; + if (!head->poll) + return -ENOSYS; + return head->poll(file, wait); +} + /** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * @@ -2443,7 +2846,14 @@ static int tomoyo_close_control(struct file *file) struct tomoyo_io_buffer *head = file->private_data; const bool is_write = !!head->write_buf; - tomoyo_read_unlock(head->reader_idx); + /* + * If the file is /sys/kernel/security/tomoyo/query , decrement the + * observer counter. + */ + if (head->type == TOMOYO_QUERY) + atomic_dec(&tomoyo_query_observers); + else + tomoyo_read_unlock(head->reader_idx); /* Release memory used for policy I/O. */ kfree(head->read_buf); head->read_buf = NULL; @@ -2562,6 +2972,8 @@ static int __init tomoyo_initerface_init(void) return 0; tomoyo_dir = securityfs_create_dir("tomoyo", NULL); + tomoyo_create_entry("query", 0600, tomoyo_dir, + TOMOYO_QUERY); tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, TOMOYO_DOMAINPOLICY); tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 3d819b139165..dc5f98f52f61 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -20,6 +20,7 @@ #include #include #include +#include struct linux_binprm; /********** Constants definitions. **********/ @@ -156,9 +157,12 @@ enum tomoyo_securityfs_interface_index { TOMOYO_SELFDOMAIN, TOMOYO_VERSION, TOMOYO_PROFILE, + TOMOYO_QUERY, TOMOYO_MANAGER }; +#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ + /********** Structure definitions. **********/ /* @@ -176,10 +180,14 @@ struct tomoyo_page_buffer { * tomoyo_request_info is a structure which is used for holding * * (1) Domain information of current process. - * (2) Access control mode of the profile. + * (2) How many retries are made for this request. + * (3) Profile number used for this request. + * (4) Access control mode of the profile. */ struct tomoyo_request_info { struct tomoyo_domain_info *domain; + u8 retry; + u8 profile; u8 mode; /* One of tomoyo_mode_index . */ }; @@ -484,6 +492,7 @@ struct tomoyo_mount_acl { struct tomoyo_io_buffer { int (*read) (struct tomoyo_io_buffer *); int (*write) (struct tomoyo_io_buffer *); + int (*poll) (struct file *file, poll_table *wait); /* Exclusive lock for this structure. */ struct mutex io_sem; /* Index returned by tomoyo_read_lock(). */ @@ -514,6 +523,8 @@ struct tomoyo_io_buffer { int write_avail; /* Size of write buffer. */ int writebuf_size; + /* Type of this interface. */ + u8 type; }; /* @@ -659,14 +670,15 @@ struct tomoyo_policy_manager_entry { /********** Function prototypes. **********/ +extern asmlinkage long sys_getpid(void); +extern asmlinkage long sys_getppid(void); + /* 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 given number matches the given number_union. */ bool tomoyo_compare_number_union(const unsigned long value, const struct tomoyo_number_union *ptr); -/* Check whether the domain has too many ACL entries to hold. */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); /* Transactional sprintf() for policy dump. */ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); @@ -763,6 +775,8 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); 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); +int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); /* Create "number_group" entry in exception policy. */ int tomoyo_write_number_group_policy(char *data, const bool is_delete); /* Find a domain by the given name. */ @@ -771,9 +785,6 @@ 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); -/* Get patterned pathname. */ -const struct tomoyo_path_info * -tomoyo_get_file_pattern(const struct tomoyo_path_info *filename); /* 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); @@ -807,6 +818,8 @@ char *tomoyo_realpath(const char *pathname); char *tomoyo_realpath_nofollow(const char *pathname); /* Same with tomoyo_realpath() except that the pathname is already solved. */ char *tomoyo_realpath_from_path(struct path *path); +/* Get patterned pathname. */ +const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename); /* Check memory quota. */ bool tomoyo_memory_ok(void *ptr); @@ -878,6 +891,9 @@ extern bool tomoyo_policy_loaded; /* The kernel's domain. */ extern struct tomoyo_domain_info tomoyo_kernel_domain; +extern unsigned int tomoyo_quota_for_query; +extern unsigned int tomoyo_query_memory_size; + /********** Inlined functions. **********/ static inline int tomoyo_read_lock(void) diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index afdf26128bfe..7e242d27da5a 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -678,6 +678,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * */ int tomoyo_find_next_domain(struct linux_binprm *bprm) { + struct tomoyo_request_info r; /* * This function assumes that the size of buffer returned by * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. @@ -693,11 +694,12 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); int retval = -ENOMEM; - struct tomoyo_path_info r; /* real name */ - struct tomoyo_path_info s; /* symlink name */ - struct tomoyo_path_info l; /* last name */ + struct tomoyo_path_info rn; /* real name */ + struct tomoyo_path_info sn; /* symlink name */ + struct tomoyo_path_info ln; /* last name */ static bool initialized; + tomoyo_init_request_info(&r, NULL); if (!tmp) goto out; @@ -713,6 +715,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) initialized = true; } + retry: /* Get tomoyo_realpath of program. */ retval = -ENOENT; /* I hope tomoyo_realpath() won't fail with -ENOMEM. */ @@ -724,37 +727,39 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (!symlink_program_name) goto out; - r.name = real_program_name; - tomoyo_fill_path_info(&r); - s.name = symlink_program_name; - tomoyo_fill_path_info(&s); - l.name = tomoyo_get_last_name(old_domain); - tomoyo_fill_path_info(&l); + rn.name = real_program_name; + tomoyo_fill_path_info(&rn); + sn.name = symlink_program_name; + tomoyo_fill_path_info(&sn); + ln.name = tomoyo_get_last_name(old_domain); + tomoyo_fill_path_info(&ln); /* Check 'alias' directive. */ - if (tomoyo_pathcmp(&r, &s)) { + if (tomoyo_pathcmp(&rn, &sn)) { struct tomoyo_alias_entry *ptr; /* Is this program allowed to be called via symbolic links? */ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { if (ptr->is_deleted || - tomoyo_pathcmp(&r, ptr->original_name) || - tomoyo_pathcmp(&s, ptr->aliased_name)) + tomoyo_pathcmp(&rn, ptr->original_name) || + tomoyo_pathcmp(&sn, ptr->aliased_name)) continue; memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN); strncpy(real_program_name, ptr->aliased_name->name, TOMOYO_MAX_PATHNAME_LEN - 1); - tomoyo_fill_path_info(&r); + tomoyo_fill_path_info(&rn); break; } } /* Check execute permission. */ - retval = tomoyo_check_exec_perm(old_domain, &r); + retval = tomoyo_check_exec_perm(old_domain, &rn); + if (retval == TOMOYO_RETRY_REQUEST) + goto retry; if (retval < 0) goto out; new_domain_name = tmp->buffer; - if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) { + if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) { /* Transit to the child of tomoyo_kernel_domain domain. */ snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, TOMOYO_ROOT_NAME " " "%s", real_program_name); @@ -766,7 +771,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) * initializers because they might start before /sbin/init. */ domain = old_domain; - } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) { + } else if (tomoyo_is_domain_keeper(old_domain->domainname, &rn, &ln)) { /* Keep current domain. */ domain = old_domain; } else { @@ -779,8 +784,14 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) domain = tomoyo_find_domain(new_domain_name); if (domain) goto done; - if (is_enforce) - goto done; + if (is_enforce) { + int error = tomoyo_supervisor(&r, "# wants to create domain\n" + "%s\n", new_domain_name); + if (error == TOMOYO_RETRY_REQUEST) + goto retry; + if (error < 0) + goto done; + } domain = tomoyo_find_or_assign_new_domain(new_domain_name, old_domain->profile); done: diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index ae32cab8ec7e..c629cb4e2c66 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -478,7 +478,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, } /** - * tomoyo_get_file_pattern - Get patterned pathname. + * tomoyo_file_pattern - Get patterned pathname. * * @filename: The filename to find patterned pathname. * @@ -486,8 +486,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, * * Caller holds tomoyo_read_lock(). */ -const struct tomoyo_path_info * -tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) +const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) { struct tomoyo_pattern_entry *ptr; const struct tomoyo_path_info *pattern = NULL; @@ -507,7 +506,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) } if (pattern) filename = pattern; - return filename; + return filename->name; } /** @@ -812,23 +811,25 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, perm = 1 << TOMOYO_TYPE_EXECUTE; } else BUG(); - error = tomoyo_path_acl(r, filename, perm, mode != 1); - if (error && mode == 4 && !r->domain->ignore_global_allow_read - && tomoyo_is_globally_readable_file(filename)) + do { + error = tomoyo_path_acl(r, filename, perm, mode != 1); + if (error && mode == 4 && !r->domain->ignore_global_allow_read + && tomoyo_is_globally_readable_file(filename)) + error = 0; + if (!error) + break; + tomoyo_warn_log(r, "%s %s", msg, filename->name); + error = tomoyo_supervisor(r, "allow_%s %s\n", msg, + mode == 1 ? filename->name : + tomoyo_file_pattern(filename)); + /* + * Do not retry for execute request, for alias may have + * changed. + */ + } while (error == TOMOYO_RETRY_REQUEST && mode != 1); + if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; - if (!error) - return 0; - tomoyo_warn_log(r, "%s %s", msg, filename->name); - if (r->mode == TOMOYO_CONFIG_ENFORCING) - return error; - if (tomoyo_domain_quota_is_ok(r)) { - /* Don't use patterns for execute permission. */ - const struct tomoyo_path_info *patterned_file = (mode != 1) ? - tomoyo_get_file_pattern(filename) : filename; - tomoyo_update_file_acl(mode, patterned_file->name, r->domain, - false); - } - return 0; + return error; } /** @@ -1123,21 +1124,21 @@ static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type, static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, const struct tomoyo_path_info *filename) { + const char *msg; int error; next: - error = tomoyo_path_acl(r, filename, 1 << operation, 1); - if (!error) - goto ok; - tomoyo_warn_log(r, "%s %s", tomoyo_path2keyword(operation), - filename->name); - if (tomoyo_domain_quota_is_ok(r)) { - const char *name = tomoyo_get_file_pattern(filename)->name; - tomoyo_update_path_acl(operation, name, r->domain, false); - } + do { + error = tomoyo_path_acl(r, filename, 1 << operation, 1); + if (!error) + break; + msg = tomoyo_path2keyword(operation); + tomoyo_warn_log(r, "%s %s", msg, filename->name); + error = tomoyo_supervisor(r, "allow_%s %s\n", msg, + tomoyo_file_pattern(filename)); + } while (error == TOMOYO_RETRY_REQUEST); if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; - ok: /* * Since "allow_truncate" doesn't imply "allow_rewrite" permission, * we need to check "allow_rewrite" permission if the filename is @@ -1267,6 +1268,7 @@ static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, char buffer[64]; int error; u8 radix; + const char *msg; if (!filename) return 0; @@ -1286,15 +1288,16 @@ static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, break; } tomoyo_print_ulong(buffer, sizeof(buffer), number, radix); - error = tomoyo_path_number_acl(r, type, filename, number); - if (!error) - return 0; - tomoyo_warn_log(r, "%s %s %s", tomoyo_path_number2keyword(type), - filename->name, buffer); - if (tomoyo_domain_quota_is_ok(r)) - tomoyo_update_path_number_acl(type, - tomoyo_get_file_pattern(filename) - ->name, buffer, r->domain, false); + do { + error = tomoyo_path_number_acl(r, type, filename, number); + if (!error) + break; + msg = tomoyo_path_number2keyword(type); + tomoyo_warn_log(r, "%s %s %s", msg, filename->name, buffer); + error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg, + tomoyo_file_pattern(filename), + buffer); + } while (error == TOMOYO_RETRY_REQUEST); if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; @@ -1484,32 +1487,23 @@ static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r, const unsigned int dev) { int error; + const char *msg; const unsigned int major = MAJOR(dev); const unsigned int minor = MINOR(dev); - error = tomoyo_path_number3_acl(r, filename, 1 << operation, mode, - major, minor); - if (!error) - return 0; - tomoyo_warn_log(r, "%s %s 0%o %u %u", - tomoyo_path_number32keyword(operation), - filename->name, mode, major, minor); - if (tomoyo_domain_quota_is_ok(r)) { - char mode_buf[64]; - char major_buf[64]; - char minor_buf[64]; - memset(mode_buf, 0, sizeof(mode_buf)); - memset(major_buf, 0, sizeof(major_buf)); - memset(minor_buf, 0, sizeof(minor_buf)); - snprintf(mode_buf, sizeof(mode_buf) - 1, "0%o", mode); - snprintf(major_buf, sizeof(major_buf) - 1, "%u", major); - snprintf(minor_buf, sizeof(minor_buf) - 1, "%u", minor); - tomoyo_update_path_number3_acl(operation, - tomoyo_get_file_pattern(filename) - ->name, mode_buf, major_buf, - minor_buf, r->domain, false); - } - if (r->mode != TOMOYO_CONFIG_ENFORCING) + do { + error = tomoyo_path_number3_acl(r, filename, 1 << operation, + mode, major, minor); + if (!error) + break; + msg = tomoyo_path_number32keyword(operation); + tomoyo_warn_log(r, "%s %s 0%o %u %u", msg, filename->name, + mode, major, minor); + error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg, + tomoyo_file_pattern(filename), mode, + major, minor); + } while (error == TOMOYO_RETRY_REQUEST); + if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } @@ -1562,6 +1556,7 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2) { int error = -ENOMEM; + const char *msg; struct tomoyo_path_info *buf1; struct tomoyo_path_info *buf2; struct tomoyo_request_info r; @@ -1591,17 +1586,16 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, } } } - error = tomoyo_path2_acl(&r, operation, buf1, buf2); - if (!error) - goto out; - tomoyo_warn_log(&r, "%s %s %s", tomoyo_path22keyword(operation), - buf1->name, buf2->name); - if (tomoyo_domain_quota_is_ok(&r)) { - const char *name1 = tomoyo_get_file_pattern(buf1)->name; - const char *name2 = tomoyo_get_file_pattern(buf2)->name; - tomoyo_update_path2_acl(operation, name1, name2, r.domain, - false); - } + do { + error = tomoyo_path2_acl(&r, operation, buf1, buf2); + if (!error) + break; + msg = tomoyo_path22keyword(operation); + tomoyo_warn_log(&r, "%s %s %s", msg, buf1->name, buf2->name); + error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg, + tomoyo_file_pattern(buf1), + tomoyo_file_pattern(buf2)); + } while (error == TOMOYO_RETRY_REQUEST); out: kfree(buf1); kfree(buf2); diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 507be09e93a9..aeac619f787d 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -178,19 +178,12 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, error = 0; break; } - if (error) { - const char *dev = tomoyo_get_file_pattern(&rdev)->name; - const char *dir = tomoyo_get_file_pattern(&rdir)->name; - int len = strlen(dev) + strlen(dir) + strlen(requested_type) - + 64; - char *buf = kzalloc(len, GFP_NOFS); - if (buf) { - snprintf(buf, len - 1, "%s %s %s 0x%lX", - dev, dir, requested_type, flags); - tomoyo_write_mount_policy(buf, r->domain, false); - kfree(buf); - } - } + if (error) + error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT + "%s %s %s 0x%lX\n", + tomoyo_file_pattern(&rdev), + tomoyo_file_pattern(&rdir), + requested_type, flags); out: kfree(requested_dev_name); kfree(requested_dir_name); @@ -279,7 +272,10 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD, flags & ~MS_SHARED); else - error = tomoyo_mount_acl2(r, dev_name, dir, type, flags); + do { + error = tomoyo_mount_acl2(r, dev_name, dir, type, + flags); + } while (error == TOMOYO_RETRY_REQUEST); if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index d1b96f019621..3ceb1724c92d 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -333,6 +333,9 @@ void __init tomoyo_realpath_init(void) panic("Can't register tomoyo_kernel_domain"); } +unsigned int tomoyo_quota_for_query; +unsigned int tomoyo_query_memory_size; + /** * tomoyo_read_memory_counter - Check for memory usage in bytes. * @@ -345,6 +348,7 @@ int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) if (!head->read_eof) { const unsigned int policy = atomic_read(&tomoyo_policy_memory_size); + const unsigned int query = tomoyo_query_memory_size; char buffer[64]; memset(buffer, 0, sizeof(buffer)); @@ -354,8 +358,17 @@ int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) tomoyo_quota_for_policy); else buffer[0] = '\0'; - tomoyo_io_printf(head, "Policy: %10u%s\n", policy, buffer); - tomoyo_io_printf(head, "Total: %10u\n", policy); + tomoyo_io_printf(head, "Policy: %10u%s\n", policy, + buffer); + if (tomoyo_quota_for_query) + snprintf(buffer, sizeof(buffer) - 1, + " (Quota: %10u)", + tomoyo_quota_for_query); + else + buffer[0] = '\0'; + tomoyo_io_printf(head, "Query lists: %10u%s\n", query, + buffer); + tomoyo_io_printf(head, "Total: %10u\n", policy + query); head->read_eof = true; } return 0; @@ -375,5 +388,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) if (sscanf(data, "Policy: %u", &size) == 1) tomoyo_quota_for_policy = size; + else if (sscanf(data, "Query lists: %u", &size) == 1) + tomoyo_quota_for_query = size; return 0; } -- 2.39.2