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; }