X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=security%2Ftomoyo%2Fcommon.c;h=a3c3b5de3b75f2e3facbf2c1bb17452b40fa4a91;hb=5db5a39b6462c8360c9178b28f4b07c320dfca1c;hp=b5dbdc9ff73c944b7ecd0583fa786c3d29f72726;hpb=62052be3a797f26f6f0fe30fc8d7f40ab54e08fd;p=deliverable%2Flinux.git diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index b5dbdc9ff73c..a3c3b5de3b75 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -3,99 +3,77 @@ * * Common functions for TOMOYO. * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 - * + * Copyright (C) 2005-2010 NTT DATA CORPORATION */ #include #include #include -#include #include "common.h" -/* Lock for protecting policy. */ -DEFINE_MUTEX(tomoyo_policy_lock); +static struct tomoyo_profile tomoyo_default_profile = { + .learning = &tomoyo_default_profile.preference, + .permissive = &tomoyo_default_profile.preference, + .enforcing = &tomoyo_default_profile.preference, + .preference.enforcing_verbose = true, + .preference.learning_max_entry = 2048, + .preference.learning_verbose = false, + .preference.permissive_verbose = true +}; + +/* Profile version. Currently only 20090903 is defined. */ +static unsigned int tomoyo_profile_version; -/* Has loading policy done? */ -bool tomoyo_policy_loaded; +/* Profile table. Memory is allocated as needed. */ +static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; /* String table for functionality that takes 4 modes. */ static const char *tomoyo_mode_4[4] = { "disabled", "learning", "permissive", "enforcing" }; -/* String table for functionality that takes 2 modes. */ -static const char *tomoyo_mode_2[4] = { - "disabled", "enabled", "enabled", "enabled" -}; -/* - * 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 /sys/kernel/security/tomoyo/profile */ +static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_FILE_EXECUTE] = "file::execute", + [TOMOYO_MAC_FILE_OPEN] = "file::open", + [TOMOYO_MAC_FILE_CREATE] = "file::create", + [TOMOYO_MAC_FILE_UNLINK] = "file::unlink", + [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir", + [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir", + [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo", + [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock", + [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate", + [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink", + [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite", + [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock", + [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar", + [TOMOYO_MAC_FILE_LINK] = "file::link", + [TOMOYO_MAC_FILE_RENAME] = "file::rename", + [TOMOYO_MAC_FILE_CHMOD] = "file::chmod", + [TOMOYO_MAC_FILE_CHOWN] = "file::chown", + [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp", + [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl", + [TOMOYO_MAC_FILE_CHROOT] = "file::chroot", + [TOMOYO_MAC_FILE_MOUNT] = "file::mount", + [TOMOYO_MAC_FILE_UMOUNT] = "file::umount", + [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", }; -/* - * 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]; - /* 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_parse_name_union - Parse a tomoyo_name_union. + * tomoyo_yesno - Return "yes" or "no". * - * @filename: Name or name group. - * @ptr: Pointer to "struct tomoyo_name_union". - * - * Returns true on success, false otherwise. + * @value: Bool value. */ -bool tomoyo_parse_name_union(const char *filename, - struct tomoyo_name_union *ptr) +static const char *tomoyo_yesno(const unsigned int value) { - if (!tomoyo_is_correct_path(filename, 0, 0, 0)) - return false; - if (filename[0] == '@') { - ptr->group = tomoyo_get_path_group(filename + 1); - ptr->is_group = true; - return ptr->group != NULL; - } - ptr->filename = tomoyo_get_name(filename); - ptr->is_group = false; - return ptr->filename != NULL; + return value ? "yes" : "no"; } /** @@ -119,661 +97,53 @@ static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head, } /** - * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value. - * - * @str: Pointer to the string. - * - * Returns true if @str is a \ooo style octal value, false otherwise. - * - * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. - * This function verifies that \ooo is in valid range. - */ -static inline bool tomoyo_is_byte_range(const char *str) -{ - return *str >= '0' && *str++ <= '3' && - *str >= '0' && *str++ <= '7' && - *str >= '0' && *str <= '7'; -} - -/** - * tomoyo_is_alphabet_char - Check whether the character is an alphabet. - * - * @c: The character to check. - * - * Returns true if @c is an alphabet character, false otherwise. - */ -static inline bool tomoyo_is_alphabet_char(const char c) -{ - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); -} - -/** - * tomoyo_make_byte - Make byte value from three octal characters. + * tomoyo_print_number_union - Print a tomoyo_number_union. * - * @c1: The first character. - * @c2: The second character. - * @c3: The third character. - * - * Returns byte value. - */ -static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) -{ - return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); -} - -/** - * tomoyo_str_starts - Check whether the given string starts with the given keyword. - * - * @src: Pointer to pointer to the string. - * @find: Pointer to the keyword. - * - * Returns true if @src starts with @find, false otherwise. - * - * The @src is updated to point the first character after the @find - * if @src starts with @find. - */ -static bool tomoyo_str_starts(char **src, const char *find) -{ - const int len = strlen(find); - char *tmp = *src; - - if (strncmp(tmp, find, len)) - return false; - tmp += len; - *src = tmp; - return true; -} - -/** - * tomoyo_normalize_line - Format string. - * - * @buffer: The line to normalize. - * - * Leading and trailing whitespaces are removed. - * Multiple whitespaces are packed into single space. - * - * Returns nothing. - */ -static void tomoyo_normalize_line(unsigned char *buffer) -{ - unsigned char *sp = buffer; - unsigned char *dp = buffer; - bool first = true; - - while (tomoyo_is_invalid(*sp)) - sp++; - while (*sp) { - if (!first) - *dp++ = ' '; - first = false; - while (tomoyo_is_valid(*sp)) - *dp++ = *sp++; - while (tomoyo_is_invalid(*sp)) - sp++; - } - *dp = '\0'; -} - -/** - * tomoyo_tokenize - Tokenize string. - * - * @buffer: The line to tokenize. - * @w: Pointer to "char *". - * @size: Sizeof @w . + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_number_union". * * Returns true on success, false otherwise. */ -bool tomoyo_tokenize(char *buffer, char *w[], size_t size) -{ - int count = size / sizeof(char *); - int i; - for (i = 0; i < count; i++) - w[i] = ""; - for (i = 0; i < count; i++) { - char *cp = strchr(buffer, ' '); - if (cp) - *cp = '\0'; - w[i] = buffer; - if (!cp) - break; - buffer = cp + 1; - } - return i < count || !*buffer; -} - -/** - * 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 - * - * 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 *const start = filename; - bool in_repetition = false; - bool contains_pattern = false; - unsigned char c; - unsigned char d; - unsigned char e; - - 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: - return false; -} - -/** - * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. - * @domainname: The domainname to check. - * - * Returns true if @domainname follows the naming rules, false otherwise. - */ -bool tomoyo_is_correct_domain(const unsigned char *domainname) -{ - unsigned char c; - unsigned char d; - unsigned char e; - - 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: - return false; -} - -/** - * tomoyo_is_domain_def - Check whether the given token can be a domainname. - * - * @buffer: The token to check. - * - * Returns true if @buffer possibly be a domainname, false otherwise. - */ -bool tomoyo_is_domain_def(const unsigned char *buffer) -{ - return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); -} - -/** - * tomoyo_find_domain - Find a domain by the given name. - * - * @domainname: The domainname to find. - * - * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) -{ - struct tomoyo_domain_info *domain; - struct tomoyo_path_info name; - - name.name = domainname; - tomoyo_fill_path_info(&name); - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - if (!domain->is_deleted && - !tomoyo_pathcmp(&name, domain->domainname)) - return domain; - } - return NULL; -} - -/** - * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. - * - * @filename: The string to evaluate. - * - * Returns the initial length without a pattern in @filename. - */ -static int tomoyo_const_part_length(const char *filename) +bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, + const struct tomoyo_number_union *ptr) { - 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; - } + unsigned long min; + unsigned long max; + u8 min_type; + u8 max_type; + if (!tomoyo_io_printf(head, " ")) + return false; + if (ptr->is_group) + return tomoyo_io_printf(head, "@%s", + ptr->group->group_name->name); + min_type = ptr->min_type; + max_type = ptr->max_type; + min = ptr->values[0]; + max = ptr->values[1]; + switch (min_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + if (!tomoyo_io_printf(head, "0x%lX", min)) + return false; break; - } - return len; -} - -/** - * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. - * - * @ptr: Pointer to "struct tomoyo_path_info" to fill in. - * - * The caller sets "struct tomoyo_path_info"->name. - */ -void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) -{ - const char *name = ptr->name; - const int len = strlen(name); - - ptr->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); -} - -/** - * tomoyo_file_matches_pattern2 - Pattern matching without '/' character - * and "\-" pattern. - * - * @filename: The start of string to check. - * @filename_end: The end of string to check. - * @pattern: The start of pattern to compare. - * @pattern_end: The end of pattern to compare. - * - * Returns true if @filename matches @pattern, false otherwise. - */ -static bool tomoyo_file_matches_pattern2(const char *filename, - const char *filename_end, - const char *pattern, - const char *pattern_end) -{ - while (filename < filename_end && pattern < pattern_end) { - char c; - if (*pattern != '\\') { - if (*filename++ != *pattern++) - return false; - continue; - } - c = *filename; - pattern++; - switch (*pattern) { - int i; - int j; - case '?': - if (c == '/') { - return false; - } else if (c == '\\') { - if (filename[1] == '\\') - filename++; - else if (tomoyo_is_byte_range(filename + 1)) - filename += 3; - else - return false; - } - break; - case '\\': - if (c != '\\') - return false; - if (*++filename != '\\') - return false; - break; - case '+': - if (!isdigit(c)) - return false; - break; - case 'x': - if (!isxdigit(c)) - return false; - break; - case 'a': - if (!tomoyo_is_alphabet_char(c)) - return false; - break; - case '0': - case '1': - case '2': - case '3': - if (c == '\\' && tomoyo_is_byte_range(filename + 1) - && strncmp(filename + 1, pattern, 3) == 0) { - filename += 3; - pattern += 2; - break; - } - return false; /* Not matched. */ - case '*': - case '@': - for (i = 0; i <= filename_end - filename; i++) { - if (tomoyo_file_matches_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; -} - -/** - * tomoyo_file_matches_pattern - Pattern matching without without '/' character. - * - * @filename: The start of string to check. - * @filename_end: The end of string to check. - * @pattern: The start of pattern to compare. - * @pattern_end: The end of pattern to compare. - * - * Returns true if @filename matches @pattern, false otherwise. - */ -static bool tomoyo_file_matches_pattern(const char *filename, - const char *filename_end, - const char *pattern, - const char *pattern_end) -{ - const char *pattern_start = pattern; - bool first = true; - bool result; - - while (pattern < pattern_end - 1) { - /* Split at "\-" pattern. */ - if (*pattern++ != '\\' || *pattern++ != '-') - continue; - result = tomoyo_file_matches_pattern2(filename, - filename_end, - pattern_start, - pattern - 2); - if (first) - result = !result; - if (result) + case TOMOYO_VALUE_TYPE_OCTAL: + if (!tomoyo_io_printf(head, "0%lo", min)) return false; - first = false; - pattern_start = pattern; - } - result = tomoyo_file_matches_pattern2(filename, filename_end, - pattern_start, pattern_end); - return first ? result : !result; -} - -/** - * tomoyo_path_matches_pattern2 - Do pathname pattern matching. - * - * @f: The start of string to check. - * @p: The start of pattern to compare. - * - * Returns true if @f matches @p, false otherwise. - */ -static bool tomoyo_path_matches_pattern2(const char *f, const char *p) -{ - 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)) + break; + default: + if (!tomoyo_io_printf(head, "%lu", min)) return false; - f = f_delimiter; - if (*f) - f++; - p = p_delimiter; - if (*p) - p++; + break; + } + if (min == max && min_type == max_type) + return true; + switch (max_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + return tomoyo_io_printf(head, "-0x%lX", max); + case TOMOYO_VALUE_TYPE_OCTAL: + return tomoyo_io_printf(head, "-0%lo", max); + default: + return tomoyo_io_printf(head, "-%lu", max); } - /* 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. - * - * \- Subtraction operator. - * - * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ - * /dir/dir/dir/ ). - */ -bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, - const struct tomoyo_path_info *pattern) -{ - 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); } /** @@ -804,136 +174,6 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) return true; } -/** - * tomoyo_get_exe - Get tomoyo_realpath() of current process. - * - * Returns the tomoyo_realpath() of current process on success, NULL otherwise. - * - * This function uses kzalloc(), so the caller must call kfree() - * if this function didn't return NULL. - */ -static const char *tomoyo_get_exe(void) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - const char *cp = NULL; - - if (!mm) - return NULL; - down_read(&mm->mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { - cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); - break; - } - } - up_read(&mm->mmap_sem); - return cp; -} - -/** - * tomoyo_get_msg - Get warning message. - * - * @is_enforce: Is it enforcing mode? - * - * Returns "ERROR" or "WARNING". - */ -const char *tomoyo_get_msg(const bool is_enforce) -{ - if (is_enforce) - return "ERROR"; - else - return "WARNING"; -} - -/** - * tomoyo_check_flags - Check mode for specified functionality. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * @index: The functionality to check mode. - * - * TOMOYO checks only process context. - * This code disables TOMOYO's enforcement in case the function is called from - * interrupt context. - */ -unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, - const u8 index) -{ - const u8 profile = domain->profile; - - if (WARN_ON(in_interrupt())) - return 0; - return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX -#if TOMOYO_MAX_PROFILES != 256 - && profile < TOMOYO_MAX_PROFILES -#endif - && tomoyo_profile_ptr[profile] ? - tomoyo_profile_ptr[profile]->value[index] : 0; -} - -/** - * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * - * Returns true if domain policy violation warning should be printed to - * console. - */ -bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) -{ - return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0; -} - -/** - * tomoyo_domain_quota_is_ok - Check for domain's quota. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * - * Returns true if the domain is not exceeded quota, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) -{ - unsigned int count = 0; - struct tomoyo_acl_info *ptr; - - if (!domain) - return true; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - switch (ptr->type) { - struct tomoyo_path_acl *acl; - u32 perm; - u8 i; - case TOMOYO_TYPE_PATH_ACL: - acl = container_of(ptr, struct tomoyo_path_acl, head); - perm = acl->perm | (((u32) acl->perm_high) << 16); - for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) - if (perm & (1 << i)) - count++; - if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) - count -= 2; - break; - case TOMOYO_TYPE_PATH2_ACL: - perm = container_of(ptr, struct tomoyo_path2_acl, head) - ->perm; - for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) - if (perm & (1 << i)) - count++; - break; - } - } - if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) - return true; - if (!domain->quota_warned) { - domain->quota_warned = true; - printk(KERN_WARNING "TOMOYO-WARNING: " - "Domain '%s' has so many ACLs to hold. " - "Stopped learning mode.\n", domain->domainname->name); - } - return false; -} - /** * tomoyo_find_or_assign_new_profile - Create a new profile. * @@ -941,36 +181,56 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) * * 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 struct tomoyo_profile *tomoyo_find_or_assign_new_profile +(const unsigned int profile) { - struct tomoyo_profile *ptr = NULL; - int i; - + struct tomoyo_profile *ptr; + struct tomoyo_profile *entry; if (profile >= TOMOYO_MAX_PROFILES) return NULL; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - return NULL; ptr = tomoyo_profile_ptr[profile]; if (ptr) - goto ok; - ptr = kmalloc(sizeof(*ptr), GFP_NOFS); - if (!tomoyo_memory_ok(ptr)) { - kfree(ptr); - ptr = NULL; - goto ok; + return ptr; + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + ptr = tomoyo_profile_ptr[profile]; + if (!ptr && tomoyo_memory_ok(entry)) { + ptr = entry; + ptr->learning = &tomoyo_default_profile.preference; + ptr->permissive = &tomoyo_default_profile.preference; + ptr->enforcing = &tomoyo_default_profile.preference; + ptr->default_config = TOMOYO_CONFIG_DISABLED; + memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, + sizeof(ptr->config)); + mb(); /* Avoid out-of-order execution. */ + tomoyo_profile_ptr[profile] = ptr; + entry = NULL; } - 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(&tomoyo_policy_lock); + out: + kfree(entry); + return ptr; +} + +/** + * tomoyo_profile - Find a profile. + * + * @profile: Profile number to find. + * + * Returns pointer to "struct tomoyo_profile". + */ +struct tomoyo_profile *tomoyo_profile(const u8 profile) +{ + struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; + if (!tomoyo_policy_loaded) + return &tomoyo_default_profile; + BUG_ON(!ptr); return ptr; } /** - * tomoyo_write_profile - Write to profile table. + * tomoyo_write_profile - Write profile table. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -980,153 +240,208 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) { char *data = head->write_buf; unsigned int i; - unsigned int value; + int value; + int mode; + u8 config; + bool use_default = false; char *cp; struct tomoyo_profile *profile; - unsigned long num; - - cp = strchr(data, '-'); - if (cp) - *cp = '\0'; - if (strict_strtoul(data, 10, &num)) - return -EINVAL; - if (cp) + if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) + return 0; + i = simple_strtoul(data, &cp, 10); + if (data == cp) { + profile = &tomoyo_default_profile; + } else { + if (*cp != '-') + return -EINVAL; data = cp + 1; - profile = tomoyo_find_or_assign_new_profile(num); - if (!profile) - return -EINVAL; + profile = tomoyo_find_or_assign_new_profile(i); + if (!profile) + return -EINVAL; + } cp = strchr(data, '='); if (!cp) return -EINVAL; - *cp = '\0'; + *cp++ = '\0'; + if (profile != &tomoyo_default_profile) + use_default = strstr(cp, "use_default") != NULL; + if (strstr(cp, "verbose=yes")) + value = 1; + else if (strstr(cp, "verbose=no")) + value = 0; + else + value = -1; + if (!strcmp(data, "PREFERENCE::enforcing")) { + if (use_default) { + profile->enforcing = &tomoyo_default_profile.preference; + return 0; + } + profile->enforcing = &profile->preference; + if (value >= 0) + profile->preference.enforcing_verbose = value; + return 0; + } + if (!strcmp(data, "PREFERENCE::permissive")) { + if (use_default) { + profile->permissive = &tomoyo_default_profile.preference; + return 0; + } + profile->permissive = &profile->preference; + if (value >= 0) + profile->preference.permissive_verbose = value; + return 0; + } + if (!strcmp(data, "PREFERENCE::learning")) { + char *cp2; + if (use_default) { + profile->learning = &tomoyo_default_profile.preference; + return 0; + } + profile->learning = &profile->preference; + if (value >= 0) + profile->preference.learning_verbose = value; + cp2 = strstr(cp, "max_entry="); + if (cp2) + sscanf(cp2 + 10, "%u", + &profile->preference.learning_max_entry); + return 0; + } + if (profile == &tomoyo_default_profile) + return -EINVAL; if (!strcmp(data, "COMMENT")) { const struct tomoyo_path_info *old_comment = profile->comment; - profile->comment = tomoyo_get_name(cp + 1); + profile->comment = tomoyo_get_name(cp); 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; + if (!strcmp(data, "CONFIG")) { + i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; + config = profile->default_config; + } else if (tomoyo_str_starts(&data, "CONFIG::")) { + config = 0; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + if (strcmp(data, tomoyo_mac_keywords[i])) + continue; + config = profile->config[i]; + break; } - profile->value[i] = value; - return 0; + if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + return -EINVAL; + } else { + return -EINVAL; } - return -EINVAL; + if (use_default) { + config = TOMOYO_CONFIG_USE_DEFAULT; + } else { + for (mode = 3; mode >= 0; mode--) + if (strstr(cp, tomoyo_mode_4[mode])) + /* + * Update lower 3 bits in order to distinguish + * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. + */ + config = (config & ~7) | mode; + } + 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_read_profile - Read from profile table. + * tomoyo_read_profile - Read profile table. * * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. */ -static int tomoyo_read_profile(struct tomoyo_io_buffer *head) +static void tomoyo_read_profile(struct tomoyo_io_buffer *head) { - static const int total = TOMOYO_MAX_CONTROL_INDEX + 1; - int step; - + int index; 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; + return; + if (head->read_bit) + goto body; + tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); + tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s " + "max_entry=%u }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + learning_verbose), + tomoyo_default_profile.preference.learning_max_entry); + tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + permissive_verbose)); + tomoyo_io_printf(head, "PREFERENCE::enforcing={ verbose=%s }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + enforcing_verbose)); + head->read_bit = 1; + body: + for (index = head->read_step; index < TOMOYO_MAX_PROFILES; index++) { + bool done; + u8 config; + int i; + int pos; const struct tomoyo_profile *profile = tomoyo_profile_ptr[index]; - head->read_step = step; + const struct tomoyo_path_info *comment; + head->read_step = index; if (!profile) continue; - if (!type) { /* Print profile' comment tag. */ - if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n", - index, profile->comment ? - profile->comment->name : "")) - break; - continue; - } - type--; - if (type < TOMOYO_MAX_CONTROL_INDEX) { - const unsigned int value = profile->value[type]; - const char **modes = NULL; - const char *keyword - = tomoyo_control_array[type].keyword; - switch (tomoyo_control_array[type].max_value) { - case 3: - modes = tomoyo_mode_4; - break; - case 1: - modes = tomoyo_mode_2; - break; - } - if (modes) { - if (!tomoyo_io_printf(head, "%u-%s=%s\n", index, - keyword, modes[value])) - break; - } else { - if (!tomoyo_io_printf(head, "%u-%s=%u\n", index, - keyword, value)) - break; - } + pos = head->read_avail; + comment = profile->comment; + done = tomoyo_io_printf(head, "%u-COMMENT=%s\n", index, + comment ? comment->name : ""); + if (!done) + goto out; + config = profile->default_config; + if (!tomoyo_io_printf(head, "%u-CONFIG={ mode=%s }\n", index, + tomoyo_mode_4[config & 3])) + goto out; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + config = profile->config[i]; + if (config == TOMOYO_CONFIG_USE_DEFAULT) + continue; + if (!tomoyo_io_printf(head, + "%u-CONFIG::%s={ mode=%s }\n", + index, tomoyo_mac_keywords[i], + tomoyo_mode_4[config & 3])) + goto out; } + if (profile->learning != &tomoyo_default_profile.preference && + !tomoyo_io_printf(head, "%u-PREFERENCE::learning={ " + "verbose=%s max_entry=%u }\n", index, + tomoyo_yesno(profile->preference. + learning_verbose), + profile->preference.learning_max_entry)) + goto out; + if (profile->permissive != &tomoyo_default_profile.preference + && !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ " + "verbose=%s }\n", index, + tomoyo_yesno(profile->preference. + permissive_verbose))) + goto out; + if (profile->enforcing != &tomoyo_default_profile.preference && + !tomoyo_io_printf(head, "%u-PREFERENCE::enforcing={ " + "verbose=%s }\n", index, + tomoyo_yesno(profile->preference. + enforcing_verbose))) + goto out; + continue; + out: + head->read_avail = pos; + break; } - if (step == TOMOYO_MAX_PROFILES * total) + if (index == TOMOYO_MAX_PROFILES) head->read_eof = true; - return 0; } -/* - * 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 ' /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 /sbin/mingetty /bin/login /bin/bash' > \ - * /sys/kernel/security/tomoyo/manager - * - * or - * - * # echo 'delete /usr/lib/ccs/editpolicy' > \ - * /sys/kernel/security/tomoyo/manager - * - * and all entries are retrieved by - * - * # cat /sys/kernel/security/tomoyo/manager - */ -LIST_HEAD(tomoyo_policy_manager_list); +static bool tomoyo_same_manager_entry(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_policy_manager_entry, head) + ->manager == + container_of(b, struct tomoyo_policy_manager_entry, head) + ->manager; +} /** * tomoyo_update_manager_entry - Add a manager entry. @@ -1141,41 +456,23 @@ LIST_HEAD(tomoyo_policy_manager_list); static int tomoyo_update_manager_entry(const char *manager, const bool is_delete) { - struct tomoyo_policy_manager_entry *ptr; struct tomoyo_policy_manager_entry e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; + int error; - if (tomoyo_is_domain_def(manager)) { - if (!tomoyo_is_correct_domain(manager)) + if (tomoyo_domain_def(manager)) { + if (!tomoyo_correct_domain(manager)) return -EINVAL; e.is_domain = true; } else { - if (!tomoyo_is_correct_path(manager, 1, -1, -1)) + if (!tomoyo_correct_path(manager)) return -EINVAL; } e.manager = tomoyo_get_name(manager); if (!e.manager) return -ENOMEM; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { - if (ptr->manager != e.manager) - continue; - ptr->is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_policy_manager_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->list, - &tomoyo_policy_manager_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); - out: + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_policy_list[TOMOYO_ID_MANAGER], + tomoyo_same_manager_entry); tomoyo_put_name(e.manager); return error; } @@ -1206,41 +503,36 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) * * @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_policy(struct tomoyo_io_buffer *head) { - struct list_head *pos; bool done = true; if (head->read_eof) - return 0; - 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) + return; + list_for_each_cookie(head->read_var2, + &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { + struct tomoyo_policy_manager_entry *ptr = + list_entry(head->read_var2, typeof(*ptr), head.list); + if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, "%s\n", ptr->manager->name); if (!done) break; } head->read_eof = done; - return 0; } /** - * tomoyo_is_policy_manager - Check whether the current process is a policy manager. + * tomoyo_policy_manager - Check whether the current process is a policy manager. * * Returns true if the current process is permitted to modify policy * via /sys/kernel/security/tomoyo/ interface. * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_policy_manager(void) +static bool tomoyo_policy_manager(void) { struct tomoyo_policy_manager_entry *ptr; const char *exe; @@ -1252,8 +544,9 @@ static bool tomoyo_is_policy_manager(void) return true; if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { - if (!ptr->is_deleted && ptr->is_domain + list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], + head.list) { + if (!ptr->head.is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager)) { found = true; break; @@ -1264,8 +557,9 @@ static bool tomoyo_is_policy_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { - if (!ptr->is_deleted && !ptr->is_domain + list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], + head.list) { + if (!ptr->head.is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) { found = true; break; @@ -1285,7 +579,7 @@ static bool tomoyo_is_policy_manager(void) } /** - * tomoyo_is_select_one - Parse select command. + * tomoyo_select_one - Parse select command. * * @head: Pointer to "struct tomoyo_io_buffer". * @data: String to parse. @@ -1294,23 +588,31 @@ static bool tomoyo_is_policy_manager(void) * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, - const char *data) +static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) { unsigned int pid; struct tomoyo_domain_info *domain = NULL; + bool global_pid = false; - if (sscanf(data, "pid=%u", &pid) == 1) { + if (!strcmp(data, "allow_execute")) { + head->print_execute_only = true; + return true; + } + if (sscanf(data, "pid=%u", &pid) == 1 || + (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; rcu_read_lock(); read_lock(&tasklist_lock); - p = find_task_by_vpid(pid); + 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)) + if (tomoyo_domain_def(data + 7)) domain = tomoyo_find_domain(data + 7); } else return false; @@ -1322,20 +624,12 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, 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; - list_for_each_entry_rcu(d, &tomoyo_domain_list, list) { - if (d == domain) - break; - head->read_var1 = &d->list; - } - 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"); - } + head->read_var1 = &domain->list; + head->read_var2 = NULL; + head->read_bit = 0; + head->read_step = 0; + if (domain && domain->is_deleted) + tomoyo_io_printf(head, "# This is a deleted domain.\n"); return true; } @@ -1372,6 +666,24 @@ static int tomoyo_delete_domain(char *domainname) return 0; } +/** + * tomoyo_write_domain_policy2 - Write domain policy. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_domain_policy2(char *data, + struct tomoyo_domain_info *domain, + const bool is_delete) +{ + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) + return tomoyo_write_mount_policy(data, domain, is_delete); + return tomoyo_write_file_policy(data, domain, is_delete); +} + /** * tomoyo_write_domain_policy - Write domain policy. * @@ -1393,12 +705,12 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) is_delete = true; else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) is_select = true; - if (is_select && tomoyo_is_select_one(head, data)) + if (is_select && tomoyo_select_one(head, data)) return 0; /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_is_policy_manager()) + if (!tomoyo_policy_manager()) return -EPERM; - if (tomoyo_is_domain_def(data)) { + if (tomoyo_domain_def(data)) { domain = NULL; if (is_delete) tomoyo_delete_domain(data); @@ -1422,132 +734,157 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) domain->ignore_global_allow_read = !is_delete; return 0; } - return tomoyo_write_file_policy(data, domain, is_delete); + if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) { + domain->quota_warned = !is_delete; + return 0; + } + if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) { + domain->transition_failed = !is_delete; + return 0; + } + return tomoyo_write_domain_policy2(data, domain, is_delete); } /** - * tomoyo_print_path_acl - Print a single path ACL entry. + * tomoyo_fns - Find next set bit. * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_path_acl". + * @perm: 8 bits value. + * @bit: First bit to find. * - * Returns true on success, false otherwise. + * Returns next on-bit on success, 8 otherwise. */ -static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, - struct tomoyo_path_acl *ptr) +static u8 tomoyo_fns(const u8 perm, u8 bit) { - int pos; - u8 bit; - const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16); - - for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { - if (!(perm & (1 << bit))) - continue; - /* Print "read/write" instead of "read" and "write". */ - if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE) - && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) - continue; - pos = head->read_avail; - if (!tomoyo_io_printf(head, "allow_%s ", - tomoyo_path2keyword(bit)) || - !tomoyo_print_name_union(head, &ptr->name) || - !tomoyo_io_printf(head, "\n")) - goto out; - } - head->read_bit = 0; - return true; - out: - head->read_bit = bit; - head->read_avail = pos; - return false; + for ( ; bit < 8; bit++) + if (perm & (1 << bit)) + break; + return bit; } /** - * tomoyo_print_path2_acl - Print a double path ACL entry. + * tomoyo_print_entry - Print an ACL entry. * * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_path2_acl". + * @acl: Pointer to an ACL entry. * * Returns true on success, false otherwise. */ -static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, - struct tomoyo_path2_acl *ptr) +static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, + struct tomoyo_acl_info *acl) { - int pos; - const u8 perm = ptr->perm; - u8 bit; + const u8 acl_type = acl->type; + u8 bit = head->read_bit; + int pos = head->read_avail; - for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { - if (!(perm & (1 << bit))) - continue; - pos = head->read_avail; + if (acl->is_deleted) + return true; + next: + 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 < TOMOYO_MAX_PATH_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (head->print_execute_only && + bit != TOMOYO_TYPE_EXECUTE) + continue; + /* Print "read/write" instead of "read" and "write". */ + if ((bit == TOMOYO_TYPE_READ || + bit == TOMOYO_TYPE_WRITE) + && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) + continue; + break; + } + if (bit >= TOMOYO_MAX_PATH_OPERATION) + goto done; + if (!tomoyo_io_printf(head, "allow_%s ", + tomoyo_path_keyword[bit]) || + !tomoyo_print_name_union(head, &ptr->name)) + goto out; + } else if (head->print_execute_only) { + return true; + } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { + struct tomoyo_path2_acl *ptr = + container_of(acl, typeof(*ptr), head); + bit = tomoyo_fns(ptr->perm, bit); + if (bit >= TOMOYO_MAX_PATH2_OPERATION) + goto done; if (!tomoyo_io_printf(head, "allow_%s ", - tomoyo_path22keyword(bit)) || + tomoyo_path2_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name1) || - !tomoyo_print_name_union(head, &ptr->name2) || - !tomoyo_io_printf(head, "\n")) + !tomoyo_print_name_union(head, &ptr->name2)) + goto out; + } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { + struct tomoyo_path_number_acl *ptr = + container_of(acl, typeof(*ptr), head); + bit = tomoyo_fns(ptr->perm, bit); + if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION) + goto done; + if (!tomoyo_io_printf(head, "allow_%s", + tomoyo_path_number_keyword[bit]) || + !tomoyo_print_name_union(head, &ptr->name) || + !tomoyo_print_number_union(head, &ptr->number)) + goto out; + } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { + struct tomoyo_mkdev_acl *ptr = + container_of(acl, typeof(*ptr), head); + bit = tomoyo_fns(ptr->perm, bit); + if (bit >= TOMOYO_MAX_MKDEV_OPERATION) + goto done; + if (!tomoyo_io_printf(head, "allow_%s", + tomoyo_mkdev_keyword[bit]) || + !tomoyo_print_name_union(head, &ptr->name) || + !tomoyo_print_number_union(head, &ptr->mode) || + !tomoyo_print_number_union(head, &ptr->major) || + !tomoyo_print_number_union(head, &ptr->minor)) + goto out; + } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { + struct tomoyo_mount_acl *ptr = + container_of(acl, typeof(*ptr), head); + if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) || + !tomoyo_print_name_union(head, &ptr->dev_name) || + !tomoyo_print_name_union(head, &ptr->dir_name) || + !tomoyo_print_name_union(head, &ptr->fs_type) || + !tomoyo_print_number_union(head, &ptr->flags)) goto out; } + if (!tomoyo_io_printf(head, "\n")) + goto out; + head->read_bit = bit; + if (acl_type != TOMOYO_TYPE_MOUNT_ACL) { + bit++; + goto next; + } + done: head->read_bit = 0; return true; out: - head->read_bit = bit; head->read_avail = pos; return false; } -/** - * tomoyo_print_entry - Print an ACL entry. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: 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) -{ - const u8 acl_type = ptr->type; - - if (acl_type == TOMOYO_TYPE_PATH_ACL) { - struct tomoyo_path_acl *acl - = container_of(ptr, struct tomoyo_path_acl, head); - return tomoyo_print_path_acl(head, acl); - } - if (acl_type == TOMOYO_TYPE_PATH2_ACL) { - struct tomoyo_path2_acl *acl - = container_of(ptr, struct tomoyo_path2_acl, head); - return tomoyo_print_path2_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. - * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) +static void 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; + return; if (head->read_step == 0) head->read_step = 1; - list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { - struct tomoyo_domain_info *domain; + list_for_each_cookie(head->read_var1, &tomoyo_domain_list) { + struct tomoyo_domain_info *domain = + list_entry(head->read_var1, typeof(*domain), list); 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) @@ -1573,17 +910,17 @@ acl_loop: if (head->read_step == 3) goto tail_mark; /* Print ACL entries in the domain. */ - list_for_each_cookie(apos, head->read_var2, + list_for_each_cookie(head->read_var2, &domain->acl_info_list) { - struct tomoyo_acl_info *ptr - = list_entry(apos, struct tomoyo_acl_info, - list); + struct tomoyo_acl_info *ptr = + list_entry(head->read_var2, typeof(*ptr), list); done = tomoyo_print_entry(head, ptr); if (!done) break; } if (!done) break; + head->read_var2 = NULL; head->read_step = 3; tail_mark: done = tomoyo_io_printf(head, "\n"); @@ -1594,7 +931,6 @@ tail_mark: break; } head->read_eof = done; - return 0; } /** @@ -1607,7 +943,7 @@ tail_mark: * This is equivalent to doing * * ( echo "select " $domainname; echo "use_profile " $profile ) | - * /usr/lib/ccs/loadpolicy -d + * /usr/sbin/tomoyo-loadpolicy -d * * Caller holds tomoyo_read_lock(). */ @@ -1646,16 +982,15 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) +static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) { - struct list_head *pos; bool done = true; if (head->read_eof) - return 0; - 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); + return; + list_for_each_cookie(head->read_var1, &tomoyo_domain_list) { + struct tomoyo_domain_info *domain = + list_entry(head->read_var1, typeof(*domain), list); if (domain->is_deleted) continue; done = tomoyo_io_printf(head, "%u %s\n", domain->profile, @@ -1664,7 +999,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) break; } head->read_eof = done; - return 0; } /** @@ -1694,7 +1028,7 @@ 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; @@ -1712,9 +1046,17 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head) domain->domainname->name); head->read_eof = true; } - return 0; } +static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { + [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] + = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN, + [TOMOYO_TRANSITION_CONTROL_INITIALIZE] + = TOMOYO_KEYWORD_INITIALIZE_DOMAIN, + [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN, + [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN +}; + /** * tomoyo_write_exception_policy - Write exception policy. * @@ -1728,20 +1070,15 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) { char *data = head->write_buf; bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); + u8 i; - 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); + for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) { + if (tomoyo_str_starts(&data, tomoyo_transition_type[i])) + return tomoyo_write_transition_control(data, is_delete, + i); + } + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_AGGREGATOR)) + return tomoyo_write_aggregator_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)) @@ -1749,161 +1086,544 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE)) return tomoyo_write_no_rewrite_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP)) - return tomoyo_write_path_group_policy(data, is_delete); + return tomoyo_write_group(data, is_delete, TOMOYO_PATH_GROUP); + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP)) + return tomoyo_write_group(data, is_delete, TOMOYO_NUMBER_GROUP); return -EINVAL; } +static void tomoyo_print_number(char *buffer, int buffer_len, + const struct tomoyo_number_union *ptr) +{ + int i; + unsigned long min = ptr->values[0]; + const unsigned long max = ptr->values[1]; + u8 min_type = ptr->min_type; + const u8 max_type = ptr->max_type; + memset(buffer, 0, buffer_len); + buffer_len -= 2; + for (i = 0; i < 2; i++) { + int len; + switch (min_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + snprintf(buffer, buffer_len, "0x%lX", min); + break; + case TOMOYO_VALUE_TYPE_OCTAL: + snprintf(buffer, buffer_len, "0%lo", min); + break; + default: + snprintf(buffer, buffer_len, "%lu", min); + break; + } + if (min == max && min_type == max_type) + break; + len = strlen(buffer); + buffer[len++] = '-'; + buffer += len; + buffer_len -= len; + min_type = max_type; + min = max; + } +} + +static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { + [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, + [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP +}; + /** - * tomoyo_read_exception_policy - Read exception policy. + * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_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: - if (!tomoyo_read_path_group_policy(head)) - break; - head->read_var1 = NULL; - head->read_var2 = NULL; - head->read_step = 10; - case 10: - head->read_eof = true; + const char *w[3] = { "", "", "" }; + w[0] = tomoyo_group_name[idx]; + list_for_each_cookie(head->read_var1, &tomoyo_group_list[idx]) { + struct tomoyo_group *group = + list_entry(head->read_var1, typeof(*group), list); + w[1] = group->group_name->name; + list_for_each_cookie(head->read_var2, &group->member_list) { + char buffer[128]; + struct tomoyo_acl_head *ptr = + list_entry(head->read_var2, typeof(*ptr), list); + if (ptr->is_deleted) + continue; + if (idx == TOMOYO_PATH_GROUP) { + w[2] = container_of(ptr, + struct tomoyo_path_group, + head)->member_name->name; + } else if (idx == TOMOYO_NUMBER_GROUP) { + tomoyo_print_number(buffer, sizeof(buffer), + &container_of + (ptr, struct + tomoyo_number_group, + head)->number); + w[2] = buffer; + } + if (!tomoyo_io_printf(head, "%s%s %s\n", w[0], w[1], + w[2])) + return false; + } + head->read_var2 = NULL; + } + head->read_var1 = 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) +{ + list_for_each_cookie(head->read_var2, &tomoyo_policy_list[idx]) { + const char *w[4] = { "", "", "", "" }; + struct tomoyo_acl_head *acl = + container_of(head->read_var2, typeof(*acl), list); + if (acl->is_deleted) + continue; + switch (idx) { + case TOMOYO_ID_TRANSITION_CONTROL: + { + struct tomoyo_transition_control *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = tomoyo_transition_type[ptr->type]; + if (ptr->program) + w[1] = ptr->program->name; + if (ptr->domainname) + w[3] = ptr->domainname->name; + if (w[1][0] && w[3][0]) + w[2] = " from "; + } + break; + case TOMOYO_ID_GLOBALLY_READABLE: + { + struct tomoyo_globally_readable_file_entry *ptr + = container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_ALLOW_READ; + w[1] = ptr->filename->name; + } + break; + case TOMOYO_ID_AGGREGATOR: + { + struct tomoyo_aggregator_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_AGGREGATOR; + w[1] = ptr->original_name->name; + w[2] = " "; + w[3] = ptr->aggregated_name->name; + } + break; + case TOMOYO_ID_PATTERN: + { + struct tomoyo_pattern_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_FILE_PATTERN; + w[1] = ptr->pattern->name; + } + break; + case TOMOYO_ID_NO_REWRITE: + { + struct tomoyo_no_rewrite_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_DENY_REWRITE; + w[1] = ptr->pattern->name; + } break; default: - return -EINVAL; + continue; } + if (!tomoyo_io_printf(head, "%s%s%s%s\n", w[0], w[1], w[2], + w[3])) + return false; } - return 0; + head->read_var2 = NULL; + return true; +} + +/** + * tomoyo_read_exception_policy - Read exception policy. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Caller holds tomoyo_read_lock(). + */ +static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) +{ + if (head->read_eof) + return; + while (head->read_step < TOMOYO_MAX_POLICY && + tomoyo_read_policy(head, head->read_step)) + head->read_step++; + if (head->read_step < TOMOYO_MAX_POLICY) + return; + while (head->read_step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP && + tomoyo_read_group(head, head->read_step - TOMOYO_MAX_POLICY)) + head->read_step++; + if (head->read_step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) + return; + head->read_eof = true; } -/* path to policy loader */ -static const char *tomoyo_loader = "/sbin/tomoyo-init"; +/** + * tomoyo_print_header - Get header line of audit log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns string representation. + * + * This function uses kmalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +static char *tomoyo_print_header(struct tomoyo_request_info *r) +{ + static const char *tomoyo_mode_4[4] = { + "disabled", "learning", "permissive", "enforcing" + }; + struct timeval tv; + const pid_t gpid = task_pid_nr(current); + static const int tomoyo_buffer_len = 4096; + char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); + if (!buffer) + return NULL; + do_gettimeofday(&tv); + snprintf(buffer, tomoyo_buffer_len - 1, + "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" + " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" + " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", + tv.tv_sec, r->profile, tomoyo_mode_4[r->mode], gpid, + (pid_t) sys_getpid(), (pid_t) sys_getppid(), + current_uid(), current_gid(), current_euid(), + current_egid(), current_suid(), current_sgid(), + current_fsuid(), current_fsgid()); + return buffer; +} /** - * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. + * tomoyo_init_audit_log - Allocate buffer for audit logs. + * + * @len: Required size. + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns pointer to allocated memory. + * + * The @len is updated to add the header lines' size on success. * - * Returns true if /sbin/tomoyo-init exists, false otherwise. + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. */ -static bool tomoyo_policy_loader_exists(void) +static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) { - /* - * 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 *buf = NULL; + const char *header; + const char *domainname; + if (!r->domain) + r->domain = tomoyo_domain(); + domainname = r->domain->domainname->name; + header = tomoyo_print_header(r); + if (!header) + return NULL; + *len += strlen(domainname) + strlen(header) + 10; + buf = kzalloc(*len, GFP_NOFS); + if (buf) + snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname); + kfree(header); + return buf; +} - 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; +/* Wait queue for tomoyo_query_list. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); + +/* Lock for manipulating tomoyo_query_list. */ +static DEFINE_SPINLOCK(tomoyo_query_list_lock); + +/* Structure for query. */ +struct tomoyo_query_entry { + struct list_head list; + char *query; + int query_len; + unsigned int serial; + int timer; + int answer; +}; + +/* The list for "struct tomoyo_query_entry". */ +static LIST_HEAD(tomoyo_query_list); + +/* + * Number of "struct file" referring /sys/kernel/security/tomoyo/query + * interface. + */ +static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); + +/** + * tomoyo_supervisor - Ask for the supervisor's decision. + * + * @r: Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. + * + * Returns 0 if the supervisor decided to permit the access request which + * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the + * supervisor decided to retry the access request which violated the policy in + * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise. + */ +int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) +{ + va_list args; + int error = -EPERM; + int pos; + int len; + static unsigned int tomoyo_serial; + struct tomoyo_query_entry *tomoyo_query_entry = NULL; + bool quota_exceeded = false; + char *header; + switch (r->mode) { + char *buffer; + case TOMOYO_CONFIG_LEARNING: + if (!tomoyo_domain_quota_is_ok(r)) + return 0; + va_start(args, fmt); + len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4; + va_end(args); + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return 0; + va_start(args, fmt); + vsnprintf(buffer, len - 1, fmt, args); + va_end(args); + tomoyo_normalize_line(buffer); + tomoyo_write_domain_policy2(buffer, r->domain, false); + kfree(buffer); + /* fall through */ + case TOMOYO_CONFIG_PERMISSIVE: + return 0; } - path_put(&path); - return true; + if (!r->domain) + r->domain = tomoyo_domain(); + if (!atomic_read(&tomoyo_query_observers)) + return -EPERM; + va_start(args, fmt); + len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; + va_end(args); + header = tomoyo_init_audit_log(&len, r); + if (!header) + goto out; + tomoyo_query_entry = kzalloc(sizeof(*tomoyo_query_entry), GFP_NOFS); + if (!tomoyo_query_entry) + goto out; + tomoyo_query_entry->query = kzalloc(len, GFP_NOFS); + if (!tomoyo_query_entry->query) + goto out; + len = ksize(tomoyo_query_entry->query); + INIT_LIST_HEAD(&tomoyo_query_entry->list); + spin_lock(&tomoyo_query_list_lock); + if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + + sizeof(*tomoyo_query_entry) >= tomoyo_quota_for_query) { + quota_exceeded = true; + } else { + tomoyo_query_memory_size += len + sizeof(*tomoyo_query_entry); + tomoyo_query_entry->serial = tomoyo_serial++; + } + spin_unlock(&tomoyo_query_list_lock); + if (quota_exceeded) + goto out; + pos = snprintf(tomoyo_query_entry->query, len - 1, "Q%u-%hu\n%s", + tomoyo_query_entry->serial, r->retry, header); + kfree(header); + header = NULL; + va_start(args, fmt); + vsnprintf(tomoyo_query_entry->query + pos, len - 1 - pos, fmt, args); + tomoyo_query_entry->query_len = strlen(tomoyo_query_entry->query) + 1; + va_end(args); + spin_lock(&tomoyo_query_list_lock); + list_add_tail(&tomoyo_query_entry->list, &tomoyo_query_list); + spin_unlock(&tomoyo_query_list_lock); + /* Give 10 seconds for supervisor's opinion. */ + for (tomoyo_query_entry->timer = 0; + atomic_read(&tomoyo_query_observers) && tomoyo_query_entry->timer < 100; + tomoyo_query_entry->timer++) { + wake_up(&tomoyo_query_wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + if (tomoyo_query_entry->answer) + break; + } + spin_lock(&tomoyo_query_list_lock); + list_del(&tomoyo_query_entry->list); + tomoyo_query_memory_size -= len + sizeof(*tomoyo_query_entry); + spin_unlock(&tomoyo_query_list_lock); + switch (tomoyo_query_entry->answer) { + case 3: /* Asked to retry by administrator. */ + error = TOMOYO_RETRY_REQUEST; + r->retry++; + break; + case 1: + /* Granted by administrator. */ + error = 0; + break; + case 0: + /* Timed out. */ + break; + default: + /* Rejected by administrator. */ + break; + } + out: + if (tomoyo_query_entry) + kfree(tomoyo_query_entry->query); + kfree(tomoyo_query_entry); + kfree(header); + return error; } /** - * tomoyo_load_policy - Run external policy loader to load policy. + * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query. * - * @filename: The program about to start. + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". * - * 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 POLLIN | POLLRDNORM when ready to read, 0 otherwise. * - * Returns nothing. + * Waits for access requests which violated policy in enforcing mode. */ -void tomoyo_load_policy(const char *filename) +static int tomoyo_poll_query(struct file *file, poll_table *wait) { - char *argv[2]; - char *envp[3]; + struct list_head *tmp; + bool found = false; + u8 i; + for (i = 0; i < 2; i++) { + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, + list); + if (ptr->answer) + continue; + found = true; + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (found) + return POLLIN | POLLRDNORM; + if (i) + break; + poll_wait(file, &tomoyo_query_wait, wait); + } + return 0; +} - 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; + int pos = 0; + int len = 0; + char *buf; + if (head->read_avail) 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; + head->readbuf_size = 0; + } + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->answer) + continue; + if (pos++ != head->read_step) + continue; + len = ptr->query_len; + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (!len) { + head->read_step = 0; return; - if (!tomoyo_policy_loader_exists()) + } + buf = kzalloc(len, GFP_NOFS); + if (!buf) return; + pos = 0; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->answer) + continue; + if (pos++ != head->read_step) + continue; + /* + * Some query can be skipped because tomoyo_query_list + * can change, but I don't care. + */ + if (len == ptr->query_len) + memmove(buf, ptr->query, len); + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (buf[0]) { + head->read_avail = len; + head->readbuf_size = head->read_avail; + head->read_buf = buf; + head->read_step++; + } else { + kfree(buf); + } +} - 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; - list_for_each_entry_rcu(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); - } +/** + * tomoyo_write_answer - Write the supervisor's decision. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0 on success, -EINVAL otherwise. + */ +static int tomoyo_write_answer(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + struct list_head *tmp; + unsigned int serial; + unsigned int answer; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + ptr->timer = 0; } + spin_unlock(&tomoyo_query_list_lock); + if (sscanf(data, "A%u=%u", &serial, &answer) != 2) + return -EINVAL; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->serial != serial) + continue; + if (!ptr->answer) + ptr->answer = answer; + break; + } + spin_unlock(&tomoyo_query_list_lock); + return 0; } /** @@ -1913,13 +1633,12 @@ 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"); + tomoyo_io_printf(head, "2.3.0-pre"); head->read_eof = true; } - return 0; } /** @@ -1929,7 +1648,7 @@ static int tomoyo_read_version(struct tomoyo_io_buffer *head) * * Returns the current process's domainname. */ -static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) +static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) { if (!head->read_eof) { /* @@ -1940,7 +1659,6 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); head->read_eof = true; } - return 0; } /** @@ -1953,13 +1671,14 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) * * Caller acquires tomoyo_read_lock(). */ -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 = 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 */ @@ -2001,6 +1720,11 @@ static int tomoyo_open_control(const u8 type, struct file *file) head->write = tomoyo_write_profile; head->read = tomoyo_read_profile; break; + case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */ + head->poll = tomoyo_poll_query; + head->write = tomoyo_write_answer; + head->read = tomoyo_read_query; + break; case TOMOYO_MANAGER: /* /sys/kernel/security/tomoyo/manager */ head->write = tomoyo_write_manager_policy; @@ -2013,7 +1737,9 @@ static int tomoyo_open_control(const u8 type, struct file *file) * for reading. */ head->read = NULL; - } else { + head->poll = NULL; + } else if (!head->poll) { + /* Don't allocate read_buf for poll() access. */ if (!head->readbuf_size) head->readbuf_size = 4096 * 2; head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); @@ -2037,7 +1763,8 @@ static int tomoyo_open_control(const u8 type, struct file *file) return -ENOMEM; } } - head->reader_idx = tomoyo_read_lock(); + if (type != TOMOYO_QUERY) + head->reader_idx = tomoyo_read_lock(); file->private_data = head; /* * Call the handler now if the file is @@ -2048,9 +1775,34 @@ static int tomoyo_open_control(const u8 type, struct file *file) */ if (type == TOMOYO_SELFDOMAIN) tomoyo_read_control(file, NULL, 0); + /* + * If the file is /sys/kernel/security/tomoyo/query , increment the + * observer counter. + * The obserber counter is used by tomoyo_supervisor() to see if + * there is some process monitoring /sys/kernel/security/tomoyo/query. + */ + else if (type == TOMOYO_QUERY) + atomic_inc(&tomoyo_query_observers); return 0; } +/** + * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Waits for read readiness. + * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + */ +int tomoyo_poll_control(struct file *file, poll_table *wait) +{ + struct tomoyo_io_buffer *head = file->private_data; + if (!head->poll) + return -ENOSYS; + return head->poll(file, wait); +} + /** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * @@ -2062,8 +1814,8 @@ static int tomoyo_open_control(const u8 type, struct file *file) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_control(struct file *file, char __user *buffer, - const int buffer_len) +int tomoyo_read_control(struct file *file, char __user *buffer, + const int buffer_len) { int len = 0; struct tomoyo_io_buffer *head = file->private_data; @@ -2074,7 +1826,7 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; /* Call the policy handler. */ - len = head->read(head); + head->read(head); if (len < 0) goto out; /* Write to buffer. */ @@ -2107,8 +1859,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_control(struct file *file, const char __user *buffer, - const int buffer_len) +int tomoyo_write_control(struct file *file, const char __user *buffer, + const int buffer_len) { struct tomoyo_io_buffer *head = file->private_data; int error = buffer_len; @@ -2122,7 +1874,7 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, /* 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()) + !tomoyo_policy_manager()) return -EPERM; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; @@ -2159,12 +1911,19 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, * * Caller looses tomoyo_read_lock(). */ -static int tomoyo_close_control(struct file *file) +int tomoyo_close_control(struct file *file) { struct tomoyo_io_buffer *head = file->private_data; const bool is_write = !!head->write_buf; - tomoyo_read_unlock(head->reader_idx); + /* + * If the file is /sys/kernel/security/tomoyo/query , decrement the + * observer counter. + */ + if (head->type == TOMOYO_QUERY) + atomic_dec(&tomoyo_query_observers); + else + tomoyo_read_unlock(head->reader_idx); /* Release memory used for policy I/O. */ kfree(head->read_buf); head->read_buf = NULL; @@ -2179,129 +1938,25 @@ static int tomoyo_close_control(struct file *file) } /** - * 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_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. - * - * Returns nothing. - */ -static void __init tomoyo_create_entry(const char *name, const mode_t mode, - struct dentry *parent, const u8 key) -{ - securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, - &tomoyo_operations); -} - -/** - * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. - * - * Returns 0. + * tomoyo_check_profile - Check all profiles currently assigned to domains are defined. */ -static int __init tomoyo_initerface_init(void) +void tomoyo_check_profile(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; + struct tomoyo_domain_info *domain; + const int idx = tomoyo_read_lock(); + tomoyo_policy_loaded = true; + /* Check all profiles currently assigned to domains are defined. */ + list_for_each_entry_rcu(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); + } + tomoyo_read_unlock(idx); + if (tomoyo_profile_version != 20090903) + panic("Profile version %u is not supported.\n", + tomoyo_profile_version); + printk(KERN_INFO "TOMOYO: 2.3.0-pre 2010/06/03\n"); + printk(KERN_INFO "Mandatory Access Control activated.\n"); } - -fs_initcall(tomoyo_initerface_init);