summaryrefslogtreecommitdiffstats
path: root/security/tomoyo
diff options
context:
space:
mode:
Diffstat (limited to 'security/tomoyo')
-rw-r--r--security/tomoyo/common.c2202
-rw-r--r--security/tomoyo/common.h359
2 files changed, 2561 insertions, 0 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
new file mode 100644
index 000000000000..8bedfb1992e5
--- /dev/null
+++ b/security/tomoyo/common.c
@@ -0,0 +1,2202 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/security.h>
+#include <linux/hardirq.h>
+#include "realpath.h"
+#include "common.h"
+#include "tomoyo.h"
+
+/* Has loading policy done? */
+bool tomoyo_policy_loaded;
+
+/* String table for functionality that takes 4 modes. */
+static const char *tomoyo_mode_4[4] = {
+ "disabled", "learning", "permissive", "enforcing"
+};
+/* String table for functionality that takes 2 modes. */
+static const char *tomoyo_mode_2[4] = {
+ "disabled", "enabled", "enabled", "enabled"
+};
+
+/* Table for profile. */
+static struct {
+ const char *keyword;
+ unsigned int current_value;
+ const unsigned int max_value;
+} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = {
+ [TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 },
+ [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX },
+ [TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 },
+};
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile {
+ unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
+ const struct tomoyo_path_info *comment;
+} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
+/* Permit policy management by non-root user? */
+static bool tomoyo_manage_by_non_root;
+
+/* Utility functions. */
+
+/* Open operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_open_control(const u8 type, struct file *file);
+/* Close /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_close_control(struct file *file);
+/* Read operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+ const int buffer_len);
+/* Write operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+ const int buffer_len);
+
+/**
+ * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ *
+ * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF.
+ * This function verifies that \ooo is in valid range.
+ */
+static inline bool tomoyo_is_byte_range(const char *str)
+{
+ return *str >= '0' && *str++ <= '3' &&
+ *str >= '0' && *str++ <= '7' &&
+ *str >= '0' && *str <= '7';
+}
+
+/**
+ * tomoyo_is_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static inline bool tomoyo_is_alphabet_char(const char c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * tomoyo_make_byte - Make byte value from three octal characters.
+ *
+ * @c1: The first character.
+ * @c2: The second character.
+ * @c3: The third character.
+ *
+ * Returns byte value.
+ */
+static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
+{
+ return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
+}
+
+/**
+ * tomoyo_str_starts - Check whether the given string starts with the given keyword.
+ *
+ * @src: Pointer to pointer to the string.
+ * @find: Pointer to the keyword.
+ *
+ * Returns true if @src starts with @find, false otherwise.
+ *
+ * The @src is updated to point the first character after the @find
+ * if @src starts with @find.
+ */
+static bool tomoyo_str_starts(char **src, const char *find)
+{
+ const int len = strlen(find);
+ char *tmp = *src;
+
+ if (strncmp(tmp, find, len))
+ return false;
+ tmp += len;
+ *src = tmp;
+ return true;
+}
+
+/**
+ * tomoyo_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_normalize_line(unsigned char *buffer)
+{
+ unsigned char *sp = buffer;
+ unsigned char *dp = buffer;
+ bool first = true;
+
+ while (tomoyo_is_invalid(*sp))
+ sp++;
+ while (*sp) {
+ if (!first)
+ *dp++ = ' ';
+ first = false;
+ while (tomoyo_is_valid(*sp))
+ *dp++ = *sp++;
+ while (tomoyo_is_invalid(*sp))
+ sp++;
+ }
+ *dp = '\0';
+}
+
+/**
+ * tomoyo_is_correct_path - Validate a pathname.
+ * @filename: The pathname to check.
+ * @start_type: Should the pathname start with '/'?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @pattern_type: Can the pathname contain a wildcard?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @end_type: Should the pathname end with '/'?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @function: The name of function calling me.
+ *
+ * Check whether the given filename follows the naming rules.
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+ const s8 pattern_type, const s8 end_type,
+ const char *function)
+{
+ bool contains_pattern = false;
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ const char *original_filename = filename;
+
+ if (!filename)
+ goto out;
+ c = *filename;
+ if (start_type == 1) { /* Must start with '/' */
+ if (c != '/')
+ goto out;
+ } else if (start_type == -1) { /* Must not start with '/' */
+ if (c == '/')
+ goto out;
+ }
+ if (c)
+ c = *(filename + strlen(filename) - 1);
+ if (end_type == 1) { /* Must end with '/' */
+ if (c != '/')
+ goto out;
+ } else if (end_type == -1) { /* Must not end with '/' */
+ if (c == '/')
+ goto out;
+ }
+ while ((c = *filename++) != '\0') {
+ if (c == '\\') {
+ switch ((c = *filename++)) {
+ case '\\': /* "\\" */
+ continue;
+ case '$': /* "\$" */
+ case '+': /* "\+" */
+ case '?': /* "\?" */
+ case '*': /* "\*" */
+ case '@': /* "\@" */
+ case 'x': /* "\x" */
+ case 'X': /* "\X" */
+ case 'a': /* "\a" */
+ case 'A': /* "\A" */
+ case '-': /* "\-" */
+ if (pattern_type == -1)
+ break; /* Must not contain pattern */
+ contains_pattern = true;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ d = *filename++;
+ if (d < '0' || d > '7')
+ break;
+ e = *filename++;
+ if (e < '0' || e > '7')
+ break;
+ c = tomoyo_make_byte(c, d, e);
+ if (tomoyo_is_invalid(c))
+ continue; /* pattern is not \000 */
+ }
+ goto out;
+ } else if (tomoyo_is_invalid(c)) {
+ goto out;
+ }
+ }
+ if (pattern_type == 1) { /* Must contain pattern */
+ if (!contains_pattern)
+ goto out;
+ }
+ return true;
+ out:
+ printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
+ original_filename);
+ return false;
+}
+
+/**
+ * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
+ * @domainname: The domainname to check.
+ * @function: The name of function calling me.
+ *
+ * Returns true if @domainname follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+ const char *function)
+{
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ const char *org_domainname = domainname;
+
+ if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
+ TOMOYO_ROOT_NAME_LEN))
+ goto out;
+ domainname += TOMOYO_ROOT_NAME_LEN;
+ if (!*domainname)
+ return true;
+ do {
+ if (*domainname++ != ' ')
+ goto out;
+ if (*domainname++ != '/')
+ goto out;
+ while ((c = *domainname) != '\0' && c != ' ') {
+ domainname++;
+ if (c == '\\') {
+ c = *domainname++;
+ switch ((c)) {
+ case '\\': /* "\\" */
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ d = *domainname++;
+ if (d < '0' || d > '7')
+ break;
+ e = *domainname++;
+ if (e < '0' || e > '7')
+ break;
+ c = tomoyo_make_byte(c, d, e);
+ if (tomoyo_is_invalid(c))
+ /* pattern is not \000 */
+ continue;
+ }
+ goto out;
+ } else if (tomoyo_is_invalid(c)) {
+ goto out;
+ }
+ }
+ } while (*domainname);
+ return true;
+ out:
+ printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,
+ org_domainname);
+ return false;
+}
+
+/**
+ * tomoyo_is_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+bool tomoyo_is_domain_def(const unsigned char *buffer)
+{
+ return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+}
+
+/**
+ * tomoyo_find_domain - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Caller must call down_read(&tomoyo_domain_list_lock); or
+ * down_write(&tomoyo_domain_list_lock); .
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ if (!domain->is_deleted &&
+ !tomoyo_pathcmp(&name, domain->domainname))
+ return domain;
+ }
+ return NULL;
+}
+
+/**
+ * tomoyo_path_depth - Evaluate the number of '/' in a string.
+ *
+ * @pathname: The string to evaluate.
+ *
+ * Returns path depth of the string.
+ *
+ * I score 2 for each of the '/' in the @pathname
+ * and score 1 if the @pathname ends with '/'.
+ */
+static int tomoyo_path_depth(const char *pathname)
+{
+ int i = 0;
+
+ if (pathname) {
+ const char *ep = pathname + strlen(pathname);
+ if (pathname < ep--) {
+ if (*ep != '/')
+ i++;
+ while (pathname <= ep)
+ if (*ep-- == '/')
+ i += 2;
+ }
+ }
+ return i;
+}
+
+/**
+ * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int tomoyo_const_part_length(const char *filename)
+{
+ char c;
+ int len = 0;
+
+ if (!filename)
+ return 0;
+ while ((c = *filename++) != '\0') {
+ if (c != '\\') {
+ len++;
+ continue;
+ }
+ c = *filename++;
+ switch (c) {
+ case '\\': /* "\\" */
+ len += 2;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ len += 4;
+ continue;
+ }
+ break;
+ }
+ return len;
+}
+
+/**
+ * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info" to fill in.
+ *
+ * The caller sets "struct tomoyo_path_info"->name.
+ */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
+{
+ const char *name = ptr->name;
+ const int len = strlen(name);
+
+ ptr->total_len = len;
+ ptr->const_len = tomoyo_const_part_length(name);
+ ptr->is_dir = len && (name[len - 1] == '/');
+ ptr->is_patterned = (ptr->const_len < len);
+ ptr->hash = full_name_hash(name, len);
+ ptr->depth = tomoyo_path_depth(name);
+}
+
+/**
+ * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character
+ * and "\-" pattern.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern2(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ while (filename < filename_end && pattern < pattern_end) {
+ char c;
+ if (*pattern != '\\') {
+ if (*filename++ != *pattern++)
+ return false;
+ continue;
+ }
+ c = *filename;
+ pattern++;
+ switch (*pattern) {
+ int i;
+ int j;
+ case '?':
+ if (c == '/') {
+ return false;
+ } else if (c == '\\') {
+ if (filename[1] == '\\')
+ filename++;
+ else if (tomoyo_is_byte_range(filename + 1))
+ filename += 3;
+ else
+ return false;
+ }
+ break;
+ case '\\':
+ if (c != '\\')
+ return false;
+ if (*++filename != '\\')
+ return false;
+ break;
+ case '+':
+ if (!isdigit(c))
+ return false;
+ break;
+ case 'x':
+ if (!isxdigit(c))
+ return false;
+ break;
+ case 'a':
+ if (!tomoyo_is_alphabet_char(c))
+ return false;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ if (c == '\\' && tomoyo_is_byte_range(filename + 1)
+ && strncmp(filename + 1, pattern, 3) == 0) {
+ filename += 3;
+ pattern += 2;
+ break;
+ }
+ return false; /* Not matched. */
+ case '*':
+ case '@':
+ for (i = 0; i <= filename_end - filename; i++) {
+ if (tomoyo_file_matches_to_pattern2(
+ filename + i, filename_end,
+ pattern + 1, pattern_end))
+ return true;
+ c = filename[i];
+ if (c == '.' && *pattern == '@')
+ break;
+ if (c != '\\')
+ continue;
+ if (filename[i + 1] == '\\')
+ i++;
+ else if (tomoyo_is_byte_range(filename + i + 1))
+ i += 3;
+ else
+ break; /* Bad pattern. */
+ }
+ return false; /* Not matched. */
+ default:
+ j = 0;
+ c = *pattern;
+ if (c == '$') {
+ while (isdigit(filename[j]))
+ j++;
+ } else if (c == 'X') {
+ while (isxdigit(filename[j]))
+ j++;
+ } else if (c == 'A') {
+ while (tomoyo_is_alphabet_char(filename[j]))
+ j++;
+ }
+ for (i = 1; i <= j; i++) {
+ if (tomoyo_file_matches_to_pattern2(
+ filename + i, filename_end,
+ pattern + 1, pattern_end))
+ return true;
+ }
+ return false; /* Not matched or bad pattern. */
+ }
+ filename++;
+ pattern++;
+ }
+ while (*pattern == '\\' &&
+ (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+ pattern += 2;
+ return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ const char *pattern_start = pattern;
+ bool first = true;
+ bool result;
+
+ while (pattern < pattern_end - 1) {
+ /* Split at "\-" pattern. */
+ if (*pattern++ != '\\' || *pattern++ != '-')
+ continue;
+ result = tomoyo_file_matches_to_pattern2(filename,
+ filename_end,
+ pattern_start,
+ pattern - 2);
+ if (first)
+ result = !result;
+ if (result)
+ return false;
+ first = false;
+ pattern_start = pattern;
+ }
+ result = tomoyo_file_matches_to_pattern2(filename, filename_end,
+ pattern_start, pattern_end);
+ return first ? result : !result;
+}
+
+/**
+ * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ * @filename: The filename to check.
+ * @pattern: The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ * \\ \ itself.
+ * \ooo Octal representation of a byte.
+ * \* More than or equals to 0 character other than '/'.
+ * \@ More than or equals to 0 character other than '/' or '.'.
+ * \? 1 byte character other than '/'.
+ * \$ More than or equals to 1 decimal digit.
+ * \+ 1 decimal digit.
+ * \X More than or equals to 1 hexadecimal digit.
+ * \x 1 hexadecimal digit.
+ * \A More than or equals to 1 alphabet character.
+ * \a 1 alphabet character.
+ * \- Subtraction operator.
+ */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+ const struct tomoyo_path_info *pattern)
+{
+ /*
+ if (!filename || !pattern)
+ return false;
+ */
+ const char *f = filename->name;
+ const char *p = pattern->name;
+ const int len = pattern->const_len;
+
+ /* If @pattern doesn't contain pattern, I can use strcmp(). */
+ if (!pattern->is_patterned)
+ return !tomoyo_pathcmp(filename, pattern);
+ /* Dont compare if the number of '/' differs. */
+ if (filename->depth != pattern->depth)
+ return false;
+ /* Compare the initial length without patterns. */
+ if (strncmp(f, p, len))
+ return false;
+ f += len;
+ p += len;
+ /* Main loop. Compare each directory component. */
+ while (*f && *p) {
+ const char *f_delimiter = strchr(f, '/');
+ const char *p_delimiter = strchr(p, '/');
+ if (!f_delimiter)
+ f_delimiter = f + strlen(f);
+ if (!p_delimiter)
+ p_delimiter = p + strlen(p);
+ if (!tomoyo_file_matches_to_pattern(f, f_delimiter,
+ p, p_delimiter))
+ return false;
+ f = f_delimiter;
+ if (*f)
+ f++;
+ p = p_delimiter;
+ if (*p)
+ p++;
+ }
+ /* Ignore trailing "\*" and "\@" in @pattern. */
+ while (*p == '\\' &&
+ (*(p + 1) == '*' || *(p + 1) == '@'))
+ p += 2;
+ return !*f && !*p;
+}
+
+/**
+ * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns true if output was written, false otherwise.
+ *
+ * The snprintf() will truncate, but tomoyo_io_printf() won't.
+ */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int pos = head->read_avail;
+ int size = head->readbuf_size - pos;
+
+ if (size <= 0)
+ return false;
+ va_start(args, fmt);
+ len = vsnprintf(head->read_buf + pos, size, fmt, args);
+ va_end(args);
+ if (pos + len >= head->readbuf_size)
+ return false;
+ head->read_avail += len;
+ return true;
+}
+
+/**
+ * tomoyo_get_exe - Get tomoyo_realpath() of current process.
+ *
+ * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static const char *tomoyo_get_exe(void)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ const char *cp = NULL;
+
+ if (!mm)
+ return NULL;
+ down_read(&mm->mmap_sem);
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+ cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
+ break;
+ }
+ }
+ up_read(&mm->mmap_sem);
+ 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.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @index: The functionality to check mode.
+ *
+ * TOMOYO checks only process context.
+ * This code disables TOMOYO's enforcement in case the function is called from
+ * interrupt context.
+ */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+ const u8 index)
+{
+ const u8 profile = domain->profile;
+
+ if (WARN_ON(in_interrupt()))
+ return 0;
+ return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX
+#if TOMOYO_MAX_PROFILES != 256
+ && profile < TOMOYO_MAX_PROFILES
+#endif
+ && tomoyo_profile_ptr[profile] ?
+ tomoyo_profile_ptr[profile]->value[index] : 0;
+}
+
+/**
+ * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if domain policy violation warning should be printed to
+ * console.
+ */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
+{
+ return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0;
+}
+
+/**
+ * tomoyo_domain_quota_is_ok - Check for domain's quota.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if the domain is not exceeded quota, false otherwise.
+ */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
+{
+ unsigned int count = 0;
+ struct tomoyo_acl_info *ptr;
+
+ if (!domain)
+ return true;
+ down_read(&tomoyo_domain_acl_info_list_lock);
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ if (ptr->type & TOMOYO_ACL_DELETED)
+ continue;
+ switch (tomoyo_acl_type2(ptr)) {
+ struct tomoyo_single_path_acl_record *acl1;
+ struct tomoyo_double_path_acl_record *acl2;
+ u16 perm;
+ case TOMOYO_TYPE_SINGLE_PATH_ACL:
+ acl1 = container_of(ptr,
+ struct tomoyo_single_path_acl_record,
+ head);
+ perm = acl1->perm;
+ if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
+ count++;
+ if (perm &
+ ((1 << TOMOYO_TYPE_READ_ACL) |
+ (1 << TOMOYO_TYPE_WRITE_ACL)))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
+ count++;
+ break;
+ case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+ acl2 = container_of(ptr,
+ struct tomoyo_double_path_acl_record,
+ head);
+ perm = acl2->perm;
+ if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
+ count++;
+ break;
+ }
+ }
+ up_read(&tomoyo_domain_acl_info_list_lock);
+ if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
+ return true;
+ if (!domain->quota_warned) {
+ domain->quota_warned = true;
+ printk(KERN_WARNING "TOMOYO-WARNING: "
+ "Domain '%s' has so many ACLs to hold. "
+ "Stopped learning mode.\n", domain->domainname->name);
+ }
+ return false;
+}
+
+/**
+ * tomoyo_find_or_assign_new_profile - Create a new profile.
+ *
+ * @profile: Profile number to create.
+ *
+ * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
+ */
+static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
+ int profile)
+{
+ static DEFINE_MUTEX(lock);
+ struct tomoyo_profile *ptr = NULL;
+ int i;
+
+ if (profile >= TOMOYO_MAX_PROFILES)
+ return NULL;
+ /***** EXCLUSIVE SECTION START *****/
+ mutex_lock(&lock);
+ ptr = tomoyo_profile_ptr[profile];
+ if (ptr)
+ goto ok;
+ ptr = tomoyo_alloc_element(sizeof(*ptr));
+ if (!ptr)
+ goto ok;
+ for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
+ ptr->value[i] = tomoyo_control_array[i].current_value;
+ mb(); /* Avoid out-of-order execution. */
+ tomoyo_profile_ptr[profile] = ptr;
+ ok:
+ mutex_unlock(&lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return ptr;
+}
+
+/**
+ * tomoyo_write_profile - Write to profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ unsigned int i;
+ unsigned int value;
+ char *cp;
+ struct tomoyo_profile *profile;
+ unsigned long num;
+
+ cp = strchr(data, '-');
+ if (cp)
+ *cp = '\0';
+ if (strict_strtoul(data, 10, &num))
+ return -EINVAL;
+ if (cp)
+ data = cp + 1;
+ profile = tomoyo_find_or_assign_new_profile(num);
+ if (!profile)
+ return -EINVAL;
+ cp = strchr(data, '=');
+ if (!cp)
+ return -EINVAL;
+ *cp = '\0';
+ if (!strcmp(data, "COMMENT")) {
+ profile->comment = tomoyo_save_name(cp + 1);
+ return 0;
+ }
+ for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
+ if (strcmp(data, tomoyo_control_array[i].keyword))
+ continue;
+ if (sscanf(cp + 1, "%u", &value) != 1) {
+ int j;
+ const char **modes;
+ switch (i) {
+ case TOMOYO_VERBOSE:
+ modes = tomoyo_mode_2;
+ break;
+ default:
+ modes = tomoyo_mode_4;
+ break;
+ }
+ for (j = 0; j < 4; j++) {
+ if (strcmp(cp + 1, modes[j]))
+ continue;
+ value = j;
+ break;
+ }
+ if (j == 4)
+ return -EINVAL;
+ } else if (value > tomoyo_control_array[i].max_value) {
+ value = tomoyo_control_array[i].max_value;
+ }
+ profile->value[i] = value;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * tomoyo_read_profile - Read from profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
+{
+ static const int total = TOMOYO_MAX_CONTROL_INDEX + 1;
+ int step;
+
+ if (head->read_eof)
+ return 0;
+ for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total;
+ step++) {
+ const u8 index = step / total;
+ u8 type = step % total;
+ const struct tomoyo_profile *profile
+ = tomoyo_profile_ptr[index];
+ head->read_step = step;
+ if (!profile)
+ continue;
+ if (!type) { /* Print profile' comment tag. */
+ if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n",
+ index, profile->comment ?
+ profile->comment->name : ""))
+ break;
+ continue;
+ }
+ type--;
+ if (type < TOMOYO_MAX_CONTROL_INDEX) {
+ const unsigned int value = profile->value[type];
+ const char **modes = NULL;
+ const char *keyword
+ = tomoyo_control_array[type].keyword;
+ switch (tomoyo_control_array[type].max_value) {
+ case 3:
+ modes = tomoyo_mode_4;
+ break;
+ case 1:
+ modes = tomoyo_mode_2;
+ break;
+ }
+ if (modes) {
+ if (!tomoyo_io_printf(head, "%u-%s=%s\n", index,
+ keyword, modes[value]))
+ break;
+ } else {
+ if (!tomoyo_io_printf(head, "%u-%s=%u\n", index,
+ keyword, value))
+ break;
+ }
+ }
+ }
+ if (step == TOMOYO_MAX_PROFILES * total)
+ head->read_eof = true;
+ return 0;
+}
+
+/* Structure for policy manager. */
+struct tomoyo_policy_manager_entry {
+ struct list_head list;
+ /* A path to program or a domainname. */
+ const struct tomoyo_path_info *manager;
+ bool is_domain; /* True if manager is a domainname. */
+ bool is_deleted; /* True if this entry is deleted. */
+};
+
+/* The list for "struct tomoyo_policy_manager_entry". */
+static LIST_HEAD(tomoyo_policy_manager_list);
+static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
+
+/**
+ * tomoyo_update_manager_entry - Add a manager entry.
+ *
+ * @manager: The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_manager_entry(const char *manager,
+ const bool is_delete)
+{
+ struct tomoyo_policy_manager_entry *new_entry;
+ struct tomoyo_policy_manager_entry *ptr;
+ const struct tomoyo_path_info *saved_manager;
+ int error = -ENOMEM;
+ bool is_domain = false;
+
+ if (tomoyo_is_domain_def(manager)) {
+ if (!tomoyo_is_correct_domain(manager, __func__))
+ return -EINVAL;
+ is_domain = true;
+ } else {
+ if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__))
+ return -EINVAL;
+ }
+ saved_manager = tomoyo_save_name(manager);
+ if (!saved_manager)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_manager_list_lock);
+ list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ if (ptr->manager != saved_manager)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->manager = saved_manager;
+ new_entry->is_domain = is_domain;
+ list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_policy_manager_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_write_manager_policy - Write manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+ if (!strcmp(data, "manage_by_non_root")) {
+ tomoyo_manage_by_non_root = !is_delete;
+ return 0;
+ }
+ return tomoyo_update_manager_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_manager_policy - Read manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ if (head->read_eof)
+ return 0;
+ down_read(&tomoyo_policy_manager_list_lock);
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_policy_manager_list) {
+ struct tomoyo_policy_manager_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, "%s\n", ptr->manager->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_policy_manager_list_lock);
+ head->read_eof = done;
+ return 0;
+}
+
+/**
+ * tomoyo_is_policy_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/tomoyo/ interface.
+ */
+static bool tomoyo_is_policy_manager(void)
+{
+ struct tomoyo_policy_manager_entry *ptr;
+ const char *exe;
+ const struct task_struct *task = current;
+ const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname;
+ bool found = false;
+
+ if (!tomoyo_policy_loaded)
+ return true;
+ if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
+ return false;
+ down_read(&tomoyo_policy_manager_list_lock);
+ list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ if (!ptr->is_deleted && ptr->is_domain
+ && !tomoyo_pathcmp(domainname, ptr->manager)) {
+ found = true;
+ break;
+ }
+ }
+ up_read(&tomoyo_policy_manager_list_lock);
+ if (found)
+ return true;
+ exe = tomoyo_get_exe();
+ if (!exe)
+ return false;
+ down_read(&tomoyo_policy_manager_list_lock);
+ list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ if (!ptr->is_deleted && !ptr->is_domain
+ && !strcmp(exe, ptr->manager->name)) {
+ found = true;
+ break;
+ }
+ }
+ up_read(&tomoyo_policy_manager_list_lock);
+ if (!found) { /* Reduce error messages. */
+ static pid_t last_pid;
+ const pid_t pid = current->pid;
+ if (last_pid != pid) {
+ printk(KERN_WARNING "%s ( %s ) is not permitted to "
+ "update policies.\n", domainname->name, exe);
+ last_pid = pid;
+ }
+ }
+ tomoyo_free(exe);
+ return found;
+}
+
+/**
+ * tomoyo_is_select_one - Parse select command.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
+ const char *data)
+{
+ unsigned int pid;
+ struct tomoyo_domain_info *domain = NULL;
+
+ if (sscanf(data, "pid=%u", &pid) == 1) {
+ struct task_struct *p;
+ /***** CRITICAL SECTION START *****/
+ read_lock(&tasklist_lock);
+ p = find_task_by_vpid(pid);
+ if (p)
+ domain =