From 3f629636320dfa65804779a3fc333f3147f3b064 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:37:26 +0900 Subject: [PATCH] TOMOYO: Allow wildcard for execute permission. Some applications create and execute programs dynamically. We need to accept wildcard for execute permission because such programs contain random suffix in their filenames. This patch loosens up regulation of string parameters. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 2 +- security/tomoyo/common.h | 7 +- security/tomoyo/domain.c | 21 ++--- security/tomoyo/file.c | 37 +++----- security/tomoyo/number_group.c | 3 +- security/tomoyo/path_group.c | 17 +--- security/tomoyo/util.c | 154 +++++++++++++-------------------- 7 files changed, 90 insertions(+), 151 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 98e3639db990..3f94011c6411 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -407,7 +407,7 @@ static int tomoyo_update_manager_entry(const char *manager, return -EINVAL; e.is_domain = true; } else { - if (!tomoyo_is_correct_path(manager, 1, -1, -1)) + if (!tomoyo_is_correct_path(manager)) return -EINVAL; } e.manager = tomoyo_get_name(manager); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f4a8aa244af5..d1b8d791bfff 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -672,16 +672,15 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) /* Check whether the domainname is correct. */ bool tomoyo_is_correct_domain(const unsigned char *domainname); /* Check whether the token is correct. */ -bool tomoyo_is_correct_path(const char *filename, const s8 start_type, - const s8 pattern_type, const s8 end_type); +bool tomoyo_is_correct_path(const char *filename); +bool tomoyo_is_correct_word(const char *string); /* Check whether the token can be a domainname. */ bool tomoyo_is_domain_def(const unsigned char *buffer); bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr); /* Check whether the given filename matches the given path_group. */ bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_path_group *group, - const bool may_use_pattern); + const struct tomoyo_path_group *group); /* Check whether the given value matches the given number_group. */ bool tomoyo_number_matches_group(const unsigned long min, const unsigned long max, diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 7b8693e29a13..50f6e7972174 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -131,11 +131,11 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(program, 1, -1, -1)) - return -EINVAL; /* No patterns allowed. */ + if (!tomoyo_is_correct_path(program)) + return -EINVAL; if (domainname) { if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1)) + tomoyo_is_correct_path(domainname)) e.is_last_name = true; else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; @@ -342,12 +342,12 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, int error = is_delete ? -ENOENT : -ENOMEM; if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1)) + tomoyo_is_correct_path(domainname)) e.is_last_name = true; else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; if (program) { - if (!tomoyo_is_correct_path(program, 1, -1, -1)) + if (!tomoyo_is_correct_path(program)) return -EINVAL; e.program = tomoyo_get_name(program); if (!e.program) @@ -533,13 +533,14 @@ static int tomoyo_update_alias_entry(const char *original_name, struct tomoyo_alias_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(original_name, 1, -1, -1) || - !tomoyo_is_correct_path(aliased_name, 1, -1, -1)) - return -EINVAL; /* No patterns allowed. */ + if (!tomoyo_is_correct_path(original_name) || + !tomoyo_is_correct_path(aliased_name)) + return -EINVAL; e.original_name = tomoyo_get_name(original_name); e.aliased_name = tomoyo_get_name(aliased_name); - if (!e.original_name || !e.aliased_name) - goto out; + if (!e.original_name || !e.aliased_name || + e.original_name->is_patterned || e.aliased_name->is_patterned) + goto out; /* No patterns allowed. */ if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index cef685415df1..83fa17a1113a 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -65,23 +65,10 @@ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr) { if (ptr->is_group) - return tomoyo_path_matches_group(name, ptr->group, 1); + return tomoyo_path_matches_group(name, ptr->group); return tomoyo_path_matches_pattern(name, ptr->filename); } -static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info - *name, - const struct tomoyo_name_union - *ptr, const bool may_use_pattern) -{ - if (ptr->is_group) - return tomoyo_path_matches_group(name, ptr->group, - may_use_pattern); - if (may_use_pattern || !ptr->filename->is_patterned) - return tomoyo_path_matches_pattern(name, ptr->filename); - return false; -} - void tomoyo_put_number_union(struct tomoyo_number_union *ptr) { if (ptr && ptr->is_group) @@ -247,7 +234,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename, struct tomoyo_globally_readable_file_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(filename, 1, 0, -1)) + if (!tomoyo_is_correct_word(filename)) return -EINVAL; e.filename = tomoyo_get_name(filename); if (!e.filename) @@ -391,13 +378,14 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, const bool is_delete) { struct tomoyo_pattern_entry *ptr; - struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) }; + struct tomoyo_pattern_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_is_correct_word(pattern)) + return -EINVAL; + e.pattern = tomoyo_get_name(pattern); if (!e.pattern) return error; - if (!e.pattern->is_patterned) - goto out; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { @@ -543,7 +531,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, struct tomoyo_no_rewrite_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(pattern, 0, 0, 0)) + if (!tomoyo_is_correct_word(pattern)) return -EINVAL; e.pattern = tomoyo_get_name(pattern); if (!e.pattern) @@ -690,7 +678,6 @@ static int tomoyo_update_file_acl(u8 perm, const char *filename, * @r: Pointer to "struct tomoyo_request_info". * @filename: Filename to check. * @perm: Permission. - * @may_use_pattern: True if patterned ACL is permitted. * * Returns 0 on success, -EPERM otherwise. * @@ -698,7 +685,7 @@ static int tomoyo_update_file_acl(u8 perm, const char *filename, */ static int tomoyo_path_acl(const struct tomoyo_request_info *r, const struct tomoyo_path_info *filename, - const u32 perm, const bool may_use_pattern) + const u32 perm) { struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; @@ -710,8 +697,7 @@ static int tomoyo_path_acl(const struct tomoyo_request_info *r, continue; acl = container_of(ptr, struct tomoyo_path_acl, head); if (!(acl->perm & perm) || - !tomoyo_compare_name_union_pattern(filename, &acl->name, - may_use_pattern)) + !tomoyo_compare_name_union(filename, &acl->name)) continue; error = 0; break; @@ -756,7 +742,7 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, } else BUG(); do { - error = tomoyo_path_acl(r, filename, perm, mode != 1); + error = tomoyo_path_acl(r, filename, perm); if (error && mode == 4 && !r->domain->ignore_global_allow_read && tomoyo_is_globally_readable_file(filename)) error = 0; @@ -764,7 +750,6 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, break; tomoyo_warn_log(r, "%s %s", msg, filename->name); error = tomoyo_supervisor(r, "allow_%s %s\n", msg, - mode == 1 ? filename->name : tomoyo_file_pattern(filename)); /* * Do not retry for execute request, for alias may have @@ -1073,7 +1058,7 @@ static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, next: do { - error = tomoyo_path_acl(r, filename, 1 << operation, 1); + error = tomoyo_path_acl(r, filename, 1 << operation); if (!error) break; msg = tomoyo_path2keyword(operation); diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index c49792e09e81..8d6ef8f006ff 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -24,8 +24,7 @@ struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) struct tomoyo_number_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; - if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || - !group_name[0]) + if (!tomoyo_is_correct_word(group_name)) return NULL; saved_group_name = tomoyo_get_name(group_name); if (!saved_group_name) diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index 636025e26b06..07e4f782367b 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -22,8 +22,7 @@ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) struct tomoyo_path_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; - if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || - !group_name[0]) + if (!tomoyo_is_correct_word(group_name)) return NULL; saved_group_name = tomoyo_get_name(group_name); if (!saved_group_name) @@ -141,29 +140,21 @@ bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) * * @pathname: The name of pathname. * @group: Pointer to "struct tomoyo_path_group". - * @may_use_pattern: True if wild card is permitted. * * Returns true if @pathname matches pathnames in @group, false otherwise. * * Caller holds tomoyo_read_lock(). */ bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_path_group *group, - const bool may_use_pattern) + const struct tomoyo_path_group *group) { struct tomoyo_path_group_member *member; bool matched = false; list_for_each_entry_rcu(member, &group->member_list, list) { if (member->is_deleted) continue; - if (!member->member_name->is_patterned) { - if (tomoyo_pathcmp(pathname, member->member_name)) - continue; - } else if (may_use_pattern) { - if (!tomoyo_path_matches_pattern(pathname, - member->member_name)) - continue; - } else + if (!tomoyo_path_matches_pattern(pathname, + member->member_name)) continue; matched = true; break; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 7b023f5e1314..592b76a2bce8 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -89,7 +89,7 @@ void tomoyo_print_ulong(char *buffer, const int buffer_len, bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr) { - if (!tomoyo_is_correct_path(filename, 0, 0, 0)) + if (!tomoyo_is_correct_word(filename)) return false; if (filename[0] == '@') { ptr->group = tomoyo_get_path_group(filename + 1); @@ -115,7 +115,7 @@ bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) unsigned long v; memset(num, 0, sizeof(*num)); if (data[0] == '@') { - if (!tomoyo_is_correct_path(data, 0, 0, 0)) + if (!tomoyo_is_correct_word(data)) return false; num->group = tomoyo_get_number_group(data + 1); num->is_group = true; @@ -265,54 +265,29 @@ bool tomoyo_tokenize(char *buffer, char *w[], size_t size) } /** - * tomoyo_is_correct_path - Validate a pathname. + * tomoyo_is_correct_word2 - Validate a string. * - * @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 + * @string: The string to check. May be non-'\0'-terminated. + * @len: Length of @string. * - * Check whether the given filename follows the naming rules. - * Returns true if @filename follows the naming rules, false otherwise. + * Check whether the given string follows the naming rules. + * Returns true if @string 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) +static bool tomoyo_is_correct_word2(const char *string, size_t len) { - const char *const start = filename; + const char *const start = string; bool in_repetition = false; - bool contains_pattern = false; unsigned char c; unsigned char d; unsigned char e; - - if (!filename) + if (!len) 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; + while (len--) { + c = *string++; if (c == '\\') { - c = *filename++; + if (!len--) + goto out; + c = *string++; switch (c) { case '\\': /* "\\" */ continue; @@ -326,21 +301,14 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, 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) != '/') + if (string - 3 < start || *(string - 3) != '/') break; - if (pattern_type == -1) - break; /* Must not contain pattern */ - contains_pattern = true; in_repetition = true; continue; case '}': /* "\}/" */ - if (*filename != '/') + if (*string != '/') break; if (!in_repetition) break; @@ -350,11 +318,11 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, case '1': case '2': case '3': - d = *filename++; - if (d < '0' || d > '7') + if (!len-- || !len--) break; - e = *filename++; - if (e < '0' || e > '7') + d = *string++; + e = *string++; + if (d < '0' || d > '7' || e < '0' || e > '7') break; c = tomoyo_make_byte(c, d, e); if (tomoyo_is_invalid(c)) @@ -367,10 +335,6 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, goto out; } } - if (pattern_type == 1) { /* Must contain pattern */ - if (!contains_pattern) - goto out; - } if (in_repetition) goto out; return true; @@ -378,59 +342,59 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, return false; } +/** + * tomoyo_is_correct_word - Validate a string. + * + * @string: The string to check. + * + * Check whether the given string follows the naming rules. + * Returns true if @string follows the naming rules, false otherwise. + */ +bool tomoyo_is_correct_word(const char *string) +{ + return tomoyo_is_correct_word2(string, strlen(string)); +} + +/** + * tomoyo_is_correct_path - Validate a pathname. + * + * @filename: The pathname to check. + * + * Check whether the given pathname follows the naming rules. + * Returns true if @filename follows the naming rules, false otherwise. + */ +bool tomoyo_is_correct_path(const char *filename) +{ + return *filename == '/' && tomoyo_is_correct_word(filename); +} + /** * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. * - * @domainname: The domainname to check. + * @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++ != ' ') + if (*domainname++ != ' ') + goto out; + while (1) { + const unsigned char *cp = strchr(domainname, ' '); + if (!cp) + break; + if (*domainname != '/' || + !tomoyo_is_correct_word2(domainname, cp - domainname - 1)) 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; + domainname = cp + 1; + } + return tomoyo_is_correct_path(domainname); out: return false; }