diff options
Diffstat (limited to 'security/tomoyo/common.c')
-rw-r--r-- | security/tomoyo/common.c | 1959 |
1 files changed, 1263 insertions, 696 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index a0d09e5..c8439cf2 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1,9 +1,7 @@ /* * security/tomoyo/common.c * - * Common functions for TOMOYO. - * - * Copyright (C) 2005-2010 NTT DATA CORPORATION + * Copyright (C) 2005-2011 NTT DATA CORPORATION */ #include <linux/uaccess.h> @@ -11,54 +9,131 @@ #include <linux/security.h> #include "common.h" -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 +/* String table for operation mode. */ +const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = { + [TOMOYO_CONFIG_DISABLED] = "disabled", + [TOMOYO_CONFIG_LEARNING] = "learning", + [TOMOYO_CONFIG_PERMISSIVE] = "permissive", + [TOMOYO_CONFIG_ENFORCING] = "enforcing" }; -/* Profile version. Currently only 20090903 is defined. */ -static unsigned int tomoyo_profile_version; +/* String table for /sys/kernel/security/tomoyo/profile */ +const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_FILE_EXECUTE] = "execute", + [TOMOYO_MAC_FILE_OPEN] = "open", + [TOMOYO_MAC_FILE_CREATE] = "create", + [TOMOYO_MAC_FILE_UNLINK] = "unlink", + [TOMOYO_MAC_FILE_GETATTR] = "getattr", + [TOMOYO_MAC_FILE_MKDIR] = "mkdir", + [TOMOYO_MAC_FILE_RMDIR] = "rmdir", + [TOMOYO_MAC_FILE_MKFIFO] = "mkfifo", + [TOMOYO_MAC_FILE_MKSOCK] = "mksock", + [TOMOYO_MAC_FILE_TRUNCATE] = "truncate", + [TOMOYO_MAC_FILE_SYMLINK] = "symlink", + [TOMOYO_MAC_FILE_MKBLOCK] = "mkblock", + [TOMOYO_MAC_FILE_MKCHAR] = "mkchar", + [TOMOYO_MAC_FILE_LINK] = "link", + [TOMOYO_MAC_FILE_RENAME] = "rename", + [TOMOYO_MAC_FILE_CHMOD] = "chmod", + [TOMOYO_MAC_FILE_CHOWN] = "chown", + [TOMOYO_MAC_FILE_CHGRP] = "chgrp", + [TOMOYO_MAC_FILE_IOCTL] = "ioctl", + [TOMOYO_MAC_FILE_CHROOT] = "chroot", + [TOMOYO_MAC_FILE_MOUNT] = "mount", + [TOMOYO_MAC_FILE_UMOUNT] = "unmount", + [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", +}; -/* Profile table. Memory is allocated as needed. */ -static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; +/* String table for conditions. */ +const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = { + [TOMOYO_TASK_UID] = "task.uid", + [TOMOYO_TASK_EUID] = "task.euid", + [TOMOYO_TASK_SUID] = "task.suid", + [TOMOYO_TASK_FSUID] = "task.fsuid", + [TOMOYO_TASK_GID] = "task.gid", + [TOMOYO_TASK_EGID] = "task.egid", + [TOMOYO_TASK_SGID] = "task.sgid", + [TOMOYO_TASK_FSGID] = "task.fsgid", + [TOMOYO_TASK_PID] = "task.pid", + [TOMOYO_TASK_PPID] = "task.ppid", + [TOMOYO_EXEC_ARGC] = "exec.argc", + [TOMOYO_EXEC_ENVC] = "exec.envc", + [TOMOYO_TYPE_IS_SOCKET] = "socket", + [TOMOYO_TYPE_IS_SYMLINK] = "symlink", + [TOMOYO_TYPE_IS_FILE] = "file", + [TOMOYO_TYPE_IS_BLOCK_DEV] = "block", + [TOMOYO_TYPE_IS_DIRECTORY] = "directory", + [TOMOYO_TYPE_IS_CHAR_DEV] = "char", + [TOMOYO_TYPE_IS_FIFO] = "fifo", + [TOMOYO_MODE_SETUID] = "setuid", + [TOMOYO_MODE_SETGID] = "setgid", + [TOMOYO_MODE_STICKY] = "sticky", + [TOMOYO_MODE_OWNER_READ] = "owner_read", + [TOMOYO_MODE_OWNER_WRITE] = "owner_write", + [TOMOYO_MODE_OWNER_EXECUTE] = "owner_execute", + [TOMOYO_MODE_GROUP_READ] = "group_read", + [TOMOYO_MODE_GROUP_WRITE] = "group_write", + [TOMOYO_MODE_GROUP_EXECUTE] = "group_execute", + [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", + [TOMOYO_PATH1_MAJOR] = "path1.major", + [TOMOYO_PATH1_MINOR] = "path1.minor", + [TOMOYO_PATH1_PERM] = "path1.perm", + [TOMOYO_PATH1_TYPE] = "path1.type", + [TOMOYO_PATH1_DEV_MAJOR] = "path1.dev_major", + [TOMOYO_PATH1_DEV_MINOR] = "path1.dev_minor", + [TOMOYO_PATH2_UID] = "path2.uid", + [TOMOYO_PATH2_GID] = "path2.gid", + [TOMOYO_PATH2_INO] = "path2.ino", + [TOMOYO_PATH2_MAJOR] = "path2.major", + [TOMOYO_PATH2_MINOR] = "path2.minor", + [TOMOYO_PATH2_PERM] = "path2.perm", + [TOMOYO_PATH2_TYPE] = "path2.type", + [TOMOYO_PATH2_DEV_MAJOR] = "path2.dev_major", + [TOMOYO_PATH2_DEV_MINOR] = "path2.dev_minor", + [TOMOYO_PATH1_PARENT_UID] = "path1.parent.uid", + [TOMOYO_PATH1_PARENT_GID] = "path1.parent.gid", + [TOMOYO_PATH1_PARENT_INO] = "path1.parent.ino", + [TOMOYO_PATH1_PARENT_PERM] = "path1.parent.perm", + [TOMOYO_PATH2_PARENT_UID] = "path2.parent.uid", + [TOMOYO_PATH2_PARENT_GID] = "path2.parent.gid", + [TOMOYO_PATH2_PARENT_INO] = "path2.parent.ino", + [TOMOYO_PATH2_PARENT_PERM] = "path2.parent.perm", +}; -/* String table for functionality that takes 4 modes. */ -static const char *tomoyo_mode[4] = { - "disabled", "learning", "permissive", "enforcing" +/* String table for PREFERENCE keyword. */ +static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = { + [TOMOYO_PREF_MAX_AUDIT_LOG] = "max_audit_log", + [TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry", }; -/* 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", +/* String table for path operation. */ +const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { + [TOMOYO_TYPE_EXECUTE] = "execute", + [TOMOYO_TYPE_READ] = "read", + [TOMOYO_TYPE_WRITE] = "write", + [TOMOYO_TYPE_APPEND] = "append", + [TOMOYO_TYPE_UNLINK] = "unlink", + [TOMOYO_TYPE_GETATTR] = "getattr", + [TOMOYO_TYPE_RMDIR] = "rmdir", + [TOMOYO_TYPE_TRUNCATE] = "truncate", + [TOMOYO_TYPE_SYMLINK] = "symlink", + [TOMOYO_TYPE_CHROOT] = "chroot", + [TOMOYO_TYPE_UMOUNT] = "unmount", +}; + +/* String table for categories. */ +static const char * const tomoyo_category_keywords +[TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_CATEGORY_FILE] = "file", }; /* Permit policy management by non-root user? */ @@ -71,11 +146,20 @@ static bool tomoyo_manage_by_non_root; * * @value: Bool value. */ -static const char *tomoyo_yesno(const unsigned int value) +const char *tomoyo_yesno(const unsigned int value) { return value ? "yes" : "no"; } +/** + * tomoyo_addprintf - strncat()-like-snprintf(). + * + * @buffer: Buffer to write to. Must be '\0'-terminated. + * @len: Size of @buffer. + * @fmt: The printf()'s format string, followed by parameters. + * + * Returns nothing. + */ static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) { va_list args; @@ -96,7 +180,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head) { while (head->r.w_pos) { const char *w = head->r.w[0]; - int len = strlen(w); + size_t len = strlen(w); if (len) { if (len > head->read_user_buf_avail) len = head->read_user_buf_avail; @@ -111,7 +195,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head) head->r.w[0] = w; if (*w) return false; - /* Add '\0' for query. */ + /* Add '\0' for audit logs and query. */ if (head->poll) { if (!head->read_user_buf_avail || copy_to_user(head->read_user_buf, "", 1)) @@ -155,8 +239,8 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) { va_list args; - int len; - int pos = head->r.avail; + size_t len; + size_t pos = head->r.avail; int size = head->readbuf_size - pos; if (size <= 0) return; @@ -171,11 +255,25 @@ void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) tomoyo_set_string(head, head->read_buf + pos); } +/** + * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ static void tomoyo_set_space(struct tomoyo_io_buffer *head) { tomoyo_set_string(head, " "); } +/** + * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) { tomoyo_set_string(head, "\n"); @@ -183,6 +281,62 @@ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) } /** + * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_set_slash(struct tomoyo_io_buffer *head) +{ + tomoyo_set_string(head, "/"); +} + +/* List of namespaces. */ +LIST_HEAD(tomoyo_namespace_list); +/* True if namespace other than tomoyo_kernel_namespace is defined. */ +static bool tomoyo_namespace_enabled; + +/** + * tomoyo_init_policy_namespace - Initialize namespace. + * + * @ns: Pointer to "struct tomoyo_policy_namespace". + * + * Returns nothing. + */ +void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) +{ + unsigned int idx; + for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++) + INIT_LIST_HEAD(&ns->acl_group[idx]); + for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) + INIT_LIST_HEAD(&ns->group_list[idx]); + for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) + INIT_LIST_HEAD(&ns->policy_list[idx]); + ns->profile_version = 20100903; + tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); + list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); +} + +/** + * tomoyo_print_namespace - Print namespace header. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_print_namespace(struct tomoyo_io_buffer *head) +{ + if (!tomoyo_namespace_enabled) + return; + tomoyo_set_string(head, + container_of(head->r.ns, + struct tomoyo_policy_namespace, + namespace_list)->name); + tomoyo_set_space(head); +} + +/** * tomoyo_print_name_union - Print a tomoyo_name_union. * * @head: Pointer to "struct tomoyo_io_buffer". @@ -192,7 +346,7 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, const struct tomoyo_name_union *ptr) { tomoyo_set_space(head); - if (ptr->is_group) { + if (ptr->group) { tomoyo_set_string(head, "@"); tomoyo_set_string(head, ptr->group->group_name->name); } else { @@ -201,24 +355,46 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, } /** - * tomoyo_print_number_union - Print a tomoyo_number_union. + * 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_number_union". + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns nothing. */ -static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, - const struct tomoyo_number_union *ptr) +static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head, + const struct tomoyo_name_union *ptr) { - tomoyo_set_space(head); - if (ptr->is_group) { + 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". + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns nothing. + */ +static void tomoyo_print_number_union_nospace +(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr) +{ + if (ptr->group) { tomoyo_set_string(head, "@"); tomoyo_set_string(head, ptr->group->group_name->name); } else { 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; + u8 min_type = ptr->value_type[0]; + const u8 max_type = ptr->value_type[1]; char buffer[128]; buffer[0] = '\0'; for (i = 0; i < 2; i++) { @@ -232,8 +408,8 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, "0%lo", min); break; default: - tomoyo_addprintf(buffer, sizeof(buffer), - "%lu", min); + tomoyo_addprintf(buffer, sizeof(buffer), "%lu", + min); break; } if (min == max && min_type == max_type) @@ -247,35 +423,53 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, } /** + * tomoyo_print_number_union - Print a tomoyo_number_union. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns nothing. + */ +static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, + const struct tomoyo_number_union *ptr) +{ + tomoyo_set_space(head); + tomoyo_print_number_union_nospace(head, ptr); +} + +/** * tomoyo_assign_profile - Create a new profile. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number to create. * * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. */ -static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) +static struct tomoyo_profile *tomoyo_assign_profile +(struct tomoyo_policy_namespace *ns, const unsigned int profile) { struct tomoyo_profile *ptr; struct tomoyo_profile *entry; if (profile >= TOMOYO_MAX_PROFILES) return NULL; - ptr = tomoyo_profile_ptr[profile]; + ptr = ns->profile_ptr[profile]; if (ptr) return ptr; entry = kzalloc(sizeof(*entry), GFP_NOFS); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - ptr = tomoyo_profile_ptr[profile]; + ptr = ns->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; + ptr->default_config = TOMOYO_CONFIG_DISABLED | + TOMOYO_CONFIG_WANT_GRANT_LOG | + TOMOYO_CONFIG_WANT_REJECT_LOG; memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, sizeof(ptr->config)); + ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024; + ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048; mb(); /* Avoid out-of-order execution. */ - tomoyo_profile_ptr[profile] = ptr; + ns->profile_ptr[profile] = ptr; entry = NULL; } mutex_unlock(&tomoyo_policy_lock); @@ -287,19 +481,29 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) /** * tomoyo_profile - Find a profile. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number to find. * * Returns pointer to "struct tomoyo_profile". */ -struct tomoyo_profile *tomoyo_profile(const u8 profile) +struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, + const u8 profile) { - struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; - if (!tomoyo_policy_loaded) - return &tomoyo_default_profile; - BUG_ON(!ptr); + static struct tomoyo_profile tomoyo_null_profile; + struct tomoyo_profile *ptr = ns->profile_ptr[profile]; + if (!ptr) + ptr = &tomoyo_null_profile; return ptr; } +/** + * tomoyo_find_yesno - Find values for specified keyword. + * + * @string: String to check. + * @find: Name of keyword. + * + * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise. + */ static s8 tomoyo_find_yesno(const char *string, const char *find) { const char *cp = strstr(string, find); @@ -313,18 +517,15 @@ static s8 tomoyo_find_yesno(const char *string, const char *find) return -1; } -static void tomoyo_set_bool(bool *b, const char *string, const char *find) -{ - switch (tomoyo_find_yesno(string, find)) { - case 1: - *b = true; - break; - case 0: - *b = false; - break; - } -} - +/** + * tomoyo_set_uint - Set value for specified preference. + * + * @i: Pointer to "unsigned int". + * @string: String to check. + * @find: Name of keyword. + * + * Returns nothing. + */ static void tomoyo_set_uint(unsigned int *i, const char *string, const char *find) { @@ -333,51 +534,16 @@ static void tomoyo_set_uint(unsigned int *i, const char *string, sscanf(cp + strlen(find), "=%u", i); } -static void tomoyo_set_pref(const char *name, const char *value, - const bool use_default, - struct tomoyo_profile *profile) -{ - struct tomoyo_preference **pref; - bool *verbose; - if (!strcmp(name, "enforcing")) { - if (use_default) { - pref = &profile->enforcing; - goto set_default; - } - profile->enforcing = &profile->preference; - verbose = &profile->preference.enforcing_verbose; - goto set_verbose; - } - if (!strcmp(name, "permissive")) { - if (use_default) { - pref = &profile->permissive; - goto set_default; - } - profile->permissive = &profile->preference; - verbose = &profile->preference.permissive_verbose; - goto set_verbose; - } - if (!strcmp(name, "learning")) { - if (use_default) { - pref = &profile->learning; - goto set_default; - } - profile->learning = &profile->preference; - tomoyo_set_uint(&profile->preference.learning_max_entry, value, - "max_entry"); - verbose = &profile->preference.learning_verbose; - goto set_verbose; - } - return; - set_default: - *pref = &tomoyo_default_profile.preference; - return; - set_verbose: - tomoyo_set_bool(verbose, value, "verbose"); -} - +/** + * tomoyo_set_mode - Set mode for specified profile. + * + * @name: Name of functionality. + * @value: Mode for @name. + * @profile: Pointer to "struct tomoyo_profile". + * + * Returns 0 on success, negative value otherwise. + */ static int tomoyo_set_mode(char *name, const char *value, - const bool use_default, struct tomoyo_profile *profile) { u8 i; @@ -389,7 +555,17 @@ static int tomoyo_set_mode(char *name, const char *value, config = 0; for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { - if (strcmp(name, tomoyo_mac_keywords[i])) + int len = 0; + if (i < TOMOYO_MAX_MAC_INDEX) { + const u8 c = tomoyo_index2category[i]; + const char *category = + tomoyo_category_keywords[c]; + len = strlen(category); + if (strncmp(name, category, len) || + name[len++] != ':' || name[len++] != ':') + continue; + } + if (strcmp(name + len, tomoyo_mac_keywords[i])) continue; config = profile->config[i]; break; @@ -399,7 +575,7 @@ static int tomoyo_set_mode(char *name, const char *value, } else { return -EINVAL; } - if (use_default) { + if (strstr(value, "use_default")) { config = TOMOYO_CONFIG_USE_DEFAULT; } else { u8 mode; @@ -410,6 +586,24 @@ static int tomoyo_set_mode(char *name, const char *value, * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. */ config = (config & ~7) | mode; + if (config != TOMOYO_CONFIG_USE_DEFAULT) { + switch (tomoyo_find_yesno(value, "grant_log")) { + case 1: + config |= TOMOYO_CONFIG_WANT_GRANT_LOG; + break; + case 0: + config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG; + break; + } + switch (tomoyo_find_yesno(value, "reject_log")) { + case 1: + config |= TOMOYO_CONFIG_WANT_REJECT_LOG; + break; + case 0: + config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG; + break; + } + } } if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) profile->config[i] = config; @@ -429,34 +623,22 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) { char *data = head->write_buf; unsigned int i; - bool use_default = false; char *cp; struct tomoyo_profile *profile; - if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) + if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->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_assign_profile(i); - if (!profile) - return -EINVAL; - } + if (*cp != '-') + return -EINVAL; + data = cp + 1; + profile = tomoyo_assign_profile(head->w.ns, i); + if (!profile) + return -EINVAL; cp = strchr(data, '='); if (!cp) return -EINVAL; *cp++ = '\0'; - if (profile != &tomoyo_default_profile) - use_default = strstr(cp, "use_default") != NULL; - if (tomoyo_str_starts(&data, "PREFERENCE::")) { - tomoyo_set_pref(data, cp, use_default, profile); - return 0; - } - if (profile == &tomoyo_default_profile) - return -EINVAL; if (!strcmp(data, "COMMENT")) { static DEFINE_SPINLOCK(lock); const struct tomoyo_path_info *new_comment @@ -471,77 +653,62 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) tomoyo_put_name(old_comment); return 0; } - return tomoyo_set_mode(data, cp, use_default, profile); -} - -static void tomoyo_print_preference(struct tomoyo_io_buffer *head, - const int idx) -{ - struct tomoyo_preference *pref = &tomoyo_default_profile.preference; - const struct tomoyo_profile *profile = idx >= 0 ? - tomoyo_profile_ptr[idx] : NULL; - char buffer[16] = ""; - if (profile) { - buffer[sizeof(buffer) - 1] = '\0'; - snprintf(buffer, sizeof(buffer) - 1, "%u-", idx); - } - if (profile) { - pref = profile->learning; - if (pref == &tomoyo_default_profile.preference) - goto skip1; - } - tomoyo_io_printf(head, "%sPREFERENCE::%s={ " - "verbose=%s max_entry=%u }\n", - buffer, "learning", - tomoyo_yesno(pref->learning_verbose), - pref->learning_max_entry); - skip1: - if (profile) { - pref = profile->permissive; - if (pref == &tomoyo_default_profile.preference) - goto skip2; - } - tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", - buffer, "permissive", - tomoyo_yesno(pref->permissive_verbose)); - skip2: - if (profile) { - pref = profile->enforcing; - if (pref == &tomoyo_default_profile.preference) - return; + if (!strcmp(data, "PREFERENCE")) { + for (i = 0; i < TOMOYO_MAX_PREF; i++) + tomoyo_set_uint(&profile->pref[i], cp, + tomoyo_pref_keywords[i]); + return 0; } - tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", - buffer, "enforcing", - tomoyo_yesno(pref->enforcing_verbose)); + return tomoyo_set_mode(data, cp, profile); } +/** + * tomoyo_print_config - Print mode for specified functionality. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @config: Mode for that functionality. + * + * Returns nothing. + * + * Caller prints functionality's name. + */ static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) { - tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]); + tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", + tomoyo_mode[config & 3], + tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG), + tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG)); } /** * tomoyo_read_profile - Read profile table. * * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. */ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) { u8 index; + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); const struct tomoyo_profile *profile; + if (head->r.eof) + return; next: index = head->r.index; - profile = tomoyo_profile_ptr[index]; + profile = ns->profile_ptr[index]; switch (head->r.step) { case 0: - tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); - tomoyo_print_preference(head, -1); + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "PROFILE_VERSION=%u\n", + ns->profile_version); head->r.step++; break; case 1: for ( ; head->r.index < TOMOYO_MAX_PROFILES; head->r.index++) - if (tomoyo_profile_ptr[head->r.index]) + if (ns->profile_ptr[head->r.index]) break; if (head->r.index == TOMOYO_MAX_PROFILES) return; @@ -549,16 +716,25 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) break; case 2: { + u8 i; const struct tomoyo_path_info *comment = profile->comment; + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-COMMENT=", index); tomoyo_set_string(head, comment ? comment->name : ""); tomoyo_set_lf(head); + tomoyo_io_printf(head, "%u-PREFERENCE={ ", index); + for (i = 0; i < TOMOYO_MAX_PREF; i++) + tomoyo_io_printf(head, "%s=%u ", + tomoyo_pref_keywords[i], + profile->pref[i]); + tomoyo_set_string(head, "}\n"); head->r.step++; } break; case 3: { + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-%s", index, "CONFIG"); tomoyo_print_config(head, profile->default_config); head->r.bit = 0; @@ -572,15 +748,22 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) const u8 config = profile->config[i]; if (config == TOMOYO_CONFIG_USE_DEFAULT) continue; - tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::", - tomoyo_mac_keywords[i]); + tomoyo_print_namespace(head); + if (i < TOMOYO_MAX_MAC_INDEX) + tomoyo_io_printf(head, "%u-CONFIG::%s::%s", + index, + tomoyo_category_keywords + [tomoyo_index2category[i]], + tomoyo_mac_keywords[i]); + else + tomoyo_io_printf(head, "%u-CONFIG::%s", index, + tomoyo_mac_keywords[i]); tomoyo_print_config(head, config); head->r.bit++; break; } if (head->r.bit == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) { - tomoyo_print_preference(head, index); head->r.index++; head->r.step = 1; } @@ -590,6 +773,14 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) goto next; } +/** + * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry. + * + * @a: Pointer to "struct tomoyo_acl_head". + * @b: Pointer to "struct tomoyo_acl_head". + * + * Returns true if @a == @b, false otherwise. + */ static bool tomoyo_same_manager(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -611,8 +802,13 @@ static int tomoyo_update_manager_entry(const char *manager, const bool is_delete) { struct tomoyo_manager e = { }; - int error; - + struct tomoyo_acl_param param = { + /* .ns = &tomoyo_kernel_namespace, */ + .is_delete = is_delete, + .list = &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], + }; + int error = is_delete ? -ENOENT : -ENOMEM; if (tomoyo_domain_def(manager)) { if (!tomoyo_correct_domain(manager)) return -EINVAL; @@ -622,12 +818,11 @@ static int tomoyo_update_manager_entry(const char *manager, return -EINVAL; } e.manager = tomoyo_get_name(manager); - if (!e.manager) - return -ENOMEM; - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_policy_list[TOMOYO_ID_MANAGER], - tomoyo_same_manager); - tomoyo_put_name(e.manager); + if (e.manager) { + error = tomoyo_update_policy(&e.head, sizeof(e), ¶m, + tomoyo_same_manager); + tomoyo_put_name(e.manager); + } return error; } @@ -643,13 +838,12 @@ static int tomoyo_update_manager_entry(const char *manager, static int tomoyo_write_manager(struct tomoyo_io_buffer *head) { char *data = head->write_buf; - bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); if (!strcmp(data, "manage_by_non_root")) { - tomoyo_manage_by_non_root = !is_delete; + tomoyo_manage_by_non_root = !head->w.is_delete; return 0; } - return tomoyo_update_manager_entry(data, is_delete); + return tomoyo_update_manager_entry(data, head->w.is_delete); } /** @@ -663,8 +857,8 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head) { if (head->r.eof) return; - list_for_each_cookie(head->r.acl, - &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { + list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER]) { struct tomoyo_manager *ptr = list_entry(head->r.acl, typeof(*ptr), head.list); if (ptr->head.is_deleted) @@ -697,8 +891,8 @@ static bool tomoyo_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_list[TOMOYO_ID_MANAGER], - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], head.list) { if (!ptr->head.is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager)) { found = true; @@ -710,8 +904,8 @@ static bool tomoyo_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], head.list) { if (!ptr->head.is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) { found = true; @@ -732,7 +926,7 @@ static bool tomoyo_manager(void) } /** - * tomoyo_select_one - Parse select command. + * tomoyo_select_domain - Parse select command. * * @head: Pointer to "struct tomoyo_io_buffer". * @data: String to parse. @@ -741,16 +935,15 @@ static bool tomoyo_manager(void) * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) +static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, + const char *data) { unsigned int pid; struct tomoyo_domain_info *domain = NULL; bool global_pid = false; - - if (!strcmp(data, "allow_execute")) { - head->r.print_execute_only = true; - return true; - } + if (strncmp(data, "select ", 7)) + return false; + data += 7; if (sscanf(data, "pid=%u", &pid) == 1 || (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; @@ -769,7 +962,7 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) domain = tomoyo_find_domain(data + 7); } else return false; - head->write_var1 = domain; + head->w.domain = domain; /* Accessing read_buf is safe because head->io_sem is held. */ if (!head->read_buf) return true; /* Do nothing if open(O_WRONLY). */ @@ -821,20 +1014,47 @@ static int tomoyo_delete_domain(char *domainname) /** * tomoyo_write_domain2 - Write domain policy. * - * @head: Pointer to "struct tomoyo_io_buffer". + * @ns: Pointer to "struct tomoyo_policy_namespace". + * @list: Pointer to "struct list_head". + * @data: Policy to be interpreted. + * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain, +static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, + struct list_head *list, char *data, const bool is_delete) { - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) - return tomoyo_write_mount(data, domain, is_delete); - return tomoyo_write_file(data, domain, is_delete); + struct tomoyo_acl_param param = { + .ns = ns, + .list = list, + .data = data, + .is_delete = is_delete, + }; + static const struct { + const char *keyword; + int (*write) (struct tomoyo_acl_param *); + } tomoyo_callback[1] = { + { "file ", tomoyo_write_file }, + }; + u8 i; + for (i = 0; i < 1; i++) { + if (!tomoyo_str_starts(¶m.data, + tomoyo_callback[i].keyword)) + continue; + return tomoyo_callback[i].write(¶m); + } + return -EINVAL; } +/* String table for domain flags. */ +const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = { + [TOMOYO_DIF_QUOTA_WARNED] = "quota_exceeded\n", + [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n", +}; + /** * tomoyo_write_domain - Write domain policy. * @@ -847,69 +1067,198 @@ static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain, static int tomoyo_write_domain(struct tomoyo_io_buffer *head) { char *data = head->write_buf; - struct tomoyo_domain_info *domain = head->write_var1; - bool is_delete = false; - bool is_select = false; + struct tomoyo_policy_namespace *ns; + struct tomoyo_domain_info *domain = head->w.domain; + const bool is_delete = head->w.is_delete; + bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); unsigned int profile; - - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE)) - is_delete = true; - else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) - is_select = true; - if (is_select && tomoyo_select_one(head, data)) - return 0; - /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_manager()) - return -EPERM; - if (tomoyo_domain_def(data)) { + if (*data == '<') { domain = NULL; if (is_delete) tomoyo_delete_domain(data); else if (is_select) domain = tomoyo_find_domain(data); else - domain = tomoyo_assign_domain(data, 0); - head->write_var1 = domain; + domain = tomoyo_assign_domain(data, false); + head->w.domain = domain; return 0; } if (!domain) return -EINVAL; - - if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1 + ns = domain->ns; + if (sscanf(data, "use_profile %u", &profile) == 1 && profile < TOMOYO_MAX_PROFILES) { - if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded) + if (!tomoyo_policy_loaded || ns->profile_ptr[profile]) domain->profile = (u8) profile; return 0; } - if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) { - domain->ignore_global_allow_read = !is_delete; + if (sscanf(data, "use_group %u\n", &profile) == 1 + && profile < TOMOYO_MAX_ACL_GROUPS) { + if (!is_delete) + domain->group = (u8) profile; return 0; } - 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; + for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) { + const char *cp = tomoyo_dif[profile]; + if (strncmp(data, cp, strlen(cp) - 1)) + continue; + domain->flags[profile] = !is_delete; return 0; } - return tomoyo_write_domain2(data, domain, is_delete); + return tomoyo_write_domain2(ns, &domain->acl_info_list, data, + is_delete); } /** - * tomoyo_fns - Find next set bit. + * tomoyo_print_condition - Print condition part. * - * @perm: 8 bits value. - * @bit: First bit to find. + * @head: Pointer to "struct tomoyo_io_buffer". + * @cond: Pointer to "struct tomoyo_condition". * - * Returns next on-bit on success, 8 otherwise. + * Returns true on success, false otherwise. */ -static u8 tomoyo_fns(const u8 perm, u8 bit) +static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, + const struct tomoyo_condition *cond) { - for ( ; bit < 8; bit++) - if (perm & (1 << bit)) + switch (head->r.cond_step) { + case 0: + head->r.cond_index = 0; + head->r.cond_step++; + /* fall through */ + case 1: + { + const u16 condc = cond->condc; + const struct tomoyo_condition_element *condp = + (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); + const struct tomoyo_argv *argv = + (typeof(argv)) (names_p + cond->names_count); + const struct tomoyo_envp *envp = + (typeof(envp)) (argv + cond->argc); + u16 skip; + for (skip = 0; skip < head->r.cond_index; skip++) { + const u8 left = condp->left; + const u8 right = condp->right; + condp++; + switch (left) { + case TOMOYO_ARGV_ENTRY: + argv++; + continue; + case TOMOYO_ENVP_ENTRY: + envp++; + continue; + case TOMOYO_NUMBER_UNION: + numbers_p++; + break; + } + switch (right) { + case TOMOYO_NAME_UNION: + names_p++; + break; + case TOMOYO_NUMBER_UNION: + numbers_p++; + break; + } + } + while (head->r.cond_index < condc) { + const u8 match = condp->equals; + const u8 left = condp->left; + const u8 right = condp->right; + if (!tomoyo_flush(head)) + return false; + condp++; + head->r.cond_index++; + tomoyo_set_space(head); + switch (left) { + case TOMOYO_ARGV_ENTRY: + tomoyo_io_printf(head, + "exec.argv[%lu]%s=\"", + argv->index, argv-> + is_not ? "!" : ""); + tomoyo_set_string(head, + argv->value->name); + tomoyo_set_string(head, "\""); + argv++; + continue; + case TOMOYO_ENVP_ENTRY: + tomoyo_set_string(head, + "exec.envp[\""); + tomoyo_set_string(head, + envp->name->name); + tomoyo_io_printf(head, "\"]%s=", envp-> + is_not ? "!" : ""); + if (envp->value) { + tomoyo_set_string(head, "\""); + tomoyo_set_string(head, envp-> + value->name); + tomoyo_set_string(head, "\""); + } else { + tomoyo_set_string(head, + "NULL"); + } + envp++; + continue; + case TOMOYO_NUMBER_UNION: + tomoyo_print_number_union_nospace + (head, numbers_p++); + break; + default: + tomoyo_set_string(head, + tomoyo_condition_keyword[left]); + break; + } + 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++); + break; + default: + tomoyo_set_string(head, + tomoyo_condition_keyword[right]); + break; + } + } + } + head->r.cond_step++; + /* fall through */ + case 2: + if (!tomoyo_flush(head)) break; - return bit; + head->r.cond_step++; + /* fall through */ + case 3: + tomoyo_set_lf(head); + return true; + } + return false; +} + +/** + * tomoyo_set_group - Print "acl_group " header keyword and category name. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @category: Category name. + * + * Returns nothing. + */ +static void tomoyo_set_group(struct tomoyo_io_buffer *head, + const char *category) +{ + if (head->type == TOMOYO_EXCEPTIONPOLICY) { + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "acl_group %u ", + head->r.acl_group_index); + } + tomoyo_set_string(head, category); } /** @@ -924,63 +1273,96 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_acl_info *acl) { const u8 acl_type = acl->type; + bool first = true; u8 bit; + if (head->r.print_cond_part) + goto print_cond_part; if (acl->is_deleted) return true; - next: - bit = head->r.bit; if (!tomoyo_flush(head)) return false; else 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++) { + for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; - if (head->r.print_execute_only && + if (head->r.print_transition_related_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 (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_path_keyword[bit]); } - if (bit >= TOMOYO_MAX_PATH_OPERATION) - goto done; - tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]); + if (first) + return true; tomoyo_print_name_union(head, &ptr->name); - } else if (head->r.print_execute_only) { + } else if (head->r.print_transition_related_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; - tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]); + const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_mac_keywords + [tomoyo_pp2mac[bit]]); + } + if (first) + return true; tomoyo_print_name_union(head, &ptr->name1); tomoyo_print_name_union(head, &ptr->name2); } 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; - tomoyo_io_printf(head, "allow_%s", - tomoyo_path_number_keyword[bit]); + const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_mac_keywords + [tomoyo_pn2mac[bit]]); + } + if (first) + return true; tomoyo_print_name_union(head, &ptr->name); tomoyo_print_number_union(head, &ptr->number); } 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; - tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]); + const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "file "); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_mac_keywords + [tomoyo_pnnn2mac[bit]]); + } + if (first) + return true; tomoyo_print_name_union(head, &ptr->name); tomoyo_print_number_union(head, &ptr->mode); tomoyo_print_number_union(head, &ptr->major); @@ -988,35 +1370,41 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { struct tomoyo_mount_acl *ptr = container_of(acl, typeof(*ptr), head); - tomoyo_io_printf(head, "allow_mount"); + tomoyo_set_group(head, "file 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); } - head->r.bit = bit + 1; - tomoyo_io_printf(head, "\n"); - if (acl_type != TOMOYO_TYPE_MOUNT_ACL) - goto next; - done: - head->r.bit = 0; + if (acl->cond) { + head->r.print_cond_part = true; + head->r.cond_step = 0; + if (!tomoyo_flush(head)) + return false; +print_cond_part: + if (!tomoyo_print_condition(head, acl->cond)) + return false; + head->r.print_cond_part = false; + } else { + tomoyo_set_lf(head); + } return true; } /** * tomoyo_read_domain2 - Read domain policy. * - * @head: Pointer to "struct tomoyo_io_buffer". - * @domain: Pointer to "struct tomoyo_domain_info". + * @head: Pointer to "struct tomoyo_io_buffer". + * @list: Pointer to "struct list_head". * * Caller holds tomoyo_read_lock(). * * Returns true on success, false otherwise. */ static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, - struct tomoyo_domain_info *domain) + struct list_head *list) { - list_for_each_cookie(head->r.acl, &domain->acl_info_list) { + list_for_each_cookie(head->r.acl, list) { struct tomoyo_acl_info *ptr = list_entry(head->r.acl, typeof(*ptr), list); if (!tomoyo_print_entry(head, ptr)) @@ -1041,6 +1429,7 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) struct tomoyo_domain_info *domain = list_entry(head->r.domain, typeof(*domain), list); switch (head->r.step) { + u8 i; case 0: if (domain->is_deleted && !head->r.print_this_domain_only) @@ -1048,22 +1437,18 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) /* Print domainname and flags. */ tomoyo_set_string(head, domain->domainname->name); tomoyo_set_lf(head); - tomoyo_io_printf(head, - TOMOYO_KEYWORD_USE_PROFILE "%u\n", + tomoyo_io_printf(head, "use_profile %u\n", domain->profile); - if (domain->quota_warned) - tomoyo_set_string(head, "quota_exceeded\n"); - if (domain->transition_failed) - tomoyo_set_string(head, "transition_failed\n"); - if (domain->ignore_global_allow_read) - tomoyo_set_string(head, - TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ - "\n"); + tomoyo_io_printf(head, "use_group %u\n", + domain->group); + for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++) + if (domain->flags[i]) + tomoyo_set_string(head, tomoyo_dif[i]); head->r.step++; tomoyo_set_lf(head); /* fall through */ case 1: - if (!tomoyo_read_domain2(head, domain)) + if (!tomoyo_read_domain2(head, &domain->acl_info_list)) return; head->r.step++; if (!tomoyo_set_lf(head)) @@ -1080,73 +1465,6 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) } /** - * tomoyo_write_domain_profile - Assign profile for specified domain. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0 on success, -EINVAL otherwise. - * - * This is equivalent to doing - * - * ( echo "select " $domainname; echo "use_profile " $profile ) | - * /usr/sbin/tomoyo-loadpolicy -d - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) -{ - char *data = head->write_buf; - char *cp = strchr(data, ' '); - struct tomoyo_domain_info *domain; - unsigned long profile; - - if (!cp) - return -EINVAL; - *cp = '\0'; - domain = tomoyo_find_domain(cp + 1); - if (strict_strtoul(data, 10, &profile)) - return -EINVAL; - if (domain && profile < TOMOYO_MAX_PROFILES - && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)) - domain->profile = (u8) profile; - return 0; -} - -/** - * tomoyo_read_domain_profile - Read only domainname and profile. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns list of profile number and domainname pairs. - * - * This is equivalent to doing - * - * grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy | - * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" ) - * domainname = $0; } else if ( $1 == "use_profile" ) { - * print $2 " " domainname; domainname = ""; } } ; ' - * - * Caller holds tomoyo_read_lock(). - */ -static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) -{ - if (head->r.eof) - return; - list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { - struct tomoyo_domain_info *domain = - list_entry(head->r.domain, typeof(*domain), list); - if (domain->is_deleted) - continue; - if (!tomoyo_flush(head)) - return; - tomoyo_io_printf(head, "%u ", domain->profile); - tomoyo_set_string(head, domain->domainname->name); - tomoyo_set_lf(head); - } - head->r.eof = true; -} - -/** * tomoyo_write_pid: Specify PID to obtain domainname. * * @head: Pointer to "struct tomoyo_io_buffer". @@ -1204,18 +1522,20 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head) tomoyo_set_string(head, domain->domainname->name); } +/* String table for domain transition control keywords. */ 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_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ", + [TOMOYO_TRANSITION_CONTROL_RESET] = "reset_domain ", + [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ", + [TOMOYO_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ", + [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ", + [TOMOYO_TRANSITION_CONTROL_KEEP] = "keep_domain ", }; +/* String table for grouping keywords. */ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { - [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, - [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP + [TOMOYO_PATH_GROUP] = "path_group ", + [TOMOYO_NUMBER_GROUP] = "number_group ", }; /** @@ -1229,29 +1549,30 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { */ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) { - char *data = head->write_buf; - bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); - u8 i; - static const struct { - const char *keyword; - int (*write) (char *, const bool); - } tomoyo_callback[4] = { - { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator }, - { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern }, - { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite }, - { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable }, + const bool is_delete = head->w.is_delete; + struct tomoyo_acl_param param = { + .ns = head->w.ns, + .is_delete = is_delete, + .data = head->write_buf, }; - + u8 i; + if (tomoyo_str_starts(¶m.data, "aggregator ")) + return tomoyo_write_aggregator(¶m); 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); - for (i = 0; i < 4; i++) - if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword)) - return tomoyo_callback[i].write(data, is_delete); + if (tomoyo_str_starts(¶m.data, tomoyo_transition_type[i])) + return tomoyo_write_transition_control(¶m, i); for (i = 0; i < TOMOYO_MAX_GROUP; i++) - if (tomoyo_str_starts(&data, tomoyo_group_name[i])) - return tomoyo_write_group(data, is_delete, i); + if (tomoyo_str_starts(¶m.data, tomoyo_group_name[i])) + return tomoyo_write_group(¶m, i); + if (tomoyo_str_starts(¶m.data, "acl_group ")) { + unsigned int group; + char *data; + group = simple_strtoul(param.data, &data, 10); + if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ') + return tomoyo_write_domain2 + (head->w.ns, &head->w.ns->acl_group[group], + data, is_delete); + } return -EINVAL; } @@ -1267,9 +1588,12 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) */ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) { - list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->group_list[idx]; + list_for_each_cookie(head->r.group, list) { struct tomoyo_group *group = - list_entry(head->r.group, typeof(*group), list); + list_entry(head->r.group, typeof(*group), head.list); list_for_each_cookie(head->r.acl, &group->member_list) { struct tomoyo_acl_head *ptr = list_entry(head->r.acl, typeof(*ptr), list); @@ -1277,6 +1601,7 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) continue; if (!tomoyo_flush(head)) return false; + tomoyo_print_namespace(head); tomoyo_set_string(head, tomoyo_group_name[idx]); tomoyo_set_string(head, group->group_name->name); if (idx == TOMOYO_PATH_GROUP) { @@ -1310,7 +1635,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) */ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { - list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->policy_list[idx]; + list_for_each_cookie(head->r.acl, list) { struct tomoyo_acl_head *acl = container_of(head->r.acl, typeof(*acl), list); if (acl->is_deleted) @@ -1322,35 +1650,23 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { struct tomoyo_transition_control *ptr = container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - tomoyo_transition_type + tomoyo_print_namespace(head); + tomoyo_set_string(head, tomoyo_transition_type [ptr->type]); - if (ptr->program) - tomoyo_set_string(head, - ptr->program->name); - if (ptr->program && ptr->domainname) - tomoyo_set_string(head, " from "); - if (ptr->domainname) - tomoyo_set_string(head, - ptr->domainname-> - name); - } - break; - case TOMOYO_ID_GLOBALLY_READABLE: - { - struct tomoyo_readable_file *ptr = - container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_ALLOW_READ); - tomoyo_set_string(head, ptr->filename->name); + tomoyo_set_string(head, ptr->program ? + ptr->program->name : "any"); + tomoyo_set_string(head, " from "); + tomoyo_set_string(head, ptr->domainname ? + ptr->domainname->name : + "any"); } break; case TOMOYO_ID_AGGREGATOR: { struct tomoyo_aggregator *ptr = container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_AGGREGATOR); + tomoyo_print_namespace(head); + tomoyo_set_string(head, "aggregator "); tomoyo_set_string(head, ptr->original_name->name); tomoyo_set_space(head); @@ -1358,24 +1674,6 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) ptr->aggregated_name->name); } break; - case TOMOYO_ID_PATTERN: - { - struct tomoyo_no_pattern *ptr = - container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_FILE_PATTERN); - tomoyo_set_string(head, ptr->pattern->name); - } - break; - case TOMOYO_ID_NO_REWRITE: - { - struct tomoyo_no_rewrite *ptr = - container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_DENY_REWRITE); - tomoyo_set_string(head, ptr->pattern->name); - } - break; default: continue; } @@ -1394,6 +1692,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) */ static void tomoyo_read_exception(struct tomoyo_io_buffer *head) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); if (head->r.eof) return; while (head->r.step < TOMOYO_MAX_POLICY && @@ -1406,95 +1706,40 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head) head->r.step++; if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) return; + while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP + + TOMOYO_MAX_ACL_GROUPS) { + head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY + - TOMOYO_MAX_GROUP; + if (!tomoyo_read_domain2(head, &ns->acl_group + [head->r.acl_group_index])) + return; + head->r.step++; + } head->r.eof = true; } -/** - * 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) -{ - 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); - pid_t ppid; - if (!buffer) - return NULL; - do_gettimeofday(&tv); - rcu_read_lock(); - ppid = task_tgid_vnr(current->real_parent); - rcu_read_unlock(); - 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[r->mode], gpid, - task_tgid_vnr(current), ppid, - current_uid(), current_gid(), current_euid(), - current_egid(), current_suid(), current_sgid(), - current_fsuid(), current_fsgid()); - return buffer; -} - -/** - * 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. - * - * This function uses kzalloc(), so caller must kfree() if this function - * didn't return NULL. - */ -static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) -{ - 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; -} - -/* Wait queue for tomoyo_query_list. */ +/* Wait queue for kernel -> userspace notification. */ static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); - -/* Lock for manipulating tomoyo_query_list. */ -static DEFINE_SPINLOCK(tomoyo_query_list_lock); +/* Wait queue for userspace -> kernel notification. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait); /* Structure for query. */ struct tomoyo_query { struct list_head list; char *query; - int query_len; + size_t query_len; unsigned int serial; - int timer; - int answer; + u8 timer; + u8 answer; + u8 retry; }; /* The list for "struct tomoyo_query". */ static LIST_HEAD(tomoyo_query_list); +/* Lock for manipulating tomoyo_query_list. */ +static DEFINE_SPINLOCK(tomoyo_query_list_lock); + /* * Number of "struct file" referring /sys/kernel/security/tomoyo/query * interface. @@ -1502,10 +1747,82 @@ static LIST_HEAD(tomoyo_query_list); 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". + * @header: Lines containing ACL. + * + * Returns nothing. + */ +static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) +{ + char *buffer; + char *realpath = NULL; + char *argv0 = NULL; + char *symlink = NULL; + char *cp = strchr(header, '\n'); + int len; + if (!cp) + return; + cp = strchr(cp + 1, '\n'); + if (!cp) + return; + *cp++ = '\0'; + len = strlen(cp) + 1; + /* strstr() will return NULL if ordering is wrong. */ + if (*cp == 'f') { + argv0 = strstr(header, " argv[]={ \""); + if (argv0) { + argv0 += 10; + len += tomoyo_truncate(argv0) + 14; + } + 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 (argv0) + tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0); + if (symlink) + tomoyo_addprintf(buffer, len, "%s", symlink); + tomoyo_normalize_line(buffer); + if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, + false)) + tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); + kfree(buffer); +} + +/** * tomoyo_supervisor - Ask for the supervisor's decision. * - * @r: Pointer to "struct tomoyo_request_info". - * @fmt: The printf()'s format string, followed by parameters. + * @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 @@ -1515,88 +1832,79 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) { va_list args; - int error = -EPERM; - int pos; + int error; int len; static unsigned int tomoyo_serial; - struct tomoyo_query *entry = NULL; + struct tomoyo_query entry = { }; bool quota_exceeded = false; - char *header; + va_start(args, fmt); + len = vsnprintf((char *) &len, 1, fmt, args) + 1; + va_end(args); + /* Write /sys/kernel/security/tomoyo/audit. */ + va_start(args, fmt); + tomoyo_write_log2(r, len, fmt, args); + va_end(args); + /* Nothing more to do if granted. */ + if (r->granted) + return 0; + if (r->mode) + tomoyo_update_stat(r->mode); switch (r->mode) { - char *buffer; + case TOMOYO_CONFIG_ENFORCING: + error = -EPERM; + if (atomic_read(&tomoyo_query_observers)) + break; + goto out; 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_domain2(buffer, r->domain, false); - kfree(buffer); + error = 0; + /* Check max_learning_entry parameter. */ + if (tomoyo_domain_quota_is_ok(r)) + break; /* fall through */ - case TOMOYO_CONFIG_PERMISSIVE: + default: return 0; } - if (!r->domain) - r->domain = tomoyo_domain(); - if (!atomic_read(&tomoyo_query_observers)) - return -EPERM; + /* Get message. */ va_start(args, fmt); - len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; + entry.query = tomoyo_init_log(r, len, fmt, args); va_end(args); - header = tomoyo_init_audit_log(&len, r); - if (!header) + if (!entry.query) goto out; - entry = kzalloc(sizeof(*entry), GFP_NOFS); - if (!entry) + entry.query_len = strlen(entry.query) + 1; + if (!error) { + tomoyo_add_entry(r->domain, entry.query); goto out; - entry->query = kzalloc(len, GFP_NOFS); - if (!entry->query) - goto out; - len = ksize(entry->query); + } + len = tomoyo_round2(entry.query_len); spin_lock(&tomoyo_query_list_lock); - if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + - sizeof(*entry) >= tomoyo_quota_for_query) { + if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] && + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len + >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) { quota_exceeded = true; } else { - tomoyo_query_memory_size += len + sizeof(*entry); - entry->serial = tomoyo_serial++; + entry.serial = tomoyo_serial++; + entry.retry = r->retry; + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len; + list_add_tail(&entry.list, &tomoyo_query_list); } spin_unlock(&tomoyo_query_list_lock); if (quota_exceeded) goto out; - pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s", - entry->serial, r->retry, header); - kfree(header); - header = NULL; - va_start(args, fmt); - vsnprintf(entry->query + pos, len - 1 - pos, fmt, args); - entry->query_len = strlen(entry->query) + 1; - va_end(args); - spin_lock(&tomoyo_query_list_lock); - list_add_tail(&entry->list, &tomoyo_query_list); - spin_unlock(&tomoyo_query_list_lock); /* Give 10 seconds for supervisor's opinion. */ - for (entry->timer = 0; - atomic_read(&tomoyo_query_observers) && entry->timer < 100; - entry->timer++) { - wake_up(&tomoyo_query_wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 10); - if (entry->answer) + while (entry.timer < 10) { + wake_up_all(&tomoyo_query_wait); + if (wait_event_interruptible_timeout + (tomoyo_answer_wait, entry.answer || + !atomic_read(&tomoyo_query_observers), HZ)) break; + else + entry.timer++; } spin_lock(&tomoyo_query_list_lock); - list_del(&entry->list); - tomoyo_query_memory_size -= len + sizeof(*entry); + list_del(&entry.list); + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len; spin_unlock(&tomoyo_query_list_lock); - switch (entry->answer) { + switch (entry.answer) { case 3: /* Asked to retry by administrator. */ error = TOMOYO_RETRY_REQUEST; r->retry++; @@ -1605,18 +1913,12 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) /* Granted by administrator. */ error = 0; break; - case 0: - /* Timed out. */ - break; default: - /* Rejected by administrator. */ + /* Timed out or rejected by administrator. */ break; } - out: - if (entry) - kfree(entry->query); - kfree(entry); - kfree(header); +out: + kfree(entry.query); return error; } @@ -1663,8 +1965,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait) static void tomoyo_read_query(struct tomoyo_io_buffer *head) { struct list_head *tmp; - int pos = 0; - int len = 0; + unsigned int pos = 0; + size_t len = 0; char *buf; if (head->r.w_pos) return; @@ -1687,7 +1989,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) head->r.query_index = 0; return; } - buf = kzalloc(len, GFP_NOFS); + buf = kzalloc(len + 32, GFP_NOFS); if (!buf) return; pos = 0; @@ -1703,7 +2005,8 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) * can change, but I don't care. */ if (len == ptr->query_len) - memmove(buf, ptr->query, len); + snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, + ptr->retry, ptr->query); break; } spin_unlock(&tomoyo_query_list_lock); @@ -1760,7 +2063,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) static void tomoyo_read_version(struct tomoyo_io_buffer *head) { if (!head->r.eof) { - tomoyo_io_printf(head, "2.3.0"); + tomoyo_io_printf(head, "2.4.0"); head->r.eof = true; } } @@ -1785,15 +2088,111 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) } } +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { + [TOMOYO_STAT_POLICY_UPDATES] = "update:", + [TOMOYO_STAT_POLICY_LEARNING] = "violation in learning mode:", + [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:", + [TOMOYO_STAT_POLICY_ENFORCING] = "violation in enforcing mode:", +}; + +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = { + [TOMOYO_MEMORY_POLICY] = "policy:", + [TOMOYO_MEMORY_AUDIT] = "audit log:", + [TOMOYO_MEMORY_QUERY] = "query message:", +}; + +/* Timestamp counter for last updated. */ +static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; +/* Counter for number of updates. */ +static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; + +/** + * tomoyo_update_stat - Update statistic counters. + * + * @index: Index for policy type. + * + * Returns nothing. + */ +void tomoyo_update_stat(const u8 index) +{ + struct timeval tv; + do_gettimeofday(&tv); + /* + * I don't use atomic operations because race condition is not fatal. + */ + tomoyo_stat_updated[index]++; + tomoyo_stat_modified[index] = tv.tv_sec; +} + +/** + * tomoyo_read_stat - Read statistic data. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_read_stat(struct tomoyo_io_buffer *head) +{ + u8 i; + unsigned int total = 0; + if (head->r.eof) + return; + for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) { + tomoyo_io_printf(head, "Policy %-30s %10u", + tomoyo_policy_headers[i], + tomoyo_stat_updated[i]); + if (tomoyo_stat_modified[i]) { + struct tomoyo_time stamp; + tomoyo_convert_time(tomoyo_stat_modified[i], &stamp); + tomoyo_io_printf(head, " (Last: %04u/%02u/%02u " + "%02u:%02u:%02u)", + stamp.year, stamp.month, stamp.day, + stamp.hour, stamp.min, stamp.sec); + } + tomoyo_set_lf(head); + } + for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) { + unsigned int used = tomoyo_memory_used[i]; + total += used; + tomoyo_io_printf(head, "Memory used by %-22s %10u", + tomoyo_memory_headers[i], used); + used = tomoyo_memory_quota[i]; + if (used) + tomoyo_io_printf(head, " (Quota: %10u)", used); + tomoyo_set_lf(head); + } + tomoyo_io_printf(head, "Total memory used: %10u\n", + total); + head->r.eof = true; +} + +/** + * tomoyo_write_stat - Set memory quota. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +static int tomoyo_write_stat(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + u8 i; + if (tomoyo_str_starts(&data, "Memory used by ")) + for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) + if (tomoyo_str_starts(&data, tomoyo_memory_headers[i])) + sscanf(data, "%u", &tomoyo_memory_quota[i]); + return 0; +} + /** * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface. * * @type: Type of interface. * @file: Pointer to "struct file". * - * Associates policy handler and returns 0 on success, -ENOMEM otherwise. - * - * Caller acquires tomoyo_read_lock(). + * Returns 0 on success, negative value otherwise. */ int tomoyo_open_control(const u8 type, struct file *file) { @@ -1814,15 +2213,15 @@ int tomoyo_open_control(const u8 type, struct file *file) head->write = tomoyo_write_exception; head->read = tomoyo_read_exception; break; + case TOMOYO_AUDIT: + /* /sys/kernel/security/tomoyo/audit */ + head->poll = tomoyo_poll_log; + head->read = tomoyo_read_log; + break; case TOMOYO_SELFDOMAIN: /* /sys/kernel/security/tomoyo/self_domain */ head->read = tomoyo_read_self_domain; break; - case TOMOYO_DOMAIN_STATUS: - /* /sys/kernel/security/tomoyo/.domain_status */ - head->write = tomoyo_write_domain_profile; - head->read = tomoyo_read_domain_profile; - break; case TOMOYO_PROCESS_STATUS: /* /sys/kernel/security/tomoyo/.process_status */ head->write = tomoyo_write_pid; @@ -1833,11 +2232,11 @@ int tomoyo_open_control(const u8 type, struct file *file) head->read = tomoyo_read_version; head->readbuf_size = 128; break; - case TOMOYO_MEMINFO: - /* /sys/kernel/security/tomoyo/meminfo */ - head->write = tomoyo_write_memory_quota; - head->read = tomoyo_read_memory_counter; - head->readbuf_size = 512; + case TOMOYO_STAT: + /* /sys/kernel/security/tomoyo/stat */ + head->write = tomoyo_write_stat; + head->read = tomoyo_read_stat; + head->readbuf_size = 1024; break; case TOMOYO_PROFILE: /* /sys/kernel/security/tomoyo/profile */ @@ -1887,26 +2286,16 @@ int tomoyo_open_control(const u8 type, struct file *file) return -ENOMEM; } } - if (type != TOMOYO_QUERY) - head->reader_idx = tomoyo_read_lock(); - file->private_data = head; - /* - * Call the handler now if the file is - * /sys/kernel/security/tomoyo/self_domain - * so that the user can use - * cat < /sys/kernel/security/tomoyo/self_domain" - * to know the current process's domainname. - */ - 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) + if (type == TOMOYO_QUERY) atomic_inc(&tomoyo_query_observers); + file->private_data = head; + tomoyo_notify_gc(head, true); return 0; } @@ -1917,7 +2306,8 @@ int tomoyo_open_control(const u8 type, struct file *file) * @wait: Pointer to "poll_table". * * Waits for read readiness. - * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and + * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd. */ int tomoyo_poll_control(struct file *file, poll_table *wait) { @@ -1928,21 +2318,58 @@ int tomoyo_poll_control(struct file *file, poll_table *wait) } /** + * tomoyo_set_namespace_cursor - Set namespace to read. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head) +{ + struct list_head *ns; + if (head->type != TOMOYO_EXCEPTIONPOLICY && + head->type != TOMOYO_PROFILE) + return; + /* + * If this is the first read, or reading previous namespace finished + * and has more namespaces to read, update the namespace cursor. + */ + ns = head->r.ns; + if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) { + /* Clearing is OK because tomoyo_flush() returned true. */ + memset(&head->r, 0, sizeof(head->r)); + head->r.ns = ns ? ns->next : tomoyo_namespace_list.next; + } +} + +/** + * tomoyo_has_more_namespace - Check for unread namespaces. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true if we have more entries to print, false otherwise. + */ +static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) +{ + return (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) && head->r.eof && + head->r.ns->next != &tomoyo_namespace_list; +} + +/** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". + * @head: Pointer to "struct tomoyo_io_buffer". * @buffer: Poiner to buffer to write to. * @buffer_len: Size of @buffer. * * Returns bytes read on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). */ -int tomoyo_read_control(struct file *file, char __user *buffer, - const int buffer_len) +ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, + const int buffer_len) { int len; - struct tomoyo_io_buffer *head = file->private_data; + int idx; if (!head->read) return -ENOSYS; @@ -1950,64 +2377,156 @@ int tomoyo_read_control(struct file *file, char __user *buffer, return -EINTR; head->read_user_buf = buffer; head->read_user_buf_avail = buffer_len; + idx = tomoyo_read_lock(); if (tomoyo_flush(head)) /* Call the policy handler. */ - head->read(head); - tomoyo_flush(head); + do { + tomoyo_set_namespace_cursor(head); + head->read(head); + } while (tomoyo_flush(head) && + tomoyo_has_more_namespace(head)); + tomoyo_read_unlock(idx); len = head->read_user_buf - buffer; mutex_unlock(&head->io_sem); return len; } /** + * tomoyo_parse_policy - Parse a policy line. + * + * @head: Poiter to "struct tomoyo_io_buffer". + * @line: Line to parse. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) +{ + /* Delete request? */ + head->w.is_delete = !strncmp(line, "delete ", 7); + if (head->w.is_delete) + memmove(line, line + 7, strlen(line + 7) + 1); + /* Selecting namespace to update. */ + if (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) { + if (*line == '<') { + char *cp = strchr(line, ' '); + if (cp) { + *cp++ = '\0'; + head->w.ns = tomoyo_assign_namespace(line); + memmove(line, cp, strlen(cp) + 1); + } else + head->w.ns = NULL; + } else + head->w.ns = &tomoyo_kernel_namespace; + /* Don't allow updating if namespace is invalid. */ + if (!head->w.ns) + return -ENOENT; + } + /* Do the update. */ + return head->write(head); +} + +/** * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". + * @head: Pointer to "struct tomoyo_io_buffer". * @buffer: Pointer to buffer to read from. * @buffer_len: Size of @buffer. * * Returns @buffer_len on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_control(struct file *file, const char __user *buffer, - const int buffer_len) +ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, + const char __user *buffer, const int buffer_len) { - struct tomoyo_io_buffer *head = file->private_data; int error = buffer_len; - int avail_len = buffer_len; + size_t avail_len = buffer_len; char *cp0 = head->write_buf; - + int idx; if (!head->write) return -ENOSYS; if (!access_ok(VERIFY_READ, buffer, buffer_len)) return -EFAULT; - /* Don't allow updating policies by non manager programs. */ - if (head->write != tomoyo_write_pid && - head->write != tomoyo_write_domain && !tomoyo_manager()) - return -EPERM; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; + idx = tomoyo_read_lock(); /* Read a line and dispatch it to the policy handler. */ while (avail_len > 0) { char c; - if (head->write_avail >= head->writebuf_size - 1) { - error = -ENOMEM; - break; - } else if (get_user(c, buffer)) { + if (head->w.avail >= head->writebuf_size - 1) { + const int len = head->writebuf_size * 2; + char *cp = kzalloc(len, GFP_NOFS); + if (!cp) { + error = -ENOMEM; + break; + } + memmove(cp, cp0, head->w.avail); + kfree(cp0); + head->write_buf = cp; + cp0 = cp; + head->writebuf_size = len; + } + if (get_user(c, buffer)) { error = -EFAULT; break; } buffer++; avail_len--; - cp0[head->write_avail++] = c; + cp0[head->w.avail++] = c; if (c != '\n') continue; - cp0[head->write_avail - 1] = '\0'; - head->write_avail = 0; + cp0[head->w.avail - 1] = '\0'; + head->w.avail = 0; tomoyo_normalize_line(cp0); - head->write(head); + if (!strcmp(cp0, "reset")) { + head->w.ns = &tomoyo_kernel_namespace; + head->w.domain = NULL; + memset(&head->r, 0, sizeof(head->r)); + continue; + } + /* Don't allow updating policies by non manager programs. */ + switch (head->type) { + case TOMOYO_PROCESS_STATUS: + /* This does not write anything. */ + break; + case TOMOYO_DOMAINPOLICY: + if (tomoyo_select_domain(head, cp0)) + continue; + /* fall through */ + case TOMOYO_EXCEPTIONPOLICY: + if (!strcmp(cp0, "select transition_only")) { + head->r.print_transition_related_only = true; + continue; + } + /* fall through */ + default: + if (!tomoyo_manager()) { + error = -EPERM; + goto out; + } + } + switch (tomoyo_parse_policy(head, cp0)) { + case -EPERM: + error = -EPERM; + goto out; + case 0: + switch (head->type) { + case TOMOYO_DOMAINPOLICY: + case TOMOYO_EXCEPTIONPOLICY: + case TOMOYO_STAT: + case TOMOYO_PROFILE: + case TOMOYO_MANAGER: + tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); + break; + default: + break; + } + break; + } } +out: + tomoyo_read_unlock(idx); mutex_unlock(&head->io_sem); return error; } @@ -2015,35 +2534,20 @@ int tomoyo_write_control(struct file *file, const char __user *buffer, /** * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". - * - * Releases memory and returns 0. + * @head: Pointer to "struct tomoyo_io_buffer". * - * Caller looses tomoyo_read_lock(). + * Returns 0. */ -int tomoyo_close_control(struct file *file) +int tomoyo_close_control(struct tomoyo_io_buffer *head) { - struct tomoyo_io_buffer *head = file->private_data; - const bool is_write = !!head->write_buf; - /* * 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; - kfree(head->write_buf); - head->write_buf = NULL; - kfree(head); - head = NULL; - file->private_data = NULL; - if (is_write) - tomoyo_run_gc(); + if (head->type == TOMOYO_QUERY && + atomic_dec_and_test(&tomoyo_query_observers)) + wake_up_all(&tomoyo_answer_wait); + tomoyo_notify_gc(head, false); return 0; } @@ -2055,27 +2559,90 @@ void tomoyo_check_profile(void) struct tomoyo_domain_info *domain; const int idx = tomoyo_read_lock(); tomoyo_policy_loaded = true; - /* Check all profiles currently assigned to domains are defined. */ + printk(KERN_INFO "TOMOYO: 2.4.0\n"); list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { const u8 profile = domain->profile; - if (tomoyo_profile_ptr[profile]) + const struct tomoyo_policy_namespace *ns = domain->ns; + if (ns->profile_version != 20100903) + printk(KERN_ERR + "Profile version %u is not supported.\n", + ns->profile_version); + else if (!ns->profile_ptr[profile]) + printk(KERN_ERR + "Profile %u (used by '%s') is not defined.\n", + profile, domain->domainname->name); + else continue; - printk(KERN_ERR "You need to define profile %u before using it.\n", - profile); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " + printk(KERN_ERR + "Userland tools for TOMOYO 2.4 must be installed and " + "policy must be initialized.\n"); + printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ " "for more information.\n"); - panic("Profile %u (used by '%s') not defined.\n", - profile, domain->domainname->name); + panic("STOP!"); } tomoyo_read_unlock(idx); - if (tomoyo_profile_version != 20090903) { - printk(KERN_ERR "You need to install userland programs for " - "TOMOYO 2.3 and initialize policy configuration.\n"); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " - "for more information.\n"); - panic("Profile version %u is not supported.\n", - tomoyo_profile_version); - } - printk(KERN_INFO "TOMOYO: 2.3.0\n"); printk(KERN_INFO "Mandatory Access Control activated.\n"); } + +/** + * tomoyo_load_builtin_policy - Load built-in policy. + * + * Returns nothing. + */ +void __init tomoyo_load_builtin_policy(void) +{ + /* + * This include file is manually created and contains built-in policy + * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy", + * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager", + * "tomoyo_builtin_stat" in the form of "static char [] __initdata". + */ +#include "builtin-policy.h" + u8 i; + const int idx = tomoyo_read_lock(); + for (i = 0; i < 5; i++) { + struct tomoyo_io_buffer head = { }; + char *start = ""; + switch (i) { + case 0: + start = tomoyo_builtin_profile; + head.type = TOMOYO_PROFILE; + head.write = tomoyo_write_profile; + break; + case 1: + start = tomoyo_builtin_exception_policy; + head.type = TOMOYO_EXCEPTIONPOLICY; + head.write = tomoyo_write_exception; + break; + case 2: + start = tomoyo_builtin_domain_policy; + head.type = TOMOYO_DOMAINPOLICY; + head.write = tomoyo_write_domain; + break; + case 3: + start = tomoyo_builtin_manager; + head.type = TOMOYO_MANAGER; + head.write = tomoyo_write_manager; + break; + case 4: + start = tomoyo_builtin_stat; + head.type = TOMOYO_STAT; + head.write = tomoyo_write_stat; + break; + } + while (1) { + char *end = strchr(start, '\n'); + if (!end) + break; + *end = '\0'; + tomoyo_normalize_line(start); + head.write_buf = start; + tomoyo_parse_policy(&head, start); + start = end + 1; + } + } + tomoyo_read_unlock(idx); +#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER + tomoyo_check_profile(); +#endif +} |