aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--security/tomoyo/common.c426
-rw-r--r--security/tomoyo/common.h28
-rw-r--r--security/tomoyo/domain.c47
-rw-r--r--security/tomoyo/file.c140
-rw-r--r--security/tomoyo/mount.c24
-rw-r--r--security/tomoyo/realpath.c19
6 files changed, 564 insertions, 120 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 0c6f9a5c37a..ee46aaa3566 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;
@@ -1531,6 +1533,24 @@ static int tomoyo_delete_domain(char *domainname)
}
/**
+ * 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.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -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);
}
/**
@@ -2186,6 +2204,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.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -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,10 +2705,35 @@ 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.
*
* @file: Pointer to "struct file".
@@ -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 3d819b13916..dc5f98f52f6 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -20,6 +20,7 @@
#include <linux/mount.h>
#include <linux/list.h>
#include <linux/cred.h>
+#include <linux/poll.h>
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 afdf26128bf..7e242d27da5 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 ae32cab8ec7..c629cb4e2c6 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 507be09e93a..aeac619f787 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 d1b96f01962..3ceb1724c92 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;
}