diff options
-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++); } /** |