diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2010-05-17 03:12:46 +0200 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-08-02 07:33:39 +0200 |
commit | c3ef1500ec833890275172c7d063333404b64d60 (patch) | |
tree | 2453368e521a1f7a00098eef06afbedb8404503d /security/tomoyo/util.c | |
parent | TOMOYO: Add interactive enforcing mode. (diff) | |
download | linux-c3ef1500ec833890275172c7d063333404b64d60.tar.xz linux-c3ef1500ec833890275172c7d063333404b64d60.zip |
TOMOYO: Split files into some pieces.
security/tomoyo/common.c became too large to read.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to '')
-rw-r--r-- | security/tomoyo/util.c | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c new file mode 100644 index 000000000000..7b023f5e1314 --- /dev/null +++ b/security/tomoyo/util.c @@ -0,0 +1,951 @@ +/* + * security/tomoyo/util.c + * + * Utility functions for TOMOYO. + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + */ + +#include <linux/slab.h> +#include "common.h" + +/* Lock for protecting policy. */ +DEFINE_MUTEX(tomoyo_policy_lock); + +/* Has /sbin/init started? */ +bool tomoyo_policy_loaded; + +/** + * tomoyo_parse_ulong - Parse an "unsigned long" value. + * + * @result: Pointer to "unsigned long". + * @str: Pointer to string to parse. + * + * Returns value type on success, 0 otherwise. + * + * The @src is updated to point the first character after the value + * on success. + */ +u8 tomoyo_parse_ulong(unsigned long *result, char **str) +{ + const char *cp = *str; + char *ep; + int base = 10; + if (*cp == '0') { + char c = *(cp + 1); + if (c == 'x' || c == 'X') { + base = 16; + cp += 2; + } else if (c >= '0' && c <= '7') { + base = 8; + cp++; + } + } + *result = simple_strtoul(cp, &ep, base); + if (cp == ep) + return 0; + *str = ep; + switch (base) { + case 16: + return TOMOYO_VALUE_TYPE_HEXADECIMAL; + case 8: + return TOMOYO_VALUE_TYPE_OCTAL; + default: + return TOMOYO_VALUE_TYPE_DECIMAL; + } +} + +/** + * tomoyo_print_ulong - Print an "unsigned long" value. + * + * @buffer: Pointer to buffer. + * @buffer_len: Size of @buffer. + * @value: An "unsigned long" value. + * @type: Type of @value. + * + * Returns nothing. + */ +void tomoyo_print_ulong(char *buffer, const int buffer_len, + const unsigned long value, const u8 type) +{ + if (type == TOMOYO_VALUE_TYPE_DECIMAL) + snprintf(buffer, buffer_len, "%lu", value); + else if (type == TOMOYO_VALUE_TYPE_OCTAL) + snprintf(buffer, buffer_len, "0%lo", value); + else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) + snprintf(buffer, buffer_len, "0x%lX", value); + else + snprintf(buffer, buffer_len, "type(%u)", type); +} + +/** + * tomoyo_parse_name_union - Parse a tomoyo_name_union. + * + * @filename: Name or name group. + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_name_union(const char *filename, + struct tomoyo_name_union *ptr) +{ + 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; +} + +/** + * tomoyo_parse_number_union - Parse a tomoyo_number_union. + * + * @data: Number or number range or number group. + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) +{ + u8 type; + unsigned long v; + memset(num, 0, sizeof(*num)); + if (data[0] == '@') { + if (!tomoyo_is_correct_path(data, 0, 0, 0)) + return false; + num->group = tomoyo_get_number_group(data + 1); + num->is_group = true; + return num->group != NULL; + } + type = tomoyo_parse_ulong(&v, &data); + if (!type) + return false; + num->values[0] = v; + num->min_type = type; + if (!*data) { + num->values[1] = v; + num->max_type = type; + return true; + } + if (*data++ != '-') + return false; + type = tomoyo_parse_ulong(&v, &data); + if (!type || *data) + return false; + num->values[1] = v; + num->max_type = type; + return true; +} + +/** + * tomoyo_is_byte_range - Check whether the string is a \ooo style octal value. + * + * @str: Pointer to the string. + * + * Returns true if @str is a \ooo style octal value, false otherwise. + * + * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. + * This function verifies that \ooo is in valid range. + */ +static inline bool tomoyo_is_byte_range(const char *str) +{ + return *str >= '0' && *str++ <= '3' && + *str >= '0' && *str++ <= '7' && + *str >= '0' && *str <= '7'; +} + +/** + * tomoyo_is_alphabet_char - Check whether the character is an alphabet. + * + * @c: The character to check. + * + * Returns true if @c is an alphabet character, false otherwise. + */ +static inline bool tomoyo_is_alphabet_char(const char c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +/** + * tomoyo_make_byte - Make byte value from three octal characters. + * + * @c1: The first character. + * @c2: The second character. + * @c3: The third character. + * + * Returns byte value. + */ +static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) +{ + return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); +} + +/** + * tomoyo_str_starts - Check whether the given string starts with the given keyword. + * + * @src: Pointer to pointer to the string. + * @find: Pointer to the keyword. + * + * Returns true if @src starts with @find, false otherwise. + * + * The @src is updated to point the first character after the @find + * if @src starts with @find. + */ +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. + */ +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 . + * + * 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) +{ + char c; + int len = 0; + + if (!filename) + return 0; + while ((c = *filename++) != '\0') { + if (c != '\\') { + len++; + continue; + } + c = *filename++; + switch (c) { + case '\\': /* "\\" */ + len += 2; + continue; + case '0': /* "\ooo" */ + case '1': + case '2': + case '3': + c = *filename++; + if (c < '0' || c > '7') + break; + c = *filename++; + if (c < '0' || c > '7') + break; + len += 4; + continue; + } + break; + } + return len; +} + +/** + * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. + * + * @ptr: Pointer to "struct tomoyo_path_info" to fill in. + * + * The caller sets "struct tomoyo_path_info"->name. + */ +void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) +{ + const char *name = ptr->name; + const int len = strlen(name); + + ptr->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 '/' 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) + 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)) + return false; + f = f_delimiter; + if (*f) + f++; + p = p_delimiter; + if (*p) + p++; + } + /* Ignore trailing "\*" and "\@" in @pattern. */ + while (*p == '\\' && + (*(p + 1) == '*' || *(p + 1) == '@')) + p += 2; + return !*f && !*p; + 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); +} + +/** + * 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. + */ +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_init_request_info - Initialize "struct tomoyo_request_info" members. + * + * @r: Pointer to "struct tomoyo_request_info" to initialize. + * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). + * + * Returns mode. + */ +int tomoyo_init_request_info(struct tomoyo_request_info *r, + struct tomoyo_domain_info *domain) +{ + memset(r, 0, sizeof(*r)); + if (!domain) + domain = tomoyo_domain(); + r->domain = domain; + r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); + return r->mode; +} + +/** + * tomoyo_warn_log - Print warning or error message on console. + * + * @r: Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. + */ +void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) +{ + int len = PAGE_SIZE; + va_list args; + char *buffer; + if (!tomoyo_verbose_mode(r->domain)) + return; + while (1) { + int len2; + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return; + va_start(args, fmt); + len2 = vsnprintf(buffer, len - 1, fmt, args); + va_end(args); + if (len2 <= len - 1) { + buffer[len2] = '\0'; + break; + } + len = len2 + 1; + kfree(buffer); + } + printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n", + r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", + buffer, tomoyo_get_last_name(r->domain)); + kfree(buffer); +} + +/** + * tomoyo_domain_quota_is_ok - Check for domain's quota. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns true if the domain is not exceeded quota, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) +{ + unsigned int count = 0; + struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + + if (r->mode != TOMOYO_CONFIG_LEARNING) + return false; + if (!domain) + return true; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + switch (ptr->type) { + u16 perm; + u8 i; + case TOMOYO_TYPE_PATH_ACL: + perm = container_of(ptr, struct tomoyo_path_acl, head) + ->perm; + for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) + if (perm & (1 << i)) + count++; + if (perm & (1 << 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; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + perm = container_of(ptr, struct tomoyo_path_number_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + perm = container_of(ptr, struct tomoyo_path_number3_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; + case TOMOYO_TYPE_MOUNT_ACL: + if (!container_of(ptr, struct tomoyo_mount_acl, head)-> + is_deleted) + count++; + } + } + if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) + return 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; +} |