diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2011-07-08 08:23:44 +0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2011-07-11 05:05:33 +0400 |
commit | 2ca9bf453bdd478bcb6c01aa2d0bd4c2f4350563 (patch) | |
tree | b9f6051059a2a90547a4501bf296b0cf3c9dbc76 /security/tomoyo | |
parent | 8761afd49ebff8ae04c1a7888af090177441d07d (diff) | |
download | linux-2ca9bf453bdd478bcb6c01aa2d0bd4c2f4350563.tar.xz |
TOMOYO: Allow using executable's realpath and symlink's target as conditions.
This patch adds support for permission checks using executable file's realpath
upon execve() and symlink's target upon symlink(). Hooks are in the last patch
of this pathset.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo')
-rw-r--r-- | security/tomoyo/audit.c | 21 | ||||
-rw-r--r-- | security/tomoyo/common.c | 66 | ||||
-rw-r--r-- | security/tomoyo/common.h | 24 | ||||
-rw-r--r-- | security/tomoyo/condition.c | 116 | ||||
-rw-r--r-- | security/tomoyo/gc.c | 5 |
5 files changed, 228 insertions, 4 deletions
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 4973edd40718..b33a20accbef 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -140,6 +140,8 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, { char *buf = NULL; const char *header = NULL; + char *realpath = NULL; + const char *symlink = NULL; int pos; const char *domainname = r->domain->domainname->name; header = tomoyo_print_header(r); @@ -147,15 +149,34 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, return NULL; /* +10 is for '\n' etc. and '\0'. */ len += strlen(domainname) + strlen(header) + 10; + if (r->ee) { + struct file *file = r->ee->bprm->file; + realpath = tomoyo_realpath_from_path(&file->f_path); + if (!realpath) + goto out; + /* +80 is for " exec={ realpath=\"%s\" }" */ + len += strlen(realpath) + 80; + } else if (r->obj && r->obj->symlink_target) { + symlink = r->obj->symlink_target->name; + /* +18 is for " symlink.target=\"%s\"" */ + len += 18 + strlen(symlink); + } len = tomoyo_round2(len); buf = kzalloc(len, GFP_NOFS); if (!buf) goto out; len--; pos = snprintf(buf, len, "%s", header); + if (realpath) { + pos += snprintf(buf + pos, len - pos, + " exec={ realpath=\"%s\" }", realpath); + } else if (symlink) + pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"", + symlink); pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname); vsnprintf(buf + pos, len - pos, fmt, args); out: + kfree(realpath); kfree(header); return buf; } diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index ec02d2ab08c3..69d6b59f5937 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -79,6 +79,8 @@ const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = { [TOMOYO_MODE_OTHERS_READ] = "others_read", [TOMOYO_MODE_OTHERS_WRITE] = "others_write", [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute", + [TOMOYO_EXEC_REALPATH] = "exec.realpath", + [TOMOYO_SYMLINK_TARGET] = "symlink.target", [TOMOYO_PATH1_UID] = "path1.uid", [TOMOYO_PATH1_GID] = "path1.gid", [TOMOYO_PATH1_INO] = "path1.ino", @@ -353,6 +355,27 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, } /** + * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns nothing. + */ +static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head, + const struct tomoyo_name_union *ptr) +{ + if (ptr->group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->group->group_name->name); + } else { + tomoyo_set_string(head, "\""); + tomoyo_set_string(head, ptr->filename->name); + tomoyo_set_string(head, "\""); + } +} + +/** * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space. * * @head: Pointer to "struct tomoyo_io_buffer". @@ -1101,6 +1124,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, (typeof(condp)) (cond + 1); const struct tomoyo_number_union *numbers_p = (typeof(numbers_p)) (condp + condc); + const struct tomoyo_name_union *names_p = + (typeof(names_p)) + (numbers_p + cond->numbers_count); u16 skip; for (skip = 0; skip < head->r.cond_index; skip++) { const u8 left = condp->left; @@ -1112,6 +1138,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, break; } switch (right) { + case TOMOYO_NAME_UNION: + names_p++; + break; case TOMOYO_NUMBER_UNION: numbers_p++; break; @@ -1138,6 +1167,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, } tomoyo_set_string(head, match ? "=" : "!="); switch (right) { + case TOMOYO_NAME_UNION: + tomoyo_print_name_union_quoted + (head, names_p++); + break; case TOMOYO_NUMBER_UNION: tomoyo_print_number_union_nospace (head, numbers_p++); @@ -1666,6 +1699,22 @@ static DEFINE_SPINLOCK(tomoyo_query_list_lock); static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); /** + * tomoyo_truncate - Truncate a line. + * + * @str: String to truncate. + * + * Returns length of truncated @str. + */ +static int tomoyo_truncate(char *str) +{ + char *start = str; + while (*(unsigned char *) str > (unsigned char) ' ') + str++; + *str = '\0'; + return strlen(start) + 1; +} + +/** * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode. * * @domain: Pointer to "struct tomoyo_domain_info". @@ -1676,6 +1725,8 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) { char *buffer; + char *realpath = NULL; + char *symlink = NULL; char *cp = strchr(header, '\n'); int len; if (!cp) @@ -1685,10 +1736,25 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) return; *cp++ = '\0'; len = strlen(cp) + 1; + /* strstr() will return NULL if ordering is wrong. */ + if (*cp == 'f') { + realpath = strstr(header, " exec={ realpath=\""); + if (realpath) { + realpath += 8; + len += tomoyo_truncate(realpath) + 6; + } + symlink = strstr(header, " symlink.target=\""); + if (symlink) + len += tomoyo_truncate(symlink + 1) + 1; + } buffer = kmalloc(len, GFP_NOFS); if (!buffer) return; snprintf(buffer, len - 1, "%s", cp); + if (realpath) + tomoyo_addprintf(buffer, len, " exec.%s", realpath); + if (symlink) + tomoyo_addprintf(buffer, len, "%s", symlink); tomoyo_normalize_line(buffer); if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, false)) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 5a0fcedb332b..7e56e6b364e5 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -73,6 +73,8 @@ enum tomoyo_conditions_index { TOMOYO_MODE_OTHERS_READ, /* S_IROTH */ TOMOYO_MODE_OTHERS_WRITE, /* S_IWOTH */ TOMOYO_MODE_OTHERS_EXECUTE, /* S_IXOTH */ + TOMOYO_EXEC_REALPATH, + TOMOYO_SYMLINK_TARGET, TOMOYO_PATH1_UID, TOMOYO_PATH1_GID, TOMOYO_PATH1_INO, @@ -101,6 +103,7 @@ enum tomoyo_conditions_index { TOMOYO_PATH2_PARENT_PERM, TOMOYO_MAX_CONDITION_KEYWORD, TOMOYO_NUMBER_UNION, + TOMOYO_NAME_UNION, }; @@ -351,6 +354,11 @@ struct tomoyo_request_info { * NULL if not dealing files. */ struct tomoyo_obj_info *obj; + /* + * For holding parameters specific to execve() request. + * NULL if not dealing do_execve(). + */ + struct tomoyo_execve *ee; struct tomoyo_domain_info *domain; /* For holding parameters. */ union { @@ -476,6 +484,20 @@ struct tomoyo_obj_info { * parent directory. */ struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT]; + /* + * Content of symbolic link to be created. NULL for operations other + * than symlink(). + */ + struct tomoyo_path_info *symlink_target; +}; + +/* Structure for execve() operation. */ +struct tomoyo_execve { + struct tomoyo_request_info r; + struct tomoyo_obj_info obj; + struct linux_binprm *bprm; + /* For temporary use. */ + char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */ }; /* Structure for entries which follows "struct tomoyo_condition". */ @@ -494,9 +516,11 @@ struct tomoyo_condition { u32 size; /* Memory size allocated for this entry. */ u16 condc; /* Number of conditions in this struct. */ u16 numbers_count; /* Number of "struct tomoyo_number_union values". */ + u16 names_count; /* Number of "struct tomoyo_name_union names". */ /* * struct tomoyo_condition_element condition[condc]; * struct tomoyo_number_union values[numbers_count]; + * struct tomoyo_name_union names[names_count]; */ }; diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index ac7ebeb47d7d..790b9872cc37 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -11,6 +11,68 @@ LIST_HEAD(tomoyo_condition_list); /** + * tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition". + * + * @file: Pointer to "struct file". + * @ptr: Pointer to "struct tomoyo_name_union". + * @match: True if "exec.realpath=", false if "exec.realpath!=". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_scan_exec_realpath(struct file *file, + const struct tomoyo_name_union *ptr, + const bool match) +{ + bool result; + struct tomoyo_path_info exe; + if (!file) + return false; + exe.name = tomoyo_realpath_from_path(&file->f_path); + if (!exe.name) + return false; + tomoyo_fill_path_info(&exe); + result = tomoyo_compare_name_union(&exe, ptr); + kfree(exe.name); + return result == match; +} + +/** + * tomoyo_get_dqword - tomoyo_get_name() for a quoted string. + * + * @start: String to save. + * + * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. + */ +static const struct tomoyo_path_info *tomoyo_get_dqword(char *start) +{ + char *cp = start + strlen(start) - 1; + if (cp == start || *start++ != '"' || *cp != '"') + return NULL; + *cp = '\0'; + if (*start && !tomoyo_correct_word(start)) + return NULL; + return tomoyo_get_name(start); +} + +/** + * tomoyo_parse_name_union_quoted - Parse a quoted word. + * + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param, + struct tomoyo_name_union *ptr) +{ + char *filename = param->data; + if (*filename == '@') + return tomoyo_parse_name_union(param, ptr); + ptr->filename = tomoyo_get_dqword(filename); + return ptr->filename != NULL; +} + +/** * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry. * * @a: Pointer to "struct tomoyo_condition". @@ -23,6 +85,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, { return a->size == b->size && a->condc == b->condc && a->numbers_count == b->numbers_count && + a->names_count == b->names_count && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); } @@ -114,6 +177,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) struct tomoyo_condition *entry = NULL; struct tomoyo_condition_element *condp = NULL; struct tomoyo_number_union *numbers_p = NULL; + struct tomoyo_name_union *names_p = NULL; struct tomoyo_condition e = { }; char * const start_of_string = param->data; char * const end_of_string = start_of_string + strlen(start_of_string); @@ -178,6 +242,20 @@ rerun: e.condc++; else e.condc--; + if (left == TOMOYO_EXEC_REALPATH || + left == TOMOYO_SYMLINK_TARGET) { + if (!names_p) { + e.names_count++; + } else { + e.names_count--; + right = TOMOYO_NAME_UNION; + param->data = right_word; + if (!tomoyo_parse_name_union_quoted(param, + names_p++)) + goto out; + } + goto store_value; + } right = tomoyo_condition_type(right_word); if (right == TOMOYO_MAX_CONDITION_KEYWORD) { if (!numbers_p) { @@ -191,6 +269,7 @@ rerun: goto out; } } +store_value: if (!condp) { dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " "match=%u\n", __LINE__, left, right, !is_not); @@ -204,21 +283,23 @@ rerun: condp->equals); condp++; } - dprintk(KERN_INFO "%u: cond=%u numbers=%u\n", - __LINE__, e.condc, e.numbers_count); + dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u\n", + __LINE__, e.condc, e.numbers_count, e.names_count); if (entry) { - BUG_ON(e.numbers_count | e.condc); + BUG_ON(e.names_count | e.numbers_count | e.condc); return tomoyo_commit_condition(entry); } e.size = sizeof(*entry) + e.condc * sizeof(struct tomoyo_condition_element) - + e.numbers_count * sizeof(struct tomoyo_number_union); + + e.numbers_count * sizeof(struct tomoyo_number_union) + + e.names_count * sizeof(struct tomoyo_name_union); entry = kzalloc(e.size, GFP_NOFS); if (!entry) return NULL; *entry = e; condp = (struct tomoyo_condition_element *) (entry + 1); numbers_p = (struct tomoyo_number_union *) (condp + e.condc); + names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count); { bool flag = false; for (pos = start_of_string; pos < end_of_string; pos++) { @@ -309,6 +390,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r, unsigned long max_v[2] = { 0, 0 }; const struct tomoyo_condition_element *condp; const struct tomoyo_number_union *numbers_p; + const struct tomoyo_name_union *names_p; struct tomoyo_obj_info *obj; u16 condc; if (!cond) @@ -317,6 +399,8 @@ bool tomoyo_condition(struct tomoyo_request_info *r, obj = r->obj; condp = (struct tomoyo_condition_element *) (cond + 1); numbers_p = (const struct tomoyo_number_union *) (condp + condc); + names_p = (const struct tomoyo_name_union *) + (numbers_p + cond->numbers_count); for (i = 0; i < condc; i++) { const bool match = condp->equals; const u8 left = condp->left; @@ -324,6 +408,30 @@ bool tomoyo_condition(struct tomoyo_request_info *r, bool is_bitop[2] = { false, false }; u8 j; condp++; + /* Check string expressions. */ + if (right == TOMOYO_NAME_UNION) { + const struct tomoyo_name_union *ptr = names_p++; + switch (left) { + struct tomoyo_path_info *symlink; + struct tomoyo_execve *ee; + struct file *file; + case TOMOYO_SYMLINK_TARGET: + symlink = obj ? obj->symlink_target : NULL; + if (!symlink || + !tomoyo_compare_name_union(symlink, ptr) + == match) + goto out; + break; + case TOMOYO_EXEC_REALPATH: + ee = r->ee; + file = ee ? ee->bprm->file : NULL; + if (!tomoyo_scan_exec_realpath(file, ptr, + match)) + goto out; + break; + } + continue; + } /* Check numeric or bit-op expressions. */ for (j = 0; j < 2; j++) { const u8 index = j ? right : left; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 21fccd67c255..e0502b6d5866 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -357,13 +357,18 @@ void tomoyo_del_condition(struct list_head *element) head.list); const u16 condc = cond->condc; const u16 numbers_count = cond->numbers_count; + const u16 names_count = cond->names_count; unsigned int i; const struct tomoyo_condition_element *condp = (const struct tomoyo_condition_element *) (cond + 1); struct tomoyo_number_union *numbers_p = (struct tomoyo_number_union *) (condp + condc); + struct tomoyo_name_union *names_p + = (struct tomoyo_name_union *) (numbers_p + numbers_count); for (i = 0; i < numbers_count; i++) tomoyo_put_number_union(numbers_p++); + for (i = 0; i < names_count; i++) + tomoyo_put_name_union(names_p++); } /** |