/* 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.
return tomoyo_io_printf(head, " %s", ptr->filename->name);
}
+/**
+ * tomoyo_parse_ulong - Parse an "unsigned long" value.
+ *
+ * @result: Pointer to "unsigned long".
+ * @str: Pointer to string to parse.
+ *
+ * Returns value type on success, 0 otherwise.
+ *
+ * The @src is updated to point the first character after the value
+ * on success.
+ */
+u8 tomoyo_parse_ulong(unsigned long *result, char **str)
+{
+ const char *cp = *str;
+ char *ep;
+ int base = 10;
+ if (*cp == '0') {
+ char c = *(cp + 1);
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ cp += 2;
+ } else if (c >= '0' && c <= '7') {
+ base = 8;
+ cp++;
+ }
+ }
+ *result = simple_strtoul(cp, &ep, base);
+ if (cp == ep)
+ return 0;
+ *str = ep;
+ switch (base) {
+ case 16:
+ return TOMOYO_VALUE_TYPE_HEXADECIMAL;
+ case 8:
+ return TOMOYO_VALUE_TYPE_OCTAL;
+ default:
+ return TOMOYO_VALUE_TYPE_DECIMAL;
+ }
+}
+
+/**
+ * tomoyo_print_ulong - Print an "unsigned long" value.
+ *
+ * @buffer: Pointer to buffer.
+ * @buffer_len: Size of @buffer.
+ * @value: An "unsigned long" value.
+ * @type: Type of @value.
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+ const unsigned long value, const u8 type)
+{
+ if (type == TOMOYO_VALUE_TYPE_DECIMAL)
+ snprintf(buffer, buffer_len, "%lu", value);
+ else if (type == TOMOYO_VALUE_TYPE_OCTAL)
+ snprintf(buffer, buffer_len, "0%lo", value);
+ else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL)
+ snprintf(buffer, buffer_len, "0x%lX", value);
+ else
+ snprintf(buffer, buffer_len, "type(%u)", type);
+}
+
+/**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_number_union *ptr)
+{
+ unsigned long min;
+ unsigned long max;
+ u8 min_type;
+ u8 max_type;
+ if (!tomoyo_io_printf(head, " "))
+ return false;
+ if (ptr->is_group)
+ return tomoyo_io_printf(head, "@%s",
+ ptr->group->group_name->name);
+ min_type = ptr->min_type;
+ max_type = ptr->max_type;
+ min = ptr->values[0];
+ max = ptr->values[1];
+ switch (min_type) {
+ case TOMOYO_VALUE_TYPE_HEXADECIMAL:
+ if (!tomoyo_io_printf(head, "0x%lX", min))
+ return false;
+ break;
+ case TOMOYO_VALUE_TYPE_OCTAL:
+ if (!tomoyo_io_printf(head, "0%lo", min))
+ return false;
+ break;
+ default:
+ if (!tomoyo_io_printf(head, "%lu", min))
+ return false;
+ break;
+ }
+ if (min == max && min_type == max_type)
+ return true;
+ switch (max_type) {
+ case TOMOYO_VALUE_TYPE_HEXADECIMAL:
+ return tomoyo_io_printf(head, "-0x%lX", max);
+ case TOMOYO_VALUE_TYPE_OCTAL:
+ return tomoyo_io_printf(head, "-0%lo", max);
+ default:
+ return tomoyo_io_printf(head, "-%lu", max);
+ }
+}
+
+/**
+ * tomoyo_parse_number_union - Parse a tomoyo_number_union.
+ *
+ * @data: Number or number range or number group.
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
+{
+ u8 type;
+ unsigned long v;
+ memset(num, 0, sizeof(*num));
+ if (data[0] == '@') {
+ if (!tomoyo_is_correct_path(data, 0, 0, 0))
+ return false;
+ num->group = tomoyo_get_number_group(data + 1);
+ num->is_group = true;
+ return num->group != NULL;
+ }
+ type = tomoyo_parse_ulong(&v, &data);
+ if (!type)
+ return false;
+ num->values[0] = v;
+ num->min_type = type;
+ if (!*data) {
+ num->values[1] = v;
+ num->max_type = type;
+ return true;
+ }
+ if (*data++ != '-')
+ return false;
+ type = tomoyo_parse_ulong(&v, &data);
+ if (!type || *data)
+ return false;
+ num->values[1] = v;
+ num->max_type = type;
+ return true;
+}
+
/**
* tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
*
return cp;
}
-/**
- * tomoyo_get_msg - Get warning message.
- *
- * @is_enforce: Is it enforcing mode?
- *
- * Returns "ERROR" or "WARNING".
- */
-const char *tomoyo_get_msg(const bool is_enforce)
-{
- if (is_enforce)
- return "ERROR";
- else
- return "WARNING";
-}
-
/**
* tomoyo_check_flags - Check mode for specified functionality.
*
/**
* tomoyo_domain_quota_is_ok - Check for domain's quota.
*
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @r: Pointer to "struct tomoyo_request_info".
*
* Returns true if the domain is not exceeded quota, false otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
+static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
{
unsigned int count = 0;
+ struct tomoyo_domain_info *domain = r->domain;
struct tomoyo_acl_info *ptr;
+ if (r->mode != TOMOYO_CONFIG_LEARNING)
+ return false;
if (!domain)
return true;
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
switch (ptr->type) {
- struct tomoyo_path_acl *acl;
- u32 perm;
+ u16 perm;
u8 i;
case TOMOYO_TYPE_PATH_ACL:
- acl = container_of(ptr, struct tomoyo_path_acl, head);
- perm = acl->perm | (((u32) acl->perm_high) << 16);
+ perm = container_of(ptr, struct tomoyo_path_acl, head)
+ ->perm;
for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++)
if (perm & (1 << i))
count++;
if (perm & (1 << i))
count++;
break;
+ case TOMOYO_TYPE_PATH_NUMBER_ACL:
+ perm = container_of(ptr, struct tomoyo_path_number_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ case TOMOYO_TYPE_PATH_NUMBER3_ACL:
+ perm = container_of(ptr, struct tomoyo_path_number3_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ case TOMOYO_TYPE_MOUNT_ACL:
+ if (!container_of(ptr, struct tomoyo_mount_acl, head)->
+ is_deleted)
+ count++;
}
}
if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
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.
*
domain->ignore_global_allow_read = !is_delete;
return 0;
}
- return tomoyo_write_file_policy(data, domain, is_delete);
+ return tomoyo_write_domain_policy2(data, domain, is_delete);
}
/**
{
int pos;
u8 bit;
- const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16);
+ const u16 perm = ptr->perm;
for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
if (!(perm & (1 << bit)))
return false;
}
+/**
+ * tomoyo_print_path_number_acl - Print a path_number ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_path_number_acl".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_path_number_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path_number_acl *ptr)
+{
+ int pos;
+ u8 bit;
+ const u8 perm = ptr->perm;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION;
+ bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s",
+ tomoyo_path_number2keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name) ||
+ !tomoyo_print_number_union(head, &ptr->number) ||
+ !tomoyo_io_printf(head, "\n"))
+ goto out;
+ }
+ head->read_bit = 0;
+ return true;
+ out:
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+}
+
+/**
+ * tomoyo_print_path_number3_acl - Print a path_number3 ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_path_number3_acl".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_path_number3_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path_number3_acl *ptr)
+{
+ int pos;
+ u8 bit;
+ const u16 perm = ptr->perm;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER3_OPERATION;
+ bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s",
+ tomoyo_path_number32keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name) ||
+ !tomoyo_print_number_union(head, &ptr->mode) ||
+ !tomoyo_print_number_union(head, &ptr->major) ||
+ !tomoyo_print_number_union(head, &ptr->minor) ||
+ !tomoyo_io_printf(head, "\n"))
+ goto out;
+ }
+ head->read_bit = 0;
+ return true;
+ out:
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+}
+
+/**
+ * tomoyo_print_mount_acl - Print a mount ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_mount_acl".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_mount_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_mount_acl *ptr)
+{
+ const int pos = head->read_avail;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) ||
+ !tomoyo_print_name_union(head, &ptr->dev_name) ||
+ !tomoyo_print_name_union(head, &ptr->dir_name) ||
+ !tomoyo_print_name_union(head, &ptr->fs_type) ||
+ !tomoyo_print_number_union(head, &ptr->flags) ||
+ !tomoyo_io_printf(head, "\n")) {
+ head->read_avail = pos;
+ return false;
+ }
+ return true;
+}
+
/**
* tomoyo_print_entry - Print an ACL entry.
*
= container_of(ptr, struct tomoyo_path2_acl, head);
return tomoyo_print_path2_acl(head, acl);
}
+ if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
+ struct tomoyo_path_number_acl *acl
+ = container_of(ptr, struct tomoyo_path_number_acl,
+ head);
+ return tomoyo_print_path_number_acl(head, acl);
+ }
+ if (acl_type == TOMOYO_TYPE_PATH_NUMBER3_ACL) {
+ struct tomoyo_path_number3_acl *acl
+ = container_of(ptr, struct tomoyo_path_number3_acl,
+ head);
+ return tomoyo_print_path_number3_acl(head, acl);
+ }
+ if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
+ struct tomoyo_mount_acl *acl
+ = container_of(ptr, struct tomoyo_mount_acl, head);
+ return tomoyo_print_mount_acl(head, acl);
+ }
BUG(); /* This must not happen. */
return false;
}
return tomoyo_write_no_rewrite_policy(data, is_delete);
if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP))
return tomoyo_write_path_group_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP))
+ return tomoyo_write_number_group_policy(data, is_delete);
return -EINVAL;
}
head->read_var2 = NULL;
head->read_step = 10;
case 10:
+ if (!tomoyo_read_number_group_policy(head))
+ break;
+ head->read_var1 = NULL;
+ head->read_var2 = NULL;
+ head->read_step = 11;
+ case 11:
head->read_eof = true;
break;
default:
}
}
+/**
+ * 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.
*
if (!head)
return -ENOMEM;
mutex_init(&head->io_sem);
+ head->type = type;
switch (type) {
case TOMOYO_DOMAINPOLICY:
/* /sys/kernel/security/tomoyo/domain_policy */
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;
* 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);
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
*/
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.
*
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;
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,