aboutsummaryrefslogtreecommitdiffstats
path: root/security/tomoyo
diff options
context:
space:
mode:
authorroot <root@artemis.panaceas.org>2015-12-25 04:40:36 +0000
committerroot <root@artemis.panaceas.org>2015-12-25 04:40:36 +0000
commit849369d6c66d3054688672f97d31fceb8e8230fb (patch)
tree6135abc790ca67dedbe07c39806591e70eda81ce /security/tomoyo
downloadlinux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.gz
linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.bz2
linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.zip
initial_commit
Diffstat (limited to 'security/tomoyo')
-rw-r--r--security/tomoyo/Kconfig11
-rw-r--r--security/tomoyo/Makefile1
-rw-r--r--security/tomoyo/common.c2081
-rw-r--r--security/tomoyo/common.h1015
-rw-r--r--security/tomoyo/domain.c542
-rw-r--r--security/tomoyo/file.c1176
-rw-r--r--security/tomoyo/gc.c354
-rw-r--r--security/tomoyo/group.c130
-rw-r--r--security/tomoyo/load_policy.c81
-rw-r--r--security/tomoyo/memory.c283
-rw-r--r--security/tomoyo/mount.c287
-rw-r--r--security/tomoyo/realpath.c183
-rw-r--r--security/tomoyo/securityfs_if.c155
-rw-r--r--security/tomoyo/tomoyo.c289
-rw-r--r--security/tomoyo/util.c963
15 files changed, 7551 insertions, 0 deletions
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
new file mode 100644
index 00000000..c8f38579
--- /dev/null
+++ b/security/tomoyo/Kconfig
@@ -0,0 +1,11 @@
+config SECURITY_TOMOYO
+ bool "TOMOYO Linux Support"
+ depends on SECURITY
+ select SECURITYFS
+ select SECURITY_PATH
+ default n
+ help
+ This selects TOMOYO Linux, pathname-based access control.
+ Required userspace tools and further information may be
+ found at <http://tomoyo.sourceforge.jp/>.
+ If you are unsure how to answer this question, answer N.
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
new file mode 100644
index 00000000..91640e96
--- /dev/null
+++ b/security/tomoyo/Makefile
@@ -0,0 +1 @@
+obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
new file mode 100644
index 00000000..a0d09e56
--- /dev/null
+++ b/security/tomoyo/common.c
@@ -0,0 +1,2081 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#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
+};
+
+/* Profile version. Currently only 20090903 is defined. */
+static unsigned int tomoyo_profile_version;
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
+/* String table for functionality that takes 4 modes. */
+static const char *tomoyo_mode[4] = {
+ "disabled", "learning", "permissive", "enforcing"
+};
+
+/* 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",
+};
+
+/* Permit policy management by non-root user? */
+static bool tomoyo_manage_by_non_root;
+
+/* Utility functions. */
+
+/**
+ * tomoyo_yesno - Return "yes" or "no".
+ *
+ * @value: Bool value.
+ */
+static const char *tomoyo_yesno(const unsigned int value)
+{
+ return value ? "yes" : "no";
+}
+
+static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
+{
+ va_list args;
+ const int pos = strlen(buffer);
+ va_start(args, fmt);
+ vsnprintf(buffer + pos, len - pos - 1, fmt, args);
+ va_end(args);
+}
+
+/**
+ * tomoyo_flush - Flush queued string to userspace's buffer.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true if all data was flushed, false otherwise.
+ */
+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);
+ if (len) {
+ if (len > head->read_user_buf_avail)
+ len = head->read_user_buf_avail;
+ if (!len)
+ return false;
+ if (copy_to_user(head->read_user_buf, w, len))
+ return false;
+ head->read_user_buf_avail -= len;
+ head->read_user_buf += len;
+ w += len;
+ }
+ head->r.w[0] = w;
+ if (*w)
+ return false;
+ /* Add '\0' for query. */
+ if (head->poll) {
+ if (!head->read_user_buf_avail ||
+ copy_to_user(head->read_user_buf, "", 1))
+ return false;
+ head->read_user_buf_avail--;
+ head->read_user_buf++;
+ }
+ head->r.w_pos--;
+ for (len = 0; len < head->r.w_pos; len++)
+ head->r.w[len] = head->r.w[len + 1];
+ }
+ head->r.avail = 0;
+ return true;
+}
+
+/**
+ * tomoyo_set_string - Queue string to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @string: String to print.
+ *
+ * Note that @string has to be kept valid until @head is kfree()d.
+ * This means that char[] allocated on stack memory cannot be passed to
+ * this function. Use tomoyo_io_printf() for char[] allocated on stack memory.
+ */
+static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
+{
+ if (head->r.w_pos < TOMOYO_MAX_IO_READ_QUEUE) {
+ head->r.w[head->r.w_pos++] = string;
+ tomoyo_flush(head);
+ } else
+ WARN_ON(1);
+}
+
+/**
+ * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @fmt: The printf()'s format string, followed by parameters.
+ */
+void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int pos = head->r.avail;
+ int size = head->readbuf_size - pos;
+ if (size <= 0)
+ return;
+ va_start(args, fmt);
+ len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1;
+ va_end(args);
+ if (pos + len >= head->readbuf_size) {
+ WARN_ON(1);
+ return;
+ }
+ head->r.avail += len;
+ tomoyo_set_string(head, head->read_buf + pos);
+}
+
+static void tomoyo_set_space(struct tomoyo_io_buffer *head)
+{
+ tomoyo_set_string(head, " ");
+}
+
+static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
+{
+ tomoyo_set_string(head, "\n");
+ return !head->r.w_pos;
+}
+
+/**
+ * tomoyo_print_name_union - Print a tomoyo_name_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ */
+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) {
+ tomoyo_set_string(head, "@");
+ tomoyo_set_string(head, ptr->group->group_name->name);
+ } else {
+ tomoyo_set_string(head, ptr->filename->name);
+ }
+}
+
+/**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ */
+static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_number_union *ptr)
+{
+ tomoyo_set_space(head);
+ if (ptr->is_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;
+ char buffer[128];
+ buffer[0] = '\0';
+ for (i = 0; i < 2; i++) {
+ switch (min_type) {
+ case TOMOYO_VALUE_TYPE_HEXADECIMAL:
+ tomoyo_addprintf(buffer, sizeof(buffer),
+ "0x%lX", min);
+ break;
+ case TOMOYO_VALUE_TYPE_OCTAL:
+ tomoyo_addprintf(buffer, sizeof(buffer),
+ "0%lo", min);
+ break;
+ default:
+ tomoyo_addprintf(buffer, sizeof(buffer),
+ "%lu", min);
+ break;
+ }
+ if (min == max && min_type == max_type)
+ break;
+ tomoyo_addprintf(buffer, sizeof(buffer), "-");
+ min_type = max_type;
+ min = max;
+ }
+ tomoyo_io_printf(head, "%s", buffer);
+ }
+}
+
+/**
+ * tomoyo_assign_profile - Create a new profile.
+ *
+ * @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)
+{
+ struct tomoyo_profile *ptr;
+ struct tomoyo_profile *entry;
+ if (profile >= TOMOYO_MAX_PROFILES)
+ return NULL;
+ ptr = tomoyo_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];
+ 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;
+ memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
+ sizeof(ptr->config));
+ mb(); /* Avoid out-of-order execution. */
+ tomoyo_profile_ptr[profile] = ptr;
+ entry = NULL;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ kfree(entry);
+ return ptr;
+}
+
+/**
+ * tomoyo_profile - Find a profile.
+ *
+ * @profile: Profile number to find.
+ *
+ * Returns pointer to "struct tomoyo_profile".
+ */
+struct tomoyo_profile *tomoyo_profile(const u8 profile)
+{
+ struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
+ if (!tomoyo_policy_loaded)
+ return &tomoyo_default_profile;
+ BUG_ON(!ptr);
+ return ptr;
+}
+
+static s8 tomoyo_find_yesno(const char *string, const char *find)
+{
+ const char *cp = strstr(string, find);
+ if (cp) {
+ cp += strlen(find);
+ if (!strncmp(cp, "=yes", 4))
+ return 1;
+ else if (!strncmp(cp, "=no", 3))
+ return 0;
+ }
+ 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;
+ }
+}
+
+static void tomoyo_set_uint(unsigned int *i, const char *string,
+ const char *find)
+{
+ const char *cp = strstr(string, find);
+ if (cp)
+ 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");
+}
+
+static int tomoyo_set_mode(char *name, const char *value,
+ const bool use_default,
+ struct tomoyo_profile *profile)
+{
+ u8 i;
+ u8 config;
+ if (!strcmp(name, "CONFIG")) {
+ i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX;
+ config = profile->default_config;
+ } else if (tomoyo_str_starts(&name, "CONFIG::")) {
+ config = 0;
+ for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
+ if (strcmp(name, tomoyo_mac_keywords[i]))
+ continue;
+ config = profile->config[i];
+ break;
+ }
+ if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+ if (use_default) {
+ config = TOMOYO_CONFIG_USE_DEFAULT;
+ } else {
+ u8 mode;
+ for (mode = 0; mode < 4; mode++)
+ if (strstr(value, tomoyo_mode[mode]))
+ /*
+ * Update lower 3 bits in order to distinguish
+ * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
+ */
+ config = (config & ~7) | mode;
+ }
+ if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
+ profile->config[i] = config;
+ else if (config != TOMOYO_CONFIG_USE_DEFAULT)
+ profile->default_config = config;
+ return 0;
+}
+
+/**
+ * tomoyo_write_profile - Write profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+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)
+ 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;
+ }
+ 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
+ = tomoyo_get_name(cp);
+ const struct tomoyo_path_info *old_comment;
+ if (!new_comment)
+ return -ENOMEM;
+ spin_lock(&lock);
+ old_comment = profile->comment;
+ profile->comment = new_comment;
+ spin_unlock(&lock);
+ 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;
+ }
+ tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
+ buffer, "enforcing",
+ tomoyo_yesno(pref->enforcing_verbose));
+}
+
+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_read_profile - Read profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
+{
+ u8 index;
+ const struct tomoyo_profile *profile;
+ next:
+ index = head->r.index;
+ profile = tomoyo_profile_ptr[index];
+ switch (head->r.step) {
+ case 0:
+ tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
+ tomoyo_print_preference(head, -1);
+ head->r.step++;
+ break;
+ case 1:
+ for ( ; head->r.index < TOMOYO_MAX_PROFILES;
+ head->r.index++)
+ if (tomoyo_profile_ptr[head->r.index])
+ break;
+ if (head->r.index == TOMOYO_MAX_PROFILES)
+ return;
+ head->r.step++;
+ break;
+ case 2:
+ {
+ const struct tomoyo_path_info *comment =
+ profile->comment;
+ tomoyo_io_printf(head, "%u-COMMENT=", index);
+ tomoyo_set_string(head, comment ? comment->name : "");
+ tomoyo_set_lf(head);
+ head->r.step++;
+ }
+ break;
+ case 3:
+ {
+ tomoyo_io_printf(head, "%u-%s", index, "CONFIG");
+ tomoyo_print_config(head, profile->default_config);
+ head->r.bit = 0;
+ head->r.step++;
+ }
+ break;
+ case 4:
+ for ( ; head->r.bit < TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) {
+ const u8 i = head->r.bit;
+ 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_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;
+ }
+ break;
+ }
+ if (tomoyo_flush(head))
+ goto next;
+}
+
+static bool tomoyo_same_manager(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ return container_of(a, struct tomoyo_manager, head)->manager ==
+ container_of(b, struct tomoyo_manager, head)->manager;
+}
+
+/**
+ * tomoyo_update_manager_entry - Add a manager entry.
+ *
+ * @manager: The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_manager_entry(const char *manager,
+ const bool is_delete)
+{
+ struct tomoyo_manager e = { };
+ int error;
+
+ if (tomoyo_domain_def(manager)) {
+ if (!tomoyo_correct_domain(manager))
+ return -EINVAL;
+ e.is_domain = true;
+ } else {
+ if (!tomoyo_correct_path(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);
+ return error;
+}
+
+/**
+ * tomoyo_write_manager - Write manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+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;
+ return 0;
+ }
+ return tomoyo_update_manager_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_manager - Read manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+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]) {
+ struct tomoyo_manager *ptr =
+ list_entry(head->r.acl, typeof(*ptr), head.list);
+ if (ptr->head.is_deleted)
+ continue;
+ if (!tomoyo_flush(head))
+ return;
+ tomoyo_set_string(head, ptr->manager->name);
+ tomoyo_set_lf(head);
+ }
+ head->r.eof = true;
+}
+
+/**
+ * tomoyo_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_manager(void)
+{
+ struct tomoyo_manager *ptr;
+ const char *exe;
+ const struct task_struct *task = current;
+ const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname;
+ bool found = false;
+
+ if (!tomoyo_policy_loaded)
+ 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) {
+ if (!ptr->head.is_deleted && ptr->is_domain
+ && !tomoyo_pathcmp(domainname, ptr->manager)) {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ return true;
+ exe = tomoyo_get_exe();
+ if (!exe)
+ return false;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
+ head.list) {
+ if (!ptr->head.is_deleted && !ptr->is_domain
+ && !strcmp(exe, ptr->manager->name)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) { /* Reduce error messages. */
+ static pid_t last_pid;
+ const pid_t pid = current->pid;
+ if (last_pid != pid) {
+ printk(KERN_WARNING "%s ( %s ) is not permitted to "
+ "update policies.\n", domainname->name, exe);
+ last_pid = pid;
+ }
+ }
+ kfree(exe);
+ return found;
+}
+
+/**
+ * tomoyo_select_one - Parse select command.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_select_one(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 (sscanf(data, "pid=%u", &pid) == 1 ||
+ (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
+ struct task_struct *p;
+ rcu_read_lock();
+ read_lock(&tasklist_lock);
+ if (global_pid)
+ p = find_task_by_pid_ns(pid, &init_pid_ns);
+ else
+ p = find_task_by_vpid(pid);
+ if (p)
+ domain = tomoyo_real_domain(p);
+ read_unlock(&tasklist_lock);
+ rcu_read_unlock();
+ } else if (!strncmp(data, "domain=", 7)) {
+ if (tomoyo_domain_def(data + 7))
+ domain = tomoyo_find_domain(data + 7);
+ } else
+ return false;
+ head->write_var1 = domain;
+ /* Accessing read_buf is safe because head->io_sem is held. */
+ if (!head->read_buf)
+ return true; /* Do nothing if open(O_WRONLY). */
+ memset(&head->r, 0, sizeof(head->r));
+ head->r.print_this_domain_only = true;
+ if (domain)
+ head->r.domain = &domain->list;
+ else
+ head->r.eof = 1;
+ tomoyo_io_printf(head, "# select %s\n", data);
+ if (domain && domain->is_deleted)
+ tomoyo_io_printf(head, "# This is a deleted domain.\n");
+ return true;
+}
+
+/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_delete_domain(char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return 0;
+ /* Is there an active domain? */
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ /* Never delete tomoyo_kernel_domain */
+ if (domain == &tomoyo_kernel_domain)
+ continue;
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(domain->domainname, &name))
+ continue;
+ domain->is_deleted = true;
+ break;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ return 0;
+}
+
+/**
+ * tomoyo_write_domain2 - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
+ 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);
+}
+
+/**
+ * tomoyo_write_domain - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+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;
+ 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)) {
+ 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;
+ return 0;
+ }
+ if (!domain)
+ return -EINVAL;
+
+ if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+ && profile < TOMOYO_MAX_PROFILES) {
+ if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+ domain->profile = (u8) profile;
+ return 0;
+ }
+ if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
+ domain->ignore_global_allow_read = !is_delete;
+ 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;
+ return 0;
+ }
+ return tomoyo_write_domain2(data, domain, is_delete);
+}
+
+/**
+ * tomoyo_fns - Find next set bit.
+ *
+ * @perm: 8 bits value.
+ * @bit: First bit to find.
+ *
+ * Returns next on-bit on success, 8 otherwise.
+ */
+static u8 tomoyo_fns(const u8 perm, u8 bit)
+{
+ for ( ; bit < 8; bit++)
+ if (perm & (1 << bit))
+ break;
+ return bit;
+}
+
+/**
+ * tomoyo_print_entry - Print an ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @acl: Pointer to an ACL entry.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
+ struct tomoyo_acl_info *acl)
+{
+ const u8 acl_type = acl->type;
+ u8 bit;
+
+ 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++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (head->r.print_execute_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 (bit >= TOMOYO_MAX_PATH_OPERATION)
+ goto done;
+ tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]);
+ tomoyo_print_name_union(head, &ptr->name);
+ } else if (head->r.print_execute_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]);
+ 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]);
+ 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]);
+ tomoyo_print_name_union(head, &ptr->name);
+ tomoyo_print_number_union(head, &ptr->mode);
+ tomoyo_print_number_union(head, &ptr->major);
+ tomoyo_print_number_union(head, &ptr->minor);
+ } 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_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;
+ return true;
+}
+
+/**
+ * tomoyo_read_domain2 - Read domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * 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)
+{
+ list_for_each_cookie(head->r.acl, &domain->acl_info_list) {
+ struct tomoyo_acl_info *ptr =
+ list_entry(head->r.acl, typeof(*ptr), list);
+ if (!tomoyo_print_entry(head, ptr))
+ return false;
+ }
+ head->r.acl = NULL;
+ return true;
+}
+
+/**
+ * tomoyo_read_domain - Read domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static void tomoyo_read_domain(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);
+ switch (head->r.step) {
+ case 0:
+ if (domain->is_deleted &&
+ !head->r.print_this_domain_only)
+ continue;
+ /* 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",
+ 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");
+ head->r.step++;
+ tomoyo_set_lf(head);
+ /* fall through */
+ case 1:
+ if (!tomoyo_read_domain2(head, domain))
+ return;
+ head->r.step++;
+ if (!tomoyo_set_lf(head))
+ return;
+ /* fall through */
+ case 2:
+ head->r.step = 0;
+ if (head->r.print_this_domain_only)
+ goto done;
+ }
+ }
+ done:
+ head->r.eof = true;
+}
+
+/**
+ * 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".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_pid(struct tomoyo_io_buffer *head)
+{
+ head->r.eof = false;
+ return 0;
+}
+
+/**
+ * tomoyo_read_pid - Get domainname of the specified PID.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the domainname which the specified PID is in on success,
+ * empty string otherwise.
+ * The PID is specified by tomoyo_write_pid() so that the user can obtain
+ * using read()/write() interface rather than sysctl() interface.
+ */
+static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
+{
+ char *buf = head->write_buf;
+ bool global_pid = false;
+ unsigned int pid;
+ struct task_struct *p;
+ struct tomoyo_domain_info *domain = NULL;
+
+ /* Accessing write_buf is safe because head->io_sem is held. */
+ if (!buf) {
+ head->r.eof = true;
+ return; /* Do nothing if open(O_RDONLY). */
+ }
+ if (head->r.w_pos || head->r.eof)
+ return;
+ head->r.eof = true;
+ if (tomoyo_str_starts(&buf, "global-pid "))
+ global_pid = true;
+ pid = (unsigned int) simple_strtoul(buf, NULL, 10);
+ rcu_read_lock();
+ read_lock(&tasklist_lock);
+ if (global_pid)
+ p = find_task_by_pid_ns(pid, &init_pid_ns);
+ else
+ p = find_task_by_vpid(pid);
+ if (p)
+ domain = tomoyo_real_domain(p);
+ read_unlock(&tasklist_lock);
+ rcu_read_unlock();
+ if (!domain)
+ return;
+ tomoyo_io_printf(head, "%u %u ", pid, domain->profile);
+ tomoyo_set_string(head, domain->domainname->name);
+}
+
+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
+};
+
+static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
+ [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP,
+ [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP
+};
+
+/**
+ * tomoyo_write_exception - Write exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+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 },
+ };
+
+ 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);
+ 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);
+ return -EINVAL;
+}
+
+/**
+ * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @idx: Index number.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+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_group *group =
+ list_entry(head->r.group, typeof(*group), list);
+ list_for_each_cookie(head->r.acl, &group->member_list) {
+ struct tomoyo_acl_head *ptr =
+ list_entry(head->r.acl, typeof(*ptr), list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_flush(head))
+ return false;
+ tomoyo_set_string(head, tomoyo_group_name[idx]);
+ tomoyo_set_string(head, group->group_name->name);
+ if (idx == TOMOYO_PATH_GROUP) {
+ tomoyo_set_space(head);
+ tomoyo_set_string(head, container_of
+ (ptr, struct tomoyo_path_group,
+ head)->member_name->name);
+ } else if (idx == TOMOYO_NUMBER_GROUP) {
+ tomoyo_print_number_union(head, &container_of
+ (ptr,
+ struct tomoyo_number_group,
+ head)->number);
+ }
+ tomoyo_set_lf(head);
+ }
+ head->r.acl = NULL;
+ }
+ head->r.group = NULL;
+ return true;
+}
+
+/**
+ * tomoyo_read_policy - Read "struct tomoyo_..._entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @idx: Index number.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+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_acl_head *acl =
+ container_of(head->r.acl, typeof(*acl), list);
+ if (acl->is_deleted)
+ continue;
+ if (!tomoyo_flush(head))
+ return false;
+ switch (idx) {
+ case TOMOYO_ID_TRANSITION_CONTROL:
+ {
+ struct tomoyo_transition_control *ptr =
+ container_of(acl, typeof(*ptr), 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);
+ }
+ break;
+ case TOMOYO_ID_AGGREGATOR:
+ {
+ struct tomoyo_aggregator *ptr =
+ container_of(acl, typeof(*ptr), head);
+ tomoyo_set_string(head,
+ TOMOYO_KEYWORD_AGGREGATOR);
+ tomoyo_set_string(head,
+ ptr->original_name->name);
+ tomoyo_set_space(head);
+ tomoyo_set_string(head,
+ 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;
+ }
+ tomoyo_set_lf(head);
+ }
+ head->r.acl = NULL;
+ return true;
+}
+
+/**
+ * tomoyo_read_exception - Read exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
+{
+ if (head->r.eof)
+ return;
+ while (head->r.step < TOMOYO_MAX_POLICY &&
+ tomoyo_read_policy(head, head->r.step))
+ head->r.step++;
+ if (head->r.step < TOMOYO_MAX_POLICY)
+ return;
+ while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP &&
+ tomoyo_read_group(head, head->r.step - TOMOYO_MAX_POLICY))
+ head->r.step++;
+ if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)
+ return;
+ 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. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
+
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
+/* Structure for query. */
+struct tomoyo_query {
+ struct list_head list;
+ char *query;
+ int query_len;
+ unsigned int serial;
+ int timer;
+ int answer;
+};
+
+/* The list for "struct tomoyo_query". */
+static LIST_HEAD(tomoyo_query_list);
+
+/*
+ * Number of "struct file" referring /sys/kernel/security/tomoyo/query
+ * interface.
+ */
+static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
+
+/**
+ * tomoyo_supervisor - Ask for the supervisor's decision.
+ *
+ * @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
+ * supervisor decided to retry the access request which violated the policy in
+ * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise.
+ */
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ va_list args;
+ int error = -EPERM;
+ int pos;
+ int len;
+ static unsigned int tomoyo_serial;
+ struct tomoyo_query *entry = NULL;
+ bool quota_exceeded = false;
+ char *header;
+ switch (r->mode) {
+ char *buffer;
+ 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);
+ /* fall through */
+ case TOMOYO_CONFIG_PERMISSIVE:
+ return 0;
+ }
+ if (!r->domain)
+ r->domain = tomoyo_domain();
+ if (!atomic_read(&tomoyo_query_observers))
+ return -EPERM;
+ va_start(args, fmt);
+ len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+ va_end(args);
+ header = tomoyo_init_audit_log(&len, r);
+ if (!header)
+ goto out;
+ entry = kzalloc(sizeof(*entry), GFP_NOFS);
+ if (!entry)
+ goto out;
+ entry->query = kzalloc(len, GFP_NOFS);
+ if (!entry->query)
+ goto out;
+ len = ksize(entry->query);
+ spin_lock(&tomoyo_query_list_lock);
+ if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
+ sizeof(*entry) >= tomoyo_quota_for_query) {
+ quota_exceeded = true;
+ } else {
+ tomoyo_query_memory_size += len + sizeof(*entry);
+ entry->serial = tomoyo_serial++;
+ }
+ 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)
+ break;
+ }
+ spin_lock(&tomoyo_query_list_lock);
+ list_del(&entry->list);
+ tomoyo_query_memory_size -= len + sizeof(*entry);
+ spin_unlock(&tomoyo_query_list_lock);
+ switch (entry->answer) {
+ case 3: /* Asked to retry by administrator. */
+ error = TOMOYO_RETRY_REQUEST;
+ r->retry++;
+ break;
+ case 1:
+ /* Granted by administrator. */
+ error = 0;
+ break;
+ case 0:
+ /* Timed out. */
+ break;
+ default:
+ /* Rejected by administrator. */
+ break;
+ }
+ out:
+ if (entry)
+ kfree(entry->query);
+ kfree(entry);
+ kfree(header);
+ return error;
+}
+
+/**
+ * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise.
+ *
+ * Waits for access requests which violated policy in enforcing mode.
+ */
+static int tomoyo_poll_query(struct file *file, poll_table *wait)
+{
+ struct list_head *tmp;
+ bool found = false;
+ u8 i;
+ for (i = 0; i < 2; i++) {
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query *ptr =
+ list_entry(tmp, typeof(*ptr), list);
+ if (ptr->answer)
+ continue;
+ found = true;
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (found)
+ return POLLIN | POLLRDNORM;
+ if (i)
+ break;
+ poll_wait(file, &tomoyo_query_wait, wait);
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_read_query - Read access requests which violated policy in enforcing mode.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+static void tomoyo_read_query(struct tomoyo_io_buffer *head)
+{
+ struct list_head *tmp;
+ int pos = 0;
+ int len = 0;
+ char *buf;
+ if (head->r.w_pos)
+ return;
+ if (head->read_buf) {
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ }
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+ if (ptr->answer)
+ continue;
+ if (pos++ != head->r.query_index)
+ continue;
+ len = ptr->query_len;
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (!len) {
+ head->r.query_index = 0;
+ return;
+ }
+ buf = kzalloc(len, GFP_NOFS);
+ if (!buf)
+ return;
+ pos = 0;
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+ if (ptr->answer)
+ continue;
+ if (pos++ != head->r.query_index)
+ continue;
+ /*
+ * Some query can be skipped because tomoyo_query_list
+ * can change, but I don't care.
+ */
+ if (len == ptr->query_len)
+ memmove(buf, ptr->query, len);
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (buf[0]) {
+ head->read_buf = buf;
+ head->r.w[head->r.w_pos++] = buf;
+ head->r.query_index++;
+ } else {
+ kfree(buf);
+ }
+}
+
+/**
+ * tomoyo_write_answer - Write the supervisor's decision.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ struct list_head *tmp;
+ unsigned int serial;
+ unsigned int answer;
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+ ptr->timer = 0;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (sscanf(data, "A%u=%u", &serial, &answer) != 2)
+ return -EINVAL;
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+ if (ptr->serial != serial)
+ continue;
+ if (!ptr->answer)
+ ptr->answer = answer;
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ return 0;
+}
+
+/**
+ * tomoyo_read_version: Get version.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns version information.
+ */
+static void tomoyo_read_version(struct tomoyo_io_buffer *head)
+{
+ if (!head->r.eof) {
+ tomoyo_io_printf(head, "2.3.0");
+ head->r.eof = true;
+ }
+}
+
+/**
+ * tomoyo_read_self_domain - Get the current process's domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the current process's domainname.
+ */
+static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
+{
+ if (!head->r.eof) {
+ /*
+ * tomoyo_domain()->domainname != NULL
+ * because every process belongs to a domain and
+ * the domain's name cannot be NULL.
+ */
+ tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
+ head->r.eof = true;
+ }
+}
+
+/**
+ * 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().
+ */
+int tomoyo_open_control(const u8 type, struct file *file)
+{
+ struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS);
+
+ if (!head)
+ return -ENOMEM;
+ mutex_init(&head->io_sem);
+ head->type = type;
+ switch (type) {
+ case TOMOYO_DOMAINPOLICY:
+ /* /sys/kernel/security/tomoyo/domain_policy */
+ head->write = tomoyo_write_domain;
+ head->read = tomoyo_read_domain;
+ break;
+ case TOMOYO_EXCEPTIONPOLICY:
+ /* /sys/kernel/security/tomoyo/exception_policy */
+ head->write = tomoyo_write_exception;
+ head->read = tomoyo_read_exception;
+ 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;
+ head->read = tomoyo_read_pid;
+ break;
+ case TOMOYO_VERSION:
+ /* /sys/kernel/security/tomoyo/version */
+ 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;
+ break;
+ case TOMOYO_PROFILE:
+ /* /sys/kernel/security/tomoyo/profile */
+ head->write = tomoyo_write_profile;
+ head->read = tomoyo_read_profile;
+ break;
+ case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */
+ head->poll = tomoyo_poll_query;
+ head->write = tomoyo_write_answer;
+ head->read = tomoyo_read_query;
+ break;
+ case TOMOYO_MANAGER:
+ /* /sys/kernel/security/tomoyo/manager */
+ head->write = tomoyo_write_manager;
+ head->read = tomoyo_read_manager;
+ break;
+ }
+ if (!(file->f_mode & FMODE_READ)) {
+ /*
+ * No need to allocate read_buf since it is not opened
+ * for reading.
+ */
+ head->read = NULL;
+ head->poll = NULL;
+ } else if (!head->poll) {
+ /* Don't allocate read_buf for poll() access. */
+ if (!head->readbuf_size)
+ head->readbuf_size = 4096 * 2;
+ head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS);
+ if (!head->read_buf) {
+ kfree(head);
+ return -ENOMEM;
+ }
+ }
+ if (!(file->f_mode & FMODE_WRITE)) {
+ /*
+ * No need to allocate write_buf since it is not opened
+ * for writing.
+ */
+ head->write = NULL;
+ } else if (head->write) {
+ head->writebuf_size = 4096 * 2;
+ head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS);
+ if (!head->write_buf) {
+ kfree(head->read_buf);
+ kfree(head);
+ 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)
+ atomic_inc(&tomoyo_query_observers);
+ return 0;
+}
+
+/**
+ * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Waits for read readiness.
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd .
+ */
+int tomoyo_poll_control(struct file *file, poll_table *wait)
+{
+ struct tomoyo_io_buffer *head = file->private_data;
+ if (!head->poll)
+ return -ENOSYS;
+ return head->poll(file, wait);
+}
+
+/**
+ * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @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)
+{
+ int len;
+ struct tomoyo_io_buffer *head = file->private_data;
+
+ if (!head->read)
+ return -ENOSYS;
+ if (mutex_lock_interruptible(&head->io_sem))
+ return -EINTR;
+ head->read_user_buf = buffer;
+ head->read_user_buf_avail = buffer_len;
+ if (tomoyo_flush(head))
+ /* Call the policy handler. */
+ head->read(head);
+ tomoyo_flush(head);
+ len = head->read_user_buf - buffer;
+ mutex_unlock(&head->io_sem);
+ return len;
+}
+
+/**
+ * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @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)
+{
+ struct tomoyo_io_buffer *head = file->private_data;
+ int error = buffer_len;
+ int avail_len = buffer_len;
+ char *cp0 = head->write_buf;
+
+ 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;
+ /* 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)) {
+ error = -EFAULT;
+ break;
+ }
+ buffer++;
+ avail_len--;
+ cp0[head->write_avail++] = c;
+ if (c != '\n')
+ continue;
+ cp0[head->write_avail - 1] = '\0';
+ head->write_avail = 0;
+ tomoyo_normalize_line(cp0);
+ head->write(head);
+ }
+ mutex_unlock(&head->io_sem);
+ return error;
+}
+
+/**
+ * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ *
+ * Releases memory and returns 0.
+ *
+ * Caller looses tomoyo_read_lock().
+ */
+int tomoyo_close_control(struct file *file)
+{
+ 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();
+ return 0;
+}
+
+/**
+ * tomoyo_check_profile - Check all profiles currently assigned to domains are defined.
+ */
+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. */
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ const u8 profile = domain->profile;
+ if (tomoyo_profile_ptr[profile])
+ 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/ "
+ "for more information.\n");
+ panic("Profile %u (used by '%s') not defined.\n",
+ profile, domain->domainname->name);
+ }
+ 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");
+}
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
new file mode 100644
index 00000000..7c66bd89
--- /dev/null
+++ b/security/tomoyo/common.h
@@ -0,0 +1,1015 @@
+/*
+ * security/tomoyo/common.h
+ *
+ * Header file for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#ifndef _SECURITY_TOMOYO_COMMON_H
+#define _SECURITY_TOMOYO_COMMON_H
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/kmod.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+#include <linux/cred.h>
+#include <linux/poll.h>
+struct linux_binprm;
+
+/********** Constants definitions. **********/
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_HASH_BITS 8
+#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
+
+#define TOMOYO_EXEC_TMPSIZE 4096
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+enum tomoyo_mode_index {
+ TOMOYO_CONFIG_DISABLED,
+ TOMOYO_CONFIG_LEARNING,
+ TOMOYO_CONFIG_PERMISSIVE,
+ TOMOYO_CONFIG_ENFORCING,
+ TOMOYO_CONFIG_USE_DEFAULT = 255
+};
+
+enum tomoyo_policy_id {
+ TOMOYO_ID_GROUP,
+ TOMOYO_ID_PATH_GROUP,
+ TOMOYO_ID_NUMBER_GROUP,
+ TOMOYO_ID_TRANSITION_CONTROL,
+ TOMOYO_ID_AGGREGATOR,
+ TOMOYO_ID_GLOBALLY_READABLE,
+ TOMOYO_ID_PATTERN,
+ TOMOYO_ID_NO_REWRITE,
+ TOMOYO_ID_MANAGER,
+ TOMOYO_ID_NAME,
+ TOMOYO_ID_ACL,
+ TOMOYO_ID_DOMAIN,
+ TOMOYO_MAX_POLICY
+};
+
+enum tomoyo_group_id {
+ TOMOYO_PATH_GROUP,
+ TOMOYO_NUMBER_GROUP,
+ TOMOYO_MAX_GROUP
+};
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_AGGREGATOR "aggregator "
+#define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount "
+#define TOMOYO_KEYWORD_ALLOW_READ "allow_read "
+#define TOMOYO_KEYWORD_DELETE "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain "
+#define TOMOYO_KEYWORD_PATH_GROUP "path_group "
+#define TOMOYO_KEYWORD_NUMBER_GROUP "number_group "
+#define TOMOYO_KEYWORD_SELECT "select "
+#define TOMOYO_KEYWORD_USE_PROFILE "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read"
+#define TOMOYO_KEYWORD_QUOTA_EXCEEDED "quota_exceeded"
+#define TOMOYO_KEYWORD_TRANSITION_FAILED "transition_failed"
+/* A domain definition starts with <kernel>. */
+#define TOMOYO_ROOT_NAME "<kernel>"
+#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/* Value type definition. */
+#define TOMOYO_VALUE_TYPE_INVALID 0
+#define TOMOYO_VALUE_TYPE_DECIMAL 1
+#define TOMOYO_VALUE_TYPE_OCTAL 2
+#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
+
+enum tomoyo_transition_type {
+ /* Do not change this order, */
+ TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE,
+ TOMOYO_TRANSITION_CONTROL_INITIALIZE,
+ TOMOYO_TRANSITION_CONTROL_NO_KEEP,
+ TOMOYO_TRANSITION_CONTROL_KEEP,
+ TOMOYO_MAX_TRANSITION_TYPE
+};
+
+/* Index numbers for Access Controls. */
+enum tomoyo_acl_entry_type_index {
+ TOMOYO_TYPE_PATH_ACL,
+ TOMOYO_TYPE_PATH2_ACL,
+ TOMOYO_TYPE_PATH_NUMBER_ACL,
+ TOMOYO_TYPE_MKDEV_ACL,
+ TOMOYO_TYPE_MOUNT_ACL,
+};
+
+/* Index numbers for File Controls. */
+
+/*
+ * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically
+ * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set.
+ * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if
+ * TOMOYO_TYPE_READ_WRITE is set.
+ * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ
+ * or TOMOYO_TYPE_WRITE is cleared.
+ * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if
+ * TOMOYO_TYPE_READ_WRITE is cleared.
+ */
+
+enum tomoyo_path_acl_index {
+ TOMOYO_TYPE_READ_WRITE,
+ TOMOYO_TYPE_EXECUTE,
+ TOMOYO_TYPE_READ,
+ TOMOYO_TYPE_WRITE,
+ TOMOYO_TYPE_UNLINK,
+ TOMOYO_TYPE_RMDIR,
+ TOMOYO_TYPE_TRUNCATE,
+ TOMOYO_TYPE_SYMLINK,
+ TOMOYO_TYPE_REWRITE,
+ TOMOYO_TYPE_CHROOT,
+ TOMOYO_TYPE_UMOUNT,
+ TOMOYO_MAX_PATH_OPERATION
+};
+
+#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE))
+
+enum tomoyo_mkdev_acl_index {
+ TOMOYO_TYPE_MKBLOCK,
+ TOMOYO_TYPE_MKCHAR,
+ TOMOYO_MAX_MKDEV_OPERATION
+};
+
+enum tomoyo_path2_acl_index {
+ TOMOYO_TYPE_LINK,
+ TOMOYO_TYPE_RENAME,
+ TOMOYO_TYPE_PIVOT_ROOT,
+ TOMOYO_MAX_PATH2_OPERATION
+};
+
+enum tomoyo_path_number_acl_index {
+ TOMOYO_TYPE_CREATE,
+ TOMOYO_TYPE_MKDIR,
+ TOMOYO_TYPE_MKFIFO,
+ TOMOYO_TYPE_MKSOCK,
+ TOMOYO_TYPE_IOCTL,
+ TOMOYO_TYPE_CHMOD,
+ TOMOYO_TYPE_CHOWN,
+ TOMOYO_TYPE_CHGRP,
+ TOMOYO_MAX_PATH_NUMBER_OPERATION
+};
+
+enum tomoyo_securityfs_interface_index {
+ TOMOYO_DOMAINPOLICY,
+ TOMOYO_EXCEPTIONPOLICY,
+ TOMOYO_DOMAIN_STATUS,
+ TOMOYO_PROCESS_STATUS,
+ TOMOYO_MEMINFO,
+ TOMOYO_SELFDOMAIN,
+ TOMOYO_VERSION,
+ TOMOYO_PROFILE,
+ TOMOYO_QUERY,
+ TOMOYO_MANAGER
+};
+
+enum tomoyo_mac_index {
+ TOMOYO_MAC_FILE_EXECUTE,
+ TOMOYO_MAC_FILE_OPEN,
+ TOMOYO_MAC_FILE_CREATE,
+ TOMOYO_MAC_FILE_UNLINK,
+ TOMOYO_MAC_FILE_MKDIR,
+ TOMOYO_MAC_FILE_RMDIR,
+ TOMOYO_MAC_FILE_MKFIFO,
+ TOMOYO_MAC_FILE_MKSOCK,
+ TOMOYO_MAC_FILE_TRUNCATE,
+ TOMOYO_MAC_FILE_SYMLINK,
+ TOMOYO_MAC_FILE_REWRITE,
+ TOMOYO_MAC_FILE_MKBLOCK,
+ TOMOYO_MAC_FILE_MKCHAR,
+ TOMOYO_MAC_FILE_LINK,
+ TOMOYO_MAC_FILE_RENAME,
+ TOMOYO_MAC_FILE_CHMOD,
+ TOMOYO_MAC_FILE_CHOWN,
+ TOMOYO_MAC_FILE_CHGRP,
+ TOMOYO_MAC_FILE_IOCTL,
+ TOMOYO_MAC_FILE_CHROOT,
+ TOMOYO_MAC_FILE_MOUNT,
+ TOMOYO_MAC_FILE_UMOUNT,
+ TOMOYO_MAC_FILE_PIVOT_ROOT,
+ TOMOYO_MAX_MAC_INDEX
+};
+
+enum tomoyo_mac_category_index {
+ TOMOYO_MAC_CATEGORY_FILE,
+ TOMOYO_MAX_MAC_CATEGORY_INDEX
+};
+
+#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */
+
+/********** Structure definitions. **********/
+
+/*
+ * tomoyo_acl_head is a structure which is used for holding elements not in
+ * domain policy.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_policy_list[] .
+ * (2) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_acl_head {
+ struct list_head list;
+ bool is_deleted;
+} __packed;
+
+/*
+ * tomoyo_request_info is a structure which is used for holding
+ *
+ * (1) Domain information of current process.
+ * (2) How many retries are made for this request.
+ * (3) Profile number used for this request.
+ * (4) Access control mode of the profile.
+ */
+struct tomoyo_request_info {
+ struct tomoyo_domain_info *domain;
+ /* For holding parameters. */
+ union {
+ struct {
+ const struct tomoyo_path_info *filename;
+ /* For using wildcards at tomoyo_find_next_domain(). */
+ const struct tomoyo_path_info *matched_path;
+ u8 operation;
+ } path;
+ struct {
+ const struct tomoyo_path_info *filename1;
+ const struct tomoyo_path_info *filename2;
+ u8 operation;
+ } path2;
+ struct {
+ const struct tomoyo_path_info *filename;
+ unsigned int mode;
+ unsigned int major;
+ unsigned int minor;
+ u8 operation;
+ } mkdev;
+ struct {
+ const struct tomoyo_path_info *filename;
+ unsigned long number;
+ u8 operation;
+ } path_number;
+ struct {
+ const struct tomoyo_path_info *type;
+ const struct tomoyo_path_info *dir;
+ const struct tomoyo_path_info *dev;
+ unsigned long flags;
+ int need_dev;
+ } mount;
+ } param;
+ u8 param_type;
+ bool granted;
+ u8 retry;
+ u8 profile;
+ u8 mode; /* One of tomoyo_mode_index . */
+ u8 type;
+};
+
+/*
+ * tomoyo_path_info is a structure which is used for holding a string data
+ * used by TOMOYO.
+ * This structure has several fields for supporting pattern matching.
+ *
+ * (1) "name" is the '\0' terminated string data.
+ * (2) "hash" is full_name_hash(name, strlen(name)).
+ * This allows tomoyo_pathcmp() to compare by hash before actually compare
+ * using strcmp().
+ * (3) "const_len" is the length of the initial segment of "name" which
+ * consists entirely of non wildcard characters. In other words, the length
+ * which we can compare two strings using strncmp().
+ * (4) "is_dir" is a bool which is true if "name" ends with "/",
+ * false otherwise.
+ * TOMOYO distinguishes directory and non-directory. A directory ends with
+ * "/" and non-directory does not end with "/".
+ * (5) "is_patterned" is a bool which is true if "name" contains wildcard
+ * characters, false otherwise. This allows TOMOYO to use "hash" and
+ * strcmp() for string comparison if "is_patterned" is false.
+ */
+struct tomoyo_path_info {
+ const char *name;
+ u32 hash; /* = full_name_hash(name, strlen(name)) */
+ u16 const_len; /* = tomoyo_const_part_length(name) */
+ bool is_dir; /* = tomoyo_strendswith(name, "/") */
+ bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
+};
+
+/*
+ * tomoyo_name is a structure which is used for linking
+ * "struct tomoyo_path_info" into tomoyo_name_list .
+ */
+struct tomoyo_name {
+ struct list_head list;
+ atomic_t users;
+ struct tomoyo_path_info entry;
+};
+
+struct tomoyo_name_union {
+ const struct tomoyo_path_info *filename;
+ struct tomoyo_group *group;
+ u8 is_group;
+};
+
+struct tomoyo_number_union {
+ unsigned long values[2];
+ struct tomoyo_group *group;
+ u8 min_type;
+ u8 max_type;
+ u8 is_group;
+};
+
+/* Structure for "path_group"/"number_group" directive. */
+struct tomoyo_group {
+ struct list_head list;
+ const struct tomoyo_path_info *group_name;
+ struct list_head member_list;
+ atomic_t users;
+};
+
+/* Structure for "path_group" directive. */
+struct tomoyo_path_group {
+ struct tomoyo_acl_head head;
+ const struct tomoyo_path_info *member_name;
+};
+
+/* Structure for "number_group" directive. */
+struct tomoyo_number_group {
+ struct tomoyo_acl_head head;
+ struct tomoyo_number_union number;
+};
+
+/*
+ * tomoyo_acl_info is a structure which is used for holding
+ *
+ * (1) "list" which is linked to the ->acl_info_list of
+ * "struct tomoyo_domain_info"
+ * (2) "is_deleted" is a bool which is true if this domain is marked as
+ * "deleted", false otherwise.
+ * (3) "type" which tells type of the entry.
+ *
+ * Packing "struct tomoyo_acl_info" allows
+ * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl"
+ * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed
+ * "u8" without enlarging their structure size.
+ */
+struct tomoyo_acl_info {
+ struct list_head list;
+ bool is_deleted;
+ u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */
+} __packed;
+
+/*
+ * tomoyo_domain_info is a structure which is used for holding permissions
+ * (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_list .
+ * (2) "acl_info_list" which is linked to "struct tomoyo_acl_info".
+ * (3) "domainname" which holds the name of the domain.
+ * (4) "profile" which remembers profile number assigned to this domain.
+ * (5) "is_deleted" is a bool which is true if this domain is marked as
+ * "deleted", false otherwise.
+ * (6) "quota_warned" is a bool which is used for suppressing warning message
+ * when learning mode learned too much entries.
+ * (7) "ignore_global_allow_read" is a bool which is true if this domain
+ * should ignore "allow_read" directive in exception policy.
+ * (8) "transition_failed" is a bool which is set to true when this domain was
+ * unable to create a new domain at tomoyo_find_next_domain() because the
+ * name of the domain to be created was too long or it could not allocate
+ * memory. If set to true, more than one process continued execve()
+ * without domain transition.
+ * (9) "users" is an atomic_t that holds how many "struct cred"->security
+ * are referring this "struct tomoyo_domain_info". If is_deleted == true
+ * and users == 0, this struct will be kfree()d upon next garbage
+ * collection.
+ *
+ * A domain's lifecycle is an analogy of files on / directory.
+ * Multiple domains with the same domainname cannot be created (as with
+ * creating files with the same filename fails with -EEXIST).
+ * If a process reached a domain, that process can reside in that domain after
+ * that domain is marked as "deleted" (as with a process can access an already
+ * open()ed file after that file was unlink()ed).
+ */
+struct tomoyo_domain_info {
+ struct list_head list;
+ struct list_head acl_info_list;
+ /* Name of this domain. Never NULL. */
+ const struct tomoyo_path_info *domainname;
+ u8 profile; /* Profile number to use. */
+ bool is_deleted; /* Delete flag. */
+ bool quota_warned; /* Quota warnning flag. */
+ bool ignore_global_allow_read; /* Ignore "allow_read" flag. */
+ bool transition_failed; /* Domain transition failed flag. */
+ atomic_t users; /* Number of referring credentials. */
+};
+
+/*
+ * tomoyo_path_acl is a structure which is used for holding an
+ * entry with one pathname operation (e.g. open(), mkdir()).
+ * It has following fields.
+ *
+ * (1) "head" which is a "struct tomoyo_acl_info".
+ * (2) "perm" which is a bitmask of permitted operations.
+ * (3) "name" is the pathname.
+ *
+ * Directives held by this structure are "allow_read/write", "allow_execute",
+ * "allow_read", "allow_write", "allow_unlink", "allow_rmdir",
+ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and
+ * "allow_unmount".
+ */
+struct tomoyo_path_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
+ u16 perm;
+ struct tomoyo_name_union name;
+};
+
+/*
+ * tomoyo_path_number_acl is a structure which is used for holding an
+ * entry with one pathname and one number operation.
+ * It has following fields.
+ *
+ * (1) "head" which is a "struct tomoyo_acl_info".
+ * (2) "perm" which is a bitmask of permitted operations.
+ * (3) "name" is the pathname.
+ * (4) "number" is the numeric value.
+ *
+ * Directives held by this structure are "allow_create", "allow_mkdir",
+ * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown"
+ * and "allow_chgrp".
+ *
+ */
+struct tomoyo_path_number_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */
+ u8 perm;
+ struct tomoyo_name_union name;
+ struct tomoyo_number_union number;
+};
+
+/*
+ * tomoyo_mkdev_acl is a structure which is used for holding an
+ * entry with one pathname and three numbers operation.
+ * It has following fields.
+ *
+ * (1) "head" which is a "struct tomoyo_acl_info".
+ * (2) "perm" which is a bitmask of permitted operations.
+ * (3) "mode" is the create mode.
+ * (4) "major" is the major number of device node.
+ * (5) "minor" is the minor number of device node.
+ *
+ * Directives held by this structure are "allow_mkchar", "allow_mkblock".
+ *
+ */
+struct tomoyo_mkdev_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */
+ u8 perm;
+ struct tomoyo_name_union name;
+ struct tomoyo_number_union mode;
+ struct tomoyo_number_union major;
+ struct tomoyo_number_union minor;
+};
+
+/*
+ * tomoyo_path2_acl is a structure which is used for holding an
+ * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
+ * It has following fields.
+ *
+ * (1) "head" which is a "struct tomoyo_acl_info".
+ * (2) "perm" which is a bitmask of permitted operations.
+ * (3) "name1" is the source/old pathname.
+ * (4) "name2" is the destination/new pathname.
+ *
+ * Directives held by this structure are "allow_rename", "allow_link" and
+ * "allow_pivot_root".
+ */
+struct tomoyo_path2_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
+ u8 perm;
+ struct tomoyo_name_union name1;
+ struct tomoyo_name_union name2;
+};
+
+/*
+ * tomoyo_mount_acl is a structure which is used for holding an
+ * entry for mount operation.
+ * It has following fields.
+ *
+ * (1) "head" which is a "struct tomoyo_acl_info".
+ * (2) "dev_name" is the device name.
+ * (3) "dir_name" is the mount point.
+ * (4) "fs_type" is the filesystem type.
+ * (5) "flags" is the mount flags.
+ *
+ * Directive held by this structure is "allow_mount".
+ */
+struct tomoyo_mount_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */
+ struct tomoyo_name_union dev_name;
+ struct tomoyo_name_union dir_name;
+ struct tomoyo_name_union fs_type;
+ struct tomoyo_number_union flags;
+};
+
+#define TOMOYO_MAX_IO_READ_QUEUE 32
+
+/*
+ * Structure for reading/writing policy via /sys/kernel/security/tomoyo
+ * interfaces.
+ */
+struct tomoyo_io_buffer {
+ void (*read) (struct tomoyo_io_buffer *);
+ int (*write) (struct tomoyo_io_buffer *);
+ int (*poll) (struct file *file, poll_table *wait);
+ /* Exclusive lock for this structure. */
+ struct mutex io_sem;
+ /* Index returned by tomoyo_read_lock(). */
+ int reader_idx;
+ char __user *read_user_buf;
+ int read_user_buf_avail;
+ struct {
+ struct list_head *domain;
+ struct list_head *group;
+ struct list_head *acl;
+ int avail;
+ int step;
+ int query_index;
+ u16 index;
+ u8 bit;
+ u8 w_pos;
+ bool eof;
+ bool print_this_domain_only;
+ bool print_execute_only;
+ const char *w[TOMOYO_MAX_IO_READ_QUEUE];
+ } r;
+ /* The position currently writing to. */
+ struct tomoyo_domain_info *write_var1;
+ /* Buffer for reading. */
+ char *read_buf;
+ /* Size of read buffer. */
+ int readbuf_size;
+ /* Buffer for writing. */
+ char *write_buf;
+ /* Bytes available for writing. */
+ int write_avail;
+ /* Size of write buffer. */
+ int writebuf_size;
+ /* Type of this interface. */
+ u8 type;
+};
+
+/*
+ * tomoyo_readable_file is a structure which is used for holding
+ * "allow_read" entries.
+ * It has following fields.
+ *
+ * (1) "head" is "struct tomoyo_acl_head".
+ * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
+ */
+struct tomoyo_readable_file {
+ struct tomoyo_acl_head head;
+ const struct tomoyo_path_info *filename;
+};
+
+/*
+ * tomoyo_no_pattern is a structure which is used for holding
+ * "file_pattern" entries.
+ * It has following fields.
+ *
+ * (1) "head" is "struct tomoyo_acl_head".
+ * (2) "pattern" is a pathname pattern which is used for converting pathnames
+ * to pathname patterns during learning mode.
+ */
+struct tomoyo_no_pattern {
+ struct tomoyo_acl_head head;
+ const struct tomoyo_path_info *pattern;
+};
+
+/*
+ * tomoyo_no_rewrite is a structure which is used for holding
+ * "deny_rewrite" entries.
+ * It has following fields.
+ *
+ * (1) "head" is "struct tomoyo_acl_head".
+ * (2) "pattern" is a pathname which is by default not permitted to modify
+ * already existing content.
+ */
+struct tomoyo_no_rewrite {
+ struct tomoyo_acl_head head;
+ const struct tomoyo_path_info *pattern;
+};
+
+/*
+ * tomoyo_transition_control is a structure which is used for holding
+ * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain"
+ * entries.
+ * It has following fields.
+ *
+ * (1) "head" is "struct tomoyo_acl_head".
+ * (2) "type" is type of this entry.
+ * (3) "is_last_name" is a bool which is true if "domainname" is "the last
+ * component of a domainname", false otherwise.
+ * (4) "domainname" which is "a domainname" or "the last component of a
+ * domainname".
+ * (5) "program" which is a program's pathname.
+ */
+struct tomoyo_transition_control {
+ struct tomoyo_acl_head head;
+ u8 type; /* One of values in "enum tomoyo_transition_type". */
+ /* True if the domainname is tomoyo_get_last_name(). */
+ bool is_last_name;
+ const struct tomoyo_path_info *domainname; /* Maybe NULL */
+ const struct tomoyo_path_info *program; /* Maybe NULL */
+};
+
+/*
+ * tomoyo_aggregator is a structure which is used for holding
+ * "aggregator" entries.
+ * It has following fields.
+ *
+ * (1) "head" is "struct tomoyo_acl_head".
+ * (2) "original_name" which is originally requested name.
+ * (3) "aggregated_name" which is name to rewrite.
+ */
+struct tomoyo_aggregator {
+ struct tomoyo_acl_head head;
+ const struct tomoyo_path_info *original_name;
+ const struct tomoyo_path_info *aggregated_name;
+};
+
+/*
+ * tomoyo_manager is a structure which is used for holding list of
+ * domainnames or programs which are permitted to modify configuration via
+ * /sys/kernel/security/tomoyo/ interface.
+ * It has following fields.
+ *
+ * (1) "head" is "struct tomoyo_acl_head".
+ * (2) "is_domain" is a bool which is true if "manager" is a domainname, false
+ * otherwise.
+ * (3) "manager" is a domainname or a program's pathname.
+ */
+struct tomoyo_manager {
+ struct tomoyo_acl_head head;
+ bool is_domain; /* True if manager is a domainname. */
+ /* A path to program or a domainname. */
+ const struct tomoyo_path_info *manager;
+};
+
+struct tomoyo_preference {
+ unsigned int learning_max_entry;
+ bool enforcing_verbose;
+ bool learning_verbose;
+ bool permissive_verbose;
+};
+
+struct tomoyo_profile {
+ const struct tomoyo_path_info *comment;
+ struct tomoyo_preference *learning;
+ struct tomoyo_preference *permissive;
+ struct tomoyo_preference *enforcing;
+ struct tomoyo_preference preference;
+ u8 default_config;
+ u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+};
+
+/********** Function prototypes. **********/
+
+/* Check whether the given string starts with the given keyword. */
+bool tomoyo_str_starts(char **src, const char *find);
+/* Get tomoyo_realpath() of current process. */
+const char *tomoyo_get_exe(void);
+/* Format string. */
+void tomoyo_normalize_line(unsigned char *buffer);
+/* Print warning or error message on console. */
+void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+/* Check all profiles currently assigned to domains are defined. */
+void tomoyo_check_profile(void);
+/* Open operation for /sys/kernel/security/tomoyo/ interface. */
+int tomoyo_open_control(const u8 type, struct file *file);
+/* Close /sys/kernel/security/tomoyo/ interface. */
+int tomoyo_close_control(struct file *file);
+/* Poll operation for /sys/kernel/security/tomoyo/ interface. */
+int tomoyo_poll_control(struct file *file, poll_table *wait);
+/* Read operation for /sys/kernel/security/tomoyo/ interface. */
+int tomoyo_read_control(struct file *file, char __user *buffer,
+ const int buffer_len);
+/* Write operation for /sys/kernel/security/tomoyo/ interface. */
+int tomoyo_write_control(struct file *file, const char __user *buffer,
+ const int buffer_len);
+/* Check whether the domain has too many ACL entries to hold. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
+/* Print out of memory warning message. */
+void tomoyo_warn_oom(const char *function);
+/* Check whether the given name matches the given name_union. */
+const struct tomoyo_path_info *
+tomoyo_compare_name_union(const struct tomoyo_path_info *name,
+ const struct tomoyo_name_union *ptr);
+/* Check whether the given number matches the given number_union. */
+bool tomoyo_compare_number_union(const unsigned long value,
+ const struct tomoyo_number_union *ptr);
+int tomoyo_get_mode(const u8 profile, const u8 index);
+void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+/* Check whether the domainname is correct. */
+bool tomoyo_correct_domain(const unsigned char *domainname);
+/* Check whether the token is correct. */
+bool tomoyo_correct_path(const char *filename);
+bool tomoyo_correct_word(const char *string);
+/* Check whether the token can be a domainname. */
+bool tomoyo_domain_def(const unsigned char *buffer);
+bool tomoyo_parse_name_union(const char *filename,
+ struct tomoyo_name_union *ptr);
+/* Check whether the given filename matches the given path_group. */
+const struct tomoyo_path_info *
+tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
+ const struct tomoyo_group *group);
+/* Check whether the given value matches the given number_group. */
+bool tomoyo_number_matches_group(const unsigned long min,
+ const unsigned long max,
+ const struct tomoyo_group *group);
+/* Check whether the given filename matches the given pattern. */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+ const struct tomoyo_path_info *pattern);
+
+bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num);
+/* Tokenize a line. */
+bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
+/* Write domain policy violation warning message to console? */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
+/* Fill "struct tomoyo_request_info". */
+int tomoyo_init_request_info(struct tomoyo_request_info *r,
+ struct tomoyo_domain_info *domain,
+ const u8 index);
+/* Check permission for mount operation. */
+int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
+ unsigned long flags, void *data_page);
+/* Create "aggregator" entry in exception policy. */
+int tomoyo_write_aggregator(char *data, const bool is_delete);
+int tomoyo_write_transition_control(char *data, const bool is_delete,
+ const u8 type);
+/*
+ * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
+ * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
+ * "allow_link" entry in domain policy.
+ */
+int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
+ const bool is_delete);
+/* Create "allow_read" entry in exception policy. */
+int tomoyo_write_globally_readable(char *data, const bool is_delete);
+/* Create "allow_mount" entry in domain policy. */
+int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
+ const bool is_delete);
+/* Create "deny_rewrite" entry in exception policy. */
+int tomoyo_write_no_rewrite(char *data, const bool is_delete);
+/* Create "file_pattern" entry in exception policy. */
+int tomoyo_write_pattern(char *data, const bool is_delete);
+/* Create "path_group"/"number_group" entry in exception policy. */
+int tomoyo_write_group(char *data, const bool is_delete, const u8 type);
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+/* Find a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+/* Find or create a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
+ const u8 profile);
+struct tomoyo_profile *tomoyo_profile(const u8 profile);
+/*
+ * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
+ */
+struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type);
+
+/* Check mode for specified functionality. */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+ const u8 index);
+/* Fill in "struct tomoyo_path_info" members. */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+/* Run policy loader when /sbin/init starts. */
+void tomoyo_load_policy(const char *filename);
+
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
+
+/* Convert binary string to ascii string. */
+char *tomoyo_encode(const char *str);
+
+/*
+ * Returns realpath(3) of the given pathname except that
+ * ignores chroot'ed root and does not follow the final symlink.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname);
+/*
+ * Returns realpath(3) of the given pathname except that
+ * ignores chroot'ed root and the pathname is already solved.
+ */
+char *tomoyo_realpath_from_path(struct path *path);
+/* Get patterned pathname. */
+const char *tomoyo_pattern(const struct tomoyo_path_info *filename);
+
+/* Check memory quota. */
+bool tomoyo_memory_ok(void *ptr);
+void *tomoyo_commit_ok(void *data, const unsigned int size);
+
+/*
+ * Keep the given name on the RAM.
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_get_name(const char *name);
+
+/* Check for memory usage. */
+void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+
+/* Set memory quota. */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+
+/* Initialize mm related code. */
+void __init tomoyo_mm_init(void);
+int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
+ const struct tomoyo_path_info *filename);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+ struct path *path, const int flag);
+int tomoyo_path_number_perm(const u8 operation, struct path *path,
+ unsigned long number);
+int tomoyo_mkdev_perm(const u8 operation, struct path *path,
+ const unsigned int mode, unsigned int dev);
+int tomoyo_path_perm(const u8 operation, struct path *path);
+int tomoyo_path2_perm(const u8 operation, struct path *path1,
+ struct path *path2);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
+
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+ const unsigned long value, const u8 type);
+
+/* Drop refcount on tomoyo_name_union. */
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
+
+/* Run garbage collector. */
+void tomoyo_run_gc(void);
+
+void tomoyo_memory_free(void *ptr);
+
+int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
+ bool is_delete, struct tomoyo_domain_info *domain,
+ bool (*check_duplicate) (const struct tomoyo_acl_info
+ *,
+ const struct tomoyo_acl_info
+ *),
+ bool (*merge_duplicate) (struct tomoyo_acl_info *,
+ struct tomoyo_acl_info *,
+ const bool));
+int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
+ bool is_delete, struct list_head *list,
+ bool (*check_duplicate) (const struct tomoyo_acl_head
+ *,
+ const struct tomoyo_acl_head
+ *));
+void tomoyo_check_acl(struct tomoyo_request_info *r,
+ bool (*check_entry) (struct tomoyo_request_info *,
+ const struct tomoyo_acl_info *));
+
+/********** External variable definitions. **********/
+
+/* Lock for GC. */
+extern struct srcu_struct tomoyo_ss;
+
+/* The list for "struct tomoyo_domain_info". */
+extern struct list_head tomoyo_domain_list;
+
+extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
+extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
+extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+
+/* Lock for protecting policy. */
+extern struct mutex tomoyo_policy_lock;
+
+/* Has /sbin/init started? */
+extern bool tomoyo_policy_loaded;
+
+/* The kernel's domain. */
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
+extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION];
+extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION];
+extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION];
+
+extern unsigned int tomoyo_quota_for_query;
+extern unsigned int tomoyo_query_memory_size;
+
+/********** Inlined functions. **********/
+
+static inline int tomoyo_read_lock(void)
+{
+ return srcu_read_lock(&tomoyo_ss);
+}
+
+static inline void tomoyo_read_unlock(int idx)
+{
+ srcu_read_unlock(&tomoyo_ss, idx);
+}
+
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+ const struct tomoyo_path_info *b)
+{
+ return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+/**
+ * tomoyo_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_valid(const unsigned char c)
+{
+ return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_invalid(const unsigned char c)
+{
+ return c && (c <= ' ' || c >= 127);
+}
+
+static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
+{
+ if (name) {
+ struct tomoyo_name *ptr =
+ container_of(name, typeof(*ptr), entry);
+ atomic_dec(&ptr->users);
+ }
+}
+
+static inline void tomoyo_put_group(struct tomoyo_group *group)
+{
+ if (group)
+ atomic_dec(&group->users);
+}
+
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+ return current_cred()->security;
+}
+
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+ *task)
+{
+ return task_cred_xxx(task, security);
+}
+
+static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1,
+ const struct tomoyo_acl_info *p2)
+{
+ return p1->type == p2->type;
+}
+
+static inline bool tomoyo_same_name_union
+(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2)
+{
+ return p1->filename == p2->filename && p1->group == p2->group &&
+ p1->is_group == p2->is_group;
+}
+
+static inline bool tomoyo_same_number_union
+(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2)
+{
+ return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1]
+ && p1->group == p2->group && p1->min_type == p2->min_type &&
+ p1->max_type == p2->max_type && p1->is_group == p2->is_group;
+}
+
+/**
+ * list_for_each_cookie - iterate over a list with cookie.
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_cookie(pos, head) \
+ if (!pos) \
+ pos = srcu_dereference((head)->next, &tomoyo_ss); \
+ for ( ; pos != (head); pos = srcu_dereference(pos->next, &tomoyo_ss))
+
+#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
new file mode 100644
index 00000000..35388408
--- /dev/null
+++ b/security/tomoyo/domain.c
@@ -0,0 +1,542 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Domain transition functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/binfmts.h>
+#include <linux/slab.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/**
+ * tomoyo_update_policy - Update an entry for exception policy.
+ *
+ * @new_entry: Pointer to "struct tomoyo_acl_info".
+ * @size: Size of @new_entry in bytes.
+ * @is_delete: True if it is a delete request.
+ * @list: Pointer to "struct list_head".
+ * @check_duplicate: Callback function to find duplicated entry.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
+ bool is_delete, struct list_head *list,
+ bool (*check_duplicate) (const struct tomoyo_acl_head
+ *,
+ const struct tomoyo_acl_head
+ *))
+{
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ struct tomoyo_acl_head *entry;
+
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return -ENOMEM;
+ list_for_each_entry_rcu(entry, list, list) {
+ if (!check_duplicate(entry, new_entry))
+ continue;
+ entry->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (error && !is_delete) {
+ entry = tomoyo_commit_ok(new_entry, size);
+ if (entry) {
+ list_add_tail_rcu(&entry->list, list);
+ error = 0;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ return error;
+}
+
+/**
+ * tomoyo_update_domain - Update an entry for domain policy.
+ *
+ * @new_entry: Pointer to "struct tomoyo_acl_info".
+ * @size: Size of @new_entry in bytes.
+ * @is_delete: True if it is a delete request.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @check_duplicate: Callback function to find duplicated entry.
+ * @merge_duplicate: Callback function to merge duplicated entry.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
+ bool is_delete, struct tomoyo_domain_info *domain,
+ bool (*check_duplicate) (const struct tomoyo_acl_info
+ *,
+ const struct tomoyo_acl_info
+ *),
+ bool (*merge_duplicate) (struct tomoyo_acl_info *,
+ struct tomoyo_acl_info *,
+ const bool))
+{
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ struct tomoyo_acl_info *entry;
+
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return error;
+ list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
+ if (!check_duplicate(entry, new_entry))
+ continue;
+ if (merge_duplicate)
+ entry->is_deleted = merge_duplicate(entry, new_entry,
+ is_delete);
+ else
+ entry->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (error && !is_delete) {
+ entry = tomoyo_commit_ok(new_entry, size);
+ if (entry) {
+ list_add_tail_rcu(&entry->list, &domain->acl_info_list);
+ error = 0;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ return error;
+}
+
+void tomoyo_check_acl(struct tomoyo_request_info *r,
+ bool (*check_entry) (struct tomoyo_request_info *,
+ const struct tomoyo_acl_info *))
+{
+ const struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ if (ptr->is_deleted || ptr->type != r->param_type)
+ continue;
+ if (check_entry(r, ptr)) {
+ r->granted = true;
+ return;
+ }
+ }
+ r->granted = false;
+}
+
+/* The list for "struct tomoyo_domain_info". */
+LIST_HEAD(tomoyo_domain_list);
+
+struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
+struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
+
+/**
+ * tomoyo_last_word - Get last component of a domainname.
+ *
+ * @domainname: Domainname to check.
+ *
+ * Returns the last word of @domainname.
+ */
+static const char *tomoyo_last_word(const char *name)
+{
+ const char *cp = strrchr(name, ' ');
+ if (cp)
+ return cp + 1;
+ return name;
+}
+
+static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ const struct tomoyo_transition_control *p1 = container_of(a,
+ typeof(*p1),
+ head);
+ const struct tomoyo_transition_control *p2 = container_of(b,
+ typeof(*p2),
+ head);
+ return p1->type == p2->type && p1->is_last_name == p2->is_last_name
+ && p1->domainname == p2->domainname
+ && p1->program == p2->program;
+}
+
+/**
+ * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list.
+ *
+ * @domainname: The name of domain. Maybe NULL.
+ * @program: The name of program. Maybe NULL.
+ * @type: Type of transition.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_transition_control_entry(const char *domainname,
+ const char *program,
+ const u8 type,
+ const bool is_delete)
+{
+ struct tomoyo_transition_control e = { .type = type };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (program) {
+ if (!tomoyo_correct_path(program))
+ return -EINVAL;
+ e.program = tomoyo_get_name(program);
+ if (!e.program)
+ goto out;
+ }
+ if (domainname) {
+ if (!tomoyo_correct_domain(domainname)) {
+ if (!tomoyo_correct_path(domainname))
+ goto out;
+ e.is_last_name = true;
+ }
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
+ goto out;
+ }
+ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
+ &tomoyo_policy_list
+ [TOMOYO_ID_TRANSITION_CONTROL],
+ tomoyo_same_transition_control);
+ out:
+ tomoyo_put_name(e.domainname);
+ tomoyo_put_name(e.program);
+ return error;
+}
+
+/**
+ * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ * @type: Type of this entry.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_transition_control(char *data, const bool is_delete,
+ const u8 type)
+{
+ char *domainname = strstr(data, " from ");
+ if (domainname) {
+ *domainname = '\0';
+ domainname += 6;
+ } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
+ type == TOMOYO_TRANSITION_CONTROL_KEEP) {
+ domainname = data;
+ data = NULL;
+ }
+ return tomoyo_update_transition_control_entry(domainname, data, type,
+ is_delete);
+}
+
+/**
+ * tomoyo_transition_type - Get domain transition type.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program.
+ *
+ * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program
+ * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing
+ * @program suppresses domain transition, others otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program)
+{
+ const struct tomoyo_transition_control *ptr;
+ const char *last_name = tomoyo_last_word(domainname->name);
+ u8 type;
+ for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) {
+ next:
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_list
+ [TOMOYO_ID_TRANSITION_CONTROL],
+ head.list) {
+ if (ptr->head.is_deleted || ptr->type != type)
+ continue;
+ if (ptr->domainname) {
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ /*
+ * Use direct strcmp() since this is
+ * unlikely used.
+ */
+ if (strcmp(ptr->domainname->name,
+ last_name))
+ continue;
+ }
+ }
+ if (ptr->program &&
+ tomoyo_pathcmp(ptr->program, program))
+ continue;
+ if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) {
+ /*
+ * Do not check for initialize_domain if
+ * no_initialize_domain matched.
+ */
+ type = TOMOYO_TRANSITION_CONTROL_NO_KEEP;
+ goto next;
+ }
+ goto done;
+ }
+ }
+ done:
+ return type;
+}
+
+static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head);
+ return p1->original_name == p2->original_name &&
+ p1->aggregated_name == p2->aggregated_name;
+}
+
+/**
+ * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list.
+ *
+ * @original_name: The original program's name.
+ * @aggregated_name: The program name to use.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_aggregator_entry(const char *original_name,
+ const char *aggregated_name,
+ const bool is_delete)
+{
+ struct tomoyo_aggregator e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+
+ if (!tomoyo_correct_path(original_name) ||
+ !tomoyo_correct_path(aggregated_name))
+ return -EINVAL;
+ e.original_name = tomoyo_get_name(original_name);
+ e.aggregated_name = tomoyo_get_name(aggregated_name);
+ if (!e.original_name || !e.aggregated_name ||
+ e.aggregated_name->is_patterned) /* No patterns allowed. */
+ goto out;
+ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
+ &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR],
+ tomoyo_same_aggregator);
+ out:
+ tomoyo_put_name(e.original_name);
+ tomoyo_put_name(e.aggregated_name);
+ return error;
+}
+
+/**
+ * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_aggregator(char *data, const bool is_delete)
+{
+ char *cp = strchr(data, ' ');
+
+ if (!cp)
+ return -EINVAL;
+ *cp++ = '\0';
+ return tomoyo_update_aggregator_entry(data, cp, is_delete);
+}
+
+/**
+ * tomoyo_assign_domain - Create a domain.
+ *
+ * @domainname: The name of domain.
+ * @profile: Profile number to assign if the domain was newly created.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
+ const u8 profile)
+{
+ struct tomoyo_domain_info *entry;
+ struct tomoyo_domain_info *domain = NULL;
+ const struct tomoyo_path_info *saved_domainname;
+ bool found = false;
+
+ if (!tomoyo_correct_domain(domainname))
+ return NULL;
+ saved_domainname = tomoyo_get_name(domainname);
+ if (!saved_domainname)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_NOFS);
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(saved_domainname, domain->domainname))
+ continue;
+ found = true;
+ break;
+ }
+ if (!found && tomoyo_memory_ok(entry)) {
+ INIT_LIST_HEAD(&entry->acl_info_list);
+ entry->domainname = saved_domainname;
+ saved_domainname = NULL;
+ entry->profile = profile;
+ list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
+ domain = entry;
+ entry = NULL;
+ found = true;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(saved_domainname);
+ kfree(entry);
+ return found ? domain : NULL;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_find_next_domain(struct linux_binprm *bprm)
+{
+ struct tomoyo_request_info r;
+ char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+ struct tomoyo_domain_info *old_domain = tomoyo_domain();
+ struct tomoyo_domain_info *domain = NULL;
+ const char *original_name = bprm->filename;
+ u8 mode;
+ bool is_enforce;
+ int retval = -ENOMEM;
+ bool need_kfree = false;
+ struct tomoyo_path_info rn = { }; /* real name */
+
+ mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+ is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
+ if (!tmp)
+ goto out;
+
+ retry:
+ if (need_kfree) {
+ kfree(rn.name);
+ need_kfree = false;
+ }
+ /* Get symlink's pathname of program. */
+ retval = -ENOENT;
+ rn.name = tomoyo_realpath_nofollow(original_name);
+ if (!rn.name)
+ goto out;
+ tomoyo_fill_path_info(&rn);
+ need_kfree = true;
+
+ /* Check 'aggregator' directive. */
+ {
+ struct tomoyo_aggregator *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_list
+ [TOMOYO_ID_AGGREGATOR], head.list) {
+ if (ptr->head.is_deleted ||
+ !tomoyo_path_matches_pattern(&rn,
+ ptr->original_name))
+ continue;
+ kfree(rn.name);
+ need_kfree = false;
+ /* This is OK because it is read only. */
+ rn = *ptr->aggregated_name;
+ break;
+ }
+ }
+
+ /* Check execute permission. */
+ retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
+ if (retval == TOMOYO_RETRY_REQUEST)
+ goto retry;
+ if (retval < 0)
+ goto out;
+ /*
+ * To be able to specify domainnames with wildcards, use the
+ * pathname specified in the policy (which may contain
+ * wildcard) rather than the pathname passed to execve()
+ * (which never contains wildcard).
+ */
+ if (r.param.path.matched_path) {
+ if (need_kfree)
+ kfree(rn.name);
+ need_kfree = false;
+ /* This is OK because it is read only. */
+ rn = *r.param.path.matched_path;
+ }
+
+ /* Calculate domain to transit to. */
+ switch (tomoyo_transition_type(old_domain->domainname, &rn)) {
+ case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
+ /* Transit to the child of tomoyo_kernel_domain domain. */
+ snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " "
+ "%s", rn.name);
+ break;
+ case TOMOYO_TRANSITION_CONTROL_KEEP:
+ /* Keep current domain. */
+ domain = old_domain;
+ break;
+ default:
+ if (old_domain == &tomoyo_kernel_domain &&
+ !tomoyo_policy_loaded) {
+ /*
+ * Needn't to transit from kernel domain before
+ * starting /sbin/init. But transit from kernel domain
+ * if executing initializers because they might start
+ * before /sbin/init.
+ */
+ domain = old_domain;
+ } else {
+ /* Normal domain transition. */
+ snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+ old_domain->domainname->name, rn.name);
+ }
+ break;
+ }
+ if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
+ goto done;
+ domain = tomoyo_find_domain(tmp);
+ if (domain)
+ goto done;
+ if (is_enforce) {
+ int error = tomoyo_supervisor(&r, "# wants to create domain\n"
+ "%s\n", tmp);
+ if (error == TOMOYO_RETRY_REQUEST)
+ goto retry;
+ if (error < 0)
+ goto done;
+ }
+ domain = tomoyo_assign_domain(tmp, old_domain->profile);
+ done:
+ if (domain)
+ goto out;
+ printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
+ if (is_enforce)
+ retval = -EPERM;
+ else
+ old_domain->transition_failed = true;
+ out:
+ if (!domain)
+ domain = old_domain;
+ /* Update reference count on "struct tomoyo_domain_info". */
+ atomic_inc(&domain->users);
+ bprm->cred->security = domain;
+ if (need_kfree)
+ kfree(rn.name);
+ kfree(tmp);
+ return retval;
+}
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
new file mode 100644
index 00000000..d64e8ecb
--- /dev/null
+++ b/security/tomoyo/file.c
@@ -0,0 +1,1176 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * Pathname restriction functions.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/* Keyword array for operations with one pathname. */
+const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_READ_WRITE] = "read/write",
+ [TOMOYO_TYPE_EXECUTE] = "execute",
+ [TOMOYO_TYPE_READ] = "read",
+ [TOMOYO_TYPE_WRITE] = "write",
+ [TOMOYO_TYPE_UNLINK] = "unlink",
+ [TOMOYO_TYPE_RMDIR] = "rmdir",
+ [TOMOYO_TYPE_TRUNCATE] = "truncate",
+ [TOMOYO_TYPE_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_REWRITE] = "rewrite",
+ [TOMOYO_TYPE_CHROOT] = "chroot",
+ [TOMOYO_TYPE_UMOUNT] = "unmount",
+};
+
+/* Keyword array for operations with one pathname and three numbers. */
+const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = {
+ [TOMOYO_TYPE_MKBLOCK] = "mkblock",
+ [TOMOYO_TYPE_MKCHAR] = "mkchar",
+};
+
+/* Keyword array for operations with two pathnames. */
+const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
+ [TOMOYO_TYPE_LINK] = "link",
+ [TOMOYO_TYPE_RENAME] = "rename",
+ [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
+};
+
+/* Keyword array for operations with one pathname and one number. */
+const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+ [TOMOYO_TYPE_CREATE] = "create",
+ [TOMOYO_TYPE_MKDIR] = "mkdir",
+ [TOMOYO_TYPE_MKFIFO] = "mkfifo",
+ [TOMOYO_TYPE_MKSOCK] = "mksock",
+ [TOMOYO_TYPE_IOCTL] = "ioctl",
+ [TOMOYO_TYPE_CHMOD] = "chmod",
+ [TOMOYO_TYPE_CHOWN] = "chown",
+ [TOMOYO_TYPE_CHGRP] = "chgrp",
+};
+
+static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE,
+ [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK,
+ [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR,
+ [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE,
+ [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK,
+ [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE,
+ [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT,
+ [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT,
+};
+
+static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
+ [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
+ [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR,
+};
+
+static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
+ [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK,
+ [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME,
+ [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
+};
+
+static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+ [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
+ [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR,
+ [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
+ [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK,
+ [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL,
+ [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD,
+ [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN,
+ [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP,
+};
+
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
+{
+ if (!ptr)
+ return;
+ if (ptr->is_group)
+ tomoyo_put_group(ptr->group);
+ else
+ tomoyo_put_name(ptr->filename);
+}
+
+const struct tomoyo_path_info *
+tomoyo_compare_name_union(const struct tomoyo_path_info *name,
+ const struct tomoyo_name_union *ptr)
+{
+ if (ptr->is_group)
+ return tomoyo_path_matches_group(name, ptr->group);
+ if (tomoyo_path_matches_pattern(name, ptr->filename))
+ return ptr->filename;
+ return NULL;
+}
+
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
+{
+ if (ptr && ptr->is_group)
+ tomoyo_put_group(ptr->group);
+}
+
+bool tomoyo_compare_number_union(const unsigned long value,
+ const struct tomoyo_number_union *ptr)
+{
+ if (ptr->is_group)
+ return tomoyo_number_matches_group(value, value, ptr->group);
+ return value >= ptr->values[0] && value <= ptr->values[1];
+}
+
+static void tomoyo_add_slash(struct tomoyo_path_info *buf)
+{
+ if (buf->is_dir)
+ return;
+ /*
+ * This is OK because tomoyo_encode() reserves space for appending "/".
+ */
+ strcat((char *) buf->name, "/");
+ tomoyo_fill_path_info(buf);
+}
+
+/**
+ * tomoyo_strendswith - Check whether the token ends with the given token.
+ *
+ * @name: The token to check.
+ * @tail: The token to find.
+ *
+ * Returns true if @name ends with @tail, false otherwise.
+ */
+static bool tomoyo_strendswith(const char *name, const char *tail)
+{
+ int len;
+
+ if (!name || !tail)
+ return false;
+ len = strlen(name) - strlen(tail);
+ return len >= 0 && !strcmp(name + len, tail);
+}
+
+/**
+ * tomoyo_get_realpath - Get realpath.
+ *
+ * @buf: Pointer to "struct tomoyo_path_info".
+ * @path: Pointer to "struct path".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)
+{
+ buf->name = tomoyo_realpath_from_path(path);
+ if (buf->name) {
+ tomoyo_fill_path_info(buf);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * tomoyo_audit_path_log - Audit path request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_path_log(struct tomoyo_request_info *r)
+{
+ const char *operation = tomoyo_path_keyword[r->param.path.operation];
+ const struct tomoyo_path_info *filename = r->param.path.filename;
+ if (r->granted)
+ return 0;
+ tomoyo_warn_log(r, "%s %s", operation, filename->name);
+ return tomoyo_supervisor(r, "allow_%s %s\n", operation,
+ tomoyo_pattern(filename));
+}
+
+/**
+ * tomoyo_audit_path2_log - Audit path/path request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)
+{
+ const char *operation = tomoyo_path2_keyword[r->param.path2.operation];
+ const struct tomoyo_path_info *filename1 = r->param.path2.filename1;
+ const struct tomoyo_path_info *filename2 = r->param.path2.filename2;
+ if (r->granted)
+ return 0;
+ tomoyo_warn_log(r, "%s %s %s", operation, filename1->name,
+ filename2->name);
+ return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
+ tomoyo_pattern(filename1),
+ tomoyo_pattern(filename2));
+}
+
+/**
+ * tomoyo_audit_mkdev_log - Audit path/number/number/number request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r)
+{
+ const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation];
+ const struct tomoyo_path_info *filename = r->param.mkdev.filename;
+ const unsigned int major = r->param.mkdev.major;
+ const unsigned int minor = r->param.mkdev.minor;
+ const unsigned int mode = r->param.mkdev.mode;
+ if (r->granted)
+ return 0;
+ tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode,
+ major, minor);
+ return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation,
+ tomoyo_pattern(filename), mode, major, minor);
+}
+
+/**
+ * tomoyo_audit_path_number_log - Audit path/number request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @error: Error code.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r)
+{
+ const u8 type = r->param.path_number.operation;
+ u8 radix;
+ const struct tomoyo_path_info *filename = r->param.path_number.filename;
+ const char *operation = tomoyo_path_number_keyword[type];
+ char buffer[64];
+ if (r->granted)
+ return 0;
+ switch (type) {
+ case TOMOYO_TYPE_CREATE:
+ case TOMOYO_TYPE_MKDIR:
+ case TOMOYO_TYPE_MKFIFO:
+ case TOMOYO_TYPE_MKSOCK:
+ case TOMOYO_TYPE_CHMOD:
+ radix = TOMOYO_VALUE_TYPE_OCTAL;
+ break;
+ case TOMOYO_TYPE_IOCTL:
+ radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
+ break;
+ default:
+ radix = TOMOYO_VALUE_TYPE_DECIMAL;
+ break;
+ }
+ tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number,
+ radix);
+ tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer);
+ return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
+ tomoyo_pattern(filename), buffer);
+}
+
+static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ return container_of(a, struct tomoyo_readable_file,
+ head)->filename ==
+ container_of(b, struct tomoyo_readable_file,
+ head)->filename;
+}
+
+/**
+ * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list.
+ *
+ * @filename: Filename unconditionally permitted to open() for reading.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_globally_readable_entry(const char *filename,
+ const bool is_delete)
+{
+ struct tomoyo_readable_file e = { };
+ int error;
+
+ if (!tomoyo_correct_word(filename))
+ return -EINVAL;
+ e.filename = tomoyo_get_name(filename);
+ if (!e.filename)
+ return -ENOMEM;
+ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
+ &tomoyo_policy_list
+ [TOMOYO_ID_GLOBALLY_READABLE],
+ tomoyo_same_globally_readable);
+ tomoyo_put_name(e.filename);
+ return error;
+}
+
+/**
+ * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
+ *
+ * @filename: The filename to check.
+ *
+ * Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_globally_readable_file(const struct tomoyo_path_info *
+ filename)
+{
+ struct tomoyo_readable_file *ptr;
+ bool found = false;
+
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_list
+ [TOMOYO_ID_GLOBALLY_READABLE], head.list) {
+ if (!ptr->head.is_deleted &&
+ tomoyo_path_matches_pattern(filename, ptr->filename)) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+/**
+ * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_globally_readable(char *data, const bool is_delete)
+{
+ return tomoyo_update_globally_readable_entry(data, is_delete);
+}
+
+static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ return container_of(a, struct tomoyo_no_pattern, head)->pattern ==
+ container_of(b, struct tomoyo_no_pattern, head)->pattern;
+}
+
+/**
+ * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list.
+ *
+ * @pattern: Pathname pattern.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_file_pattern_entry(const char *pattern,
+ const bool is_delete)
+{
+ struct tomoyo_no_pattern e = { };
+ int error;
+
+ if (!tomoyo_correct_word(pattern))
+ return -EINVAL;
+ e.pattern = tomoyo_get_name(pattern);
+ if (!e.pattern)
+ return -ENOMEM;
+ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
+ &tomoyo_policy_list[TOMOYO_ID_PATTERN],
+ tomoyo_same_pattern);
+ tomoyo_put_name(e.pattern);
+ return error;
+}
+
+/**
+ * tomoyo_pattern - Get patterned pathname.
+ *
+ * @filename: The filename to find patterned pathname.
+ *
+ * Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+const char *tomoyo_pattern(const struct tomoyo_path_info *filename)
+{
+ struct tomoyo_no_pattern *ptr;
+ const struct tomoyo_path_info *pattern = NULL;
+
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN],
+ head.list) {
+ if (ptr->head.is_deleted)
+ continue;
+ if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+ continue;
+ pattern = ptr->pattern;
+ if (tomoyo_strendswith(pattern->name, "/\\*")) {
+ /* Do nothing. Try to find the better match. */
+ } else {
+ /* This would be the better match. Use this. */
+ break;
+ }
+ }
+ if (pattern)
+ filename = pattern;
+ return filename->name;
+}
+
+/**
+ * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_pattern(char *data, const bool is_delete)
+{
+ return tomoyo_update_file_pattern_entry(data, is_delete);
+}
+
+static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ return container_of(a, struct tomoyo_no_rewrite, head)->pattern
+ == container_of(b, struct tomoyo_no_rewrite, head)
+ ->pattern;
+}
+
+/**
+ * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list.
+ *
+ * @pattern: Pathname pattern that are not rewritable by default.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_no_rewrite_entry(const char *pattern,
+ const bool is_delete)
+{
+ struct tomoyo_no_rewrite e = { };
+ int error;
+
+ if (!tomoyo_correct_word(pattern))
+ return -EINVAL;
+ e.pattern = tomoyo_get_name(pattern);
+ if (!e.pattern)
+ return -ENOMEM;
+ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
+ &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
+ tomoyo_same_no_rewrite);
+ tomoyo_put_name(e.pattern);
+ return error;
+}
+
+/**
+ * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
+ *
+ * @filename: Filename to check.
+ *
+ * Returns true if @filename is specified by "deny_rewrite" directive,
+ * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename)
+{
+ struct tomoyo_no_rewrite *ptr;
+ bool found = false;
+
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
+ head.list) {
+ if (ptr->head.is_deleted)
+ continue;
+ if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+ continue;
+ found = true;
+ break;
+ }
+ return found;
+}
+
+/**
+ * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_no_rewrite(char *data, const bool is_delete)
+{
+ return tomoyo_update_no_rewrite_entry(data, is_delete);
+}
+
+static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *ptr)
+{
+ const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl),
+ head);
+ if (acl->perm & (1 << r->param.path.operation)) {
+ r->param.path.matched_path =
+ tomoyo_compare_name_union(r->param.path.filename,
+ &acl->name);
+ return r->param.path.matched_path != NULL;
+ }
+ return false;
+}
+
+static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *ptr)
+{
+ const struct tomoyo_path_number_acl *acl =
+ container_of(ptr, typeof(*acl), head);
+ return (acl->perm & (1 << r->param.path_number.operation)) &&
+ tomoyo_compare_number_union(r->param.path_number.number,
+ &acl->number) &&
+ tomoyo_compare_name_union(r->param.path_number.filename,
+ &acl->name);
+}
+
+static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *ptr)
+{
+ const struct tomoyo_path2_acl *acl =
+ container_of(ptr, typeof(*acl), head);
+ return (acl->perm & (1 << r->param.path2.operation)) &&
+ tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1)
+ && tomoyo_compare_name_union(r->param.path2.filename2,
+ &acl->name2);
+}
+
+static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *ptr)
+{
+ const struct tomoyo_mkdev_acl *acl =
+ container_of(ptr, typeof(*acl), head);
+ return (acl->perm & (1 << r->param.mkdev.operation)) &&
+ tomoyo_compare_number_union(r->param.mkdev.mode,
+ &acl->mode) &&
+ tomoyo_compare_number_union(r->param.mkdev.major,
+ &acl->major) &&
+ tomoyo_compare_number_union(r->param.mkdev.minor,
+ &acl->minor) &&
+ tomoyo_compare_name_union(r->param.mkdev.filename,
+ &acl->name);
+}
+
+static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
+ return tomoyo_same_acl_head(&p1->head, &p2->head) &&
+ tomoyo_same_name_union(&p1->name, &p2->name);
+}
+
+static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
+ struct tomoyo_acl_info *b,
+ const bool is_delete)
+{
+ u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head)
+ ->perm;
+ u16 perm = *a_perm;
+ const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
+ if (is_delete) {
+ perm &= ~b_perm;
+ if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK)
+ perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
+ else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE)))
+ perm &= ~TOMOYO_RW_MASK;
+ } else {
+ perm |= b_perm;
+ if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK)
+ perm |= (1 << TOMOYO_TYPE_READ_WRITE);
+ else if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
+ perm |= TOMOYO_RW_MASK;
+ }
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_path_acl(const u8 type, const char *filename,
+ struct tomoyo_domain_info * const domain,
+ const bool is_delete)
+{
+ struct tomoyo_path_acl e = {
+ .head.type = TOMOYO_TYPE_PATH_ACL,
+ .perm = 1 << type
+ };
+ int error;
+ if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE))
+ e.perm |= TOMOYO_RW_MASK;
+ if (!tomoyo_parse_name_union(filename, &e.name))
+ return -EINVAL;
+ error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
+ tomoyo_same_path_acl,
+ tomoyo_merge_path_acl);
+ tomoyo_put_name_union(&e.name);
+ return error;
+}
+
+static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1),
+ head);
+ const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2),
+ head);
+ return tomoyo_same_acl_head(&p1->head, &p2->head)
+ && tomoyo_same_name_union(&p1->name, &p2->name)
+ && tomoyo_same_number_union(&p1->mode, &p2->mode)
+ && tomoyo_same_number_union(&p1->major, &p2->major)
+ && tomoyo_same_number_union(&p1->minor, &p2->minor);
+}
+
+static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,
+ struct tomoyo_acl_info *b,
+ const bool is_delete)
+{
+ u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl,
+ head)->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head)
+ ->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @mode: Create mode.
+ * @major: Device major number.
+ * @minor: Device minor number.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_mkdev_acl(const u8 type, const char *filename,
+ char *mode, char *major, char *minor,
+ struct tomoyo_domain_info * const
+ domain, const bool is_delete)
+{
+ struct tomoyo_mkdev_acl e = {
+ .head.type = TOMOYO_TYPE_MKDEV_ACL,
+ .perm = 1 << type
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_parse_name_union(filename, &e.name) ||
+ !tomoyo_parse_number_union(mode, &e.mode) ||
+ !tomoyo_parse_number_union(major, &e.major) ||
+ !tomoyo_parse_number_union(minor, &e.minor))
+ goto out;
+ error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
+ tomoyo_same_mkdev_acl,
+ tomoyo_merge_mkdev_acl);
+ out:
+ tomoyo_put_name_union(&e.name);
+ tomoyo_put_number_union(&e.mode);
+ tomoyo_put_number_union(&e.major);
+ tomoyo_put_number_union(&e.minor);
+ return error;
+}
+
+static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
+ return tomoyo_same_acl_head(&p1->head, &p2->head)
+ && tomoyo_same_name_union(&p1->name1, &p2->name1)
+ && tomoyo_same_name_union(&p1->name2, &p2->name2);
+}
+
+static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
+ struct tomoyo_acl_info *b,
+ const bool is_delete)
+{
+ u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head)
+ ->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
+ *
+ * @type: Type of operation.
+ * @filename1: First filename.
+ * @filename2: Second filename.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
+ const char *filename2,
+ struct tomoyo_domain_info * const domain,
+ const bool is_delete)
+{
+ struct tomoyo_path2_acl e = {
+ .head.type = TOMOYO_TYPE_PATH2_ACL,
+ .perm = 1 << type
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_parse_name_union(filename1, &e.name1) ||
+ !tomoyo_parse_name_union(filename2, &e.name2))
+ goto out;
+ error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
+ tomoyo_same_path2_acl,
+ tomoyo_merge_path2_acl);
+ out:
+ tomoyo_put_name_union(&e.name1);
+ tomoyo_put_name_union(&e.name2);
+ return error;
+}
+
+/**
+ * tomoyo_path_permission - Check permission for single path operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: Type of operation.
+ * @filename: Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
+ const struct tomoyo_path_info *filename)
+{
+ int error;
+
+ next:
+ r->type = tomoyo_p2mac[operation];
+ r->mode = tomoyo_get_mode(r->profile, r->type);
+ if (r->mode == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ r->param_type = TOMOYO_TYPE_PATH_ACL;
+ r->param.path.filename = filename;
+ r->param.path.operation = operation;
+ do {
+ tomoyo_check_acl(r, tomoyo_check_path_acl);
+ if (!r->granted && operation == TOMOYO_TYPE_READ &&
+ !r->domain->ignore_global_allow_read &&
+ tomoyo_globally_readable_file(filename))
+ r->granted = true;
+ error = tomoyo_audit_path_log(r);
+ /*
+ * Do not retry for execute request, for alias may have
+ * changed.
+ */
+ } while (error == TOMOYO_RETRY_REQUEST &&
+ operation != TOMOYO_TYPE_EXECUTE);
+ /*
+ * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
+ * we need to check "allow_rewrite" permission if the filename is
+ * specified by "deny_rewrite" keyword.
+ */
+ if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
+ tomoyo_no_rewrite_file(filename)) {
+ operation = TOMOYO_TYPE_REWRITE;
+ goto next;
+ }
+ return error;
+}
+
+static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1),
+ head);
+ const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
+ head);
+ return tomoyo_same_acl_head(&p1->head, &p2->head)
+ && tomoyo_same_name_union(&p1->name, &p2->name)
+ && tomoyo_same_number_union(&p1->number, &p2->number);
+}
+
+static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
+ struct tomoyo_acl_info *b,
+ const bool is_delete)
+{
+ u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl,
+ head)->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head)
+ ->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @number: Number.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_path_number_acl(const u8 type, const char *filename,
+ char *number,
+ struct tomoyo_domain_info * const
+ domain,
+ const bool is_delete)
+{
+ struct tomoyo_path_number_acl e = {
+ .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
+ .perm = 1 << type
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_parse_name_union(filename, &e.name))
+ return -EINVAL;
+ if (!tomoyo_parse_number_union(number, &e.number))
+ goto out;
+ error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
+ tomoyo_same_path_number_acl,
+ tomoyo_merge_path_number_acl);
+ out:
+ tomoyo_put_name_union(&e.name);
+ tomoyo_put_number_union(&e.number);
+ return error;
+}
+
+/**
+ * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
+ *
+ * @type: Type of operation.
+ * @path: Pointer to "struct path".
+ * @number: Number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_path_number_perm(const u8 type, struct path *path,
+ unsigned long number)
+{
+ struct tomoyo_request_info r;
+ int error = -ENOMEM;
+ struct tomoyo_path_info buf;
+ int idx;
+
+ if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
+ == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
+ return 0;
+ idx = tomoyo_read_lock();
+ if (!tomoyo_get_realpath(&buf, path))
+ goto out;
+ if (type == TOMOYO_TYPE_MKDIR)
+ tomoyo_add_slash(&buf);
+ r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL;
+ r.param.path_number.operation = type;
+ r.param.path_number.filename = &buf;
+ r.param.path_number.number = number;
+ do {
+ tomoyo_check_acl(&r, tomoyo_check_path_number_acl);
+ error = tomoyo_audit_path_number_log(&r);
+ } while (error == TOMOYO_RETRY_REQUEST);
+ kfree(buf.name);
+ out:
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_check_open_permission - Check permission for "read" and "write".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @path: Pointer to "struct path".
+ * @flag: Flags for open().
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+ struct path *path, const int flag)
+{
+ const u8 acc_mode = ACC_MODE(flag);
+ int error = 0;
+ struct tomoyo_path_info buf;
+ struct tomoyo_request_info r;
+ int idx;
+
+ if (!path->mnt ||
+ (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
+ return 0;
+ buf.name = NULL;
+ r.mode = TOMOYO_CONFIG_DISABLED;
+ idx = tomoyo_read_lock();
+ /*
+ * If the filename is specified by "deny_rewrite" keyword,
+ * we need to check "allow_rewrite" permission when the filename is not
+ * opened for append mode or the filename is truncated at open time.
+ */
+ if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
+ && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
+ != TOMOYO_CONFIG_DISABLED) {
+ if (!tomoyo_get_realpath(&buf, path)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ if (tomoyo_no_rewrite_file(&buf))
+ error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
+ &buf);
+ }
+ if (!error && acc_mode &&
+ tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
+ != TOMOYO_CONFIG_DISABLED) {
+ u8 operation;
+ if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ if (acc_mode == (MAY_READ | MAY_WRITE))
+ operation = TOMOYO_TYPE_READ_WRITE;
+ else if (acc_mode == MAY_READ)
+ operation = TOMOYO_TYPE_READ;
+ else
+ operation = TOMOYO_TYPE_WRITE;
+ error = tomoyo_path_permission(&r, operation, &buf);
+ }
+ out:
+ kfree(buf.name);
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
+ *
+ * @operation: Type of operation.
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_path_perm(const u8 operation, struct path *path)
+{
+ int error = -ENOMEM;
+ struct tomoyo_path_info buf;
+ struct tomoyo_request_info r;
+ int idx;
+
+ if (!path->mnt)
+ return 0;
+ if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
+ == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ buf.name = NULL;
+ idx = tomoyo_read_lock();
+ if (!tomoyo_get_realpath(&buf, path))
+ goto out;
+ switch (operation) {
+ case TOMOYO_TYPE_REWRITE:
+ if (!tomoyo_no_rewrite_file(&buf)) {
+ error = 0;
+ goto out;
+ }
+ break;
+ case TOMOYO_TYPE_RMDIR:
+ case TOMOYO_TYPE_CHROOT:
+ tomoyo_add_slash(&buf);
+ break;
+ }
+ error = tomoyo_path_permission(&r, operation, &buf);
+ out:
+ kfree(buf.name);
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_mkdev_perm - Check permission for "mkblock" and "mkchar".
+ *
+ * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK)
+ * @path: Pointer to "struct path".
+ * @mode: Create mode.
+ * @dev: Device number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_mkdev_perm(const u8 operation, struct path *path,
+ const unsigned int mode, unsigned int dev)
+{
+ struct tomoyo_request_info r;
+ int error = -ENOMEM;
+ struct tomoyo_path_info buf;
+ int idx;
+
+ if (!path->mnt ||
+ tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
+ == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ idx = tomoyo_read_lock();
+ error = -ENOMEM;
+ if (tomoyo_get_realpath(&buf, path)) {
+ dev = new_decode_dev(dev);
+ r.param_type = TOMOYO_TYPE_MKDEV_ACL;
+ r.param.mkdev.filename = &buf;
+ r.param.mkdev.operation = operation;
+ r.param.mkdev.mode = mode;
+ r.param.mkdev.major = MAJOR(dev);
+ r.param.mkdev.minor = MINOR(dev);
+ tomoyo_check_acl(&r, tomoyo_check_mkdev_acl);
+ error = tomoyo_audit_mkdev_log(&r);
+ kfree(buf.name);
+ }
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
+ *
+ * @operation: Type of operation.
+ * @path1: Pointer to "struct path".
+ * @path2: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_path2_perm(const u8 operation, struct path *path1,
+ struct path *path2)
+{
+ int error = -ENOMEM;
+ struct tomoyo_path_info buf1;
+ struct tomoyo_path_info buf2;
+ struct tomoyo_request_info r;
+ int idx;
+
+ if (!path1->mnt || !path2->mnt ||
+ tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
+ == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ buf1.name = NULL;
+ buf2.name = NULL;
+ idx = tomoyo_read_lock();
+ if (!tomoyo_get_realpath(&buf1, path1) ||
+ !tomoyo_get_realpath(&buf2, path2))
+ goto out;
+ switch (operation) {
+ struct dentry *dentry;
+ case TOMOYO_TYPE_RENAME:
+ case TOMOYO_TYPE_LINK:
+ dentry = path1->dentry;
+ if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
+ break;
+ /* fall through */
+ case TOMOYO_TYPE_PIVOT_ROOT:
+ tomoyo_add_slash(&buf1);
+ tomoyo_add_slash(&buf2);
+ break;
+ }
+ r.param_type = TOMOYO_TYPE_PATH2_ACL;
+ r.param.path2.operation = operation;
+ r.param.path2.filename1 = &buf1;
+ r.param.path2.filename2 = &buf2;
+ do {
+ tomoyo_check_acl(&r, tomoyo_check_path2_acl);
+ error = tomoyo_audit_path2_log(&r);
+ } while (error == TOMOYO_RETRY_REQUEST);
+ out:
+ kfree(buf1.name);
+ kfree(buf2.name);
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_write_file - Update file related list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
+ const bool is_delete)
+{
+ char *w[5];
+ u8 type;
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ if (strncmp(w[0], "allow_", 6))
+ goto out;
+ w[0] += 6;
+ for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_path_keyword[type]))
+ continue;
+ return tomoyo_update_path_acl(type, w[1], domain, is_delete);
+ }
+ if (!w[2][0])
+ goto out;
+ for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_path2_keyword[type]))
+ continue;
+ return tomoyo_update_path2_acl(type, w[1], w[2], domain,
+ is_delete);
+ }
+ for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_path_number_keyword[type]))
+ continue;
+ return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
+ is_delete);
+ }
+ if (!w[3][0] || !w[4][0])
+ goto out;
+ for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_mkdev_keyword[type]))
+ continue;
+ return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3],
+ w[4], domain, is_delete);
+ }
+ out:
+ return -EINVAL;
+}
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
new file mode 100644
index 00000000..a877e4c3
--- /dev/null
+++ b/security/tomoyo/gc.c
@@ -0,0 +1,354 @@
+/*
+ * security/tomoyo/gc.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ *
+ */
+
+#include "common.h"
+#include <linux/kthread.h>
+#include <linux/slab.h>
+
+struct tomoyo_gc {
+ struct list_head list;
+ int type;
+ struct list_head *element;
+};
+static LIST_HEAD(tomoyo_gc_queue);
+static DEFINE_MUTEX(tomoyo_gc_mutex);
+
+/* Caller holds tomoyo_policy_lock mutex. */
+static bool tomoyo_add_to_gc(const int type, struct list_head *element)
+{
+ struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return false;
+ entry->type = type;
+ entry->element = element;
+ list_add(&entry->list, &tomoyo_gc_queue);
+ list_del_rcu(element);
+ return true;
+}
+
+static void tomoyo_del_allow_read(struct list_head *element)
+{
+ struct tomoyo_readable_file *ptr =
+ container_of(element, typeof(*ptr), head.list);
+ tomoyo_put_name(ptr->filename);
+}
+
+static void tomoyo_del_file_pattern(struct list_head *element)
+{
+ struct tomoyo_no_pattern *ptr =
+ container_of(element, typeof(*ptr), head.list);
+ tomoyo_put_name(ptr->pattern);
+}
+
+static void tomoyo_del_no_rewrite(struct list_head *element)
+{
+ struct tomoyo_no_rewrite *ptr =
+ container_of(element, typeof(*ptr), head.list);
+ tomoyo_put_name(ptr->pattern);
+}
+
+static void tomoyo_del_transition_control(struct list_head *element)
+{
+ struct tomoyo_transition_control *ptr =
+ container_of(element, typeof(*ptr), head.list);
+ tomoyo_put_name(ptr->domainname);
+ tomoyo_put_name(ptr->program);
+}
+
+static void tomoyo_del_aggregator(struct list_head *element)
+{
+ struct tomoyo_aggregator *ptr =
+ container_of(element, typeof(*ptr), head.list);
+ tomoyo_put_name(ptr->original_name);
+ tomoyo_put_name(ptr->aggregated_name);
+}
+
+static void tomoyo_del_manager(struct list_head *element)
+{
+ struct tomoyo_manager *ptr =
+ container_of(element, typeof(*ptr), head.list);
+ tomoyo_put_name(ptr->manager);
+}
+
+static void tomoyo_del_acl(struct list_head *element)
+{
+ struct tomoyo_acl_info *acl =
+ container_of(element, typeof(*acl), list);
+ switch (acl->type) {
+ case TOMOYO_TYPE_PATH_ACL:
+ {
+ struct tomoyo_path_acl *entry
+ = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name);
+ }
+ break;
+ case TOMOYO_TYPE_PATH2_ACL:
+ {
+ struct tomoyo_path2_acl *entry
+ = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name1);
+ tomoyo_put_name_union(&entry->name2);
+ }
+ break;
+ case TOMOYO_TYPE_PATH_NUMBER_ACL:
+ {
+ struct tomoyo_path_number_acl *entry
+ = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name);
+ tomoyo_put_number_union(&entry->number);
+ }
+ break;
+ case TOMOYO_TYPE_MKDEV_ACL:
+ {
+ struct tomoyo_mkdev_acl *entry
+ = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name);
+ tomoyo_put_number_union(&entry->mode);
+ tomoyo_put_number_union(&entry->major);
+ tomoyo_put_number_union(&entry->minor);
+ }
+ break;
+ case TOMOYO_TYPE_MOUNT_ACL:
+ {
+ struct tomoyo_mount_acl *entry
+ = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->dev_name);
+ tomoyo_put_name_union(&entry->dir_name);
+ tomoyo_put_name_union(&entry->fs_type);
+ tomoyo_put_number_union(&entry->flags);
+ }
+ break;
+ }
+}
+
+static bool tomoyo_del_domain(struct list_head *element)
+{
+ struct tomoyo_domain_info *domain =
+ container_of(element, typeof(*domain), list);
+ struct tomoyo_acl_info *acl;
+ struct tomoyo_acl_info *tmp;
+ /*
+ * Since we don't protect whole execve() operation using SRCU,
+ * we need to recheck domain->users at this point.
+ *
+ * (1) Reader starts SRCU section upon execve().
+ * (2) Reader traverses tomoyo_domain_list and finds this domain.
+ * (3) Writer marks this domain as deleted.
+ * (4) Garbage collector removes this domain from tomoyo_domain_list
+ * because this domain is marked as deleted and used by nobody.
+ * (5) Reader saves reference to this domain into
+ * "struct linux_binprm"->cred->security .
+ * (6) Reader finishes SRCU section, although execve() operation has
+ * not finished yet.
+ * (7) Garbage collector waits for SRCU synchronization.
+ * (8) Garbage collector kfree() this domain because this domain is
+ * used by nobody.
+ * (9) Reader finishes execve() operation and restores this domain from
+ * "struct linux_binprm"->cred->security.
+ *
+ * By updating domain->users at (5), we can solve this race problem
+ * by rechecking domain->users at (8).
+ */
+ if (atomic_read(&domain->users))
+ return false;
+ list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
+ tomoyo_del_acl(&acl->list);
+ tomoyo_memory_free(acl);
+ }
+ tomoyo_put_name(domain->domainname);
+ return true;
+}
+
+
+static void tomoyo_del_name(struct list_head *element)
+{
+ const struct tomoyo_name *ptr =
+ container_of(element, typeof(*ptr), list);
+}
+
+static void tomoyo_del_path_group(struct list_head *element)
+{
+ struct tomoyo_path_group *member =
+ container_of(element, typeof(*member), head.list);
+ tomoyo_put_name(member->member_name);
+}
+
+static void tomoyo_del_group(struct list_head *element)
+{
+ struct tomoyo_group *group =
+ container_of(element, typeof(*group), list);
+ tomoyo_put_name(group->group_name);
+}
+
+static void tomoyo_del_number_group(struct list_head *element)
+{
+ struct tomoyo_number_group *member =
+ container_of(element, typeof(*member), head.list);
+}
+
+static bool tomoyo_collect_member(struct list_head *member_list, int id)
+{
+ struct tomoyo_acl_head *member;
+ list_for_each_entry(member, member_list, list) {
+ if (!member->is_deleted)
+ continue;
+ if (!tomoyo_add_to_gc(id, &member->list))
+ return false;
+ }
+ return true;
+}
+
+static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain)
+{
+ struct tomoyo_acl_info *acl;
+ list_for_each_entry(acl, &domain->acl_info_list, list) {
+ if (!acl->is_deleted)
+ continue;
+ if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list))
+ return false;
+ }
+ return true;
+}
+
+static void tomoyo_collect_entry(void)
+{
+ int i;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return;
+ for (i = 0; i < TOMOYO_MAX_POLICY; i++) {
+ if (!tomoyo_collect_member(&tomoyo_policy_list[i], i))
+ goto unlock;
+ }
+ {
+ struct tomoyo_domain_info *domain;
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ if (!tomoyo_collect_acl(domain))
+ goto unlock;
+ if (!domain->is_deleted || atomic_read(&domain->users))
+ continue;
+ /*
+ * Nobody is referring this domain. But somebody may
+ * refer this domain after successful execve().
+ * We recheck domain->users after SRCU synchronization.
+ */
+ if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list))
+ goto unlock;
+ }
+ }
+ for (i = 0; i < TOMOYO_MAX_HASH; i++) {
+ struct tomoyo_name *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) {
+ if (atomic_read(&ptr->users))
+ continue;
+ if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list))
+ goto unlock;
+ }
+ }
+ for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
+ struct list_head *list = &tomoyo_group_list[i];
+ int id;
+ struct tomoyo_group *group;
+ switch (i) {
+ case 0:
+ id = TOMOYO_ID_PATH_GROUP;
+ break;
+ default:
+ id = TOMOYO_ID_NUMBER_GROUP;
+ break;
+ }
+ list_for_each_entry(group, list, list) {
+ if (!tomoyo_collect_member(&group->member_list, id))
+ goto unlock;
+ if (!list_empty(&group->member_list) ||
+ atomic_read(&group->users))
+ continue;
+ if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list))
+ goto unlock;
+ }
+ }
+ unlock:
+ mutex_unlock(&tomoyo_policy_lock);
+}
+
+static void tomoyo_kfree_entry(void)
+{
+ struct tomoyo_gc *p;
+ struct tomoyo_gc *tmp;
+
+ list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) {
+ struct list_head *element = p->element;
+ switch (p->type) {
+ case TOMOYO_ID_TRANSITION_CONTROL:
+ tomoyo_del_transition_control(element);
+ break;
+ case TOMOYO_ID_AGGREGATOR:
+ tomoyo_del_aggregator(element);
+ break;
+ case TOMOYO_ID_GLOBALLY_READABLE:
+ tomoyo_del_allow_read(element);
+ break;
+ case TOMOYO_ID_PATTERN:
+ tomoyo_del_file_pattern(element);
+ break;
+ case TOMOYO_ID_NO_REWRITE:
+ tomoyo_del_no_rewrite(element);
+ break;
+ case TOMOYO_ID_MANAGER:
+ tomoyo_del_manager(element);
+ break;
+ case TOMOYO_ID_NAME:
+ tomoyo_del_name(element);
+ break;
+ case TOMOYO_ID_ACL:
+ tomoyo_del_acl(element);
+ break;
+ case TOMOYO_ID_DOMAIN:
+ if (!tomoyo_del_domain(element))
+ continue;
+ break;
+ case TOMOYO_ID_PATH_GROUP:
+ tomoyo_del_path_group(element);
+ break;
+ case TOMOYO_ID_GROUP:
+ tomoyo_del_group(element);
+ break;
+ case TOMOYO_ID_NUMBER_GROUP:
+ tomoyo_del_number_group(element);
+ break;
+ }
+ tomoyo_memory_free(element);
+ list_del(&p->list);
+ kfree(p);
+ }
+}
+
+static int tomoyo_gc_thread(void *unused)
+{
+ daemonize("GC for TOMOYO");
+ if (mutex_trylock(&tomoyo_gc_mutex)) {
+ int i;
+ for (i = 0; i < 10; i++) {
+ tomoyo_collect_entry();
+ if (list_empty(&tomoyo_gc_queue))
+ break;
+ synchronize_srcu(&tomoyo_ss);
+ tomoyo_kfree_entry();
+ }
+ mutex_unlock(&tomoyo_gc_mutex);
+ }
+ do_exit(0);
+}
+
+void tomoyo_run_gc(void)
+{
+ struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL,
+ "GC for TOMOYO");
+ if (!IS_ERR(task))
+ wake_up_process(task);
+}
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c
new file mode 100644
index 00000000..e94352ce
--- /dev/null
+++ b/security/tomoyo/group.c
@@ -0,0 +1,130 @@
+/*
+ * security/tomoyo/group.c
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/slab.h>
+#include "common.h"
+
+static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ return container_of(a, struct tomoyo_path_group, head)->member_name ==
+ container_of(b, struct tomoyo_path_group, head)->member_name;
+}
+
+static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
+ const struct tomoyo_acl_head *b)
+{
+ return !memcmp(&container_of(a, struct tomoyo_number_group, head)
+ ->number,
+ &container_of(b, struct tomoyo_number_group, head)
+ ->number,
+ sizeof(container_of(a, struct tomoyo_number_group, head)
+ ->number));
+}
+
+/**
+ * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ * @type: Type of this group.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_group(char *data, const bool is_delete, const u8 type)
+{
+ struct tomoyo_group *group;
+ struct list_head *member;
+ char *w[2];
+ int error = -EINVAL;
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ group = tomoyo_get_group(w[0], type);
+ if (!group)
+ return -ENOMEM;
+ member = &group->member_list;
+ if (type == TOMOYO_PATH_GROUP) {
+ struct tomoyo_path_group e = { };
+ e.member_name = tomoyo_get_name(w[1]);
+ if (!e.member_name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
+ member, tomoyo_same_path_group);
+ tomoyo_put_name(e.member_name);
+ } else if (type == TOMOYO_NUMBER_GROUP) {
+ struct tomoyo_number_group e = { };
+ if (w[1][0] == '@'
+ || !tomoyo_parse_number_union(w[1], &e.number)
+ || e.number.values[0] > e.number.values[1])
+ goto out;
+ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
+ member, tomoyo_same_number_group);
+ /*
+ * tomoyo_put_number_union() is not needed because
+ * w[1][0] != '@'.
+ */
+ }
+ out:
+ tomoyo_put_group(group);
+ return error;
+}
+
+/**
+ * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.
+ *
+ * @pathname: The name of pathname.
+ * @group: Pointer to "struct tomoyo_path_group".
+ *
+ * Returns matched member's pathname if @pathname matches pathnames in @group,
+ * NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+const struct tomoyo_path_info *
+tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
+ const struct tomoyo_group *group)
+{
+ struct tomoyo_path_group *member;
+ list_for_each_entry_rcu(member, &group->member_list, head.list) {
+ if (member->head.is_deleted)
+ continue;
+ if (!tomoyo_path_matches_pattern(pathname, member->member_name))
+ continue;
+ return member->member_name;
+ }
+ return NULL;
+}
+
+/**
+ * tomoyo_number_matches_group - Check whether the given number matches members of the given number group.
+ *
+ * @min: Min number.
+ * @max: Max number.
+ * @group: Pointer to "struct tomoyo_number_group".
+ *
+ * Returns true if @min and @max partially overlaps @group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_number_matches_group(const unsigned long min,
+ const unsigned long max,
+ const struct tomoyo_group *group)
+{
+ struct tomoyo_number_group *member;
+ bool matched = false;
+ list_for_each_entry_rcu(member, &group->member_list, head.list) {
+ if (member->head.is_deleted)
+ continue;
+ if (min > member->number.values[1] ||
+ max < member->number.values[0])
+ continue;
+ matched = true;
+ break;
+ }
+ return matched;
+}
diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c
new file mode 100644
index 00000000..3312e562
--- /dev/null
+++ b/security/tomoyo/load_policy.c
@@ -0,0 +1,81 @@
+/*
+ * security/tomoyo/load_policy.c
+ *
+ * Policy loader launcher for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include "common.h"
+
+/* path to policy loader */
+static const char *tomoyo_loader = "/sbin/tomoyo-init";
+
+/**
+ * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
+ *
+ * Returns true if /sbin/tomoyo-init exists, false otherwise.
+ */
+static bool tomoyo_policy_loader_exists(void)
+{
+ /*
+ * Don't activate MAC if the policy loader doesn't exist.
+ * If the initrd includes /sbin/init but real-root-dev has not
+ * mounted on / yet, activating MAC will block the system since
+ * policies are not loaded yet.
+ * Thus, let do_execve() call this function every time.
+ */
+ struct path path;
+
+ if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
+ printk(KERN_INFO "Not activating Mandatory Access Control now "
+ "since %s doesn't exist.\n", tomoyo_loader);
+ return false;
+ }
+ path_put(&path);
+ return true;
+}
+
+/**
+ * tomoyo_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * This function checks whether @filename is /sbin/init , and if so
+ * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init
+ * and then continues invocation of /sbin/init.
+ * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and
+ * writes to /sys/kernel/security/tomoyo/ interfaces.
+ *
+ * Returns nothing.
+ */
+void tomoyo_load_policy(const char *filename)
+{
+ char *argv[2];
+ char *envp[3];
+
+ if (tomoyo_policy_loaded)
+ return;
+ /*
+ * Check filename is /sbin/init or /sbin/tomoyo-start.
+ * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
+ * be passed.
+ * You can create /sbin/tomoyo-start by
+ * "ln -s /bin/true /sbin/tomoyo-start".
+ */
+ if (strcmp(filename, "/sbin/init") &&
+ strcmp(filename, "/sbin/tomoyo-start"))
+ return;
+ if (!tomoyo_policy_loader_exists())
+ return;
+
+ printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
+ tomoyo_loader);
+ argv[0] = (char *) tomoyo_loader;
+ argv[1] = NULL;
+ envp[0] = "HOME=/";
+ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+ envp[2] = NULL;
+ call_usermodehelper(argv[0], argv, envp, 1);
+ tomoyo_check_profile();
+}
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c
new file mode 100644
index 00000000..42a7b1ba
--- /dev/null
+++ b/security/tomoyo/memory.c
@@ -0,0 +1,283 @@
+/*
+ * security/tomoyo/memory.c
+ *
+ * Memory management functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/hash.h>
+#include <linux/slab.h>
+#include "common.h"
+
+/**
+ * tomoyo_warn_oom - Print out of memory warning message.
+ *
+ * @function: Function's name.
+ */
+void tomoyo_warn_oom(const char *function)
+{
+ /* Reduce error messages. */
+ static pid_t tomoyo_last_pid;
+ const pid_t pid = current->pid;
+ if (tomoyo_last_pid != pid) {
+ printk(KERN_WARNING "ERROR: Out of memory at %s.\n",
+ function);
+ tomoyo_last_pid = pid;
+ }
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
+}
+
+/* Memory allocated for policy. */
+static atomic_t tomoyo_policy_memory_size;
+/* Quota for holding policy. */
+static unsigned int tomoyo_quota_for_policy;
+
+/**
+ * tomoyo_memory_ok - Check memory quota.
+ *
+ * @ptr: Pointer to allocated memory.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Returns true if @ptr is not NULL and quota not exceeded, false otherwise.
+ */
+bool tomoyo_memory_ok(void *ptr)
+{
+ size_t s = ptr ? ksize(ptr) : 0;
+ atomic_add(s, &tomoyo_policy_memory_size);
+ if (ptr && (!tomoyo_quota_for_policy ||
+ atomic_read(&tomoyo_policy_memory_size)
+ <= tomoyo_quota_for_policy)) {
+ memset(ptr, 0, s);
+ return true;
+ }
+ atomic_sub(s, &tomoyo_policy_memory_size);
+ tomoyo_warn_oom(__func__);
+ return false;
+}
+
+/**
+ * tomoyo_commit_ok - Check memory quota.
+ *
+ * @data: Data to copy from.
+ * @size: Size in byte.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ * @data is zero-cleared on success.
+ */
+void *tomoyo_commit_ok(void *data, const unsigned int size)
+{
+ void *ptr = kzalloc(size, GFP_NOFS);
+ if (tomoyo_memory_ok(ptr)) {
+ memmove(ptr, data, size);
+ memset(data, 0, size);
+ return ptr;
+ }
+ kfree(ptr);
+ return NULL;
+}
+
+/**
+ * tomoyo_memory_free - Free memory for elements.
+ *
+ * @ptr: Pointer to allocated memory.
+ */
+void tomoyo_memory_free(void *ptr)
+{
+ atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);
+ kfree(ptr);
+}
+
+/**
+ * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
+ *
+ * @group_name: The name of address group.
+ * @idx: Index number.
+ *
+ * Returns pointer to "struct tomoyo_group" on success, NULL otherwise.
+ */
+struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
+{
+ struct tomoyo_group e = { };
+ struct tomoyo_group *group = NULL;
+ bool found = false;
+ if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)
+ return NULL;
+ e.group_name = tomoyo_get_name(group_name);
+ if (!e.group_name)
+ return NULL;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry(group, &tomoyo_group_list[idx], list) {
+ if (e.group_name != group->group_name)
+ continue;
+ atomic_inc(&group->users);
+ found = true;
+ break;
+ }
+ if (!found) {
+ struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ INIT_LIST_HEAD(&entry->member_list);
+ atomic_set(&entry->users, 1);
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_group_list[idx]);
+ group = entry;
+ found = true;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.group_name);
+ return found ? group : NULL;
+}
+
+/*
+ * tomoyo_name_list is used for holding string data used by TOMOYO.
+ * Since same string data is likely used for multiple times (e.g.
+ * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of
+ * "const struct tomoyo_path_info *".
+ */
+struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+
+/**
+ * tomoyo_get_name - Allocate permanent memory for string data.
+ *
+ * @name: The string to store into the permernent memory.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+const struct tomoyo_path_info *tomoyo_get_name(const char *name)
+{
+ struct tomoyo_name *ptr;
+ unsigned int hash;
+ int len;
+ int allocated_len;
+ struct list_head *head;
+
+ if (!name)
+ return NULL;
+ len = strlen(name) + 1;
+ hash = full_name_hash((const unsigned char *) name, len - 1);
+ head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return NULL;
+ list_for_each_entry(ptr, head, list) {
+ if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+ continue;
+ atomic_inc(&ptr->users);
+ goto out;
+ }
+ ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS);
+ allocated_len = ptr ? ksize(ptr) : 0;
+ if (!ptr || (tomoyo_quota_for_policy &&
+ atomic_read(&tomoyo_policy_memory_size) + allocated_len
+ > tomoyo_quota_for_policy)) {
+ kfree(ptr);
+ ptr = NULL;
+ tomoyo_warn_oom(__func__);
+ goto out;
+ }
+ atomic_add(allocated_len, &tomoyo_policy_memory_size);
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
+ atomic_set(&ptr->users, 1);
+ tomoyo_fill_path_info(&ptr->entry);
+ list_add_tail(&ptr->list, head);
+ out:
+ mutex_unlock(&tomoyo_policy_lock);
+ return ptr ? &ptr->entry : NULL;
+}
+
+/**
+ * tomoyo_mm_init - Initialize mm related code.
+ */
+void __init tomoyo_mm_init(void)
+{
+ int idx;
+
+ for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
+ INIT_LIST_HEAD(&tomoyo_policy_list[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
+ INIT_LIST_HEAD(&tomoyo_group_list[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
+ INIT_LIST_HEAD(&tomoyo_name_list[idx]);
+ INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+ tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+ idx = tomoyo_read_lock();
+ if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
+ panic("Can't register tomoyo_kernel_domain");
+ {
+ /* Load built-in policy. */
+ tomoyo_write_transition_control("/sbin/hotplug", false,
+ TOMOYO_TRANSITION_CONTROL_INITIALIZE);
+ tomoyo_write_transition_control("/sbin/modprobe", false,
+ TOMOYO_TRANSITION_CONTROL_INITIALIZE);
+ }
+ tomoyo_read_unlock(idx);
+}
+
+
+/* Memory allocated for query lists. */
+unsigned int tomoyo_query_memory_size;
+/* Quota for holding query lists. */
+unsigned int tomoyo_quota_for_query;
+
+/**
+ * tomoyo_read_memory_counter - Check for memory usage in bytes.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns memory usage.
+ */
+void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
+{
+ if (!head->r.eof) {
+ const unsigned int policy
+ = atomic_read(&tomoyo_policy_memory_size);
+ const unsigned int query = tomoyo_query_memory_size;
+ char buffer[64];
+
+ memset(buffer, 0, sizeof(buffer));
+ if (tomoyo_quota_for_policy)
+ snprintf(buffer, sizeof(buffer) - 1,
+ " (Quota: %10u)",
+ tomoyo_quota_for_policy);
+ else
+ buffer[0] = '\0';
+ tomoyo_io_printf(head, "Policy: %10u%s\n", policy,
+ buffer);
+ if (tomoyo_quota_for_query)
+ snprintf(buffer, sizeof(buffer) - 1,
+ " (Quota: %10u)",
+ tomoyo_quota_for_query);
+ else
+ buffer[0] = '\0';
+ tomoyo_io_printf(head, "Query lists: %10u%s\n", query,
+ buffer);
+ tomoyo_io_printf(head, "Total: %10u\n", policy + query);
+ head->r.eof = true;
+ }
+}
+
+/**
+ * tomoyo_write_memory_quota - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ unsigned int size;
+
+ if (sscanf(data, "Policy: %u", &size) == 1)
+ tomoyo_quota_for_policy = size;
+ else if (sscanf(data, "Query lists: %u", &size) == 1)
+ tomoyo_quota_for_query = size;
+ return 0;
+}
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
new file mode 100644
index 00000000..892494ac
--- /dev/null
+++ b/security/tomoyo/mount.c
@@ -0,0 +1,287 @@
+/*
+ * security/tomoyo/mount.c
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/slab.h>
+#include "common.h"
+
+/* Keywords for mount restrictions. */
+
+/* Allow to call 'mount --bind /source_dir /dest_dir' */
+#define TOMOYO_MOUNT_BIND_KEYWORD "--bind"
+/* Allow to call 'mount --move /old_dir /new_dir ' */
+#define TOMOYO_MOUNT_MOVE_KEYWORD "--move"
+/* Allow to call 'mount -o remount /dir ' */
+#define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount"
+/* Allow to call 'mount --make-unbindable /dir' */
+#define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable"
+/* Allow to call 'mount --make-private /dir' */
+#define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private"
+/* Allow to call 'mount --make-slave /dir' */
+#define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave"
+/* Allow to call 'mount --make-shared /dir' */
+#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared"
+
+/**
+ * tomoyo_audit_mount_log - Audit mount log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_mount_log(struct tomoyo_request_info *r)
+{
+ const char *dev = r->param.mount.dev->name;
+ const char *dir = r->param.mount.dir->name;
+ const char *type = r->param.mount.type->name;
+ const unsigned long flags = r->param.mount.flags;
+ if (r->granted)
+ return 0;
+ if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD))
+ tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags);
+ else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD)
+ || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD))
+ tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir,
+ flags);
+ else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD))
+ tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags);
+ else
+ tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir,
+ flags);
+ return tomoyo_supervisor(r,
+ TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n",
+ tomoyo_pattern(r->param.mount.dev),
+ tomoyo_pattern(r->param.mount.dir), type,
+ flags);
+}
+
+static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *ptr)
+{
+ const struct tomoyo_mount_acl *acl =
+ container_of(ptr, typeof(*acl), head);
+ return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) &&
+ tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) &&
+ tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) &&
+ (!r->param.mount.need_dev ||
+ tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name));
+}
+
+/**
+ * tomoyo_mount_acl - Check permission for mount() operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @dev_name: Name of device file.
+ * @dir: Pointer to "struct path".
+ * @type: Name of filesystem type.
+ * @flags: Mount options.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
+ struct path *dir, char *type, unsigned long flags)
+{
+ struct path path;
+ struct file_system_type *fstype = NULL;
+ const char *requested_type = NULL;
+ const char *requested_dir_name = NULL;
+ const char *requested_dev_name = NULL;
+ struct tomoyo_path_info rtype;
+ struct tomoyo_path_info rdev;
+ struct tomoyo_path_info rdir;
+ int need_dev = 0;
+ int error = -ENOMEM;
+
+ /* Get fstype. */
+ requested_type = tomoyo_encode(type);
+ if (!requested_type)
+ goto out;
+ rtype.name = requested_type;
+ tomoyo_fill_path_info(&rtype);
+
+ /* Get mount point. */
+ requested_dir_name = tomoyo_realpath_from_path(dir);
+ if (!requested_dir_name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ rdir.name = requested_dir_name;
+ tomoyo_fill_path_info(&rdir);
+
+ /* Compare fs name. */
+ if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) {
+ /* dev_name is ignored. */
+ } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) {
+ /* dev_name is ignored. */
+ } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) {
+ need_dev = -1; /* dev_name is a directory */
+ } else {
+ fstype = get_fs_type(type);
+ if (!fstype) {
+ error = -ENODEV;
+ goto out;
+ }
+ if (fstype->fs_flags & FS_REQUIRES_DEV)
+ /* dev_name is a block device file. */
+ need_dev = 1;
+ }
+ if (need_dev) {
+ /* Get mount point or device file. */
+ if (!dev_name || kern_path(dev_name, LOOKUP_FOLLOW, &path)) {
+ error = -ENOENT;
+ goto out;
+ }
+ requested_dev_name = tomoyo_realpath_from_path(&path);
+ path_put(&path);
+ if (!requested_dev_name) {
+ error = -ENOENT;
+ goto out;
+ }
+ } else {
+ /* Map dev_name to "<NULL>" if no dev_name given. */
+ if (!dev_name)
+ dev_name = "<NULL>";
+ requested_dev_name = tomoyo_encode(dev_name);
+ if (!requested_dev_name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ }
+ rdev.name = requested_dev_name;
+ tomoyo_fill_path_info(&rdev);
+ r->param_type = TOMOYO_TYPE_MOUNT_ACL;
+ r->param.mount.need_dev = need_dev;
+ r->param.mount.dev = &rdev;
+ r->param.mount.dir = &rdir;
+ r->param.mount.type = &rtype;
+ r->param.mount.flags = flags;
+ do {
+ tomoyo_check_acl(r, tomoyo_check_mount_acl);
+ error = tomoyo_audit_mount_log(r);
+ } while (error == TOMOYO_RETRY_REQUEST);
+ out:
+ kfree(requested_dev_name);
+ kfree(requested_dir_name);
+ if (fstype)
+ put_filesystem(fstype);
+ kfree(requested_type);
+ return error;
+}
+
+/**
+ * tomoyo_mount_permission - Check permission for mount() operation.
+ *
+ * @dev_name: Name of device file.
+ * @path: Pointer to "struct path".
+ * @type: Name of filesystem type. May be NULL.
+ * @flags: Mount options.
+ * @data_page: Optional data. May be NULL.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
+ unsigned long flags, void *data_page)
+{
+ struct tomoyo_request_info r;
+ int error;
+ int idx;
+
+ if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT)
+ == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
+ flags &= ~MS_MGC_MSK;
+ if (flags & MS_REMOUNT) {
+ type = TOMOYO_MOUNT_REMOUNT_KEYWORD;
+ flags &= ~MS_REMOUNT;
+ } else if (flags & MS_BIND) {
+ type = TOMOYO_MOUNT_BIND_KEYWORD;
+ flags &= ~MS_BIND;
+ } else if (flags & MS_SHARED) {
+ if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+ return -EINVAL;
+ type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD;
+ flags &= ~MS_SHARED;
+ } else if (flags & MS_PRIVATE) {
+ if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE))
+ return -EINVAL;
+ type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD;
+ flags &= ~MS_PRIVATE;
+ } else if (flags & MS_SLAVE) {
+ if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE))
+ return -EINVAL;
+ type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD;
+ flags &= ~MS_SLAVE;
+ } else if (flags & MS_UNBINDABLE) {
+ if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE))
+ return -EINVAL;
+ type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD;
+ flags &= ~MS_UNBINDABLE;
+ } else if (flags & MS_MOVE) {
+ type = TOMOYO_MOUNT_MOVE_KEYWORD;
+ flags &= ~MS_MOVE;
+ }
+ if (!type)
+ type = "<NULL>";
+ idx = tomoyo_read_lock();
+ error = tomoyo_mount_acl(&r, dev_name, path, type, flags);
+ tomoyo_read_unlock(idx);
+ return error;
+}
+
+static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
+ return tomoyo_same_acl_head(&p1->head, &p2->head) &&
+ tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
+ tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
+ tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
+ tomoyo_same_number_union(&p1->flags, &p2->flags);
+}
+
+/**
+ * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
+ const bool is_delete)
+{
+ struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ char *w[4];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0])
+ return -EINVAL;
+ if (!tomoyo_parse_name_union(w[0], &e.dev_name) ||
+ !tomoyo_parse_name_union(w[1], &e.dir_name) ||
+ !tomoyo_parse_name_union(w[2], &e.fs_type) ||
+ !tomoyo_parse_number_union(w[3], &e.flags))
+ goto out;
+ error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
+ tomoyo_same_mount_acl, NULL);
+ out:
+ tomoyo_put_name_union(&e.dev_name);
+ tomoyo_put_name_union(&e.dir_name);
+ tomoyo_put_name_union(&e.fs_type);
+ tomoyo_put_number_union(&e.flags);
+ return error;
+}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
new file mode 100644
index 00000000..a339187c
--- /dev/null
+++ b/security/tomoyo/realpath.c
@@ -0,0 +1,183 @@
+/*
+ * security/tomoyo/realpath.c
+ *
+ * Pathname calculation functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/types.h>
+#include <linux/mount.h>
+#include <linux/mnt_namespace.h>
+#include <linux/fs_struct.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+#include <net/sock.h>
+#include "common.h"
+#include "../../fs/internal.h"
+
+/**
+ * tomoyo_encode: Convert binary string to ascii string.
+ *
+ * @str: String in binary format.
+ *
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_encode(const char *str)
+{
+ int len = 0;
+ const char *p = str;
+ char *cp;
+ char *cp0;
+
+ if (!p)
+ return NULL;
+ while (*p) {
+ const unsigned char c = *p++;
+ if (c == '\\')
+ len += 2;
+ else if (c > ' ' && c < 127)
+ len++;
+ else
+ len += 4;
+ }
+ len++;
+ /* Reserve space for appending "/". */
+ cp = kzalloc(len + 10, GFP_NOFS);
+ if (!cp)
+ return NULL;
+ cp0 = cp;
+ p = str;
+ while (*p) {
+ const unsigned char c = *p++;
+
+ if (c == '\\') {
+ *cp++ = '\\';
+ *cp++ = '\\';
+ } else if (c > ' ' && c < 127) {
+ *cp++ = c;
+ } else {
+ *cp++ = '\\';
+ *cp++ = (c >> 6) + '0';
+ *cp++ = ((c >> 3) & 7) + '0';
+ *cp++ = (c & 7) + '0';
+ }
+ }
+ return cp0;
+}
+
+/**
+ * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns the realpath of the given @path on success, NULL otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ * Characters out of 0x20 < c < 0x7F range are converted to
+ * \ooo style octal string.
+ * Character \ is converted to \\ string.
+ *
+ * These functions use kzalloc(), so the caller must call kfree()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath_from_path(struct path *path)
+{
+ char *buf = NULL;
+ char *name = NULL;
+ unsigned int buf_len = PAGE_SIZE / 2;
+ struct dentry *dentry = path->dentry;
+ bool is_dir;
+ if (!dentry)
+ return NULL;
+ is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode);
+ while (1) {
+ char *pos;
+ buf_len <<= 1;
+ kfree(buf);
+ buf = kmalloc(buf_len, GFP_NOFS);
+ if (!buf)
+ break;
+ /* Get better name for socket. */
+ if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {
+ struct inode *inode = dentry->d_inode;
+ struct socket *sock = inode ? SOCKET_I(inode) : NULL;
+ struct sock *sk = sock ? sock->sk : NULL;
+ if (sk) {
+ snprintf(buf, buf_len - 1, "socket:[family=%u:"
+ "type=%u:protocol=%u]", sk->sk_family,
+ sk->sk_type, sk->sk_protocol);
+ } else {
+ snprintf(buf, buf_len - 1, "socket:[unknown]");
+ }
+ name = tomoyo_encode(buf);
+ break;
+ }
+ /* For "socket:[\$]" and "pipe:[\$]". */
+ if (dentry->d_op && dentry->d_op->d_dname) {
+ pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
+ if (IS_ERR(pos))
+ continue;
+ name = tomoyo_encode(pos);
+ break;
+ }
+ /* If we don't have a vfsmount, we can't calculate. */
+ if (!path->mnt)
+ break;
+ pos = d_absolute_path(path, buf, buf_len - 1);
+ /* If path is disconnected, use "[unknown]" instead. */
+ if (pos == ERR_PTR(-EINVAL)) {
+ name = tomoyo_encode("[unknown]");
+ break;
+ }
+ /* Prepend "/proc" prefix if using internal proc vfs mount. */
+ if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
+ (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
+ pos -= 5;
+ if (pos >= buf)
+ memcpy(pos, "/proc", 5);
+ else
+ pos = ERR_PTR(-ENOMEM);
+ }
+ if (IS_ERR(pos))
+ continue;
+ name = tomoyo_encode(pos);
+ break;
+ }
+ kfree(buf);
+ if (!name)
+ tomoyo_warn_oom(__func__);
+ else if (is_dir && *name) {
+ /* Append trailing '/' if dentry is a directory. */
+ char *pos = name + strlen(name) - 1;
+ if (*pos != '/')
+ /*
+ * This is OK because tomoyo_encode() reserves space
+ * for appending "/".
+ */
+ *++pos = '/';
+ }
+ return name;
+}
+
+/**
+ * tomoyo_realpath_nofollow - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname)
+{
+ struct path path;
+
+ if (pathname && kern_path(pathname, 0, &path) == 0) {
+ char *buf = tomoyo_realpath_from_path(&path);
+ path_put(&path);
+ return buf;
+ }
+ return NULL;
+}
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
new file mode 100644
index 00000000..e43d5554
--- /dev/null
+++ b/security/tomoyo/securityfs_if.c
@@ -0,0 +1,155 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Securityfs interface for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/security.h>
+#include "common.h"
+
+/**
+ * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_open(struct inode *inode, struct file *file)
+{
+ const int key = ((u8 *) file->f_path.dentry->d_inode->i_private)
+ - ((u8 *) NULL);
+ return tomoyo_open_control(key, file);
+}
+
+/**
+ * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_release(struct inode *inode, struct file *file)
+{
+ return tomoyo_close_control(file);
+}
+
+/**
+ * tomoyo_poll - poll() for /proc/ccs/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static unsigned int tomoyo_poll(struct file *file, poll_table *wait)
+{
+ return tomoyo_poll_control(file, wait);
+}
+
+/**
+ * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buf: Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos: Unused.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ return tomoyo_read_control(file, buf, count);
+}
+
+/**
+ * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buf: Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos: Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ */
+static ssize_t tomoyo_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return tomoyo_write_control(file, buf, count);
+}
+
+/*
+ * tomoyo_operations is a "struct file_operations" which is used for handling
+ * /sys/kernel/security/tomoyo/ interface.
+ *
+ * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR).
+ * See tomoyo_io_buffer for internals.
+ */
+static const struct file_operations tomoyo_operations = {
+ .open = tomoyo_open,
+ .release = tomoyo_release,
+ .poll = tomoyo_poll,
+ .read = tomoyo_read,
+ .write = tomoyo_write,
+ .llseek = noop_llseek,
+};
+
+/**
+ * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
+ *
+ * @name: The name of the interface file.
+ * @mode: The permission of the interface file.
+ * @parent: The parent directory.
+ * @key: Type of interface.
+ *
+ * Returns nothing.
+ */
+static void __init tomoyo_create_entry(const char *name, const mode_t mode,
+ struct dentry *parent, const u8 key)
+{
+ securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key,
+ &tomoyo_operations);
+}
+
+/**
+ * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_initerface_init(void)
+{
+ struct dentry *tomoyo_dir;
+
+ /* Don't create securityfs entries unless registered. */
+ if (current_cred()->security != &tomoyo_kernel_domain)
+ return 0;
+
+ tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
+ tomoyo_create_entry("query", 0600, tomoyo_dir,
+ TOMOYO_QUERY);
+ tomoyo_create_entry("domain_policy", 0600, tomoyo_dir,
+ TOMOYO_DOMAINPOLICY);
+ tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
+ TOMOYO_EXCEPTIONPOLICY);
+ tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
+ TOMOYO_SELFDOMAIN);
+ tomoyo_create_entry(".domain_status", 0600, tomoyo_dir,
+ TOMOYO_DOMAIN_STATUS);
+ tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
+ TOMOYO_PROCESS_STATUS);
+ tomoyo_create_entry("meminfo", 0600, tomoyo_dir,
+ TOMOYO_MEMINFO);
+ tomoyo_create_entry("profile", 0600, tomoyo_dir,
+ TOMOYO_PROFILE);
+ tomoyo_create_entry("manager", 0600, tomoyo_dir,
+ TOMOYO_MANAGER);
+ tomoyo_create_entry("version", 0400, tomoyo_dir,
+ TOMOYO_VERSION);
+ return 0;
+}
+
+fs_initcall(tomoyo_initerface_init);
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
new file mode 100644
index 00000000..95d3f957
--- /dev/null
+++ b/security/tomoyo/tomoyo.c
@@ -0,0 +1,289 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/security.h>
+#include "common.h"
+
+static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
+{
+ new->security = NULL;
+ return 0;
+}
+
+static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ struct tomoyo_domain_info *domain = old->security;
+ new->security = domain;
+ if (domain)
+ atomic_inc(&domain->users);
+ return 0;
+}
+
+static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
+{
+ tomoyo_cred_prepare(new, old, 0);
+}
+
+static void tomoyo_cred_free(struct cred *cred)
+{
+ struct tomoyo_domain_info *domain = cred->security;
+ if (domain)
+ atomic_dec(&domain->users);
+}
+
+static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
+{
+ int rc;
+
+ rc = cap_bprm_set_creds(bprm);
+ if (rc)
+ return rc;
+
+ /*
+ * Do only if this function is called for the first time of an execve
+ * operation.
+ */
+ if (bprm->cred_prepared)
+ return 0;
+ /*
+ * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
+ * for the first time.
+ */
+ if (!tomoyo_policy_loaded)
+ tomoyo_load_policy(bprm->filename);
+ /*
+ * Release reference to "struct tomoyo_domain_info" stored inside
+ * "bprm->cred->security". New reference to "struct tomoyo_domain_info"
+ * stored inside "bprm->cred->security" will be acquired later inside
+ * tomoyo_find_next_domain().
+ */
+ atomic_dec(&((struct tomoyo_domain_info *)
+ bprm->cred->security)->users);
+ /*
+ * Tell tomoyo_bprm_check_security() is called for the first time of an
+ * execve operation.
+ */
+ bprm->cred->security = NULL;
+ return 0;
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
+{
+ struct tomoyo_domain_info *domain = bprm->cred->security;
+
+ /*
+ * Execute permission is checked against pathname passed to do_execve()
+ * using current domain.
+ */
+ if (!domain) {
+ const int idx = tomoyo_read_lock();
+ const int err = tomoyo_find_next_domain(bprm);
+ tomoyo_read_unlock(idx);
+ return err;
+ }
+ /*
+ * Read permission is checked against interpreters using next domain.
+ */
+ return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY);
+}
+
+static int tomoyo_path_truncate(struct path *path)
+{
+ return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
+}
+
+static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path);
+}
+
+static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
+ int mode)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
+ mode & S_IALLUGO);
+}
+
+static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path);
+}
+
+static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
+ const char *old_name)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path);
+}
+
+static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
+ int mode, unsigned int dev)
+{
+ struct path path = { parent->mnt, dentry };
+ int type = TOMOYO_TYPE_CREATE;
+ const unsigned int perm = mode & S_IALLUGO;
+
+ switch (mode & S_IFMT) {
+ case S_IFCHR:
+ type = TOMOYO_TYPE_MKCHAR;
+ break;
+ case S_IFBLK:
+ type = TOMOYO_TYPE_MKBLOCK;
+ break;
+ default:
+ goto no_dev;
+ }
+ return tomoyo_mkdev_perm(type, &path, perm, dev);
+ no_dev:
+ switch (mode & S_IFMT) {
+ case S_IFIFO:
+ type = TOMOYO_TYPE_MKFIFO;
+ break;
+ case S_IFSOCK:
+ type = TOMOYO_TYPE_MKSOCK;
+ break;
+ }
+ return tomoyo_path_number_perm(type, &path, perm);
+}
+
+static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
+ struct dentry *new_dentry)
+{
+ struct path path1 = { new_dir->mnt, old_dentry };
+ struct path path2 = { new_dir->mnt, new_dentry };
+ return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
+}
+
+static int tomoyo_path_rename(struct path *old_parent,
+ struct dentry *old_dentry,
+ struct path *new_parent,
+ struct dentry *new_dentry)
+{
+ struct path path1 = { old_parent->mnt, old_dentry };
+ struct path path2 = { new_parent->mnt, new_dentry };
+ return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+ return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path);
+ return 0;
+}
+
+static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
+{
+ int flags = f->f_flags;
+ /* Don't check read permission here if called from do_execve(). */
+ if (current->in_execve)
+ return 0;
+ return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
+}
+
+static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd);
+}
+
+static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ struct path path = { mnt, dentry };
+ return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, &path,
+ mode & S_IALLUGO);
+}
+
+static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ int error = 0;
+ if (uid != (uid_t) -1)
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid);
+ if (!error && gid != (gid_t) -1)
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid);
+ return error;
+}
+
+static int tomoyo_path_chroot(struct path *path)
+{
+ return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path);
+}
+
+static int tomoyo_sb_mount(char *dev_name, struct path *path,
+ char *type, unsigned long flags, void *data)
+{
+ return tomoyo_mount_permission(dev_name, path, type, flags, data);
+}
+
+static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
+{
+ struct path path = { mnt, mnt->mnt_root };
+ return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path);
+}
+
+static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+ return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
+}
+
+/*
+ * tomoyo_security_ops is a "struct security_operations" which is used for
+ * registering TOMOYO.
+ */
+static struct security_operations tomoyo_security_ops = {
+ .name = "tomoyo",
+ .cred_alloc_blank = tomoyo_cred_alloc_blank,
+ .cred_prepare = tomoyo_cred_prepare,
+ .cred_transfer = tomoyo_cred_transfer,
+ .cred_free = tomoyo_cred_free,
+ .bprm_set_creds = tomoyo_bprm_set_creds,
+ .bprm_check_security = tomoyo_bprm_check_security,
+ .file_fcntl = tomoyo_file_fcntl,
+ .dentry_open = tomoyo_dentry_open,
+ .path_truncate = tomoyo_path_truncate,
+ .path_unlink = tomoyo_path_unlink,
+ .path_mkdir = tomoyo_path_mkdir,
+ .path_rmdir = tomoyo_path_rmdir,
+ .path_symlink = tomoyo_path_symlink,
+ .path_mknod = tomoyo_path_mknod,
+ .path_link = tomoyo_path_link,
+ .path_rename = tomoyo_path_rename,
+ .file_ioctl = tomoyo_file_ioctl,
+ .path_chmod = tomoyo_path_chmod,
+ .path_chown = tomoyo_path_chown,
+ .path_chroot = tomoyo_path_chroot,
+ .sb_mount = tomoyo_sb_mount,
+ .sb_umount = tomoyo_sb_umount,
+ .sb_pivotroot = tomoyo_sb_pivotroot,
+};
+
+/* Lock for GC. */
+struct srcu_struct tomoyo_ss;
+
+static int __init tomoyo_init(void)
+{
+ struct cred *cred = (struct cred *) current_cred();
+
+ if (!security_module_enable(&tomoyo_security_ops))
+ return 0;
+ /* register ourselves with the security framework */
+ if (register_security(&tomoyo_security_ops) ||
+ init_srcu_struct(&tomoyo_ss))
+ panic("Failure registering TOMOYO Linux");
+ printk(KERN_INFO "TOMOYO Linux initialized\n");
+ cred->security = &tomoyo_kernel_domain;
+ tomoyo_mm_init();
+ return 0;
+}
+
+security_initcall(tomoyo_init);
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
new file mode 100644
index 00000000..6d539320
--- /dev/null
+++ b/security/tomoyo/util.c
@@ -0,0 +1,963 @@
+/*
+ * security/tomoyo/util.c
+ *
+ * Utility functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include <linux/slab.h>
+#include "common.h"
+
+/* Lock for protecting policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);
+
+/* Has /sbin/init started? */
+bool tomoyo_policy_loaded;
+
+/**
+ * tomoyo_parse_ulong - Parse an "unsigned long" value.
+ *
+ * @result: Pointer to "unsigned long".
+ * @str: Pointer to string to parse.
+ *
+ * Returns value type on success, 0 otherwise.
+ *
+ * The @src is updated to point the first character after the value
+ * on success.
+ */
+static u8 tomoyo_parse_ulong(unsigned long *result, char **str)
+{
+ const char *cp = *str;
+ char *ep;
+ int base = 10;
+ if (*cp == '0') {
+ char c = *(cp + 1);
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ cp += 2;
+ } else if (c >= '0' && c <= '7') {
+ base = 8;
+ cp++;
+ }
+ }
+ *result = simple_strtoul(cp, &ep, base);
+ if (cp == ep)
+ return 0;
+ *str = ep;
+ switch (base) {
+ case 16:
+ return TOMOYO_VALUE_TYPE_HEXADECIMAL;
+ case 8:
+ return TOMOYO_VALUE_TYPE_OCTAL;
+ default:
+ return TOMOYO_VALUE_TYPE_DECIMAL;
+ }
+}
+
+/**
+ * tomoyo_print_ulong - Print an "unsigned long" value.
+ *
+ * @buffer: Pointer to buffer.
+ * @buffer_len: Size of @buffer.
+ * @value: An "unsigned long" value.
+ * @type: Type of @value.
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+ const unsigned long value, const u8 type)
+{
+ if (type == TOMOYO_VALUE_TYPE_DECIMAL)
+ snprintf(buffer, buffer_len, "%lu", value);
+ else if (type == TOMOYO_VALUE_TYPE_OCTAL)
+ snprintf(buffer, buffer_len, "0%lo", value);
+ else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL)
+ snprintf(buffer, buffer_len, "0x%lX", value);
+ else
+ snprintf(buffer, buffer_len, "type(%u)", type);
+}
+
+/**
+ * tomoyo_parse_name_union - Parse a tomoyo_name_union.
+ *
+ * @filename: Name or name group.
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_name_union(const char *filename,
+ struct tomoyo_name_union *ptr)
+{
+ if (!tomoyo_correct_word(filename))
+ return false;
+ if (filename[0] == '@') {
+ ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP);
+ ptr->is_group = true;
+ return ptr->group != NULL;
+ }
+ ptr->filename = tomoyo_get_name(filename);
+ ptr->is_group = false;
+ return ptr->filename != NULL;
+}
+
+/**
+ * tomoyo_parse_number_union - Parse a tomoyo_number_union.
+ *
+ * @data: Number or number range or number group.
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
+{
+ u8 type;
+ unsigned long v;
+ memset(num, 0, sizeof(*num));
+ if (data[0] == '@') {
+ if (!tomoyo_correct_word(data))
+ return false;
+ num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP);
+ num->is_group = true;
+ return num->group != NULL;
+ }
+ type = tomoyo_parse_ulong(&v, &data);
+ if (!type)
+ return false;
+ num->values[0] = v;
+ num->min_type = type;
+ if (!*data) {
+ num->values[1] = v;
+ num->max_type = type;
+ return true;
+ }
+ if (*data++ != '-')
+ return false;
+ type = tomoyo_parse_ulong(&v, &data);
+ if (!type || *data)
+ return false;
+ num->values[1] = v;
+ num->max_type = type;
+ return true;
+}
+
+/**
+ * tomoyo_byte_range - Check whether the string is a \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ *
+ * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF.
+ * This function verifies that \ooo is in valid range.
+ */
+static inline bool tomoyo_byte_range(const char *str)
+{
+ return *str >= '0' && *str++ <= '3' &&
+ *str >= '0' && *str++ <= '7' &&
+ *str >= '0' && *str <= '7';
+}
+
+/**
+ * tomoyo_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static inline bool tomoyo_alphabet_char(const char c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * tomoyo_make_byte - Make byte value from three octal characters.
+ *
+ * @c1: The first character.
+ * @c2: The second character.
+ * @c3: The third character.
+ *
+ * Returns byte value.
+ */
+static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
+{
+ return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
+}
+
+/**
+ * tomoyo_str_starts - Check whether the given string starts with the given keyword.
+ *
+ * @src: Pointer to pointer to the string.
+ * @find: Pointer to the keyword.
+ *
+ * Returns true if @src starts with @find, false otherwise.
+ *
+ * The @src is updated to point the first character after the @find
+ * if @src starts with @find.
+ */
+bool tomoyo_str_starts(char **src, const char *find)
+{
+ const int len = strlen(find);
+ char *tmp = *src;
+
+ if (strncmp(tmp, find, len))
+ return false;
+ tmp += len;
+ *src = tmp;
+ return true;
+}
+
+/**
+ * tomoyo_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ *
+ * Returns nothing.
+ */
+void tomoyo_normalize_line(unsigned char *buffer)
+{
+ unsigned char *sp = buffer;
+ unsigned char *dp = buffer;
+ bool first = true;
+
+ while (tomoyo_invalid(*sp))
+ sp++;
+ while (*sp) {
+ if (!first)
+ *dp++ = ' ';
+ first = false;
+ while (tomoyo_valid(*sp))
+ *dp++ = *sp++;
+ while (tomoyo_invalid(*sp))
+ sp++;
+ }
+ *dp = '\0';
+}
+
+/**
+ * tomoyo_tokenize - Tokenize string.
+ *
+ * @buffer: The line to tokenize.
+ * @w: Pointer to "char *".
+ * @size: Sizeof @w .
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_tokenize(char *buffer, char *w[], size_t size)
+{
+ int count = size / sizeof(char *);
+ int i;
+ for (i = 0; i < count; i++)
+ w[i] = "";
+ for (i = 0; i < count; i++) {
+ char *cp = strchr(buffer, ' ');
+ if (cp)
+ *cp = '\0';
+ w[i] = buffer;
+ if (!cp)
+ break;
+ buffer = cp + 1;
+ }
+ return i < count || !*buffer;
+}
+
+/**
+ * tomoyo_correct_word2 - Validate a string.
+ *
+ * @string: The string to check. May be non-'\0'-terminated.
+ * @len: Length of @string.
+ *
+ * Check whether the given string follows the naming rules.
+ * Returns true if @string follows the naming rules, false otherwise.
+ */
+static bool tomoyo_correct_word2(const char *string, size_t len)
+{
+ const char *const start = string;
+ bool in_repetition = false;
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ if (!len)
+ goto out;
+ while (len--) {
+ c = *string++;
+ if (c == '\\') {
+ if (!len--)
+ goto out;
+ c = *string++;
+ switch (c) {
+ case '\\': /* "\\" */
+ continue;
+ case '$': /* "\$" */
+ case '+': /* "\+" */
+ case '?': /* "\?" */
+ case '*': /* "\*" */
+ case '@': /* "\@" */
+ case 'x': /* "\x" */
+ case 'X': /* "\X" */
+ case 'a': /* "\a" */
+ case 'A': /* "\A" */
+ case '-': /* "\-" */
+ continue;
+ case '{': /* "/\{" */
+ if (string - 3 < start || *(string - 3) != '/')
+ break;
+ in_repetition = true;
+ continue;
+ case '}': /* "\}/" */
+ if (*string != '/')
+ break;
+ if (!in_repetition)
+ break;
+ in_repetition = false;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ if (!len-- || !len--)
+ break;
+ d = *string++;
+ e = *string++;
+ if (d < '0' || d > '7' || e < '0' || e > '7')
+ break;
+ c = tomoyo_make_byte(c, d, e);
+ if (tomoyo_invalid(c))
+ continue; /* pattern is not \000 */
+ }
+ goto out;
+ } else if (in_repetition && c == '/') {
+ goto out;
+ } else if (tomoyo_invalid(c)) {
+ goto out;
+ }
+ }
+ if (in_repetition)
+ goto out;
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_correct_word - Validate a string.
+ *
+ * @string: The string to check.
+ *
+ * Check whether the given string follows the naming rules.
+ * Returns true if @string follows the naming rules, false otherwise.
+ */
+bool tomoyo_correct_word(const char *string)
+{
+ return tomoyo_correct_word2(string, strlen(string));
+}
+
+/**
+ * tomoyo_correct_path - Validate a pathname.
+ *
+ * @filename: The pathname to check.
+ *
+ * Check whether the given pathname follows the naming rules.
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+bool tomoyo_correct_path(const char *filename)
+{
+ return *filename == '/' && tomoyo_correct_word(filename);
+}
+
+/**
+ * tomoyo_correct_domain - Check whether the given domainname follows the naming rules.
+ *
+ * @domainname: The domainname to check.
+ *
+ * Returns true if @domainname follows the naming rules, false otherwise.
+ */
+bool tomoyo_correct_domain(const unsigned char *domainname)
+{
+ if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
+ TOMOYO_ROOT_NAME_LEN))
+ goto out;
+ domainname += TOMOYO_ROOT_NAME_LEN;
+ if (!*domainname)
+ return true;
+ if (*domainname++ != ' ')
+ goto out;
+ while (1) {
+ const unsigned char *cp = strchr(domainname, ' ');
+ if (!cp)
+ break;
+ if (*domainname != '/' ||
+ !tomoyo_correct_word2(domainname, cp - domainname))
+ goto out;
+ domainname = cp + 1;
+ }
+ return tomoyo_correct_path(domainname);
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+bool tomoyo_domain_def(const unsigned char *buffer)
+{
+ return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+}
+
+/**
+ * tomoyo_find_domain - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ if (!domain->is_deleted &&
+ !tomoyo_pathcmp(&name, domain->domainname))
+ return domain;
+ }
+ return NULL;
+}
+
+/**
+ * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int tomoyo_const_part_length(const char *filename)
+{
+ char c;
+ int len = 0;
+
+ if (!filename)
+ return 0;
+ while ((c = *filename++) != '\0') {
+ if (c != '\\') {
+ len++;
+ continue;
+ }
+ c = *filename++;
+ switch (c) {
+ case '\\': /* "\\" */
+ len += 2;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ len += 4;
+ continue;
+ }
+ break;
+ }
+ return len;
+}
+
+/**
+ * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info" to fill in.
+ *
+ * The caller sets "struct tomoyo_path_info"->name.
+ */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
+{
+ const char *name = ptr->name;
+ const int len = strlen(name);
+
+ ptr->const_len = tomoyo_const_part_length(name);
+ ptr->is_dir = len && (name[len - 1] == '/');
+ ptr->is_patterned = (ptr->const_len < len);
+ ptr->hash = full_name_hash(name, len);
+}
+
+/**
+ * tomoyo_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_pattern2(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ while (filename < filename_end && pattern < pattern_end) {
+ char c;
+ if (*pattern != '\\') {
+ if (*filename++ != *pattern++)
+ return false;
+ continue;
+ }
+ c = *filename;
+ pattern++;
+ switch (*pattern) {
+ int i;
+ int j;
+ case '?':
+ if (c == '/') {
+ return false;
+ } else if (c == '\\') {
+ if (filename[1] == '\\')
+ filename++;
+ else if (tomoyo_byte_range(filename + 1))
+ filename += 3;
+ else
+ return false;
+ }
+ break;
+ case '\\':
+ if (c != '\\')
+ return false;
+ if (*++filename != '\\')
+ return false;
+ break;
+ case '+':
+ if (!isdigit(c))
+ return false;
+ break;
+ case 'x':
+ if (!isxdigit(c))
+ return false;
+ break;
+ case 'a':
+ if (!tomoyo_alphabet_char(c))
+ return false;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ if (c == '\\' && tomoyo_byte_range(filename + 1)
+ && strncmp(filename + 1, pattern, 3) == 0) {
+ filename += 3;
+ pattern += 2;
+ break;
+ }
+ return false; /* Not matched. */
+ case '*':
+ case '@':
+ for (i = 0; i <= filename_end - filename; i++) {
+ if (tomoyo_file_matches_pattern2(
+ filename + i, filename_end,
+ pattern + 1, pattern_end))
+ return true;
+ c = filename[i];
+ if (c == '.' && *pattern == '@')
+ break;
+ if (c != '\\')
+ continue;
+ if (filename[i + 1] == '\\')
+ i++;
+ else if (tomoyo_byte_range(filename + i + 1))
+ i += 3;
+ else
+ break; /* Bad pattern. */
+ }
+ return false; /* Not matched. */
+ default:
+ j = 0;
+ c = *pattern;
+ if (c == '$') {
+ while (isdigit(filename[j]))
+ j++;
+ } else if (c == 'X') {
+ while (isxdigit(filename[j]))
+ j++;
+ } else if (c == 'A') {
+ while (tomoyo_alphabet_char(filename[j]))
+ j++;
+ }
+ for (i = 1; i <= j; i++) {
+ if (tomoyo_file_matches_pattern2(
+ filename + i, filename_end,
+ pattern + 1, pattern_end))
+ return true;
+ }
+ return false; /* Not matched or bad pattern. */
+ }
+ filename++;
+ pattern++;
+ }
+ while (*pattern == '\\' &&
+ (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+ pattern += 2;
+ return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * tomoyo_file_matches_pattern - Pattern matching without '/' character.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_pattern(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ const char *pattern_start = pattern;
+ bool first = true;
+ bool result;
+
+ while (pattern < pattern_end - 1) {
+ /* Split at "\-" pattern. */
+ if (*pattern++ != '\\' || *pattern++ != '-')
+ continue;
+ result = tomoyo_file_matches_pattern2(filename,
+ filename_end,
+ pattern_start,
+ pattern - 2);
+ if (first)
+ result = !result;
+ if (result)
+ return false;
+ first = false;
+ pattern_start = pattern;
+ }
+ result = tomoyo_file_matches_pattern2(filename, filename_end,
+ pattern_start, pattern_end);
+ return first ? result : !result;
+}
+
+/**
+ * tomoyo_path_matches_pattern2 - Do pathname pattern matching.
+ *
+ * @f: The start of string to check.
+ * @p: The start of pattern to compare.
+ *
+ * Returns true if @f matches @p, false otherwise.
+ */
+static bool tomoyo_path_matches_pattern2(const char *f, const char *p)
+{
+ const char *f_delimiter;
+ const char *p_delimiter;
+
+ while (*f && *p) {
+ f_delimiter = strchr(f, '/');
+ if (!f_delimiter)
+ f_delimiter = f + strlen(f);
+ p_delimiter = strchr(p, '/');
+ if (!p_delimiter)
+ p_delimiter = p + strlen(p);
+ if (*p == '\\' && *(p + 1) == '{')
+ goto recursive;
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p,
+ p_delimiter))
+ return false;
+ f = f_delimiter;
+ if (*f)
+ f++;
+ p = p_delimiter;
+ if (*p)
+ p++;
+ }
+ /* Ignore trailing "\*" and "\@" in @pattern. */
+ while (*p == '\\' &&
+ (*(p + 1) == '*' || *(p + 1) == '@'))
+ p += 2;
+ return !*f && !*p;
+ recursive:
+ /*
+ * The "\{" pattern is permitted only after '/' character.
+ * This guarantees that below "*(p - 1)" is safe.
+ * Also, the "\}" pattern is permitted only before '/' character
+ * so that "\{" + "\}" pair will not break the "\-" operator.
+ */
+ if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' ||
+ *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\')
+ return false; /* Bad pattern. */
+ do {
+ /* Compare current component with pattern. */
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2,
+ p_delimiter - 2))
+ break;
+ /* Proceed to next component. */
+ f = f_delimiter;
+ if (!*f)
+ break;
+ f++;
+ /* Continue comparison. */
+ if (tomoyo_path_matches_pattern2(f, p_delimiter + 1))
+ return true;
+ f_delimiter = strchr(f, '/');
+ } while (f_delimiter);
+ return false; /* Not matched. */
+}
+
+/**
+ * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ *
+ * @filename: The filename to check.
+ * @pattern: The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ * \\ \ itself.
+ * \ooo Octal representation of a byte.
+ * \* Zero or more repetitions of characters other than '/'.
+ * \@ Zero or more repetitions of characters other than '/' or '.'.
+ * \? 1 byte character other than '/'.
+ * \$ One or more repetitions of decimal digits.
+ * \+ 1 decimal digit.
+ * \X One or more repetitions of hexadecimal digits.
+ * \x 1 hexadecimal digit.
+ * \A One or more repetitions of alphabet characters.
+ * \a 1 alphabet character.
+ *
+ * \- Subtraction operator.
+ *
+ * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/
+ * /dir/dir/dir/ ).
+ */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+ const struct tomoyo_path_info *pattern)
+{
+ const char *f = filename->name;
+ const char *p = pattern->name;
+ const int len = pattern->const_len;
+
+ /* If @pattern doesn't contain pattern, I can use strcmp(). */
+ if (!pattern->is_patterned)
+ return !tomoyo_pathcmp(filename, pattern);
+ /* Don't compare directory and non-directory. */
+ if (filename->is_dir != pattern->is_dir)
+ return false;
+ /* Compare the initial length without patterns. */
+ if (strncmp(f, p, len))
+ return false;
+ f += len;
+ p += len;
+ return tomoyo_path_matches_pattern2(f, p);
+}
+
+/**
+ * tomoyo_get_exe - Get tomoyo_realpath() of current process.
+ *
+ * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so the caller must call kfree()
+ * if this function didn't return NULL.
+ */
+const char *tomoyo_get_exe(void)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ const char *cp = NULL;
+
+ if (!mm)
+ return NULL;
+ down_read(&mm->mmap_sem);
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+ cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
+ break;
+ }
+ }
+ up_read(&mm->mmap_sem);
+ return cp;
+}
+
+/**
+ * tomoyo_get_mode - Get MAC mode.
+ *
+ * @profile: Profile number.
+ * @index: Index number of functionality.
+ *
+ * Returns mode.
+ */
+int tomoyo_get_mode(const u8 profile, const u8 index)
+{
+ u8 mode;
+ const u8 category = TOMOYO_MAC_CATEGORY_FILE;
+ if (!tomoyo_policy_loaded)
+ return TOMOYO_CONFIG_DISABLED;
+ mode = tomoyo_profile(profile)->config[index];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = tomoyo_profile(profile)->config[category];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = tomoyo_profile(profile)->default_config;
+ return mode & 3;
+}
+
+/**
+ * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members.
+ *
+ * @r: Pointer to "struct tomoyo_request_info" to initialize.
+ * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain().
+ * @index: Index number of functionality.
+ *
+ * Returns mode.
+ */
+int tomoyo_init_request_info(struct tomoyo_request_info *r,
+ struct tomoyo_domain_info *domain, const u8 index)
+{
+ u8 profile;
+ memset(r, 0, sizeof(*r));
+ if (!domain)
+ domain = tomoyo_domain();
+ r->domain = domain;
+ profile = domain->profile;
+ r->profile = profile;
+ r->type = index;
+ r->mode = tomoyo_get_mode(profile, index);
+ return r->mode;
+}
+
+/**
+ * tomoyo_last_word - Get last component of a line.
+ *
+ * @line: A line.
+ *
+ * Returns the last word of a line.
+ */
+const char *tomoyo_last_word(const char *name)
+{
+ const char *cp = strrchr(name, ' ');
+ if (cp)
+ return cp + 1;
+ return name;
+}
+
+/**
+ * tomoyo_warn_log - Print warning or error message on console.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ */
+void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ va_list args;
+ char *buffer;
+ const struct tomoyo_domain_info * const domain = r->domain;
+ const struct tomoyo_profile *profile = tomoyo_profile(domain->profile);
+ switch (r->mode) {
+ case TOMOYO_CONFIG_ENFORCING:
+ if (!profile->enforcing->enforcing_verbose)
+ return;
+ break;
+ case TOMOYO_CONFIG_PERMISSIVE:
+ if (!profile->permissive->permissive_verbose)
+ return;
+ break;
+ case TOMOYO_CONFIG_LEARNING:
+ if (!profile->learning->learning_verbose)
+ return;
+ break;
+ }
+ buffer = kmalloc(4096, GFP_NOFS);
+ if (!buffer)
+ return;
+ va_start(args, fmt);
+ vsnprintf(buffer, 4095, fmt, args);
+ va_end(args);
+ buffer[4095] = '\0';
+ printk(KERN_WARNING "%s: Access %s denied for %s\n",
+ r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer,
+ tomoyo_last_word(domain->domainname->name));
+ kfree(buffer);
+}
+
+/**
+ * tomoyo_domain_quota_is_ok - Check for domain's quota.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
+{
+ unsigned int count = 0;
+ struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+
+ if (r->mode != TOMOYO_CONFIG_LEARNING)
+ return false;
+ if (!domain)
+ return true;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ switch (ptr->type) {
+ u16 perm;
+ u8 i;
+ case TOMOYO_TYPE_PATH_ACL:
+ perm = container_of(ptr, struct tomoyo_path_acl, head)
+ ->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
+ count -= 2;
+ break;
+ case TOMOYO_TYPE_PATH2_ACL:
+ perm = container_of(ptr, struct tomoyo_path2_acl, head)
+ ->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ case TOMOYO_TYPE_PATH_NUMBER_ACL:
+ perm = container_of(ptr, struct tomoyo_path_number_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ case TOMOYO_TYPE_MKDEV_ACL:
+ perm = container_of(ptr, struct tomoyo_mkdev_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ default:
+ count++;
+ }
+ }
+ if (count < tomoyo_profile(domain->profile)->learning->
+ learning_max_entry)
+ return true;
+ if (!domain->quota_warned) {
+ domain->quota_warned = true;
+ printk(KERN_WARNING "TOMOYO-WARNING: "
+ "Domain '%s' has so many ACLs to hold. "
+ "Stopped learning mode.\n", domain->domainname->name);
+ }
+ return false;
+}