]> Pileus Git - ~andy/linux/blobdiff - security/tomoyo/common.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[~andy/linux] / security / tomoyo / common.c
index 50481d2cf9705f018976b5dd102ed8d5771d4d5e..c8439cf2a448869820cc5dbf175c45956d9d7698 100644 (file)
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/common.c
  *
- * Common functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/uaccess.h>
@@ -20,40 +18,124 @@ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
 };
 
 /* String table for /sys/kernel/security/tomoyo/profile */
-static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+const char * const 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_GETATTR]    = "file::getattr",
-       [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_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::unmount",
-       [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root",
+       [TOMOYO_MAC_FILE_EXECUTE]    = "execute",
+       [TOMOYO_MAC_FILE_OPEN]       = "open",
+       [TOMOYO_MAC_FILE_CREATE]     = "create",
+       [TOMOYO_MAC_FILE_UNLINK]     = "unlink",
+       [TOMOYO_MAC_FILE_GETATTR]    = "getattr",
+       [TOMOYO_MAC_FILE_MKDIR]      = "mkdir",
+       [TOMOYO_MAC_FILE_RMDIR]      = "rmdir",
+       [TOMOYO_MAC_FILE_MKFIFO]     = "mkfifo",
+       [TOMOYO_MAC_FILE_MKSOCK]     = "mksock",
+       [TOMOYO_MAC_FILE_TRUNCATE]   = "truncate",
+       [TOMOYO_MAC_FILE_SYMLINK]    = "symlink",
+       [TOMOYO_MAC_FILE_MKBLOCK]    = "mkblock",
+       [TOMOYO_MAC_FILE_MKCHAR]     = "mkchar",
+       [TOMOYO_MAC_FILE_LINK]       = "link",
+       [TOMOYO_MAC_FILE_RENAME]     = "rename",
+       [TOMOYO_MAC_FILE_CHMOD]      = "chmod",
+       [TOMOYO_MAC_FILE_CHOWN]      = "chown",
+       [TOMOYO_MAC_FILE_CHGRP]      = "chgrp",
+       [TOMOYO_MAC_FILE_IOCTL]      = "ioctl",
+       [TOMOYO_MAC_FILE_CHROOT]     = "chroot",
+       [TOMOYO_MAC_FILE_MOUNT]      = "mount",
+       [TOMOYO_MAC_FILE_UMOUNT]     = "unmount",
+       [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
        [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
 };
 
+/* String table for conditions. */
+const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
+       [TOMOYO_TASK_UID]             = "task.uid",
+       [TOMOYO_TASK_EUID]            = "task.euid",
+       [TOMOYO_TASK_SUID]            = "task.suid",
+       [TOMOYO_TASK_FSUID]           = "task.fsuid",
+       [TOMOYO_TASK_GID]             = "task.gid",
+       [TOMOYO_TASK_EGID]            = "task.egid",
+       [TOMOYO_TASK_SGID]            = "task.sgid",
+       [TOMOYO_TASK_FSGID]           = "task.fsgid",
+       [TOMOYO_TASK_PID]             = "task.pid",
+       [TOMOYO_TASK_PPID]            = "task.ppid",
+       [TOMOYO_EXEC_ARGC]            = "exec.argc",
+       [TOMOYO_EXEC_ENVC]            = "exec.envc",
+       [TOMOYO_TYPE_IS_SOCKET]       = "socket",
+       [TOMOYO_TYPE_IS_SYMLINK]      = "symlink",
+       [TOMOYO_TYPE_IS_FILE]         = "file",
+       [TOMOYO_TYPE_IS_BLOCK_DEV]    = "block",
+       [TOMOYO_TYPE_IS_DIRECTORY]    = "directory",
+       [TOMOYO_TYPE_IS_CHAR_DEV]     = "char",
+       [TOMOYO_TYPE_IS_FIFO]         = "fifo",
+       [TOMOYO_MODE_SETUID]          = "setuid",
+       [TOMOYO_MODE_SETGID]          = "setgid",
+       [TOMOYO_MODE_STICKY]          = "sticky",
+       [TOMOYO_MODE_OWNER_READ]      = "owner_read",
+       [TOMOYO_MODE_OWNER_WRITE]     = "owner_write",
+       [TOMOYO_MODE_OWNER_EXECUTE]   = "owner_execute",
+       [TOMOYO_MODE_GROUP_READ]      = "group_read",
+       [TOMOYO_MODE_GROUP_WRITE]     = "group_write",
+       [TOMOYO_MODE_GROUP_EXECUTE]   = "group_execute",
+       [TOMOYO_MODE_OTHERS_READ]     = "others_read",
+       [TOMOYO_MODE_OTHERS_WRITE]    = "others_write",
+       [TOMOYO_MODE_OTHERS_EXECUTE]  = "others_execute",
+       [TOMOYO_EXEC_REALPATH]        = "exec.realpath",
+       [TOMOYO_SYMLINK_TARGET]       = "symlink.target",
+       [TOMOYO_PATH1_UID]            = "path1.uid",
+       [TOMOYO_PATH1_GID]            = "path1.gid",
+       [TOMOYO_PATH1_INO]            = "path1.ino",
+       [TOMOYO_PATH1_MAJOR]          = "path1.major",
+       [TOMOYO_PATH1_MINOR]          = "path1.minor",
+       [TOMOYO_PATH1_PERM]           = "path1.perm",
+       [TOMOYO_PATH1_TYPE]           = "path1.type",
+       [TOMOYO_PATH1_DEV_MAJOR]      = "path1.dev_major",
+       [TOMOYO_PATH1_DEV_MINOR]      = "path1.dev_minor",
+       [TOMOYO_PATH2_UID]            = "path2.uid",
+       [TOMOYO_PATH2_GID]            = "path2.gid",
+       [TOMOYO_PATH2_INO]            = "path2.ino",
+       [TOMOYO_PATH2_MAJOR]          = "path2.major",
+       [TOMOYO_PATH2_MINOR]          = "path2.minor",
+       [TOMOYO_PATH2_PERM]           = "path2.perm",
+       [TOMOYO_PATH2_TYPE]           = "path2.type",
+       [TOMOYO_PATH2_DEV_MAJOR]      = "path2.dev_major",
+       [TOMOYO_PATH2_DEV_MINOR]      = "path2.dev_minor",
+       [TOMOYO_PATH1_PARENT_UID]     = "path1.parent.uid",
+       [TOMOYO_PATH1_PARENT_GID]     = "path1.parent.gid",
+       [TOMOYO_PATH1_PARENT_INO]     = "path1.parent.ino",
+       [TOMOYO_PATH1_PARENT_PERM]    = "path1.parent.perm",
+       [TOMOYO_PATH2_PARENT_UID]     = "path2.parent.uid",
+       [TOMOYO_PATH2_PARENT_GID]     = "path2.parent.gid",
+       [TOMOYO_PATH2_PARENT_INO]     = "path2.parent.ino",
+       [TOMOYO_PATH2_PARENT_PERM]    = "path2.parent.perm",
+};
+
 /* String table for PREFERENCE keyword. */
 static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = {
        [TOMOYO_PREF_MAX_AUDIT_LOG]      = "max_audit_log",
        [TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",
 };
 
+/* String table for path operation. */
+const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+       [TOMOYO_TYPE_EXECUTE]    = "execute",
+       [TOMOYO_TYPE_READ]       = "read",
+       [TOMOYO_TYPE_WRITE]      = "write",
+       [TOMOYO_TYPE_APPEND]     = "append",
+       [TOMOYO_TYPE_UNLINK]     = "unlink",
+       [TOMOYO_TYPE_GETATTR]    = "getattr",
+       [TOMOYO_TYPE_RMDIR]      = "rmdir",
+       [TOMOYO_TYPE_TRUNCATE]   = "truncate",
+       [TOMOYO_TYPE_SYMLINK]    = "symlink",
+       [TOMOYO_TYPE_CHROOT]     = "chroot",
+       [TOMOYO_TYPE_UMOUNT]     = "unmount",
+};
+
+/* String table for categories. */
+static const char * const tomoyo_category_keywords
+[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+       [TOMOYO_MAC_CATEGORY_FILE]       = "file",
+};
+
 /* Permit policy management by non-root user? */
 static bool tomoyo_manage_by_non_root;
 
@@ -98,7 +180,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
 {
        while (head->r.w_pos) {
                const char *w = head->r.w[0];
-               int len = strlen(w);
+               size_t len = strlen(w);
                if (len) {
                        if (len > head->read_user_buf_avail)
                                len = head->read_user_buf_avail;
@@ -157,8 +239,8 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
 void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
 {
        va_list args;
-       int len;
-       int pos = head->r.avail;
+       size_t len;
+       size_t pos = head->r.avail;
        int size = head->readbuf_size - pos;
        if (size <= 0)
                return;
@@ -273,15 +355,37 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
 }
 
 /**
- * tomoyo_print_number_union - Print a tomoyo_number_union.
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
  *
- * @head:       Pointer to "struct tomoyo_io_buffer".
- * @ptr:        Pointer to "struct tomoyo_number_union".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
  */
-static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
-                                     const struct tomoyo_number_union *ptr)
+static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+                                          const struct tomoyo_name_union *ptr)
+{
+       if (ptr->group) {
+               tomoyo_set_string(head, "@");
+               tomoyo_set_string(head, ptr->group->group_name->name);
+       } else {
+               tomoyo_set_string(head, "\"");
+               tomoyo_set_string(head, ptr->filename->name);
+               tomoyo_set_string(head, "\"");
+       }
+}
+
+/**
+ * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union_nospace
+(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr)
 {
-       tomoyo_set_space(head);
        if (ptr->group) {
                tomoyo_set_string(head, "@");
                tomoyo_set_string(head, ptr->group->group_name->name);
@@ -304,8 +408,8 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
                                                 "0%lo", min);
                                break;
                        default:
-                               tomoyo_addprintf(buffer, sizeof(buffer),
-                                                "%lu", min);
+                               tomoyo_addprintf(buffer, sizeof(buffer), "%lu",
+                                                min);
                                break;
                        }
                        if (min == max && min_type == max_type)
@@ -318,6 +422,21 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
        }
 }
 
+/**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+                                     const struct tomoyo_number_union *ptr)
+{
+       tomoyo_set_space(head);
+       tomoyo_print_number_union_nospace(head, ptr);
+}
+
 /**
  * tomoyo_assign_profile - Create a new profile.
  *
@@ -436,7 +555,17 @@ static int tomoyo_set_mode(char *name, const char *value,
                config = 0;
                for (i = 0; i < TOMOYO_MAX_MAC_INDEX
                             + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
-                       if (strcmp(name, tomoyo_mac_keywords[i]))
+                       int len = 0;
+                       if (i < TOMOYO_MAX_MAC_INDEX) {
+                               const u8 c = tomoyo_index2category[i];
+                               const char *category =
+                                       tomoyo_category_keywords[c];
+                               len = strlen(category);
+                               if (strncmp(name, category, len) ||
+                                   name[len++] != ':' || name[len++] != ':')
+                                       continue;
+                       }
+                       if (strcmp(name + len, tomoyo_mac_keywords[i]))
                                continue;
                        config = profile->config[i];
                        break;
@@ -620,8 +749,15 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
                        if (config == TOMOYO_CONFIG_USE_DEFAULT)
                                continue;
                        tomoyo_print_namespace(head);
-                       tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::",
-                                        tomoyo_mac_keywords[i]);
+                       if (i < TOMOYO_MAX_MAC_INDEX)
+                               tomoyo_io_printf(head, "%u-CONFIG::%s::%s",
+                                                index,
+                                                tomoyo_category_keywords
+                                                [tomoyo_index2category[i]],
+                                                tomoyo_mac_keywords[i]);
+                       else
+                               tomoyo_io_printf(head, "%u-CONFIG::%s", index,
+                                                tomoyo_mac_keywords[i]);
                        tomoyo_print_config(head, config);
                        head->r.bit++;
                        break;
@@ -637,6 +773,14 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
                goto next;
 }
 
+/**
+ * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static bool tomoyo_same_manager(const struct tomoyo_acl_head *a,
                                const struct tomoyo_acl_head *b)
 {
@@ -905,6 +1049,12 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
        return -EINVAL;
 }
 
+/* String table for domain flags. */
+const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = {
+       [TOMOYO_DIF_QUOTA_WARNED]      = "quota_exceeded\n",
+       [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n",
+};
+
 /**
  * tomoyo_write_domain - Write domain policy.
  *
@@ -948,18 +1098,150 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
                        domain->group = (u8) profile;
                return 0;
        }
-       if (!strcmp(data, "quota_exceeded")) {
-               domain->quota_warned = !is_delete;
-               return 0;
-       }
-       if (!strcmp(data, "transition_failed")) {
-               domain->transition_failed = !is_delete;
+       for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
+               const char *cp = tomoyo_dif[profile];
+               if (strncmp(data, cp, strlen(cp) - 1))
+                       continue;
+               domain->flags[profile] = !is_delete;
                return 0;
        }
        return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
                                    is_delete);
 }
 
+/**
+ * tomoyo_print_condition - Print condition part.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @cond: Pointer to "struct tomoyo_condition".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
+                                  const struct tomoyo_condition *cond)
+{
+       switch (head->r.cond_step) {
+       case 0:
+               head->r.cond_index = 0;
+               head->r.cond_step++;
+               /* fall through */
+       case 1:
+               {
+                       const u16 condc = cond->condc;
+                       const struct tomoyo_condition_element *condp =
+                               (typeof(condp)) (cond + 1);
+                       const struct tomoyo_number_union *numbers_p =
+                               (typeof(numbers_p)) (condp + condc);
+                       const struct tomoyo_name_union *names_p =
+                               (typeof(names_p))
+                               (numbers_p + cond->numbers_count);
+                       const struct tomoyo_argv *argv =
+                               (typeof(argv)) (names_p + cond->names_count);
+                       const struct tomoyo_envp *envp =
+                               (typeof(envp)) (argv + cond->argc);
+                       u16 skip;
+                       for (skip = 0; skip < head->r.cond_index; skip++) {
+                               const u8 left = condp->left;
+                               const u8 right = condp->right;
+                               condp++;
+                               switch (left) {
+                               case TOMOYO_ARGV_ENTRY:
+                                       argv++;
+                                       continue;
+                               case TOMOYO_ENVP_ENTRY:
+                                       envp++;
+                                       continue;
+                               case TOMOYO_NUMBER_UNION:
+                                       numbers_p++;
+                                       break;
+                               }
+                               switch (right) {
+                               case TOMOYO_NAME_UNION:
+                                       names_p++;
+                                       break;
+                               case TOMOYO_NUMBER_UNION:
+                                       numbers_p++;
+                                       break;
+                               }
+                       }
+                       while (head->r.cond_index < condc) {
+                               const u8 match = condp->equals;
+                               const u8 left = condp->left;
+                               const u8 right = condp->right;
+                               if (!tomoyo_flush(head))
+                                       return false;
+                               condp++;
+                               head->r.cond_index++;
+                               tomoyo_set_space(head);
+                               switch (left) {
+                               case TOMOYO_ARGV_ENTRY:
+                                       tomoyo_io_printf(head,
+                                                        "exec.argv[%lu]%s=\"",
+                                                        argv->index, argv->
+                                                        is_not ? "!" : "");
+                                       tomoyo_set_string(head,
+                                                         argv->value->name);
+                                       tomoyo_set_string(head, "\"");
+                                       argv++;
+                                       continue;
+                               case TOMOYO_ENVP_ENTRY:
+                                       tomoyo_set_string(head,
+                                                         "exec.envp[\"");
+                                       tomoyo_set_string(head,
+                                                         envp->name->name);
+                                       tomoyo_io_printf(head, "\"]%s=", envp->
+                                                        is_not ? "!" : "");
+                                       if (envp->value) {
+                                               tomoyo_set_string(head, "\"");
+                                               tomoyo_set_string(head, envp->
+                                                                 value->name);
+                                               tomoyo_set_string(head, "\"");
+                                       } else {
+                                               tomoyo_set_string(head,
+                                                                 "NULL");
+                                       }
+                                       envp++;
+                                       continue;
+                               case TOMOYO_NUMBER_UNION:
+                                       tomoyo_print_number_union_nospace
+                                               (head, numbers_p++);
+                                       break;
+                               default:
+                                       tomoyo_set_string(head,
+                                              tomoyo_condition_keyword[left]);
+                                       break;
+                               }
+                               tomoyo_set_string(head, match ? "=" : "!=");
+                               switch (right) {
+                               case TOMOYO_NAME_UNION:
+                                       tomoyo_print_name_union_quoted
+                                               (head, names_p++);
+                                       break;
+                               case TOMOYO_NUMBER_UNION:
+                                       tomoyo_print_number_union_nospace
+                                               (head, numbers_p++);
+                                       break;
+                               default:
+                                       tomoyo_set_string(head,
+                                         tomoyo_condition_keyword[right]);
+                                       break;
+                               }
+                       }
+               }
+               head->r.cond_step++;
+               /* fall through */
+       case 2:
+               if (!tomoyo_flush(head))
+                       break;
+               head->r.cond_step++;
+               /* fall through */
+       case 3:
+               tomoyo_set_lf(head);
+               return true;
+       }
+       return false;
+}
+
 /**
  * tomoyo_set_group - Print "acl_group " header keyword and category name.
  *
@@ -994,6 +1276,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
        bool first = true;
        u8 bit;
 
+       if (head->r.print_cond_part)
+               goto print_cond_part;
        if (acl->is_deleted)
                return true;
        if (!tomoyo_flush(head))
@@ -1092,7 +1376,18 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
                tomoyo_print_name_union(head, &ptr->fs_type);
                tomoyo_print_number_union(head, &ptr->flags);
        }
-       tomoyo_set_lf(head);
+       if (acl->cond) {
+               head->r.print_cond_part = true;
+               head->r.cond_step = 0;
+               if (!tomoyo_flush(head))
+                       return false;
+print_cond_part:
+               if (!tomoyo_print_condition(head, acl->cond))
+                       return false;
+               head->r.print_cond_part = false;
+       } else {
+               tomoyo_set_lf(head);
+       }
        return true;
 }
 
@@ -1134,6 +1429,7 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
                struct tomoyo_domain_info *domain =
                        list_entry(head->r.domain, typeof(*domain), list);
                switch (head->r.step) {
+                       u8 i;
                case 0:
                        if (domain->is_deleted &&
                            !head->r.print_this_domain_only)
@@ -1145,10 +1441,9 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
                                         domain->profile);
                        tomoyo_io_printf(head, "use_group %u\n",
                                         domain->group);
-                       if (domain->quota_warned)
-                               tomoyo_set_string(head, "quota_exceeded\n");
-                       if (domain->transition_failed)
-                               tomoyo_set_string(head, "transition_failed\n");
+                       for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
+                               if (domain->flags[i])
+                                       tomoyo_set_string(head, tomoyo_dif[i]);
                        head->r.step++;
                        tomoyo_set_lf(head);
                        /* fall through */
@@ -1169,73 +1464,6 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
        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 && (!tomoyo_policy_loaded ||
-                      head->w.ns->profile_ptr[(u8) profile]))
-               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.
  *
@@ -1294,6 +1522,7 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
        tomoyo_set_string(head, domain->domainname->name);
 }
 
+/* String table for domain transition control keywords. */
 static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
        [TOMOYO_TRANSITION_CONTROL_NO_RESET]      = "no_reset_domain ",
        [TOMOYO_TRANSITION_CONTROL_RESET]         = "reset_domain ",
@@ -1303,6 +1532,7 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
        [TOMOYO_TRANSITION_CONTROL_KEEP]          = "keep_domain ",
 };
 
+/* String table for grouping keywords. */
 static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
        [TOMOYO_PATH_GROUP]   = "path_group ",
        [TOMOYO_NUMBER_GROUP] = "number_group ",
@@ -1516,6 +1746,22 @@ static DEFINE_SPINLOCK(tomoyo_query_list_lock);
  */
 static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 
+/**
+ * tomoyo_truncate - Truncate a line.
+ *
+ * @str: String to truncate.
+ *
+ * Returns length of truncated @str.
+ */
+static int tomoyo_truncate(char *str)
+{
+       char *start = str;
+       while (*(unsigned char *) str > (unsigned char) ' ')
+               str++;
+       *str = '\0';
+       return strlen(start) + 1;
+}
+
 /**
  * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
  *
@@ -1527,6 +1773,9 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
 {
        char *buffer;
+       char *realpath = NULL;
+       char *argv0 = NULL;
+       char *symlink = NULL;
        char *cp = strchr(header, '\n');
        int len;
        if (!cp)
@@ -1536,13 +1785,36 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
                return;
        *cp++ = '\0';
        len = strlen(cp) + 1;
+       /* strstr() will return NULL if ordering is wrong. */
+       if (*cp == 'f') {
+               argv0 = strstr(header, " argv[]={ \"");
+               if (argv0) {
+                       argv0 += 10;
+                       len += tomoyo_truncate(argv0) + 14;
+               }
+               realpath = strstr(header, " exec={ realpath=\"");
+               if (realpath) {
+                       realpath += 8;
+                       len += tomoyo_truncate(realpath) + 6;
+               }
+               symlink = strstr(header, " symlink.target=\"");
+               if (symlink)
+                       len += tomoyo_truncate(symlink + 1) + 1;
+       }
        buffer = kmalloc(len, GFP_NOFS);
        if (!buffer)
                return;
        snprintf(buffer, len - 1, "%s", cp);
+       if (realpath)
+               tomoyo_addprintf(buffer, len, " exec.%s", realpath);
+       if (argv0)
+               tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0);
+       if (symlink)
+               tomoyo_addprintf(buffer, len, "%s", symlink);
        tomoyo_normalize_line(buffer);
-       tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
-                            false);
+       if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
+                                 false))
+               tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
        kfree(buffer);
 }
 
@@ -1575,6 +1847,8 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
        /* Nothing more to do if granted. */
        if (r->granted)
                return 0;
+       if (r->mode)
+               tomoyo_update_stat(r->mode);
        switch (r->mode) {
        case TOMOYO_CONFIG_ENFORCING:
                error = -EPERM;
@@ -1691,8 +1965,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait)
 static void tomoyo_read_query(struct tomoyo_io_buffer *head)
 {
        struct list_head *tmp;
-       int pos = 0;
-       int len = 0;
+       unsigned int pos = 0;
+       size_t len = 0;
        char *buf;
        if (head->r.w_pos)
                return;
@@ -1814,15 +2088,111 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
        }
 }
 
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
+       [TOMOYO_STAT_POLICY_UPDATES]    = "update:",
+       [TOMOYO_STAT_POLICY_LEARNING]   = "violation in learning mode:",
+       [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:",
+       [TOMOYO_STAT_POLICY_ENFORCING]  = "violation in enforcing mode:",
+};
+
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = {
+       [TOMOYO_MEMORY_POLICY] = "policy:",
+       [TOMOYO_MEMORY_AUDIT]  = "audit log:",
+       [TOMOYO_MEMORY_QUERY]  = "query message:",
+};
+
+/* Timestamp counter for last updated. */
+static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT];
+/* Counter for number of updates. */
+static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT];
+
+/**
+ * tomoyo_update_stat - Update statistic counters.
+ *
+ * @index: Index for policy type.
+ *
+ * Returns nothing.
+ */
+void tomoyo_update_stat(const u8 index)
+{
+       struct timeval tv;
+       do_gettimeofday(&tv);
+       /*
+        * I don't use atomic operations because race condition is not fatal.
+        */
+       tomoyo_stat_updated[index]++;
+       tomoyo_stat_modified[index] = tv.tv_sec;
+}
+
+/**
+ * tomoyo_read_stat - Read statistic data.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
+{
+       u8 i;
+       unsigned int total = 0;
+       if (head->r.eof)
+               return;
+       for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
+               tomoyo_io_printf(head, "Policy %-30s %10u",
+                                tomoyo_policy_headers[i],
+                                tomoyo_stat_updated[i]);
+               if (tomoyo_stat_modified[i]) {
+                       struct tomoyo_time stamp;
+                       tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
+                       tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
+                                        "%02u:%02u:%02u)",
+                                        stamp.year, stamp.month, stamp.day,
+                                        stamp.hour, stamp.min, stamp.sec);
+               }
+               tomoyo_set_lf(head);
+       }
+       for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
+               unsigned int used = tomoyo_memory_used[i];
+               total += used;
+               tomoyo_io_printf(head, "Memory used by %-22s %10u",
+                                tomoyo_memory_headers[i], used);
+               used = tomoyo_memory_quota[i];
+               if (used)
+                       tomoyo_io_printf(head, " (Quota: %10u)", used);
+               tomoyo_set_lf(head);
+       }
+       tomoyo_io_printf(head, "Total memory used:                    %10u\n",
+                        total);
+       head->r.eof = true;
+}
+
+/**
+ * tomoyo_write_stat - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_stat(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       u8 i;
+       if (tomoyo_str_starts(&data, "Memory used by "))
+               for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
+                       if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
+                               sscanf(data, "%u", &tomoyo_memory_quota[i]);
+       return 0;
+}
+
 /**
  * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
  *
  * @type: Type of interface.
  * @file: Pointer to "struct file".
  *
- * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
- *
- * Caller acquires tomoyo_read_lock().
+ * Returns 0 on success, negative value otherwise.
  */
 int tomoyo_open_control(const u8 type, struct file *file)
 {
@@ -1852,11 +2222,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
                /* /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;
@@ -1867,11 +2232,11 @@ int tomoyo_open_control(const u8 type, struct file *file)
                head->read = tomoyo_read_version;
                head->readbuf_size = 128;
                break;
-       case TOMOYO_MEMINFO:
-               /* /sys/kernel/security/tomoyo/meminfo */
-               head->write = tomoyo_write_memory_quota;
-               head->read = tomoyo_read_memory_counter;
-               head->readbuf_size = 512;
+       case TOMOYO_STAT:
+               /* /sys/kernel/security/tomoyo/stat */
+               head->write = tomoyo_write_stat;
+               head->read = tomoyo_read_stat;
+               head->readbuf_size = 1024;
                break;
        case TOMOYO_PROFILE:
                /* /sys/kernel/security/tomoyo/profile */
@@ -1921,9 +2286,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
                        return -ENOMEM;
                }
        }
-       if (type != TOMOYO_QUERY && type != TOMOYO_AUDIT)
-               head->reader_idx = tomoyo_read_lock();
-       file->private_data = head;
        /*
         * If the file is /sys/kernel/security/tomoyo/query , increment the
         * observer counter.
@@ -1932,6 +2294,8 @@ int tomoyo_open_control(const u8 type, struct file *file)
         */
        if (type == TOMOYO_QUERY)
                atomic_inc(&tomoyo_query_observers);
+       file->private_data = head;
+       tomoyo_notify_gc(head, true);
        return 0;
 }
 
@@ -2000,13 +2364,12 @@ static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head)
  * @buffer_len: Size of @buffer.
  *
  * Returns bytes read on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
  */
-int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
-                       const int buffer_len)
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+                           const int buffer_len)
 {
        int len;
+       int idx;
 
        if (!head->read)
                return -ENOSYS;
@@ -2014,6 +2377,7 @@ int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
                return -EINTR;
        head->read_user_buf = buffer;
        head->read_user_buf_avail = buffer_len;
+       idx = tomoyo_read_lock();
        if (tomoyo_flush(head))
                /* Call the policy handler. */
                do {
@@ -2021,6 +2385,7 @@ int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
                        head->read(head);
                } while (tomoyo_flush(head) &&
                         tomoyo_has_more_namespace(head));
+       tomoyo_read_unlock(idx);
        len = head->read_user_buf - buffer;
        mutex_unlock(&head->io_sem);
        return len;
@@ -2071,21 +2436,21 @@ static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line)
  * @buffer_len: Size of @buffer.
  *
  * Returns @buffer_len on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
  */
-int tomoyo_write_control(struct tomoyo_io_buffer *head,
-                        const char __user *buffer, const int buffer_len)
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+                            const char __user *buffer, const int buffer_len)
 {
        int error = buffer_len;
        size_t avail_len = buffer_len;
        char *cp0 = head->write_buf;
+       int idx;
        if (!head->write)
                return -ENOSYS;
        if (!access_ok(VERIFY_READ, buffer, buffer_len))
                return -EFAULT;
        if (mutex_lock_interruptible(&head->io_sem))
                return -EINTR;
+       idx = tomoyo_read_lock();
        /* Read a line and dispatch it to the policy handler. */
        while (avail_len > 0) {
                char c;
@@ -2145,9 +2510,23 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head,
                case -EPERM:
                        error = -EPERM;
                        goto out;
+               case 0:
+                       switch (head->type) {
+                       case TOMOYO_DOMAINPOLICY:
+                       case TOMOYO_EXCEPTIONPOLICY:
+                       case TOMOYO_STAT:
+                       case TOMOYO_PROFILE:
+                       case TOMOYO_MANAGER:
+                               tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
                }
        }
 out:
+       tomoyo_read_unlock(idx);
        mutex_unlock(&head->io_sem);
        return error;
 }
@@ -2157,30 +2536,18 @@ out:
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
- * Releases memory and returns 0.
- *
- * Caller looses tomoyo_read_lock().
+ * Returns 0.
  */
 int tomoyo_close_control(struct tomoyo_io_buffer *head)
 {
-       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 if (head->type != TOMOYO_AUDIT)
-               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);
-       if (is_write)
-               tomoyo_run_gc();
+       if (head->type == TOMOYO_QUERY &&
+           atomic_dec_and_test(&tomoyo_query_observers))
+               wake_up_all(&tomoyo_answer_wait);
+       tomoyo_notify_gc(head, false);
        return 0;
 }
 
@@ -2216,3 +2583,66 @@ void tomoyo_check_profile(void)
        tomoyo_read_unlock(idx);
        printk(KERN_INFO "Mandatory Access Control activated.\n");
 }
+
+/**
+ * tomoyo_load_builtin_policy - Load built-in policy.
+ *
+ * Returns nothing.
+ */
+void __init tomoyo_load_builtin_policy(void)
+{
+       /*
+        * This include file is manually created and contains built-in policy
+        * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy",
+        * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager",
+        * "tomoyo_builtin_stat" in the form of "static char [] __initdata".
+        */
+#include "builtin-policy.h"
+       u8 i;
+       const int idx = tomoyo_read_lock();
+       for (i = 0; i < 5; i++) {
+               struct tomoyo_io_buffer head = { };
+               char *start = "";
+               switch (i) {
+               case 0:
+                       start = tomoyo_builtin_profile;
+                       head.type = TOMOYO_PROFILE;
+                       head.write = tomoyo_write_profile;
+                       break;
+               case 1:
+                       start = tomoyo_builtin_exception_policy;
+                       head.type = TOMOYO_EXCEPTIONPOLICY;
+                       head.write = tomoyo_write_exception;
+                       break;
+               case 2:
+                       start = tomoyo_builtin_domain_policy;
+                       head.type = TOMOYO_DOMAINPOLICY;
+                       head.write = tomoyo_write_domain;
+                       break;
+               case 3:
+                       start = tomoyo_builtin_manager;
+                       head.type = TOMOYO_MANAGER;
+                       head.write = tomoyo_write_manager;
+                       break;
+               case 4:
+                       start = tomoyo_builtin_stat;
+                       head.type = TOMOYO_STAT;
+                       head.write = tomoyo_write_stat;
+                       break;
+               }
+               while (1) {
+                       char *end = strchr(start, '\n');
+                       if (!end)
+                               break;
+                       *end = '\0';
+                       tomoyo_normalize_line(start);
+                       head.write_buf = start;
+                       tomoyo_parse_policy(&head, start);
+                       start = end + 1;
+               }
+       }
+       tomoyo_read_unlock(idx);
+#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+       tomoyo_check_profile();
+#endif
+}