diff options
Diffstat (limited to 'security/tomoyo/common.c')
| -rw-r--r-- | security/tomoyo/common.c | 3845 |
1 files changed, 2159 insertions, 1686 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index e0d0354008b..283862aebdc 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1,942 +1,657 @@ /* * security/tomoyo/common.c * - * Common functions for TOMOYO. - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 - * + * Copyright (C) 2005-2011 NTT DATA CORPORATION */ #include <linux/uaccess.h> +#include <linux/slab.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 operation mode. */ +const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = { + [TOMOYO_CONFIG_DISABLED] = "disabled", + [TOMOYO_CONFIG_LEARNING] = "learning", + [TOMOYO_CONFIG_PERMISSIVE] = "permissive", + [TOMOYO_CONFIG_ENFORCING] = "enforcing" +}; -/* String table for functionality that takes 4 modes. */ -static const char *tomoyo_mode_4[4] = { - "disabled", "learning", "permissive", "enforcing" +/* String table for /sys/kernel/security/tomoyo/profile */ +const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + /* CONFIG::file group */ + [TOMOYO_MAC_FILE_EXECUTE] = "execute", + [TOMOYO_MAC_FILE_OPEN] = "open", + [TOMOYO_MAC_FILE_CREATE] = "create", + [TOMOYO_MAC_FILE_UNLINK] = "unlink", + [TOMOYO_MAC_FILE_GETATTR] = "getattr", + [TOMOYO_MAC_FILE_MKDIR] = "mkdir", + [TOMOYO_MAC_FILE_RMDIR] = "rmdir", + [TOMOYO_MAC_FILE_MKFIFO] = "mkfifo", + [TOMOYO_MAC_FILE_MKSOCK] = "mksock", + [TOMOYO_MAC_FILE_TRUNCATE] = "truncate", + [TOMOYO_MAC_FILE_SYMLINK] = "symlink", + [TOMOYO_MAC_FILE_MKBLOCK] = "mkblock", + [TOMOYO_MAC_FILE_MKCHAR] = "mkchar", + [TOMOYO_MAC_FILE_LINK] = "link", + [TOMOYO_MAC_FILE_RENAME] = "rename", + [TOMOYO_MAC_FILE_CHMOD] = "chmod", + [TOMOYO_MAC_FILE_CHOWN] = "chown", + [TOMOYO_MAC_FILE_CHGRP] = "chgrp", + [TOMOYO_MAC_FILE_IOCTL] = "ioctl", + [TOMOYO_MAC_FILE_CHROOT] = "chroot", + [TOMOYO_MAC_FILE_MOUNT] = "mount", + [TOMOYO_MAC_FILE_UMOUNT] = "unmount", + [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", + /* CONFIG::network group */ + [TOMOYO_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind", + [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen", + [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect", + [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind", + [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send", + [TOMOYO_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind", + [TOMOYO_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send", + [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind", + [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen", + [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect", + [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind", + [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send", + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", + /* CONFIG::misc group */ + [TOMOYO_MAC_ENVIRON] = "env", + /* CONFIG group */ + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc", }; -/* String table for functionality that takes 2 modes. */ -static const char *tomoyo_mode_2[4] = { - "disabled", "enabled", "enabled", "enabled" + +/* String table for conditions. */ +const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = { + [TOMOYO_TASK_UID] = "task.uid", + [TOMOYO_TASK_EUID] = "task.euid", + [TOMOYO_TASK_SUID] = "task.suid", + [TOMOYO_TASK_FSUID] = "task.fsuid", + [TOMOYO_TASK_GID] = "task.gid", + [TOMOYO_TASK_EGID] = "task.egid", + [TOMOYO_TASK_SGID] = "task.sgid", + [TOMOYO_TASK_FSGID] = "task.fsgid", + [TOMOYO_TASK_PID] = "task.pid", + [TOMOYO_TASK_PPID] = "task.ppid", + [TOMOYO_EXEC_ARGC] = "exec.argc", + [TOMOYO_EXEC_ENVC] = "exec.envc", + [TOMOYO_TYPE_IS_SOCKET] = "socket", + [TOMOYO_TYPE_IS_SYMLINK] = "symlink", + [TOMOYO_TYPE_IS_FILE] = "file", + [TOMOYO_TYPE_IS_BLOCK_DEV] = "block", + [TOMOYO_TYPE_IS_DIRECTORY] = "directory", + [TOMOYO_TYPE_IS_CHAR_DEV] = "char", + [TOMOYO_TYPE_IS_FIFO] = "fifo", + [TOMOYO_MODE_SETUID] = "setuid", + [TOMOYO_MODE_SETGID] = "setgid", + [TOMOYO_MODE_STICKY] = "sticky", + [TOMOYO_MODE_OWNER_READ] = "owner_read", + [TOMOYO_MODE_OWNER_WRITE] = "owner_write", + [TOMOYO_MODE_OWNER_EXECUTE] = "owner_execute", + [TOMOYO_MODE_GROUP_READ] = "group_read", + [TOMOYO_MODE_GROUP_WRITE] = "group_write", + [TOMOYO_MODE_GROUP_EXECUTE] = "group_execute", + [TOMOYO_MODE_OTHERS_READ] = "others_read", + [TOMOYO_MODE_OTHERS_WRITE] = "others_write", + [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute", + [TOMOYO_EXEC_REALPATH] = "exec.realpath", + [TOMOYO_SYMLINK_TARGET] = "symlink.target", + [TOMOYO_PATH1_UID] = "path1.uid", + [TOMOYO_PATH1_GID] = "path1.gid", + [TOMOYO_PATH1_INO] = "path1.ino", + [TOMOYO_PATH1_MAJOR] = "path1.major", + [TOMOYO_PATH1_MINOR] = "path1.minor", + [TOMOYO_PATH1_PERM] = "path1.perm", + [TOMOYO_PATH1_TYPE] = "path1.type", + [TOMOYO_PATH1_DEV_MAJOR] = "path1.dev_major", + [TOMOYO_PATH1_DEV_MINOR] = "path1.dev_minor", + [TOMOYO_PATH2_UID] = "path2.uid", + [TOMOYO_PATH2_GID] = "path2.gid", + [TOMOYO_PATH2_INO] = "path2.ino", + [TOMOYO_PATH2_MAJOR] = "path2.major", + [TOMOYO_PATH2_MINOR] = "path2.minor", + [TOMOYO_PATH2_PERM] = "path2.perm", + [TOMOYO_PATH2_TYPE] = "path2.type", + [TOMOYO_PATH2_DEV_MAJOR] = "path2.dev_major", + [TOMOYO_PATH2_DEV_MINOR] = "path2.dev_minor", + [TOMOYO_PATH1_PARENT_UID] = "path1.parent.uid", + [TOMOYO_PATH1_PARENT_GID] = "path1.parent.gid", + [TOMOYO_PATH1_PARENT_INO] = "path1.parent.ino", + [TOMOYO_PATH1_PARENT_PERM] = "path1.parent.perm", + [TOMOYO_PATH2_PARENT_UID] = "path2.parent.uid", + [TOMOYO_PATH2_PARENT_GID] = "path2.parent.gid", + [TOMOYO_PATH2_PARENT_INO] = "path2.parent.ino", + [TOMOYO_PATH2_PARENT_PERM] = "path2.parent.perm", }; -/* - * tomoyo_control_array is a static data which contains - * - * (1) functionality name used by /sys/kernel/security/tomoyo/profile . - * (2) initial values for "struct tomoyo_profile". - * (3) max values for "struct tomoyo_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 }, +/* String table for PREFERENCE keyword. */ +static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = { + [TOMOYO_PREF_MAX_AUDIT_LOG] = "max_audit_log", + [TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry", }; -/* - * tomoyo_profile is a structure which is used for holding the mode of access - * controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing. - * An administrator can define up to 256 profiles. - * The ->profile of "struct tomoyo_domain_info" is used for remembering - * the profile's number (0 - 255) assigned to that domain. - */ -static struct tomoyo_profile { - unsigned int value[TOMOYO_MAX_CONTROL_INDEX]; - const struct tomoyo_path_info *comment; -} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; +/* String table for path operation. */ +const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { + [TOMOYO_TYPE_EXECUTE] = "execute", + [TOMOYO_TYPE_READ] = "read", + [TOMOYO_TYPE_WRITE] = "write", + [TOMOYO_TYPE_APPEND] = "append", + [TOMOYO_TYPE_UNLINK] = "unlink", + [TOMOYO_TYPE_GETATTR] = "getattr", + [TOMOYO_TYPE_RMDIR] = "rmdir", + [TOMOYO_TYPE_TRUNCATE] = "truncate", + [TOMOYO_TYPE_SYMLINK] = "symlink", + [TOMOYO_TYPE_CHROOT] = "chroot", + [TOMOYO_TYPE_UMOUNT] = "unmount", +}; + +/* String table for socket's operation. */ +const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = { + [TOMOYO_NETWORK_BIND] = "bind", + [TOMOYO_NETWORK_LISTEN] = "listen", + [TOMOYO_NETWORK_CONNECT] = "connect", + [TOMOYO_NETWORK_SEND] = "send", +}; + +/* String table for categories. */ +static const char * const tomoyo_category_keywords +[TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_CATEGORY_FILE] = "file", + [TOMOYO_MAC_CATEGORY_NETWORK] = "network", + [TOMOYO_MAC_CATEGORY_MISC] = "misc", +}; /* 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. + * tomoyo_yesno - Return "yes" or "no". * - * Returns true if @c is an alphabet character, false otherwise. + * @value: Bool value. */ -static inline bool tomoyo_is_alphabet_char(const char c) +const char *tomoyo_yesno(const unsigned int value) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + return value ? "yes" : "no"; } /** - * tomoyo_make_byte - Make byte value from three octal characters. + * tomoyo_addprintf - strncat()-like-snprintf(). * - * @c1: The first character. - * @c2: The second character. - * @c3: The third character. + * @buffer: Buffer to write to. Must be '\0'-terminated. + * @len: Size of @buffer. + * @fmt: The printf()'s format string, followed by parameters. * - * Returns byte value. + * Returns nothing. */ -static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) +static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) { - return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); + va_list args; + const int pos = strlen(buffer); + va_start(args, fmt); + vsnprintf(buffer + pos, len - pos - 1, fmt, args); + va_end(args); } /** - * tomoyo_str_starts - Check whether the given string starts with the given keyword. + * tomoyo_flush - Flush queued string to userspace's buffer. * - * @src: Pointer to pointer to the string. - * @find: Pointer to the keyword. + * @head: Pointer to "struct tomoyo_io_buffer". * - * 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. + * Returns true if all data was flushed, false otherwise. */ -static bool tomoyo_str_starts(char **src, const char *find) +static bool tomoyo_flush(struct tomoyo_io_buffer *head) { - const int len = strlen(find); - char *tmp = *src; - - if (strncmp(tmp, find, len)) - return false; - tmp += len; - *src = tmp; + while (head->r.w_pos) { + const char *w = head->r.w[0]; + size_t len = strlen(w); + if (len) { + if (len > head->read_user_buf_avail) + len = head->read_user_buf_avail; + if (!len) + return false; + if (copy_to_user(head->read_user_buf, w, len)) + return false; + head->read_user_buf_avail -= len; + head->read_user_buf += len; + w += len; + } + head->r.w[0] = w; + if (*w) + return false; + /* Add '\0' for audit logs and query. */ + if (head->poll) { + if (!head->read_user_buf_avail || + copy_to_user(head->read_user_buf, "", 1)) + return false; + head->read_user_buf_avail--; + head->read_user_buf++; + } + head->r.w_pos--; + for (len = 0; len < head->r.w_pos; len++) + head->r.w[len] = head->r.w[len + 1]; + } + head->r.avail = 0; return true; } /** - * tomoyo_normalize_line - Format string. - * - * @buffer: The line to normalize. + * tomoyo_set_string - Queue string to "struct tomoyo_io_buffer" structure. * - * Leading and trailing whitespaces are removed. - * Multiple whitespaces are packed into single space. + * @head: Pointer to "struct tomoyo_io_buffer". + * @string: String to print. * - * Returns nothing. + * Note that @string has to be kept valid until @head is kfree()d. + * This means that char[] allocated on stack memory cannot be passed to + * this function. Use tomoyo_io_printf() for char[] allocated on stack memory. */ -static void tomoyo_normalize_line(unsigned char *buffer) +static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) { - 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'; + if (head->r.w_pos < TOMOYO_MAX_IO_READ_QUEUE) { + head->r.w[head->r.w_pos++] = string; + tomoyo_flush(head); + } else + WARN_ON(1); } -/** - * 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) -{ - const char *const start = filename; - bool in_repetition = false; - 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 (1) { - c = *filename++; - if (!c) - break; - if (c == '\\') { - c = *filename++; - switch (c) { - 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 '{': /* "/\{" */ - if (filename - 3 < start || - *(filename - 3) != '/') - break; - if (pattern_type == -1) - break; /* Must not contain pattern */ - contains_pattern = true; - in_repetition = true; - continue; - case '}': /* "\}/" */ - if (*filename != '/') - break; - if (!in_repetition) - break; - in_repetition = false; - 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 (in_repetition && c == '/') { - goto out; - } else if (tomoyo_is_invalid(c)) { - goto out; - } - } - if (pattern_type == 1) { /* Must contain pattern */ - if (!contains_pattern) - goto out; - } - if (in_repetition) - goto out; - return true; - out: - printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, - original_filename); - return false; -} +static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, + ...) __printf(2, 3); /** - * 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. + * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. * - * Returns true if @domainname follows the naming rules, false otherwise. + * @head: Pointer to "struct tomoyo_io_buffer". + * @fmt: The printf()'s format string, followed by parameters. */ -bool tomoyo_is_correct_domain(const unsigned char *domainname, - const char *function) +static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, + ...) { - 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; + va_list args; + size_t len; + size_t pos = head->r.avail; + int size = head->readbuf_size - pos; + if (size <= 0) + return; + va_start(args, fmt); + len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; + va_end(args); + if (pos + len >= head->readbuf_size) { + WARN_ON(1); + return; + } + head->r.avail += len; + tomoyo_set_string(head, head->read_buf + pos); } /** - * tomoyo_is_domain_def - Check whether the given token can be a domainname. + * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure. * - * @buffer: The token to check. + * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns true if @buffer possibly be a domainname, false otherwise. + * Returns nothing. */ -bool tomoyo_is_domain_def(const unsigned char *buffer) +static void tomoyo_set_space(struct tomoyo_io_buffer *head) { - return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); + tomoyo_set_string(head, " "); } /** - * tomoyo_find_domain - Find a domain by the given name. - * - * @domainname: The domainname to find. + * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure. * - * Caller must call down_read(&tomoyo_domain_list_lock); or - * down_write(&tomoyo_domain_list_lock); . + * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. + * Returns nothing. */ -struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) +static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) { - 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_set_string(head, "\n"); + return !head->r.w_pos; } /** - * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. + * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure. * - * @filename: The string to evaluate. + * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns the initial length without a pattern in @filename. + * Returns nothing. */ -static int tomoyo_const_part_length(const char *filename) +static void tomoyo_set_slash(struct tomoyo_io_buffer *head) { - 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_set_string(head, "/"); } +/* List of namespaces. */ +LIST_HEAD(tomoyo_namespace_list); +/* True if namespace other than tomoyo_kernel_namespace is defined. */ +static bool tomoyo_namespace_enabled; + /** - * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. + * tomoyo_init_policy_namespace - Initialize namespace. * - * @ptr: Pointer to "struct tomoyo_path_info" to fill in. + * @ns: Pointer to "struct tomoyo_policy_namespace". * - * The caller sets "struct tomoyo_path_info"->name. + * Returns nothing. */ -void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) +void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) { - const char *name = ptr->name; - const int len = strlen(name); - - 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); + unsigned int idx; + for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++) + INIT_LIST_HEAD(&ns->acl_group[idx]); + for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) + INIT_LIST_HEAD(&ns->group_list[idx]); + for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) + INIT_LIST_HEAD(&ns->policy_list[idx]); + ns->profile_version = 20110903; + tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); + list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); } /** - * tomoyo_file_matches_pattern2 - Pattern matching without '/' character - * and "\-" pattern. + * tomoyo_print_namespace - Print namespace header. * - * @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. + * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns true if @filename matches @pattern, false otherwise. + * Returns nothing. */ -static bool tomoyo_file_matches_pattern2(const char *filename, - const char *filename_end, - const char *pattern, - const char *pattern_end) +static void tomoyo_print_namespace(struct tomoyo_io_buffer *head) { - 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_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_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; + if (!tomoyo_namespace_enabled) + return; + tomoyo_set_string(head, + container_of(head->r.ns, + struct tomoyo_policy_namespace, + namespace_list)->name); + tomoyo_set_space(head); } /** - * tomoyo_file_matches_pattern - Pattern matching without without '/' character. + * tomoyo_print_name_union - Print a tomoyo_name_union. * - * @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. + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_name_union". */ -static bool tomoyo_file_matches_pattern(const char *filename, - const char *filename_end, - const char *pattern, - const char *pattern_end) +static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, + const struct tomoyo_name_union *ptr) { - 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_pattern2(filename, - filename_end, - pattern_start, - pattern - 2); - if (first) - result = !result; - if (result) - return false; - first = false; - pattern_start = pattern; + tomoyo_set_space(head); + if (ptr->group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->group->group_name->name); + } else { + tomoyo_set_string(head, ptr->filename->name); } - result = tomoyo_file_matches_pattern2(filename, filename_end, - pattern_start, pattern_end); - return first ? result : !result; } /** - * tomoyo_path_matches_pattern2 - Do pathname pattern matching. + * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote. * - * @f: The start of string to check. - * @p: The start of pattern to compare. + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_name_union". * - * Returns true if @f matches @p, false otherwise. + * Returns nothing. */ -static bool tomoyo_path_matches_pattern2(const char *f, const char *p) +static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head, + const struct tomoyo_name_union *ptr) { - const char *f_delimiter; - const char *p_delimiter; - - while (*f && *p) { - f_delimiter = strchr(f, '/'); - if (!f_delimiter) - f_delimiter = f + strlen(f); - p_delimiter = strchr(p, '/'); - if (!p_delimiter) - p_delimiter = p + strlen(p); - if (*p == '\\' && *(p + 1) == '{') - goto recursive; - if (!tomoyo_file_matches_pattern(f, f_delimiter, p, - p_delimiter)) - return false; - f = f_delimiter; - if (*f) - f++; - p = p_delimiter; - if (*p) - p++; + if (ptr->group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->group->group_name->name); + } else { + tomoyo_set_string(head, "\""); + tomoyo_set_string(head, ptr->filename->name); + tomoyo_set_string(head, "\""); } - /* Ignore trailing "\*" and "\@" in @pattern. */ - while (*p == '\\' && - (*(p + 1) == '*' || *(p + 1) == '@')) - p += 2; - return !*f && !*p; - recursive: - /* - * The "\{" pattern is permitted only after '/' character. - * This guarantees that below "*(p - 1)" is safe. - * Also, the "\}" pattern is permitted only before '/' character - * so that "\{" + "\}" pair will not break the "\-" operator. - */ - if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || - *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') - return false; /* Bad pattern. */ - do { - /* Compare current component with pattern. */ - if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, - p_delimiter - 2)) - break; - /* Proceed to next component. */ - f = f_delimiter; - if (!*f) - break; - f++; - /* Continue comparison. */ - if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) - return true; - f_delimiter = strchr(f, '/'); - } while (f_delimiter); - return false; /* Not matched. */ } /** - * 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. - * \* Zero or more repetitions of characters other than '/'. - * \@ Zero or more repetitions of characters other than '/' or '.'. - * \? 1 byte character other than '/'. - * \$ One or more repetitions of decimal digits. - * \+ 1 decimal digit. - * \X One or more repetitions of hexadecimal digits. - * \x 1 hexadecimal digit. - * \A One or more repetitions of alphabet characters. - * \a 1 alphabet character. + * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space. * - * \- Subtraction operator. + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_number_union". * - * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ - * /dir/dir/dir/ ). + * Returns nothing. */ -bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, - const struct tomoyo_path_info *pattern) +static void tomoyo_print_number_union_nospace +(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr) { - 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); - /* Don't compare directory and non-directory. */ - if (filename->is_dir != pattern->is_dir) - return false; - /* Compare the initial length without patterns. */ - if (strncmp(f, p, len)) - return false; - f += len; - p += len; - return tomoyo_path_matches_pattern2(f, p); + if (ptr->group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->group->group_name->name); + } else { + int i; + unsigned long min = ptr->values[0]; + const unsigned long max = ptr->values[1]; + u8 min_type = ptr->value_type[0]; + const u8 max_type = ptr->value_type[1]; + char buffer[128]; + buffer[0] = '\0'; + for (i = 0; i < 2; i++) { + switch (min_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + tomoyo_addprintf(buffer, sizeof(buffer), + "0x%lX", min); + break; + case TOMOYO_VALUE_TYPE_OCTAL: + tomoyo_addprintf(buffer, sizeof(buffer), + "0%lo", min); + break; + default: + tomoyo_addprintf(buffer, sizeof(buffer), "%lu", + min); + break; + } + if (min == max && min_type == max_type) + break; + tomoyo_addprintf(buffer, sizeof(buffer), "-"); + min_type = max_type; + min = max; + } + tomoyo_io_printf(head, "%s", buffer); + } } /** - * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure. + * tomoyo_print_number_union - Print a tomoyo_number_union. * * @head: Pointer to "struct tomoyo_io_buffer". - * @fmt: The printf()'s format string, followed by parameters. + * @ptr: Pointer to "struct tomoyo_number_union". * - * Returns true if output was written, false otherwise. - * - * The snprintf() will truncate, but tomoyo_io_printf() won't. + * Returns nothing. */ -bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) +static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, + const struct tomoyo_number_union *ptr) { - 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_set_space(head); + tomoyo_print_number_union_nospace(head, ptr); } /** - * tomoyo_get_exe - Get tomoyo_realpath() of current process. + * tomoyo_assign_profile - Create a new profile. * - * Returns the tomoyo_realpath() of current process on success, NULL otherwise. + * @ns: Pointer to "struct tomoyo_policy_namespace". + * @profile: Profile number to create. * - * This function uses tomoyo_alloc(), so the caller must call tomoyo_free() - * if this function didn't return NULL. + * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. */ -static const char *tomoyo_get_exe(void) +static struct tomoyo_profile *tomoyo_assign_profile +(struct tomoyo_policy_namespace *ns, const unsigned int profile) { - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - const char *cp = NULL; - - if (!mm) + struct tomoyo_profile *ptr; + struct tomoyo_profile *entry; + if (profile >= TOMOYO_MAX_PROFILES) 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; - } + ptr = ns->profile_ptr[profile]; + if (ptr) + return ptr; + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + ptr = ns->profile_ptr[profile]; + if (!ptr && tomoyo_memory_ok(entry)) { + ptr = entry; + ptr->default_config = TOMOYO_CONFIG_DISABLED | + TOMOYO_CONFIG_WANT_GRANT_LOG | + TOMOYO_CONFIG_WANT_REJECT_LOG; + memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, + sizeof(ptr->config)); + ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = + CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG; + ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = + CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY; + mb(); /* Avoid out-of-order execution. */ + ns->profile_ptr[profile] = ptr; + entry = NULL; } - up_read(&mm->mmap_sem); - return cp; + mutex_unlock(&tomoyo_policy_lock); + out: + kfree(entry); + return ptr; } /** - * tomoyo_get_msg - Get warning message. + * tomoyo_profile - Find a profile. * - * @is_enforce: Is it enforcing mode? + * @ns: Pointer to "struct tomoyo_policy_namespace". + * @profile: Profile number to find. * - * Returns "ERROR" or "WARNING". + * Returns pointer to "struct tomoyo_profile". */ -const char *tomoyo_get_msg(const bool is_enforce) +struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, + const u8 profile) { - if (is_enforce) - return "ERROR"; - else - return "WARNING"; + static struct tomoyo_profile tomoyo_null_profile; + struct tomoyo_profile *ptr = ns->profile_ptr[profile]; + if (!ptr) + ptr = &tomoyo_null_profile; + return ptr; } /** - * tomoyo_check_flags - Check mode for specified functionality. + * tomoyo_find_yesno - Find values for specified keyword. * - * @domain: Pointer to "struct tomoyo_domain_info". - * @index: The functionality to check mode. + * @string: String to check. + * @find: Name of keyword. * - * TOMOYO checks only process context. - * This code disables TOMOYO's enforcement in case the function is called from - * interrupt context. + * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise. */ -unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, - const u8 index) +static s8 tomoyo_find_yesno(const char *string, const char *find) { - 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; + const char *cp = strstr(string, find); + if (cp) { + cp += strlen(find); + if (!strncmp(cp, "=yes", 4)) + return 1; + else if (!strncmp(cp, "=no", 3)) + return 0; + } + return -1; } /** - * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode. + * tomoyo_set_uint - Set value for specified preference. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @i: Pointer to "unsigned int". + * @string: String to check. + * @find: Name of keyword. * - * Returns true if domain policy violation warning should be printed to - * console. + * Returns nothing. */ -bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) +static void tomoyo_set_uint(unsigned int *i, const char *string, + const char *find) { - return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0; + const char *cp = strstr(string, find); + if (cp) + sscanf(cp + strlen(find), "=%u", i); } /** - * tomoyo_domain_quota_is_ok - Check for domain's quota. + * tomoyo_set_mode - Set mode for specified profile. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @name: Name of functionality. + * @value: Mode for @name. + * @profile: Pointer to "struct tomoyo_profile". * - * Returns true if the domain is not exceeded quota, false otherwise. + * Returns 0 on success, negative value otherwise. */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) +static int tomoyo_set_mode(char *name, const char *value, + struct tomoyo_profile *profile) { - 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++; + u8 i; + u8 config; + if (!strcmp(name, "CONFIG")) { + i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; + config = profile->default_config; + } else if (tomoyo_str_starts(&name, "CONFIG::")) { + config = 0; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + int len = 0; + if (i < TOMOYO_MAX_MAC_INDEX) { + const u8 c = tomoyo_index2category[i]; + const char *category = + tomoyo_category_keywords[c]; + len = strlen(category); + if (strncmp(name, category, len) || + name[len++] != ':' || name[len++] != ':') + continue; + } + if (strcmp(name + len, tomoyo_mac_keywords[i])) + continue; + config = profile->config[i]; break; } + if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + return -EINVAL; + } else { + return -EINVAL; } - 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); + if (strstr(value, "use_default")) { + config = TOMOYO_CONFIG_USE_DEFAULT; + } else { + u8 mode; + for (mode = 0; mode < 4; mode++) + if (strstr(value, tomoyo_mode[mode])) + /* + * Update lower 3 bits in order to distinguish + * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. + */ + config = (config & ~7) | mode; + if (config != TOMOYO_CONFIG_USE_DEFAULT) { + switch (tomoyo_find_yesno(value, "grant_log")) { + case 1: + config |= TOMOYO_CONFIG_WANT_GRANT_LOG; + break; + case 0: + config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG; + break; + } + switch (tomoyo_find_yesno(value, "reject_log")) { + case 1: + config |= TOMOYO_CONFIG_WANT_REJECT_LOG; + break; + case 0: + config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG; + break; + } + } } - 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; - 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); - return ptr; + if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + profile->config[i] = config; + else if (config != TOMOYO_CONFIG_USE_DEFAULT) + profile->default_config = config; + return 0; } /** - * tomoyo_write_profile - Write to profile table. + * tomoyo_write_profile - Write profile table. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -946,173 +661,173 @@ 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)) + if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version) + == 1) + return 0; + i = simple_strtoul(data, &cp, 10); + if (*cp != '-') return -EINVAL; - if (cp) - data = cp + 1; - profile = tomoyo_find_or_assign_new_profile(num); + data = cp + 1; + profile = tomoyo_assign_profile(head->w.ns, i); if (!profile) return -EINVAL; cp = strchr(data, '='); if (!cp) return -EINVAL; - *cp = '\0'; + *cp++ = '\0'; if (!strcmp(data, "COMMENT")) { - profile->comment = tomoyo_save_name(cp + 1); + static DEFINE_SPINLOCK(lock); + const struct tomoyo_path_info *new_comment + = tomoyo_get_name(cp); + const struct tomoyo_path_info *old_comment; + if (!new_comment) + return -ENOMEM; + spin_lock(&lock); + old_comment = profile->comment; + profile->comment = new_comment; + spin_unlock(&lock); + tomoyo_put_name(old_comment); 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; + if (!strcmp(data, "PREFERENCE")) { + for (i = 0; i < TOMOYO_MAX_PREF; i++) + tomoyo_set_uint(&profile->pref[i], cp, + tomoyo_pref_keywords[i]); return 0; } - return -EINVAL; + return tomoyo_set_mode(data, cp, profile); } /** - * tomoyo_read_profile - Read from profile table. + * tomoyo_print_config - Print mode for specified functionality. * - * @head: Pointer to "struct tomoyo_io_buffer". + * @head: Pointer to "struct tomoyo_io_buffer". + * @config: Mode for that functionality. * - * Returns 0. + * Returns nothing. + * + * Caller prints functionality's name. */ -static int tomoyo_read_profile(struct tomoyo_io_buffer *head) +static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) { - static const int total = TOMOYO_MAX_CONTROL_INDEX + 1; - int step; + tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", + tomoyo_mode[config & 3], + tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG), + tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG)); +} - 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 : "")) +/** + * tomoyo_read_profile - Read profile table. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_read_profile(struct tomoyo_io_buffer *head) +{ + u8 index; + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + const struct tomoyo_profile *profile; + if (head->r.eof) + return; + next: + index = head->r.index; + profile = ns->profile_ptr[index]; + switch (head->r.step) { + case 0: + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "PROFILE_VERSION=%u\n", + ns->profile_version); + head->r.step++; + break; + case 1: + for ( ; head->r.index < TOMOYO_MAX_PROFILES; + head->r.index++) + if (ns->profile_ptr[head->r.index]) break; - continue; + if (head->r.index == TOMOYO_MAX_PROFILES) { + head->r.eof = true; + return; } - 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; - } + head->r.step++; + break; + case 2: + { + u8 i; + const struct tomoyo_path_info *comment = + profile->comment; + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "%u-COMMENT=", index); + tomoyo_set_string(head, comment ? comment->name : ""); + tomoyo_set_lf(head); + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "%u-PREFERENCE={ ", index); + for (i = 0; i < TOMOYO_MAX_PREF; i++) + tomoyo_io_printf(head, "%s=%u ", + tomoyo_pref_keywords[i], + profile->pref[i]); + tomoyo_set_string(head, "}\n"); + head->r.step++; + } + break; + case 3: + { + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "%u-%s", index, "CONFIG"); + tomoyo_print_config(head, profile->default_config); + head->r.bit = 0; + head->r.step++; + } + break; + case 4: + for ( ; head->r.bit < TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) { + const u8 i = head->r.bit; + const u8 config = profile->config[i]; + if (config == TOMOYO_CONFIG_USE_DEFAULT) + continue; + tomoyo_print_namespace(head); + if (i < TOMOYO_MAX_MAC_INDEX) + tomoyo_io_printf(head, "%u-CONFIG::%s::%s", + index, + tomoyo_category_keywords + [tomoyo_index2category[i]], + tomoyo_mac_keywords[i]); + else + tomoyo_io_printf(head, "%u-CONFIG::%s", index, + tomoyo_mac_keywords[i]); + tomoyo_print_config(head, config); + head->r.bit++; + break; + } + if (head->r.bit == TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX) { + head->r.index++; + head->r.step = 1; } + break; } - if (step == TOMOYO_MAX_PROFILES * total) - head->read_eof = true; - return 0; + if (tomoyo_flush(head)) + goto next; } -/* - * tomoyo_policy_manager_entry is a structure which is used for holding list of - * domainnames or programs which are permitted to modify configuration via - * /sys/kernel/security/tomoyo/ interface. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_policy_manager_list . - * (2) "manager" is a domainname or a program's pathname. - * (3) "is_domain" is a bool which is true if "manager" is a domainname, false - * otherwise. - * (4) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - */ -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. */ -}; - -/* - * tomoyo_policy_manager_list is used for holding list of domainnames or - * programs which are permitted to modify configuration via - * /sys/kernel/security/tomoyo/ interface. - * - * An entry is added by - * - * # echo '<kernel> /sbin/mingetty /bin/login /bin/bash' > \ - * /sys/kernel/security/tomoyo/manager - * (if you want to specify by a domainname) - * - * or - * - * # echo '/usr/lib/ccs/editpolicy' > /sys/kernel/security/tomoyo/manager - * (if you want to specify by a program's location) - * - * and is deleted by - * - * # echo 'delete <kernel> /sbin/mingetty /bin/login /bin/bash' > \ - * /sys/kernel/security/tomoyo/manager - * - * or - * - * # echo 'delete /usr/lib/ccs/editpolicy' > \ - * /sys/kernel/security/tomoyo/manager +/** + * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry. * - * and all entries are retrieved by + * @a: Pointer to "struct tomoyo_acl_head". + * @b: Pointer to "struct tomoyo_acl_head". * - * # cat /sys/kernel/security/tomoyo/manager + * Returns true if @a == @b, false otherwise. */ -static LIST_HEAD(tomoyo_policy_manager_list); -static DECLARE_RWSEM(tomoyo_policy_manager_list_lock); +static bool tomoyo_same_manager(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_manager, head)->manager == + container_of(b, struct tomoyo_manager, head)->manager; +} /** * tomoyo_update_manager_entry - Add a manager entry. @@ -1121,110 +836,88 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ 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; - 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; + struct tomoyo_manager e = { }; + struct tomoyo_acl_param param = { + /* .ns = &tomoyo_kernel_namespace, */ + .is_delete = is_delete, + .list = &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], + }; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_correct_domain(manager) && + !tomoyo_correct_word(manager)) + return -EINVAL; + e.manager = tomoyo_get_name(manager); + if (e.manager) { + error = tomoyo_update_policy(&e.head, sizeof(e), ¶m, + tomoyo_same_manager); + tomoyo_put_name(e.manager); } - 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); return error; } /** - * tomoyo_write_manager_policy - Write manager policy. + * tomoyo_write_manager - Write manager policy. * * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) +static int tomoyo_write_manager(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; + tomoyo_manage_by_non_root = !head->w.is_delete; return 0; } - return tomoyo_update_manager_entry(data, is_delete); + return tomoyo_update_manager_entry(data, head->w.is_delete); } /** - * tomoyo_read_manager_policy - Read manager policy. + * tomoyo_read_manager - Read manager policy. * * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns 0. + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) +static void tomoyo_read_manager(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) + if (head->r.eof) + return; + list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER]) { + struct tomoyo_manager *ptr = + list_entry(head->r.acl, typeof(*ptr), head.list); + if (ptr->head.is_deleted) continue; - done = tomoyo_io_printf(head, "%s\n", ptr->manager->name); - if (!done) - break; + if (!tomoyo_flush(head)) + return; + tomoyo_set_string(head, ptr->manager->name); + tomoyo_set_lf(head); } - up_read(&tomoyo_policy_manager_list_lock); - head->read_eof = done; - return 0; + head->r.eof = true; } /** - * tomoyo_is_policy_manager - Check whether the current process is a policy manager. + * tomoyo_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. + * + * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_policy_manager(void) +static bool tomoyo_manager(void) { - struct tomoyo_policy_manager_entry *ptr; + struct tomoyo_manager *ptr; const char *exe; const struct task_struct *task = current; const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname; @@ -1232,31 +925,22 @@ static bool tomoyo_is_policy_manager(void) if (!tomoyo_policy_loaded) return true; - if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) + if (!tomoyo_manage_by_non_root && + (!uid_eq(task->cred->uid, GLOBAL_ROOT_UID) || + !uid_eq(task->cred->euid, GLOBAL_ROOT_UID))) 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)) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], head.list) { + if (!ptr->head.is_deleted && + (!tomoyo_pathcmp(domainname, ptr->manager) || + !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; @@ -1266,72 +950,116 @@ static bool tomoyo_is_policy_manager(void) last_pid = pid; } } - tomoyo_free(exe); + kfree(exe); return found; } +static struct tomoyo_domain_info *tomoyo_find_domain_by_qid +(unsigned int serial); + /** - * tomoyo_is_select_one - Parse select command. + * tomoyo_select_domain - Parse select command. * * @head: Pointer to "struct tomoyo_io_buffer". * @data: String to parse. * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, +static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, const char *data) { unsigned int pid; struct tomoyo_domain_info *domain = NULL; - - if (sscanf(data, "pid=%u", &pid) == 1) { + bool global_pid = false; + if (strncmp(data, "select ", 7)) + return false; + data += 7; + if (sscanf(data, "pid=%u", &pid) == 1 || + (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; - read_lock(&tasklist_lock); - p = find_task_by_vpid(pid); + rcu_read_lock(); + if (global_pid) + p = find_task_by_pid_ns(pid, &init_pid_ns); + else + p = find_task_by_vpid(pid); if (p) domain = tomoyo_real_domain(p); - read_unlock(&tasklist_lock); + rcu_read_unlock(); } else if (!strncmp(data, "domain=", 7)) { - if (tomoyo_is_domain_def(data + 7)) { - down_read(&tomoyo_domain_list_lock); + if (tomoyo_domain_def(data + 7)) domain = tomoyo_find_domain(data + 7); - up_read(&tomoyo_domain_list_lock); - } + } else if (sscanf(data, "Q=%u", &pid) == 1) { + domain = tomoyo_find_domain_by_qid(pid); } else return false; - head->write_var1 = domain; + head->w.domain = domain; /* Accessing read_buf is safe because head->io_sem is held. */ if (!head->read_buf) return true; /* Do nothing if open(O_WRONLY). */ - head->read_avail = 0; + memset(&head->r, 0, sizeof(head->r)); + head->r.print_this_domain_only = true; + if (domain) + head->r.domain = &domain->list; + else + head->r.eof = 1; tomoyo_io_printf(head, "# select %s\n", data); - head->read_single_domain = true; - head->read_eof = !domain; - if (domain) { - struct tomoyo_domain_info *d; - head->read_var1 = NULL; - down_read(&tomoyo_domain_list_lock); - list_for_each_entry(d, &tomoyo_domain_list, list) { - if (d == domain) - break; - head->read_var1 = &d->list; - } - up_read(&tomoyo_domain_list_lock); - head->read_var2 = NULL; - head->read_bit = 0; - head->read_step = 0; - if (domain->is_deleted) - tomoyo_io_printf(head, "# This is a deleted domain.\n"); - } + if (domain && domain->is_deleted) + tomoyo_io_printf(head, "# This is a deleted domain.\n"); return true; } /** + * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head); + return p1->domainname == p2->domainname; +} + +/** + * tomoyo_write_task - Update task related list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_task(struct tomoyo_acl_param *param) +{ + int error = -EINVAL; + if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) { + struct tomoyo_task_acl e = { + .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL, + .domainname = tomoyo_get_domainname(param), + }; + if (e.domainname) + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_task_acl, + NULL); + tomoyo_put_name(e.domainname); + } + return error; +} + +/** * tomoyo_delete_domain - Delete a domain. * * @domainname: The name of domain. * - * Returns 0. + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_delete_domain(char *domainname) { @@ -1340,9 +1068,10 @@ static int tomoyo_delete_domain(char *domainname) name.name = domainname; tomoyo_fill_path_info(&name); - down_write(&tomoyo_domain_list_lock); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return -EINTR; /* Is there an active domain? */ - list_for_each_entry(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { /* Never delete tomoyo_kernel_domain */ if (domain == &tomoyo_kernel_domain) continue; @@ -1352,326 +1081,541 @@ static int tomoyo_delete_domain(char *domainname) domain->is_deleted = true; break; } - up_write(&tomoyo_domain_list_lock); + mutex_unlock(&tomoyo_policy_lock); return 0; } /** - * tomoyo_write_domain_policy - Write domain policy. + * tomoyo_write_domain2 - Write domain policy. + * + * @ns: Pointer to "struct tomoyo_policy_namespace". + * @list: Pointer to "struct list_head". + * @data: Policy to be interpreted. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, + struct list_head *list, char *data, + const bool is_delete) +{ + struct tomoyo_acl_param param = { + .ns = ns, + .list = list, + .data = data, + .is_delete = is_delete, + }; + static const struct { + const char *keyword; + int (*write) (struct tomoyo_acl_param *); + } tomoyo_callback[5] = { + { "file ", tomoyo_write_file }, + { "network inet ", tomoyo_write_inet_network }, + { "network unix ", tomoyo_write_unix_network }, + { "misc ", tomoyo_write_misc }, + { "task ", tomoyo_write_task }, + }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) { + if (!tomoyo_str_starts(¶m.data, + tomoyo_callback[i].keyword)) + continue; + return tomoyo_callback[i].write(¶m); + } + return -EINVAL; +} + +/* String table for domain flags. */ +const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = { + [TOMOYO_DIF_QUOTA_WARNED] = "quota_exceeded\n", + [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n", +}; + +/** + * tomoyo_write_domain - 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_policy(struct tomoyo_io_buffer *head) +static int tomoyo_write_domain(struct tomoyo_io_buffer *head) { char *data = head->write_buf; - struct tomoyo_domain_info *domain = head->write_var1; - bool is_delete = false; - bool is_select = false; + struct tomoyo_policy_namespace *ns; + struct tomoyo_domain_info *domain = head->w.domain; + const bool is_delete = head->w.is_delete; + bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); unsigned int profile; - - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE)) - is_delete = true; - else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) - is_select = true; - if (is_select && tomoyo_is_select_one(head, data)) - return 0; - /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_is_policy_manager()) - return -EPERM; - if (tomoyo_is_domain_def(data)) { + if (*data == '<') { + int ret = 0; domain = NULL; if (is_delete) - tomoyo_delete_domain(data); - else if (is_select) { - down_read(&tomoyo_domain_list_lock); + ret = tomoyo_delete_domain(data); + else if (is_select) domain = tomoyo_find_domain(data); - up_read(&tomoyo_domain_list_lock); - } else - domain = tomoyo_find_or_assign_new_domain(data, 0); - head->write_var1 = domain; - return 0; + else + domain = tomoyo_assign_domain(data, false); + head->w.domain = domain; + return ret; } if (!domain) return -EINVAL; - - if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1 + ns = domain->ns; + if (sscanf(data, "use_profile %u", &profile) == 1 && profile < TOMOYO_MAX_PROFILES) { - if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded) + if (!tomoyo_policy_loaded || ns->profile_ptr[profile]) domain->profile = (u8) profile; return 0; } - if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) { - tomoyo_set_domain_flag(domain, is_delete, - TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ); + if (sscanf(data, "use_group %u\n", &profile) == 1 + && profile < TOMOYO_MAX_ACL_GROUPS) { + if (!is_delete) + domain->group = (u8) profile; return 0; } - return tomoyo_write_file_policy(data, domain, is_delete); + for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) { + const char *cp = tomoyo_dif[profile]; + if (strncmp(data, cp, strlen(cp) - 1)) + continue; + domain->flags[profile] = !is_delete; + return 0; + } + return tomoyo_write_domain2(ns, &domain->acl_info_list, data, + is_delete); } /** - * tomoyo_print_single_path_acl - Print a single path ACL entry. + * tomoyo_print_condition - Print condition part. * * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_single_path_acl_record". + * @cond: Pointer to "struct tomoyo_condition". * * Returns true on success, false otherwise. */ -static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head, - struct tomoyo_single_path_acl_record * - ptr) +static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, + const struct tomoyo_condition *cond) { - int pos; - u8 bit; - const char *atmark = ""; - const char *filename; - const u16 perm = ptr->perm; - - filename = ptr->filename->name; - for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION; - bit++) { - const char *msg; - if (!(perm & (1 << bit))) - continue; - /* Print "read/write" instead of "read" and "write". */ - if ((bit == TOMOYO_TYPE_READ_ACL || - bit == TOMOYO_TYPE_WRITE_ACL) - && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))) - continue; - msg = tomoyo_sp2keyword(bit); - pos = head->read_avail; - if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg, - atmark, filename)) - goto out; + switch (head->r.cond_step) { + case 0: + head->r.cond_index = 0; + head->r.cond_step++; + if (cond->transit) { + tomoyo_set_space(head); + tomoyo_set_string(head, cond->transit->name); + } + /* fall through */ + case 1: + { + const u16 condc = cond->condc; + const struct tomoyo_condition_element *condp = + (typeof(condp)) (cond + 1); + const struct tomoyo_number_union *numbers_p = + (typeof(numbers_p)) (condp + condc); + const struct tomoyo_name_union *names_p = + (typeof(names_p)) + (numbers_p + cond->numbers_count); + const struct tomoyo_argv *argv = + (typeof(argv)) (names_p + cond->names_count); + const struct tomoyo_envp *envp = + (typeof(envp)) (argv + cond->argc); + u16 skip; + for (skip = 0; skip < head->r.cond_index; skip++) { + const u8 left = condp->left; + const u8 right = condp->right; + condp++; + switch (left) { + case TOMOYO_ARGV_ENTRY: + argv++; + continue; + case TOMOYO_ENVP_ENTRY: + envp++; + continue; + case TOMOYO_NUMBER_UNION: + numbers_p++; + break; + } + switch (right) { + case TOMOYO_NAME_UNION: + names_p++; + break; + case TOMOYO_NUMBER_UNION: + numbers_p++; + break; + } + } + while (head->r.cond_index < condc) { + const u8 match = condp->equals; + const u8 left = condp->left; + const u8 right = condp->right; + if (!tomoyo_flush(head)) + return false; + condp++; + head->r.cond_index++; + tomoyo_set_space(head); + switch (left) { + case TOMOYO_ARGV_ENTRY: + tomoyo_io_printf(head, + "exec.argv[%lu]%s=\"", + argv->index, argv-> + is_not ? "!" : ""); + tomoyo_set_string(head, + argv->value->name); + tomoyo_set_string(head, "\""); + argv++; + continue; + case TOMOYO_ENVP_ENTRY: + tomoyo_set_string(head, + "exec.envp[\""); + tomoyo_set_string(head, + envp->name->name); + tomoyo_io_printf(head, "\"]%s=", envp-> + is_not ? "!" : ""); + if (envp->value) { + tomoyo_set_string(head, "\""); + tomoyo_set_string(head, envp-> + value->name); + tomoyo_set_string(head, "\""); + } else { + tomoyo_set_string(head, + "NULL"); + } + envp++; + continue; + case TOMOYO_NUMBER_UNION: + tomoyo_print_number_union_nospace + (head, numbers_p++); + break; + default: + tomoyo_set_string(head, + tomoyo_condition_keyword[left]); + break; + } + tomoyo_set_string(head, match ? "=" : "!="); + switch (right) { + case TOMOYO_NAME_UNION: + tomoyo_print_name_union_quoted + (head, names_p++); + break; + case TOMOYO_NUMBER_UNION: + tomoyo_print_number_union_nospace + (head, numbers_p++); + break; + default: + tomoyo_set_string(head, + tomoyo_condition_keyword[right]); + break; + } + } + } + head->r.cond_step++; + /* fall through */ + case 2: + if (!tomoyo_flush(head)) + break; + head->r.cond_step++; + /* fall through */ + case 3: + if (cond->grant_log != TOMOYO_GRANTLOG_AUTO) + tomoyo_io_printf(head, " grant_log=%s", + tomoyo_yesno(cond->grant_log == + TOMOYO_GRANTLOG_YES)); + tomoyo_set_lf(head); + return true; } - head->read_bit = 0; - return true; - out: - head->read_bit = bit; - head->read_avail = pos; return false; } /** - * tomoyo_print_double_path_acl - Print a double path ACL entry. + * tomoyo_set_group - Print "acl_group " header keyword and category name. * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_double_path_acl_record". + * @head: Pointer to "struct tomoyo_io_buffer". + * @category: Category name. * - * Returns true on success, false otherwise. + * Returns nothing. */ -static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head, - struct tomoyo_double_path_acl_record * - ptr) +static void tomoyo_set_group(struct tomoyo_io_buffer *head, + const char *category) { - int pos; - const char *atmark1 = ""; - const char *atmark2 = ""; - const char *filename1; - const char *filename2; - const u8 perm = ptr->perm; - u8 bit; - - filename1 = ptr->filename1->name; - filename2 = ptr->filename2->name; - for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION; - bit++) { - const char *msg; - if (!(perm & (1 << bit))) - continue; - msg = tomoyo_dp2keyword(bit); - pos = head->read_avail; - if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg, - atmark1, filename1, atmark2, filename2)) - goto out; + if (head->type == TOMOYO_EXCEPTIONPOLICY) { + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "acl_group %u ", + head->r.acl_group_index); } - head->read_bit = 0; - return true; - out: - head->read_bit = bit; - head->read_avail = pos; - return false; + tomoyo_set_string(head, category); } /** * tomoyo_print_entry - Print an ACL entry. * * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to an ACL entry. + * @acl: Pointer to an ACL entry. * * Returns true on success, false otherwise. */ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, - struct tomoyo_acl_info *ptr) + struct tomoyo_acl_info *acl) { - const u8 acl_type = tomoyo_acl_type2(ptr); + const u8 acl_type = acl->type; + bool first = true; + u8 bit; - if (acl_type & TOMOYO_ACL_DELETED) + if (head->r.print_cond_part) + goto print_cond_part; + if (acl->is_deleted) return true; - if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) { - struct tomoyo_single_path_acl_record *acl - = container_of(ptr, - struct tomoyo_single_path_acl_record, - head); - return tomoyo_print_single_path_acl(head, acl); - } - if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) { - struct tomoyo_double_path_acl_record *acl - = container_of(ptr, - struct tomoyo_double_path_acl_record, - head); - return tomoyo_print_double_path_acl(head, acl); - } - BUG(); /* This must not happen. */ - return false; -} - -/** - * tomoyo_read_domain_policy - Read domain policy. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. - */ -static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *dpos; - struct list_head *apos; - bool done = true; - - if (head->read_eof) - return 0; - if (head->read_step == 0) - head->read_step = 1; - down_read(&tomoyo_domain_list_lock); - list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { - struct tomoyo_domain_info *domain; - const char *quota_exceeded = ""; - const char *transition_failed = ""; - const char *ignore_global_allow_read = ""; - domain = list_entry(dpos, struct tomoyo_domain_info, list); - if (head->read_step != 1) - goto acl_loop; - if (domain->is_deleted && !head->read_single_domain) - continue; - /* Print domainname and flags. */ - if (domain->quota_warned) - quota_exceeded = "quota_exceeded\n"; - if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED) - transition_failed = "transition_failed\n"; - if (domain->flags & - TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) - ignore_global_allow_read - = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n"; - done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE - "%u\n%s%s%s\n", - domain->domainname->name, - domain->profile, quota_exceeded, - transition_failed, - ignore_global_allow_read); - if (!done) - break; - head->read_step = 2; -acl_loop: - if (head->read_step == 3) - goto tail_mark; - /* Print ACL entries in the domain. */ - down_read(&tomoyo_domain_acl_info_list_lock); - list_for_each_cookie(apos, head->read_var2, - &domain->acl_info_list) { - struct tomoyo_acl_info *ptr - = list_entry(apos, struct tomoyo_acl_info, - list); - done = tomoyo_print_entry(head, ptr); - if (!done) - break; + if (!tomoyo_flush(head)) + return false; + else if (acl_type == TOMOYO_TYPE_PATH_ACL) { + struct tomoyo_path_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u16 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (head->r.print_transition_related_only && + bit != TOMOYO_TYPE_EXECUTE) + continue; + if (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_path_keyword[bit]); } - up_read(&tomoyo_domain_acl_info_list_lock); - if (!done) - break; - head->read_step = 3; -tail_mark: - done = tomoyo_io_printf(head, "\n"); - if (!done) - break; - head->read_step = 1; - if (head->read_single_domain) - break; + if (first) + return true; + tomoyo_print_name_union(head, &ptr->name); + } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) { + struct tomoyo_task_acl *ptr = + container_of(acl, typeof(*ptr), head); + tomoyo_set_group(head, "task "); + tomoyo_set_string(head, "manual_domain_transition "); + tomoyo_set_string(head, ptr->domainname->name); + } else if (head->r.print_transition_related_only) { + return true; + } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { + struct tomoyo_path2_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_mac_keywords + [tomoyo_pp2mac[bit]]); + } + if (first) + return true; + tomoyo_print_name_union(head, &ptr->name1); + tomoyo_print_name_union(head, &ptr->name2); + } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { + struct tomoyo_path_number_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_mac_keywords + [tomoyo_pn2mac[bit]]); + } + if (first) + return true; + tomoyo_print_name_union(head, &ptr->name); + tomoyo_print_number_union(head, &ptr->number); + } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { + struct tomoyo_mkdev_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_mac_keywords + [tomoyo_pnnn2mac[bit]]); + } + if (first) + return true; + 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); + } else if (acl_type == TOMOYO_TYPE_INET_ACL) { + struct tomoyo_inet_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u8 perm = ptr->perm; + + for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "network inet "); + tomoyo_set_string(head, tomoyo_proto_keyword + [ptr->protocol]); + tomoyo_set_space(head); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_socket_keyword[bit]); + } + if (first) + return true; + tomoyo_set_space(head); + if (ptr->address.group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->address.group->group_name + ->name); + } else { + char buf[128]; + tomoyo_print_ip(buf, sizeof(buf), &ptr->address); + tomoyo_io_printf(head, "%s", buf); + } + tomoyo_print_number_union(head, &ptr->port); + } else if (acl_type == TOMOYO_TYPE_UNIX_ACL) { + struct tomoyo_unix_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u8 perm = ptr->perm; + + for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "network unix "); + tomoyo_set_string(head, tomoyo_proto_keyword + [ptr->protocol]); + tomoyo_set_space(head); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_socket_keyword[bit]); + } + if (first) + return true; + tomoyo_print_name_union(head, &ptr->name); + } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { + struct tomoyo_mount_acl *ptr = + container_of(acl, typeof(*ptr), head); + tomoyo_set_group(head, "file 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); + } else if (acl_type == TOMOYO_TYPE_ENV_ACL) { + struct tomoyo_env_acl *ptr = + container_of(acl, typeof(*ptr), head); + + tomoyo_set_group(head, "misc env "); + tomoyo_set_string(head, ptr->env->name); } - up_read(&tomoyo_domain_list_lock); - head->read_eof = done; - return 0; + if (acl->cond) { + head->r.print_cond_part = true; + head->r.cond_step = 0; + if (!tomoyo_flush(head)) + return false; +print_cond_part: + if (!tomoyo_print_condition(head, acl->cond)) + return false; + head->r.print_cond_part = false; + } else { + tomoyo_set_lf(head); + } + return true; } /** - * tomoyo_write_domain_profile - Assign profile for specified domain. + * tomoyo_read_domain2 - Read domain policy. * * @head: Pointer to "struct tomoyo_io_buffer". + * @list: Pointer to "struct list_head". * - * Returns 0 on success, -EINVAL otherwise. - * - * This is equivalent to doing + * Caller holds tomoyo_read_lock(). * - * ( echo "select " $domainname; echo "use_profile " $profile ) | - * /usr/lib/ccs/loadpolicy -d + * Returns true on success, false otherwise. */ -static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) +static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, + struct list_head *list) { - char *data = head->write_buf; - char *cp = strchr(data, ' '); - struct tomoyo_domain_info *domain; - unsigned long profile; - - if (!cp) - return -EINVAL; - *cp = '\0'; - down_read(&tomoyo_domain_list_lock); - domain = tomoyo_find_domain(cp + 1); - up_read(&tomoyo_domain_list_lock); - if (strict_strtoul(data, 10, &profile)) - return -EINVAL; - if (domain && profile < TOMOYO_MAX_PROFILES - && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)) - domain->profile = (u8) profile; - return 0; + list_for_each_cookie(head->r.acl, list) { + struct tomoyo_acl_info *ptr = + list_entry(head->r.acl, typeof(*ptr), list); + if (!tomoyo_print_entry(head, ptr)) + return false; + } + head->r.acl = NULL; + return true; } /** - * tomoyo_read_domain_profile - Read only domainname and profile. + * tomoyo_read_domain - Read domain policy. * * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns list of profile number and domainname pairs. - * - * This is equivalent to doing - * - * grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy | - * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" ) - * domainname = $0; } else if ( $1 == "use_profile" ) { - * print $2 " " domainname; domainname = ""; } } ; ' + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) +static void tomoyo_read_domain(struct tomoyo_io_buffer *head) { - struct list_head *pos; - bool done = true; - - if (head->read_eof) - return 0; - down_read(&tomoyo_domain_list_lock); - list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) { - struct tomoyo_domain_info *domain; - domain = list_entry(pos, struct tomoyo_domain_info, list); - if (domain->is_deleted) - continue; - done = tomoyo_io_printf(head, "%u %s\n", domain->profile, - domain->domainname->name); - if (!done) - break; + if (head->r.eof) + return; + list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { + struct tomoyo_domain_info *domain = + list_entry(head->r.domain, typeof(*domain), list); + switch (head->r.step) { + u8 i; + case 0: + if (domain->is_deleted && + !head->r.print_this_domain_only) + continue; + /* Print domainname and flags. */ + tomoyo_set_string(head, domain->domainname->name); + tomoyo_set_lf(head); + tomoyo_io_printf(head, "use_profile %u\n", + domain->profile); + tomoyo_io_printf(head, "use_group %u\n", + domain->group); + for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++) + if (domain->flags[i]) + tomoyo_set_string(head, tomoyo_dif[i]); + head->r.step++; + tomoyo_set_lf(head); + /* fall through */ + case 1: + if (!tomoyo_read_domain2(head, &domain->acl_info_list)) + return; + head->r.step++; + if (!tomoyo_set_lf(head)) + return; + /* fall through */ + case 2: + head->r.step = 0; + if (head->r.print_this_domain_only) + goto done; + } } - up_read(&tomoyo_domain_list_lock); - head->read_eof = done; - return 0; + done: + head->r.eof = true; } /** @@ -1683,11 +1627,7 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) */ static int tomoyo_write_pid(struct tomoyo_io_buffer *head) { - unsigned long pid; - /* No error check. */ - strict_strtoul(head->write_buf, 10, &pid); - head->read_step = (int) pid; - head->read_eof = false; + head->r.eof = false; return 0; } @@ -1701,204 +1641,586 @@ static int tomoyo_write_pid(struct tomoyo_io_buffer *head) * The PID is specified by tomoyo_write_pid() so that the user can obtain * using read()/write() interface rather than sysctl() interface. */ -static int tomoyo_read_pid(struct tomoyo_io_buffer *head) +static void tomoyo_read_pid(struct tomoyo_io_buffer *head) { - if (head->read_avail == 0 && !head->read_eof) { - const int pid = head->read_step; - struct task_struct *p; - struct tomoyo_domain_info *domain = NULL; - read_lock(&tasklist_lock); - p = find_task_by_vpid(pid); - if (p) - domain = tomoyo_real_domain(p); - read_unlock(&tasklist_lock); - if (domain) - tomoyo_io_printf(head, "%d %u %s", pid, domain->profile, - domain->domainname->name); - head->read_eof = true; + char *buf = head->write_buf; + bool global_pid = false; + unsigned int pid; + struct task_struct *p; + struct tomoyo_domain_info *domain = NULL; + + /* Accessing write_buf is safe because head->io_sem is held. */ + if (!buf) { + head->r.eof = true; + return; /* Do nothing if open(O_RDONLY). */ } - return 0; + if (head->r.w_pos || head->r.eof) + return; + head->r.eof = true; + if (tomoyo_str_starts(&buf, "global-pid ")) + global_pid = true; + pid = (unsigned int) simple_strtoul(buf, NULL, 10); + rcu_read_lock(); + if (global_pid) + p = find_task_by_pid_ns(pid, &init_pid_ns); + else + p = find_task_by_vpid(pid); + if (p) + domain = tomoyo_real_domain(p); + rcu_read_unlock(); + if (!domain) + return; + tomoyo_io_printf(head, "%u %u ", pid, domain->profile); + tomoyo_set_string(head, domain->domainname->name); } +/* String table for domain transition control keywords. */ +static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { + [TOMOYO_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ", + [TOMOYO_TRANSITION_CONTROL_RESET] = "reset_domain ", + [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ", + [TOMOYO_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ", + [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ", + [TOMOYO_TRANSITION_CONTROL_KEEP] = "keep_domain ", +}; + +/* String table for grouping keywords. */ +static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { + [TOMOYO_PATH_GROUP] = "path_group ", + [TOMOYO_NUMBER_GROUP] = "number_group ", + [TOMOYO_ADDRESS_GROUP] = "address_group ", +}; + /** - * tomoyo_write_exception_policy - Write exception policy. + * tomoyo_write_exception - Write exception policy. * * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) +static int tomoyo_write_exception(struct tomoyo_io_buffer *head) { - char *data = head->write_buf; - bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); - - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN)) - return tomoyo_write_domain_keeper_policy(data, false, - is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN)) - return tomoyo_write_domain_keeper_policy(data, true, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN)) - return tomoyo_write_domain_initializer_policy(data, false, - is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN)) - return tomoyo_write_domain_initializer_policy(data, true, - is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS)) - return tomoyo_write_alias_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ)) - return tomoyo_write_globally_readable_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN)) - return tomoyo_write_pattern_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE)) - return tomoyo_write_no_rewrite_policy(data, is_delete); + const bool is_delete = head->w.is_delete; + struct tomoyo_acl_param param = { + .ns = head->w.ns, + .is_delete = is_delete, + .data = head->write_buf, + }; + u8 i; + if (tomoyo_str_starts(¶m.data, "aggregator ")) + return tomoyo_write_aggregator(¶m); + for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) + if (tomoyo_str_starts(¶m.data, tomoyo_transition_type[i])) + return tomoyo_write_transition_control(¶m, i); + for (i = 0; i < TOMOYO_MAX_GROUP; i++) + if (tomoyo_str_starts(¶m.data, tomoyo_group_name[i])) + return tomoyo_write_group(¶m, i); + if (tomoyo_str_starts(¶m.data, "acl_group ")) { + unsigned int group; + char *data; + group = simple_strtoul(param.data, &data, 10); + if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ') + return tomoyo_write_domain2 + (head->w.ns, &head->w.ns->acl_group[group], + data, is_delete); + } return -EINVAL; } /** - * tomoyo_read_exception_policy - Read exception policy. + * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list. * * @head: Pointer to "struct tomoyo_io_buffer". + * @idx: Index number. * - * Returns 0 on success, -EINVAL otherwise. + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) +static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) { - if (!head->read_eof) { - switch (head->read_step) { - case 0: - head->read_var2 = NULL; - head->read_step = 1; - case 1: - if (!tomoyo_read_domain_keeper_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 2; - case 2: - if (!tomoyo_read_globally_readable_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 3; - case 3: - head->read_var2 = NULL; - head->read_step = 4; - case 4: - if (!tomoyo_read_domain_initializer_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 5; - case 5: - if (!tomoyo_read_alias_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 6; - case 6: - head->read_var2 = NULL; - head->read_step = 7; - case 7: - if (!tomoyo_read_file_pattern(head)) - break; - head->read_var2 = NULL; - head->read_step = 8; - case 8: - if (!tomoyo_read_no_rewrite_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 9; - case 9: - head->read_eof = true; + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->group_list[idx]; + list_for_each_cookie(head->r.group, list) { + struct tomoyo_group *group = + list_entry(head->r.group, typeof(*group), head.list); + list_for_each_cookie(head->r.acl, &group->member_list) { + struct tomoyo_acl_head *ptr = + list_entry(head->r.acl, typeof(*ptr), list); + if (ptr->is_deleted) + continue; + if (!tomoyo_flush(head)) + return false; + tomoyo_print_namespace(head); + tomoyo_set_string(head, tomoyo_group_name[idx]); + tomoyo_set_string(head, group->group_name->name); + if (idx == TOMOYO_PATH_GROUP) { + tomoyo_set_space(head); + tomoyo_set_string(head, container_of + (ptr, struct tomoyo_path_group, + head)->member_name->name); + } else if (idx == TOMOYO_NUMBER_GROUP) { + tomoyo_print_number_union(head, &container_of + (ptr, + struct tomoyo_number_group, + head)->number); + } else if (idx == TOMOYO_ADDRESS_GROUP) { + char buffer[128]; + + struct tomoyo_address_group *member = + container_of(ptr, typeof(*member), + head); + tomoyo_print_ip(buffer, sizeof(buffer), + &member->address); + tomoyo_io_printf(head, " %s", buffer); + } + tomoyo_set_lf(head); + } + head->r.acl = NULL; + } + head->r.group = NULL; + return true; +} + +/** + * tomoyo_read_policy - Read "struct tomoyo_..._entry" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @idx: Index number. + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) +{ + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->policy_list[idx]; + list_for_each_cookie(head->r.acl, list) { + struct tomoyo_acl_head *acl = + container_of(head->r.acl, typeof(*acl), list); + if (acl->is_deleted) + continue; + if (!tomoyo_flush(head)) + return false; + switch (idx) { + case TOMOYO_ID_TRANSITION_CONTROL: + { + struct tomoyo_transition_control *ptr = + container_of(acl, typeof(*ptr), head); + tomoyo_print_namespace(head); + tomoyo_set_string(head, tomoyo_transition_type + [ptr->type]); + tomoyo_set_string(head, ptr->program ? + ptr->program->name : "any"); + tomoyo_set_string(head, " from "); + tomoyo_set_string(head, ptr->domainname ? + ptr->domainname->name : + "any"); + } + break; + case TOMOYO_ID_AGGREGATOR: + { + struct tomoyo_aggregator *ptr = + container_of(acl, typeof(*ptr), head); + tomoyo_print_namespace(head); + tomoyo_set_string(head, "aggregator "); + tomoyo_set_string(head, + ptr->original_name->name); + tomoyo_set_space(head); + tomoyo_set_string(head, + ptr->aggregated_name->name); + } break; default: - return -EINVAL; + continue; } + tomoyo_set_lf(head); } - return 0; + head->r.acl = NULL; + return true; } -/* path to policy loader */ -static const char *tomoyo_loader = "/sbin/tomoyo-init"; +/** + * tomoyo_read_exception - Read exception policy. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Caller holds tomoyo_read_lock(). + */ +static void tomoyo_read_exception(struct tomoyo_io_buffer *head) +{ + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + if (head->r.eof) + return; + while (head->r.step < TOMOYO_MAX_POLICY && + tomoyo_read_policy(head, head->r.step)) + head->r.step++; + if (head->r.step < TOMOYO_MAX_POLICY) + return; + while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP && + tomoyo_read_group(head, head->r.step - TOMOYO_MAX_POLICY)) + head->r.step++; + if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) + return; + while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP + + TOMOYO_MAX_ACL_GROUPS) { + head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY + - TOMOYO_MAX_GROUP; + if (!tomoyo_read_domain2(head, &ns->acl_group + [head->r.acl_group_index])) + return; + head->r.step++; + } + head->r.eof = true; +} + +/* Wait queue for kernel -> userspace notification. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); +/* Wait queue for userspace -> kernel notification. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait); + +/* Structure for query. */ +struct tomoyo_query { + struct list_head list; + struct tomoyo_domain_info *domain; + char *query; + size_t query_len; + unsigned int serial; + u8 timer; + u8 answer; + u8 retry; +}; + +/* The list for "struct tomoyo_query". */ +static LIST_HEAD(tomoyo_query_list); + +/* Lock for manipulating tomoyo_query_list. */ +static DEFINE_SPINLOCK(tomoyo_query_list_lock); + +/* + * Number of "struct file" referring /sys/kernel/security/tomoyo/query + * interface. + */ +static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); /** - * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. + * tomoyo_truncate - Truncate a line. + * + * @str: String to truncate. * - * Returns true if /sbin/tomoyo-init exists, false otherwise. + * Returns length of truncated @str. */ -static bool tomoyo_policy_loader_exists(void) +static int tomoyo_truncate(char *str) { - /* - * Don't activate MAC if the policy loader doesn't exist. - * If the initrd includes /sbin/init but real-root-dev has not - * mounted on / yet, activating MAC will block the system since - * policies are not loaded yet. - * Thus, let do_execve() call this function everytime. - */ - struct path path; + char *start = str; + while (*(unsigned char *) str > (unsigned char) ' ') + str++; + *str = '\0'; + return strlen(start) + 1; +} - if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { - printk(KERN_INFO "Not activating Mandatory Access Control now " - "since %s doesn't exist.\n", tomoyo_loader); - return false; +/** + * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode. + * + * @domain: Pointer to "struct tomoyo_domain_info". + * @header: Lines containing ACL. + * + * Returns nothing. + */ +static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) +{ + char *buffer; + char *realpath = NULL; + char *argv0 = NULL; + char *symlink = NULL; + char *cp = strchr(header, '\n'); + int len; + if (!cp) + return; + cp = strchr(cp + 1, '\n'); + if (!cp) + return; + *cp++ = '\0'; + len = strlen(cp) + 1; + /* strstr() will return NULL if ordering is wrong. */ + if (*cp == 'f') { + argv0 = strstr(header, " argv[]={ \""); + if (argv0) { + argv0 += 10; + len += tomoyo_truncate(argv0) + 14; + } + realpath = strstr(header, " exec={ realpath=\""); + if (realpath) { + realpath += 8; + len += tomoyo_truncate(realpath) + 6; + } + symlink = strstr(header, " symlink.target=\""); + if (symlink) + len += tomoyo_truncate(symlink + 1) + 1; } - path_put(&path); - return true; + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return; + snprintf(buffer, len - 1, "%s", cp); + if (realpath) + tomoyo_addprintf(buffer, len, " exec.%s", realpath); + if (argv0) + tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0); + if (symlink) + tomoyo_addprintf(buffer, len, "%s", symlink); + tomoyo_normalize_line(buffer); + if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, + false)) + tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); + kfree(buffer); } /** - * tomoyo_load_policy - Run external policy loader to load policy. + * tomoyo_supervisor - Ask for the supervisor's decision. * - * @filename: The program about to start. + * @r: Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. * - * This function checks whether @filename is /sbin/init , and if so - * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init - * and then continues invocation of /sbin/init. - * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and - * writes to /sys/kernel/security/tomoyo/ interfaces. + * 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; + int len; + static unsigned int tomoyo_serial; + struct tomoyo_query entry = { }; + bool quota_exceeded = false; + va_start(args, fmt); + len = vsnprintf((char *) &len, 1, fmt, args) + 1; + va_end(args); + /* Write /sys/kernel/security/tomoyo/audit. */ + va_start(args, fmt); + tomoyo_write_log2(r, len, fmt, args); + va_end(args); + /* Nothing more to do if granted. */ + if (r->granted) + return 0; + if (r->mode) + tomoyo_update_stat(r->mode); + switch (r->mode) { + case TOMOYO_CONFIG_ENFORCING: + error = -EPERM; + if (atomic_read(&tomoyo_query_observers)) + break; + goto out; + case TOMOYO_CONFIG_LEARNING: + error = 0; + /* Check max_learning_entry parameter. */ + if (tomoyo_domain_quota_is_ok(r)) + break; + /* fall through */ + default: + return 0; + } + /* Get message. */ + va_start(args, fmt); + entry.query = tomoyo_init_log(r, len, fmt, args); + va_end(args); + if (!entry.query) + goto out; + entry.query_len = strlen(entry.query) + 1; + if (!error) { + tomoyo_add_entry(r->domain, entry.query); + goto out; + } + len = tomoyo_round2(entry.query_len); + entry.domain = r->domain; + spin_lock(&tomoyo_query_list_lock); + if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] && + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len + >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) { + quota_exceeded = true; + } else { + entry.serial = tomoyo_serial++; + entry.retry = r->retry; + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len; + list_add_tail(&entry.list, &tomoyo_query_list); + } + spin_unlock(&tomoyo_query_list_lock); + if (quota_exceeded) + goto out; + /* Give 10 seconds for supervisor's opinion. */ + while (entry.timer < 10) { + wake_up_all(&tomoyo_query_wait); + if (wait_event_interruptible_timeout + (tomoyo_answer_wait, entry.answer || + !atomic_read(&tomoyo_query_observers), HZ)) + break; + else + entry.timer++; + } + spin_lock(&tomoyo_query_list_lock); + list_del(&entry.list); + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len; + spin_unlock(&tomoyo_query_list_lock); + switch (entry.answer) { + case 3: /* Asked to retry by administrator. */ + error = TOMOYO_RETRY_REQUEST; + r->retry++; + break; + case 1: + /* Granted by administrator. */ + error = 0; + break; + default: + /* Timed out or rejected by administrator. */ + break; + } +out: + kfree(entry.query); + return error; +} + +/** + * tomoyo_find_domain_by_qid - Get domain by query id. * - * Returns nothing. + * @serial: Query ID assigned by tomoyo_supervisor(). + * + * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. */ -void tomoyo_load_policy(const char *filename) +static struct tomoyo_domain_info *tomoyo_find_domain_by_qid +(unsigned int serial) { - char *argv[2]; - char *envp[3]; + struct tomoyo_query *ptr; + struct tomoyo_domain_info *domain = NULL; + spin_lock(&tomoyo_query_list_lock); + list_for_each_entry(ptr, &tomoyo_query_list, list) { + if (ptr->serial != serial) + continue; + domain = ptr->domain; + break; + } + spin_unlock(&tomoyo_query_list_lock); + return domain; +} + +/** + * 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 unsigned int tomoyo_poll_query(struct file *file, poll_table *wait) +{ + if (!list_empty(&tomoyo_query_list)) + return POLLIN | POLLRDNORM; + poll_wait(file, &tomoyo_query_wait, wait); + if (!list_empty(&tomoyo_query_list)) + return POLLIN | POLLRDNORM; + return 0; +} - if (tomoyo_policy_loaded) +/** + * tomoyo_read_query - Read access requests which violated policy in enforcing mode. + * + * @head: Pointer to "struct tomoyo_io_buffer". + */ +static void tomoyo_read_query(struct tomoyo_io_buffer *head) +{ + struct list_head *tmp; + unsigned int pos = 0; + size_t len = 0; + char *buf; + if (head->r.w_pos) return; - /* - * Check filename is /sbin/init or /sbin/tomoyo-start. - * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't - * be passed. - * You can create /sbin/tomoyo-start by - * "ln -s /bin/true /sbin/tomoyo-start". - */ - if (strcmp(filename, "/sbin/init") && - strcmp(filename, "/sbin/tomoyo-start")) + if (head->read_buf) { + kfree(head->read_buf); + head->read_buf = NULL; + } + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (pos++ != head->r.query_index) + continue; + len = ptr->query_len; + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (!len) { + head->r.query_index = 0; return; - if (!tomoyo_policy_loader_exists()) + } + buf = kzalloc(len + 32, GFP_NOFS); + if (!buf) return; + pos = 0; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (pos++ != head->r.query_index) + continue; + /* + * Some query can be skipped because tomoyo_query_list + * can change, but I don't care. + */ + if (len == ptr->query_len) + snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, + ptr->retry, ptr->query); + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (buf[0]) { + head->read_buf = buf; + head->r.w[head->r.w_pos++] = buf; + head->r.query_index++; + } else { + kfree(buf); + } +} - printk(KERN_INFO "Calling %s to load policy. Please wait.\n", - tomoyo_loader); - argv[0] = (char *) tomoyo_loader; - argv[1] = NULL; - envp[0] = "HOME=/"; - envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - envp[2] = NULL; - call_usermodehelper(argv[0], argv, envp, 1); - - printk(KERN_INFO "TOMOYO: 2.2.0 2009/04/01\n"); - printk(KERN_INFO "Mandatory Access Control activated.\n"); - tomoyo_policy_loaded = true; - { /* Check all profiles currently assigned to domains are defined. */ - struct tomoyo_domain_info *domain; - down_read(&tomoyo_domain_list_lock); - list_for_each_entry(domain, &tomoyo_domain_list, list) { - const u8 profile = domain->profile; - if (tomoyo_profile_ptr[profile]) - continue; - panic("Profile %u (used by '%s') not defined.\n", - profile, domain->domainname->name); - } - up_read(&tomoyo_domain_list_lock); +/** + * 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 *ptr = list_entry(tmp, typeof(*ptr), 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 *ptr = list_entry(tmp, typeof(*ptr), list); + if (ptr->serial != serial) + continue; + ptr->answer = answer; + /* Remove from tomoyo_query_list. */ + if (ptr->answer) + list_del_init(&ptr->list); + break; } + spin_unlock(&tomoyo_query_list_lock); + return 0; } /** @@ -1908,33 +2230,109 @@ void tomoyo_load_policy(const char *filename) * * Returns version information. */ -static int tomoyo_read_version(struct tomoyo_io_buffer *head) +static void tomoyo_read_version(struct tomoyo_io_buffer *head) { - if (!head->read_eof) { - tomoyo_io_printf(head, "2.2.0"); - head->read_eof = true; + if (!head->r.eof) { + tomoyo_io_printf(head, "2.5.0"); + head->r.eof = true; } - return 0; } +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { + [TOMOYO_STAT_POLICY_UPDATES] = "update:", + [TOMOYO_STAT_POLICY_LEARNING] = "violation in learning mode:", + [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:", + [TOMOYO_STAT_POLICY_ENFORCING] = "violation in enforcing mode:", +}; + +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = { + [TOMOYO_MEMORY_POLICY] = "policy:", + [TOMOYO_MEMORY_AUDIT] = "audit log:", + [TOMOYO_MEMORY_QUERY] = "query message:", +}; + +/* Timestamp counter for last updated. */ +static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; +/* Counter for number of updates. */ +static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; + /** - * tomoyo_read_self_domain - Get the current process's domainname. + * tomoyo_update_stat - Update statistic counters. + * + * @index: Index for policy type. + * + * Returns nothing. + */ +void tomoyo_update_stat(const u8 index) +{ + struct timeval tv; + do_gettimeofday(&tv); + /* + * I don't use atomic operations because race condition is not fatal. + */ + tomoyo_stat_updated[index]++; + tomoyo_stat_modified[index] = tv.tv_sec; +} + +/** + * tomoyo_read_stat - Read statistic data. * * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns the current process's domainname. + * Returns nothing. */ -static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) +static void tomoyo_read_stat(struct tomoyo_io_buffer *head) { - if (!head->read_eof) { - /* - * tomoyo_domain()->domainname != NULL - * because every process belongs to a domain and - * the domain's name cannot be NULL. - */ - tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); - head->read_eof = true; + u8 i; + unsigned int total = 0; + if (head->r.eof) + return; + for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) { + tomoyo_io_printf(head, "Policy %-30s %10u", + tomoyo_policy_headers[i], + tomoyo_stat_updated[i]); + if (tomoyo_stat_modified[i]) { + struct tomoyo_time stamp; + tomoyo_convert_time(tomoyo_stat_modified[i], &stamp); + tomoyo_io_printf(head, " (Last: %04u/%02u/%02u " + "%02u:%02u:%02u)", + stamp.year, stamp.month, stamp.day, + stamp.hour, stamp.min, stamp.sec); + } + tomoyo_set_lf(head); + } + for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) { + unsigned int used = tomoyo_memory_used[i]; + total += used; + tomoyo_io_printf(head, "Memory used by %-22s %10u", + tomoyo_memory_headers[i], used); + used = tomoyo_memory_quota[i]; + if (used) + tomoyo_io_printf(head, " (Quota: %10u)", used); + tomoyo_set_lf(head); } + tomoyo_io_printf(head, "Total memory used: %10u\n", + total); + head->r.eof = true; +} + +/** + * tomoyo_write_stat - Set memory quota. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +static int tomoyo_write_stat(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + u8 i; + if (tomoyo_str_starts(&data, "Memory used by ")) + for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) + if (tomoyo_str_starts(&data, tomoyo_memory_headers[i])) + sscanf(data, "%u", &tomoyo_memory_quota[i]); return 0; } @@ -1944,34 +2342,31 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) * @type: Type of interface. * @file: Pointer to "struct file". * - * Associates policy handler and returns 0 on success, -ENOMEM otherwise. + * Returns 0 on success, negative value otherwise. */ -static int tomoyo_open_control(const u8 type, struct file *file) +int tomoyo_open_control(const u8 type, struct file *file) { - struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head)); + struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); 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_domain_policy; - head->read = tomoyo_read_domain_policy; + head->write = tomoyo_write_domain; + head->read = tomoyo_read_domain; break; case TOMOYO_EXCEPTIONPOLICY: /* /sys/kernel/security/tomoyo/exception_policy */ - head->write = tomoyo_write_exception_policy; - head->read = tomoyo_read_exception_policy; + head->write = tomoyo_write_exception; + head->read = tomoyo_read_exception; break; - case TOMOYO_SELFDOMAIN: - /* /sys/kernel/security/tomoyo/self_domain */ - head->read = tomoyo_read_self_domain; - break; - case TOMOYO_DOMAIN_STATUS: - /* /sys/kernel/security/tomoyo/.domain_status */ - head->write = tomoyo_write_domain_profile; - head->read = tomoyo_read_domain_profile; + case TOMOYO_AUDIT: + /* /sys/kernel/security/tomoyo/audit */ + head->poll = tomoyo_poll_log; + head->read = tomoyo_read_log; break; case TOMOYO_PROCESS_STATUS: /* /sys/kernel/security/tomoyo/.process_status */ @@ -1983,21 +2378,26 @@ static int tomoyo_open_control(const u8 type, struct file *file) head->read = tomoyo_read_version; head->readbuf_size = 128; break; - case TOMOYO_MEMINFO: - /* /sys/kernel/security/tomoyo/meminfo */ - head->write = tomoyo_write_memory_quota; - head->read = tomoyo_read_memory_counter; - head->readbuf_size = 512; + case TOMOYO_STAT: + /* /sys/kernel/security/tomoyo/stat */ + head->write = tomoyo_write_stat; + head->read = tomoyo_read_stat; + head->readbuf_size = 1024; break; case TOMOYO_PROFILE: /* /sys/kernel/security/tomoyo/profile */ 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; - head->read = tomoyo_read_manager_policy; + head->write = tomoyo_write_manager; + head->read = tomoyo_read_manager; break; } if (!(file->f_mode & FMODE_READ)) { @@ -2006,12 +2406,14 @@ 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 = tomoyo_alloc(head->readbuf_size); + head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); if (!head->read_buf) { - tomoyo_free(head); + kfree(head); return -ENOMEM; } } @@ -2023,117 +2425,254 @@ static int tomoyo_open_control(const u8 type, struct file *file) head->write = NULL; } else if (head->write) { head->writebuf_size = 4096 * 2; - head->write_buf = tomoyo_alloc(head->writebuf_size); + head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS); if (!head->write_buf) { - tomoyo_free(head->read_buf); - tomoyo_free(head); + kfree(head->read_buf); + kfree(head); return -ENOMEM; } } - file->private_data = head; /* - * Call the handler now if the file is - * /sys/kernel/security/tomoyo/self_domain - * so that the user can use - * cat < /sys/kernel/security/tomoyo/self_domain" - * to know the current process's domainname. + * 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. */ - if (type == TOMOYO_SELFDOMAIN) - tomoyo_read_control(file, NULL, 0); + if (type == TOMOYO_QUERY) + atomic_inc(&tomoyo_query_observers); + file->private_data = head; + tomoyo_notify_gc(head, true); return 0; } /** + * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". Maybe NULL. + * + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, + * POLLOUT | POLLWRNORM otherwise. + */ +unsigned int tomoyo_poll_control(struct file *file, poll_table *wait) +{ + struct tomoyo_io_buffer *head = file->private_data; + if (head->poll) + return head->poll(file, wait) | POLLOUT | POLLWRNORM; + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; +} + +/** + * tomoyo_set_namespace_cursor - Set namespace to read. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head) +{ + struct list_head *ns; + if (head->type != TOMOYO_EXCEPTIONPOLICY && + head->type != TOMOYO_PROFILE) + return; + /* + * If this is the first read, or reading previous namespace finished + * and has more namespaces to read, update the namespace cursor. + */ + ns = head->r.ns; + if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) { + /* Clearing is OK because tomoyo_flush() returned true. */ + memset(&head->r, 0, sizeof(head->r)); + head->r.ns = ns ? ns->next : tomoyo_namespace_list.next; + } +} + +/** + * tomoyo_has_more_namespace - Check for unread namespaces. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true if we have more entries to print, false otherwise. + */ +static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) +{ + return (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) && head->r.eof && + head->r.ns->next != &tomoyo_namespace_list; +} + +/** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". + * @head: Pointer to "struct tomoyo_io_buffer". * @buffer: Poiner to buffer to write to. * @buffer_len: Size of @buffer. * * Returns bytes read on success, negative value otherwise. */ -static int tomoyo_read_control(struct file *file, char __user *buffer, - const int buffer_len) +ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, + const int buffer_len) { - int len = 0; - struct tomoyo_io_buffer *head = file->private_data; - char *cp; + int len; + int idx; if (!head->read) return -ENOSYS; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; - /* Call the policy handler. */ - len = head->read(head); - if (len < 0) - goto out; - /* Write to buffer. */ - len = head->read_avail; - if (len > buffer_len) - len = buffer_len; - if (!len) - goto out; - /* head->read_buf changes by some functions. */ - cp = head->read_buf; - if (copy_to_user(buffer, cp, len)) { - len = -EFAULT; - goto out; - } - head->read_avail -= len; - memmove(cp, cp + len, head->read_avail); - out: + head->read_user_buf = buffer; + head->read_user_buf_avail = buffer_len; + idx = tomoyo_read_lock(); + if (tomoyo_flush(head)) + /* Call the policy handler. */ + do { + tomoyo_set_namespace_cursor(head); + head->read(head); + } while (tomoyo_flush(head) && + tomoyo_has_more_namespace(head)); + tomoyo_read_unlock(idx); + len = head->read_user_buf - buffer; mutex_unlock(&head->io_sem); return len; } /** + * tomoyo_parse_policy - Parse a policy line. + * + * @head: Poiter to "struct tomoyo_io_buffer". + * @line: Line to parse. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) +{ + /* Delete request? */ + head->w.is_delete = !strncmp(line, "delete ", 7); + if (head->w.is_delete) + memmove(line, line + 7, strlen(line + 7) + 1); + /* Selecting namespace to update. */ + if (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) { + if (*line == '<') { + char *cp = strchr(line, ' '); + if (cp) { + *cp++ = '\0'; + head->w.ns = tomoyo_assign_namespace(line); + memmove(line, cp, strlen(cp) + 1); + } else + head->w.ns = NULL; + } else + head->w.ns = &tomoyo_kernel_namespace; + /* Don't allow updating if namespace is invalid. */ + if (!head->w.ns) + return -ENOENT; + } + /* Do the update. */ + return head->write(head); +} + +/** * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". + * @head: Pointer to "struct tomoyo_io_buffer". * @buffer: Pointer to buffer to read from. * @buffer_len: Size of @buffer. * * Returns @buffer_len on success, negative value otherwise. */ -static int tomoyo_write_control(struct file *file, const char __user *buffer, - const int buffer_len) +ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, + const char __user *buffer, const int buffer_len) { - struct tomoyo_io_buffer *head = file->private_data; int error = buffer_len; - int avail_len = buffer_len; + size_t avail_len = buffer_len; char *cp0 = head->write_buf; - + int idx; if (!head->write) return -ENOSYS; if (!access_ok(VERIFY_READ, buffer, buffer_len)) return -EFAULT; - /* Don't allow updating policies by non manager programs. */ - if (head->write != tomoyo_write_pid && - head->write != tomoyo_write_domain_policy && - !tomoyo_is_policy_manager()) - return -EPERM; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; + head->read_user_buf_avail = 0; + idx = tomoyo_read_lock(); /* Read a line and dispatch it to the policy handler. */ while (avail_len > 0) { char c; - if (head->write_avail >= head->writebuf_size - 1) { - error = -ENOMEM; - break; - } else if (get_user(c, buffer)) { + if (head->w.avail >= head->writebuf_size - 1) { + const int len = head->writebuf_size * 2; + char *cp = kzalloc(len, GFP_NOFS); + if (!cp) { + error = -ENOMEM; + break; + } + memmove(cp, cp0, head->w.avail); + kfree(cp0); + head->write_buf = cp; + cp0 = cp; + head->writebuf_size = len; + } + if (get_user(c, buffer)) { error = -EFAULT; break; } buffer++; avail_len--; - cp0[head->write_avail++] = c; + cp0[head->w.avail++] = c; if (c != '\n') continue; - cp0[head->write_avail - 1] = '\0'; - head->write_avail = 0; + cp0[head->w.avail - 1] = '\0'; + head->w.avail = 0; tomoyo_normalize_line(cp0); - head->write(head); + if (!strcmp(cp0, "reset")) { + head->w.ns = &tomoyo_kernel_namespace; + head->w.domain = NULL; + memset(&head->r, 0, sizeof(head->r)); + continue; + } + /* Don't allow updating policies by non manager programs. */ + switch (head->type) { + case TOMOYO_PROCESS_STATUS: + /* This does not write anything. */ + break; + case TOMOYO_DOMAINPOLICY: + if (tomoyo_select_domain(head, cp0)) + continue; + /* fall through */ + case TOMOYO_EXCEPTIONPOLICY: + if (!strcmp(cp0, "select transition_only")) { + head->r.print_transition_related_only = true; + continue; + } + /* fall through */ + default: + if (!tomoyo_manager()) { + error = -EPERM; + goto out; + } + } + switch (tomoyo_parse_policy(head, cp0)) { + case -EPERM: + error = -EPERM; + goto out; + case 0: + switch (head->type) { + case TOMOYO_DOMAINPOLICY: + case TOMOYO_EXCEPTIONPOLICY: + case TOMOYO_STAT: + case TOMOYO_PROFILE: + case TOMOYO_MANAGER: + tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); + break; + default: + break; + } + break; + } } +out: + tomoyo_read_unlock(idx); mutex_unlock(&head->io_sem); return error; } @@ -2141,178 +2680,112 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, /** * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". - * - * Releases memory and returns 0. + * @head: Pointer to "struct tomoyo_io_buffer". */ -static int tomoyo_close_control(struct file *file) +void tomoyo_close_control(struct tomoyo_io_buffer *head) { - struct tomoyo_io_buffer *head = file->private_data; - - /* Release memory used for policy I/O. */ - tomoyo_free(head->read_buf); - head->read_buf = NULL; - tomoyo_free(head->write_buf); - head->write_buf = NULL; - tomoyo_free(head); - head = NULL; - file->private_data = NULL; - return 0; + /* + * If the file is /sys/kernel/security/tomoyo/query , decrement the + * observer counter. + */ + if (head->type == TOMOYO_QUERY && + atomic_dec_and_test(&tomoyo_query_observers)) + wake_up_all(&tomoyo_answer_wait); + tomoyo_notify_gc(head, false); } /** - * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry. - * - * @acl_type: Type of ACL entry. - * - * Returns pointer to the ACL entry on success, NULL otherwise. + * tomoyo_check_profile - Check all profiles currently assigned to domains are defined. */ -void *tomoyo_alloc_acl_element(const u8 acl_type) +void tomoyo_check_profile(void) { - int len; - struct tomoyo_acl_info *ptr; - - switch (acl_type) { - case TOMOYO_TYPE_SINGLE_PATH_ACL: - len = sizeof(struct tomoyo_single_path_acl_record); - break; - case TOMOYO_TYPE_DOUBLE_PATH_ACL: - len = sizeof(struct tomoyo_double_path_acl_record); - break; - default: - return NULL; + struct tomoyo_domain_info *domain; + const int idx = tomoyo_read_lock(); + tomoyo_policy_loaded = true; + printk(KERN_INFO "TOMOYO: 2.5.0\n"); + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + const u8 profile = domain->profile; + const struct tomoyo_policy_namespace *ns = domain->ns; + if (ns->profile_version != 20110903) + printk(KERN_ERR + "Profile version %u is not supported.\n", + ns->profile_version); + else if (!ns->profile_ptr[profile]) + printk(KERN_ERR + "Profile %u (used by '%s') is not defined.\n", + profile, domain->domainname->name); + else + continue; + printk(KERN_ERR + "Userland tools for TOMOYO 2.5 must be installed and " + "policy must be initialized.\n"); + printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ " + "for more information.\n"); + panic("STOP!"); } - ptr = tomoyo_alloc_element(len); - if (!ptr) - return NULL; - ptr->type = acl_type; - return ptr; -} - -/** - * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. - * - * @inode: Pointer to "struct inode". - * @file: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. - */ -static int tomoyo_open(struct inode *inode, struct file *file) -{ - const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) - - ((u8 *) NULL); - return tomoyo_open_control(key, file); -} - -/** - * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface. - * - * @inode: Pointer to "struct inode". - * @file: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. - */ -static int tomoyo_release(struct inode *inode, struct file *file) -{ - return tomoyo_close_control(file); -} - -/** - * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. - * - * @file: Pointer to "struct file". - * @buf: Pointer to buffer. - * @count: Size of @buf. - * @ppos: Unused. - * - * Returns bytes read on success, negative value otherwise. - */ -static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - return tomoyo_read_control(file, buf, count); -} - -/** - * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface. - * - * @file: Pointer to "struct file". - * @buf: Pointer to buffer. - * @count: Size of @buf. - * @ppos: Unused. - * - * Returns @count on success, negative value otherwise. - */ -static ssize_t tomoyo_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return tomoyo_write_control(file, buf, count); + tomoyo_read_unlock(idx); + printk(KERN_INFO "Mandatory Access Control activated.\n"); } -/* - * tomoyo_operations is a "struct file_operations" which is used for handling - * /sys/kernel/security/tomoyo/ interface. - * - * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR). - * See tomoyo_io_buffer for internals. - */ -static const struct file_operations tomoyo_operations = { - .open = tomoyo_open, - .release = tomoyo_release, - .read = tomoyo_read, - .write = tomoyo_write, -}; - /** - * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. - * - * @name: The name of the interface file. - * @mode: The permission of the interface file. - * @parent: The parent directory. - * @key: Type of interface. + * tomoyo_load_builtin_policy - Load built-in policy. * * Returns nothing. */ -static void __init tomoyo_create_entry(const char *name, const mode_t mode, - struct dentry *parent, const u8 key) +void __init tomoyo_load_builtin_policy(void) { - securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, - &tomoyo_operations); -} - -/** - * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. - * - * Returns 0. - */ -static int __init tomoyo_initerface_init(void) -{ - struct dentry *tomoyo_dir; - - /* Don't create securityfs entries unless registered. */ - if (current_cred()->security != &tomoyo_kernel_domain) - return 0; - - tomoyo_dir = securityfs_create_dir("tomoyo", NULL); - tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, - TOMOYO_DOMAINPOLICY); - tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, - TOMOYO_EXCEPTIONPOLICY); - tomoyo_create_entry("self_domain", 0400, tomoyo_dir, - TOMOYO_SELFDOMAIN); - tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, - TOMOYO_DOMAIN_STATUS); - tomoyo_create_entry(".process_status", 0600, tomoyo_dir, - TOMOYO_PROCESS_STATUS); - tomoyo_create_entry("meminfo", 0600, tomoyo_dir, - TOMOYO_MEMINFO); - tomoyo_create_entry("profile", 0600, tomoyo_dir, - TOMOYO_PROFILE); - tomoyo_create_entry("manager", 0600, tomoyo_dir, - TOMOYO_MANAGER); - tomoyo_create_entry("version", 0400, tomoyo_dir, - TOMOYO_VERSION); - return 0; + /* + * This include file is manually created and contains built-in policy + * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy", + * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager", + * "tomoyo_builtin_stat" in the form of "static char [] __initdata". + */ +#include "builtin-policy.h" + u8 i; + const int idx = tomoyo_read_lock(); + for (i = 0; i < 5; i++) { + struct tomoyo_io_buffer head = { }; + char *start = ""; + switch (i) { + case 0: + start = tomoyo_builtin_profile; + head.type = TOMOYO_PROFILE; + head.write = tomoyo_write_profile; + break; + case 1: + start = tomoyo_builtin_exception_policy; + head.type = TOMOYO_EXCEPTIONPOLICY; + head.write = tomoyo_write_exception; + break; + case 2: + start = tomoyo_builtin_domain_policy; + head.type = TOMOYO_DOMAINPOLICY; + head.write = tomoyo_write_domain; + break; + case 3: + start = tomoyo_builtin_manager; + head.type = TOMOYO_MANAGER; + head.write = tomoyo_write_manager; + break; + case 4: + start = tomoyo_builtin_stat; + head.type = TOMOYO_STAT; + head.write = tomoyo_write_stat; + break; + } + while (1) { + char *end = strchr(start, '\n'); + if (!end) + break; + *end = '\0'; + tomoyo_normalize_line(start); + head.write_buf = start; + tomoyo_parse_policy(&head, start); + start = end + 1; + } + } + tomoyo_read_unlock(idx); +#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER + tomoyo_check_profile(); +#endif } - -fs_initcall(tomoyo_initerface_init); |
