Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  common.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * security/tomoyo/common.c
 *
 * Copyright (C) 2005-2011  NTT DATA CORPORATION
 */


#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/string_helpers.h>
#include "common.h"

/* String table for operation mode. */
const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
 [TOMOYO_CONFIG_DISABLED]   = "disabled",
 [TOMOYO_CONFIG_LEARNING]   = "learning",
 [TOMOYO_CONFIG_PERMISSIVE] = "permissive",
 [TOMOYO_CONFIG_ENFORCING]  = "enforcing"
};

/* String table for /sys/kernel/security/tomoyo/profile */
const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
           + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
 /* CONFIG::file group */
 [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",
 /* CONFIG::network group */
 [TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       = "inet_stream_bind",
 [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     = "inet_stream_listen",
 [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    = "inet_stream_connect",
 [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        = "inet_dgram_bind",
 [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        = "inet_dgram_send",
 [TOMOYO_MAC_NETWORK_INET_RAW_BIND]          = "inet_raw_bind",
 [TOMOYO_MAC_NETWORK_INET_RAW_SEND]          = "inet_raw_send",
 [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       = "unix_stream_bind",
 [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     = "unix_stream_listen",
 [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    = "unix_stream_connect",
 [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        = "unix_dgram_bind",
 [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        = "unix_dgram_send",
 [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    = "unix_seqpacket_bind",
 [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  = "unix_seqpacket_listen",
 [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect",
 /* CONFIG::misc group */
 [TOMOYO_MAC_ENVIRON] = "env",
 /* CONFIG group */
 [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
 [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network",
 [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc",
};

/* 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 socket's operation. */
const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = {
 [TOMOYO_NETWORK_BIND]    = "bind",
 [TOMOYO_NETWORK_LISTEN]  = "listen",
 [TOMOYO_NETWORK_CONNECT] = "connect",
 [TOMOYO_NETWORK_SEND]    = "send",
};

/* String table for categories. */
static const char * const tomoyo_category_keywords
[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
 [TOMOYO_MAC_CATEGORY_FILE]    = "file",
 [TOMOYO_MAC_CATEGORY_NETWORK] = "network",
 [TOMOYO_MAC_CATEGORY_MISC]    = "misc",
};

/* Permit policy management by non-root user? */
static bool tomoyo_manage_by_non_root;

/* Utility functions. */

/**
 * tomoyo_addprintf - strncat()-like-snprintf().
 *
 * @buffer: Buffer to write to. Must be '\0'-terminated.
 * @len:    Size of @buffer.
 * @fmt:    The printf()'s format string, followed by parameters.
 *
 * Returns nothing.
 */

__printf(3, 4)
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];
  size_t 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 audit logs and 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);
}

static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
        ...) __printf(2, 3);

/**
 * 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.
 */

static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
        ...)
{
 va_list args;
 size_t len;
 size_t 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);
}

/**
 * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns nothing.
 */

static void tomoyo_set_space(struct tomoyo_io_buffer *head)
{
 tomoyo_set_string(head, " ");
}

/**
 * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns nothing.
 */

static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
{
 tomoyo_set_string(head, "\n");
 return !head->r.w_pos;
}

/**
 * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns nothing.
 */

static void tomoyo_set_slash(struct tomoyo_io_buffer *head)
{
 tomoyo_set_string(head, "/");
}

/* List of namespaces. */
LIST_HEAD(tomoyo_namespace_list);
/* True if namespace other than tomoyo_kernel_namespace is defined. */
static bool tomoyo_namespace_enabled;

/**
 * tomoyo_init_policy_namespace - Initialize namespace.
 *
 * @ns: Pointer to "struct tomoyo_policy_namespace".
 *
 * Returns nothing.
 */

void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
{
 unsigned int idx;

 for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
  INIT_LIST_HEAD(&ns->acl_group[idx]);
 for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
  INIT_LIST_HEAD(&ns->group_list[idx]);
 for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
  INIT_LIST_HEAD(&ns->policy_list[idx]);
 ns->profile_version = 20150505;
 tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
 list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
}

/**
 * tomoyo_print_namespace - Print namespace header.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns nothing.
 */

static void tomoyo_print_namespace(struct tomoyo_io_buffer *head)
{
 if (!tomoyo_namespace_enabled)
  return;
 tomoyo_set_string(head,
     container_of(head->r.ns,
           struct tomoyo_policy_namespace,
           namespace_list)->name);
 tomoyo_set_space(head);
}

/**
 * tomoyo_print_name_union - Print a tomoyo_name_union.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 * @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->group) {
  tomoyo_set_string(head, "@");
  tomoyo_set_string(head, ptr->group->group_name->name);
 } else {
  tomoyo_set_string(head, ptr->filename->name);
 }
}

/**
 * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 * @ptr:  Pointer to "struct tomoyo_name_union".
 *
 * Returns nothing.
 */

static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
        const struct tomoyo_name_union *ptr)
{
 if (ptr->group) {
  tomoyo_set_string(head, "@");
  tomoyo_set_string(head, ptr->group->group_name->name);
 } else {
  tomoyo_set_string(head, "\"");
  tomoyo_set_string(head, ptr->filename->name);
  tomoyo_set_string(head, "\"");
 }
}

/**
 * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 * @ptr:  Pointer to "struct tomoyo_number_union".
 *
 * Returns nothing.
 */

static void tomoyo_print_number_union_nospace
(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr)
{
 if (ptr->group) {
  tomoyo_set_string(head, "@");
  tomoyo_set_string(head, ptr->group->group_name->name);
 } else {
  int i;
  unsigned long min = ptr->values[0];
  const unsigned long max = ptr->values[1];
  u8 min_type = ptr->value_type[0];
  const u8 max_type = ptr->value_type[1];
  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_print_number_union - Print a tomoyo_number_union.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 * @ptr:  Pointer to "struct tomoyo_number_union".
 *
 * Returns nothing.
 */

static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
          const struct tomoyo_number_union *ptr)
{
 tomoyo_set_space(head);
 tomoyo_print_number_union_nospace(head, ptr);
}

/**
 * tomoyo_assign_profile - Create a new profile.
 *
 * @ns:      Pointer to "struct tomoyo_policy_namespace".
 * @profile: Profile number to create.
 *
 * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
 */

static struct tomoyo_profile *tomoyo_assign_profile
(struct tomoyo_policy_namespace *ns, const unsigned int profile)
{
 struct tomoyo_profile *ptr;
 struct tomoyo_profile *entry;

 if (profile >= TOMOYO_MAX_PROFILES)
  return NULL;
 ptr = ns->profile_ptr[profile];
 if (ptr)
  return ptr;
 entry = kzalloc(sizeof(*entry), GFP_NOFS | __GFP_NOWARN);
 if (mutex_lock_interruptible(&tomoyo_policy_lock))
  goto out;
 ptr = ns->profile_ptr[profile];
 if (!ptr && tomoyo_memory_ok(entry)) {
  ptr = entry;
  ptr->default_config = TOMOYO_CONFIG_DISABLED |
   TOMOYO_CONFIG_WANT_GRANT_LOG |
   TOMOYO_CONFIG_WANT_REJECT_LOG;
  memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
         sizeof(ptr->config));
  ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] =
   CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG;
  ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] =
   CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY;
  mb(); /* Avoid out-of-order execution. */
  ns->profile_ptr[profile] = ptr;
  entry = NULL;
 }
 mutex_unlock(&tomoyo_policy_lock);
 out:
 kfree(entry);
 return ptr;
}

/**
 * tomoyo_profile - Find a profile.
 *
 * @ns:      Pointer to "struct tomoyo_policy_namespace".
 * @profile: Profile number to find.
 *
 * Returns pointer to "struct tomoyo_profile".
 */

struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
          const u8 profile)
{
 static struct tomoyo_profile tomoyo_null_profile;
 struct tomoyo_profile *ptr = ns->profile_ptr[profile];

 if (!ptr)
  ptr = &tomoyo_null_profile;
 return ptr;
}

/**
 * tomoyo_find_yesno - Find values for specified keyword.
 *
 * @string: String to check.
 * @find:   Name of keyword.
 *
 * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise.
 */

static s8 tomoyo_find_yesno(const char *string, const char *find)
{
 const char *cp = strstr(string, find);

 if (cp) {
  cp += strlen(find);
  if (!strncmp(cp, "=yes", 4))
   return 1;
  else if (!strncmp(cp, "=no", 3))
   return 0;
 }
 return -1;
}

/**
 * tomoyo_set_uint - Set value for specified preference.
 *
 * @i:      Pointer to "unsigned int".
 * @string: String to check.
 * @find:   Name of keyword.
 *
 * Returns nothing.
 */

static void tomoyo_set_uint(unsigned int *i, const char *string,
       const char *find)
{
 const char *cp = strstr(string, find);

 if (cp)
  sscanf(cp + strlen(find), "=%u", i);
}

/**
 * tomoyo_set_mode - Set mode for specified profile.
 *
 * @name:    Name of functionality.
 * @value:   Mode for @name.
 * @profile: Pointer to "struct tomoyo_profile".
 *
 * Returns 0 on success, negative value otherwise.
 */

static int tomoyo_set_mode(char *name, const char *value,
      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++) {
   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;
  }
  if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
   return -EINVAL;
 } else {
  return -EINVAL;
 }
 if (strstr(value, "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_DEFAULT'.
 */

    config = (config & ~7) | mode;
  if (config != TOMOYO_CONFIG_USE_DEFAULT) {
   switch (tomoyo_find_yesno(value, "grant_log")) {
   case 1:
    config |= TOMOYO_CONFIG_WANT_GRANT_LOG;
    break;
   case 0:
    config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG;
    break;
   }
   switch (tomoyo_find_yesno(value, "reject_log")) {
   case 1:
    config |= TOMOYO_CONFIG_WANT_REJECT_LOG;
    break;
   case 0:
    config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG;
    break;
   }
  }
 }
 if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
  profile->config[i] = config;
 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;
 char *cp;
 struct tomoyo_profile *profile;

 if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
     == 1)
  return 0;
 i = simple_strtoul(data, &cp, 10);
 if (*cp != '-')
  return -EINVAL;
 data = cp + 1;
 profile = tomoyo_assign_profile(head->w.ns, i);
 if (!profile)
  return -EINVAL;
 cp = strchr(data, '=');
 if (!cp)
  return -EINVAL;
 *cp++ = '\0';
 if (!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;
 }
 if (!strcmp(data, "PREFERENCE")) {
  for (i = 0; i < TOMOYO_MAX_PREF; i++)
   tomoyo_set_uint(&profile->pref[i], cp,
     tomoyo_pref_keywords[i]);
  return 0;
 }
 return tomoyo_set_mode(data, cp, profile);
}

/**
 * tomoyo_print_config - Print mode for specified functionality.
 *
 * @head:   Pointer to "struct tomoyo_io_buffer".
 * @config: Mode for that functionality.
 *
 * Returns nothing.
 *
 * Caller prints functionality's name.
 */

static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config)
{
 tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n",
    tomoyo_mode[config & 3],
    str_yes_no(config & TOMOYO_CONFIG_WANT_GRANT_LOG),
    str_yes_no(config & TOMOYO_CONFIG_WANT_REJECT_LOG));
}

/**
 * tomoyo_read_profile - Read profile table.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns nothing.
 */

static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
{
 u8 index;
 struct tomoyo_policy_namespace *ns =
  container_of(head->r.ns, typeof(*ns), namespace_list);
 const struct tomoyo_profile *profile;

 if (head->r.eof)
  return;
 next:
 index = head->r.index;
 profile = ns->profile_ptr[index];
 switch (head->r.step) {
 case 0:
  tomoyo_print_namespace(head);
  tomoyo_io_printf(head, "PROFILE_VERSION=%u\n",
     ns->profile_version);
  head->r.step++;
  break;
 case 1:
  for ( ; head->r.index < TOMOYO_MAX_PROFILES;
        head->r.index++)
   if (ns->profile_ptr[head->r.index])
    break;
  if (head->r.index == TOMOYO_MAX_PROFILES) {
   head->r.eof = true;
   return;
  }
  head->r.step++;
  break;
 case 2:
  {
   u8 i;
   const struct tomoyo_path_info *comment =
    profile->comment;

   tomoyo_print_namespace(head);
   tomoyo_io_printf(head, "%u-COMMENT=", index);
   tomoyo_set_string(head, comment ? comment->name : "");
   tomoyo_set_lf(head);
   tomoyo_print_namespace(head);
   tomoyo_io_printf(head, "%u-PREFERENCE={ ", index);
   for (i = 0; i < TOMOYO_MAX_PREF; i++)
    tomoyo_io_printf(head, "%s=%u ",
       tomoyo_pref_keywords[i],
       profile->pref[i]);
   tomoyo_set_string(head, "}\n");
   head->r.step++;
  }
  break;
 case 3:
  {
   tomoyo_print_namespace(head);
   tomoyo_io_printf(head, "%u-%s", index, "CONFIG");
   tomoyo_print_config(head, profile->default_config);
   head->r.bit = 0;
   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_print_namespace(head);
   if (i < TOMOYO_MAX_MAC_INDEX)
    tomoyo_io_printf(head, "%u-CONFIG::%s::%s",
       index,
       tomoyo_category_keywords
       [tomoyo_index2category[i]],
       tomoyo_mac_keywords[i]);
   else
    tomoyo_io_printf(head, "%u-CONFIG::%s", index,
       tomoyo_mac_keywords[i]);
   tomoyo_print_config(head, config);
   head->r.bit++;
   break;
  }
  if (head->r.bit == TOMOYO_MAX_MAC_INDEX
      + TOMOYO_MAX_MAC_CATEGORY_INDEX) {
   head->r.index++;
   head->r.step = 1;
  }
  break;
 }
 if (tomoyo_flush(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)
{
 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 = { };
 struct tomoyo_acl_param param = {
  /* .ns = &tomoyo_kernel_namespace, */
  .is_delete = is_delete,
  .list = &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER],
 };
 int error = is_delete ? -ENOENT : -ENOMEM;

 if (!tomoyo_correct_domain(manager) &&
     !tomoyo_correct_word(manager))
  return -EINVAL;
 e.manager = tomoyo_get_name(manager);
 if (e.manager) {
  error = tomoyo_update_policy(&e.head, sizeof(e), ¶m,
          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;

 if (!strcmp(data, "manage_by_non_root")) {
  tomoyo_manage_by_non_root = !head->w.is_delete;
  return 0;
 }
 return tomoyo_update_manager_entry(data, head->w.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_kernel_namespace.policy_list[TOMOYO_ID_MANAGER]) {
  struct tomoyo_manager *ptr =
   list_entry(head->r.acl, typeof(*ptr), head.list);

  if (ptr->head.is_deleted)
   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 = IS_ENABLED(CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING);

 if (!tomoyo_policy_loaded)
  return true;
 if (!tomoyo_manage_by_non_root &&
     (!uid_eq(task->cred->uid,  GLOBAL_ROOT_UID) ||
      !uid_eq(task->cred->euid, GLOBAL_ROOT_UID)))
  return false;
 exe = tomoyo_get_exe();
 if (!exe)
  return false;
 list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list,
    srcu_read_lock_held(&tomoyo_ss)) {
  if (!ptr->head.is_deleted &&
      (!tomoyo_pathcmp(domainname, ptr->manager) ||
       !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) {
   pr_warn("%s ( %s ) is not permitted to update policies.\n",
    domainname->name, exe);
   last_pid = pid;
  }
 }
 kfree(exe);
 return found;
}

static struct tomoyo_domain_info *tomoyo_find_domain_by_qid
(unsigned int serial);

/**
 * tomoyo_select_domain - 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_domain(struct tomoyo_io_buffer *head,
     const char *data)
{
 unsigned int pid;
 struct tomoyo_domain_info *domain = NULL;
 bool global_pid = false;

 if (strncmp(data, "select ", 7))
  return false;
 data += 7;
 if (sscanf(data, "pid=%u", &pid) == 1 ||
     (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
  struct task_struct *p;

  rcu_read_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_task(p)->domain_info;
  rcu_read_unlock();
 } else if (!strncmp(data, "domain=", 7)) {
  if (tomoyo_domain_def(data + 7))
   domain = tomoyo_find_domain(data + 7);
 } else if (sscanf(data, "Q=%u", &pid) == 1) {
  domain = tomoyo_find_domain_by_qid(pid);
 } else
  return false;
 head->w.domain = domain;
 /* Accessing read_buf is safe because head->io_sem is held. */
 if (!head->read_buf)
  return true/* Do nothing if open(O_WRONLY). */
 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 = true;
 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_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry.
 *
 * @a: Pointer to "struct tomoyo_acl_info".
 * @b: Pointer to "struct tomoyo_acl_info".
 *
 * Returns true if @a == @b, false otherwise.
 */

static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
     const struct tomoyo_acl_info *b)
{
 const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
 const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);

 return p1->domainname == p2->domainname;
}

/**
 * tomoyo_write_task - Update task related list.
 *
 * @param: Pointer to "struct tomoyo_acl_param".
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */

static int tomoyo_write_task(struct tomoyo_acl_param *param)
{
 int error = -EINVAL;

 if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) {
  struct tomoyo_task_acl e = {
   .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
   .domainname = tomoyo_get_domainname(param),
  };

  if (e.domainname)
   error = tomoyo_update_domain(&e.head, sizeof(e), param,
           tomoyo_same_task_acl,
           NULL);
  tomoyo_put_name(e.domainname);
 }
 return error;
}

/**
 * tomoyo_delete_domain - Delete a domain.
 *
 * @domainname: The name of domain.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * 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 -EINTR;
 /* Is there an active domain? */
 list_for_each_entry_rcu(domain, &tomoyo_domain_list, list,
    srcu_read_lock_held(&tomoyo_ss)) {
  /* 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.
 *
 * @ns:        Pointer to "struct tomoyo_policy_namespace".
 * @list:      Pointer to "struct list_head".
 * @data:      Policy to be interpreted.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */

static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
    struct list_head *list, char *data,
    const bool is_delete)
{
 struct tomoyo_acl_param param = {
  .ns = ns,
  .list = list,
  .data = data,
  .is_delete = is_delete,
 };
 static const struct {
  const char *keyword;
  int (*write)(struct tomoyo_acl_param *param);
 } tomoyo_callback[5] = {
  { "file ", tomoyo_write_file },
  { "network inet ", tomoyo_write_inet_network },
  { "network unix ", tomoyo_write_unix_network },
  { "misc ", tomoyo_write_misc },
  { "task ", tomoyo_write_task },
 };
 u8 i;

 for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) {
  if (!tomoyo_str_starts(¶m.data,
           tomoyo_callback[i].keyword))
   continue;
  return tomoyo_callback[i].write(¶m);
 }
 return -EINVAL;
}

/* String table for domain flags. */
const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = {
 [TOMOYO_DIF_QUOTA_WARNED]      = "quota_exceeded\n",
 [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n",
};

/**
 * tomoyo_write_domain - Write domain policy.
 *
 * @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_policy_namespace *ns;
 struct tomoyo_domain_info *domain = head->w.domain;
 const bool is_delete = head->w.is_delete;
 bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
 unsigned int idx;

 if (*data == '<') {
  int ret = 0;

  domain = NULL;
  if (is_delete)
   ret = tomoyo_delete_domain(data);
  else if (is_select)
   domain = tomoyo_find_domain(data);
  else
   domain = tomoyo_assign_domain(data, false);
  head->w.domain = domain;
  return ret;
 }
 if (!domain)
  return -EINVAL;
 ns = domain->ns;
 if (sscanf(data, "use_profile %u", &idx) == 1
     && idx < TOMOYO_MAX_PROFILES) {
  if (!tomoyo_policy_loaded || ns->profile_ptr[idx])
   if (!is_delete)
    domain->profile = (u8) idx;
  return 0;
 }
 if (sscanf(data, "use_group %u\n", &idx) == 1
     && idx < TOMOYO_MAX_ACL_GROUPS) {
  if (!is_delete)
   set_bit(idx, domain->group);
  else
   clear_bit(idx, domain->group);
  return 0;
 }
 for (idx = 0; idx < TOMOYO_MAX_DOMAIN_INFO_FLAGS; idx++) {
  const char *cp = tomoyo_dif[idx];

  if (strncmp(data, cp, strlen(cp) - 1))
   continue;
  domain->flags[idx] = !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++;
  if (cond->transit) {
   tomoyo_set_space(head);
   tomoyo_set_string(head, cond->transit->name);
  }
  fallthrough;
 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++;
  fallthrough;
 case 2:
  if (!tomoyo_flush(head))
   break;
  head->r.cond_step++;
  fallthrough;
 case 3:
  if (cond->grant_log != TOMOYO_GRANTLOG_AUTO)
   tomoyo_io_printf(head, " grant_log=%s",
      str_yes_no(cond->grant_log ==
          TOMOYO_GRANTLOG_YES));
  tomoyo_set_lf(head);
  return true;
 }
 return false;
}

/**
 * tomoyo_set_group - Print "acl_group " header keyword and category name.
 *
 * @head:     Pointer to "struct tomoyo_io_buffer".
 * @category: Category name.
 *
 * Returns nothing.
 */

static void tomoyo_set_group(struct tomoyo_io_buffer *head,
        const char *category)
{
 if (head->type == TOMOYO_EXCEPTIONPOLICY) {
  tomoyo_print_namespace(head);
  tomoyo_io_printf(head, "acl_group %u ",
     head->r.acl_group_index);
 }
 tomoyo_set_string(head, category);
}

/**
 * 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;
 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))
  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 = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
   if (!(perm & (1 << bit)))
    continue;
   if (head->r.print_transition_related_only &&
       bit != TOMOYO_TYPE_EXECUTE)
    continue;
   if (first) {
    tomoyo_set_group(head, "file ");
    first = false;
   } else {
    tomoyo_set_slash(head);
   }
   tomoyo_set_string(head, tomoyo_path_keyword[bit]);
  }
  if (first)
   return true;
  tomoyo_print_name_union(head, &ptr->name);
 } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
  struct tomoyo_task_acl *ptr =
   container_of(acl, typeof(*ptr), head);

  tomoyo_set_group(head, "task ");
  tomoyo_set_string(head, "manual_domain_transition ");
  tomoyo_set_string(head, ptr->domainname->name);
 } else if (head->r.print_transition_related_only) {
  return true;
 } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
  struct tomoyo_path2_acl *ptr =
   container_of(acl, typeof(*ptr), head);
  const u8 perm = ptr->perm;

  for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
   if (!(perm & (1 << bit)))
    continue;
   if (first) {
    tomoyo_set_group(head, "file ");
    first = false;
   } else {
    tomoyo_set_slash(head);
   }
   tomoyo_set_string(head, tomoyo_mac_keywords
       [tomoyo_pp2mac[bit]]);
  }
  if (first)
   return true;
  tomoyo_print_name_union(head, &ptr->name1);
  tomoyo_print_name_union(head, &ptr->name2);
 } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
  struct tomoyo_path_number_acl *ptr =
   container_of(acl, typeof(*ptr), head);
  const u8 perm = ptr->perm;

  for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
   if (!(perm & (1 << bit)))
    continue;
   if (first) {
    tomoyo_set_group(head, "file ");
    first = false;
   } else {
    tomoyo_set_slash(head);
   }
   tomoyo_set_string(head, tomoyo_mac_keywords
       [tomoyo_pn2mac[bit]]);
  }
  if (first)
   return true;
  tomoyo_print_name_union(head, &ptr->name);
  tomoyo_print_number_union(head, &ptr->number);
 } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) {
  struct tomoyo_mkdev_acl *ptr =
   container_of(acl, typeof(*ptr), head);
  const u8 perm = ptr->perm;

  for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
   if (!(perm & (1 << bit)))
    continue;
   if (first) {
    tomoyo_set_group(head, "file ");
    first = false;
   } else {
    tomoyo_set_slash(head);
   }
   tomoyo_set_string(head, tomoyo_mac_keywords
       [tomoyo_pnnn2mac[bit]]);
  }
  if (first)
   return true;
  tomoyo_print_name_union(head, &ptr->name);
  tomoyo_print_number_union(head, &ptr->mode);
  tomoyo_print_number_union(head, &ptr->major);
  tomoyo_print_number_union(head, &ptr->minor);
 } else if (acl_type == TOMOYO_TYPE_INET_ACL) {
  struct tomoyo_inet_acl *ptr =
   container_of(acl, typeof(*ptr), head);
  const u8 perm = ptr->perm;

  for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
   if (!(perm & (1 << bit)))
    continue;
   if (first) {
    tomoyo_set_group(head, "network inet ");
    tomoyo_set_string(head, tomoyo_proto_keyword
        [ptr->protocol]);
    tomoyo_set_space(head);
    first = false;
   } else {
    tomoyo_set_slash(head);
   }
   tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
  }
  if (first)
   return true;
  tomoyo_set_space(head);
  if (ptr->address.group) {
   tomoyo_set_string(head, "@");
   tomoyo_set_string(head, ptr->address.group->group_name
       ->name);
  } else {
   char buf[128];

   tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
   tomoyo_io_printf(head, "%s", buf);
  }
  tomoyo_print_number_union(head, &ptr->port);
 } else if (acl_type == TOMOYO_TYPE_UNIX_ACL) {
  struct tomoyo_unix_acl *ptr =
   container_of(acl, typeof(*ptr), head);
  const u8 perm = ptr->perm;

  for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
   if (!(perm & (1 << bit)))
    continue;
   if (first) {
    tomoyo_set_group(head, "network unix ");
    tomoyo_set_string(head, tomoyo_proto_keyword
        [ptr->protocol]);
    tomoyo_set_space(head);
    first = false;
   } else {
    tomoyo_set_slash(head);
   }
   tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
  }
  if (first)
   return true;
  tomoyo_print_name_union(head, &ptr->name);
 } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
  struct tomoyo_mount_acl *ptr =
   container_of(acl, typeof(*ptr), head);

  tomoyo_set_group(head, "file mount");
  tomoyo_print_name_union(head, &ptr->dev_name);
  tomoyo_print_name_union(head, &ptr->dir_name);
  tomoyo_print_name_union(head, &ptr->fs_type);
  tomoyo_print_number_union(head, &ptr->flags);
 } else if (acl_type == TOMOYO_TYPE_ENV_ACL) {
  struct tomoyo_env_acl *ptr =
   container_of(acl, typeof(*ptr), head);

  tomoyo_set_group(head, "misc env ");
  tomoyo_set_string(head, ptr->env->name);
 }
 if (acl->cond) {
  head->r.print_cond_part = true;
  head->r.cond_step = 0;
  if (!tomoyo_flush(head))
   return false;
print_cond_part:
  if (!tomoyo_print_condition(head, acl->cond))
   return false;
  head->r.print_cond_part = false;
 } else {
  tomoyo_set_lf(head);
 }
 return true;
}

/**
 * tomoyo_read_domain2 - Read domain policy.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 * @list: Pointer to "struct list_head".
 *
 * Caller holds tomoyo_read_lock().
 *
 * Returns true on success, false otherwise.
 */

static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
    struct list_head *list)
{
 list_for_each_cookie(head->r.acl, list) {
  struct tomoyo_acl_info *ptr =
   list_entry(head->r.acl, typeof(*ptr), list);

  if (!tomoyo_print_entry(head, ptr))
   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);
  u8 i;

  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, "use_profile %u\n",
      domain->profile);
   for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
    if (domain->flags[i])
     tomoyo_set_string(head, tomoyo_dif[i]);
   head->r.index = 0;
   head->r.step++;
   fallthrough;
  case 1:
   while (head->r.index < TOMOYO_MAX_ACL_GROUPS) {
    i = head->r.index++;
    if (!test_bit(i, domain->group))
     continue;
    tomoyo_io_printf(head, "use_group %u\n", i);
    if (!tomoyo_flush(head))
     return;
   }
   head->r.index = 0;
   head->r.step++;
   tomoyo_set_lf(head);
   fallthrough;
  case 2:
   if (!tomoyo_read_domain2(head, &domain->acl_info_list))
    return;
   head->r.step++;
   if (!tomoyo_set_lf(head))
    return;
   fallthrough;
  case 3:
   head->r.step = 0;
   if (head->r.print_this_domain_only)
    goto done;
  }
 }
 done:
 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;
 if (kstrtouint(buf, 10, &pid))
  return;
 rcu_read_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_task(p)->domain_info;
 rcu_read_unlock();
 if (!domain)
  return;
 tomoyo_io_printf(head, "%u %u ", pid, domain->profile);
 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 ",
 [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
 [TOMOYO_TRANSITION_CONTROL_INITIALIZE]    = "initialize_domain ",
 [TOMOYO_TRANSITION_CONTROL_NO_KEEP]       = "no_keep_domain ",
 [TOMOYO_TRANSITION_CONTROL_KEEP]          = "keep_domain ",
};

/* String table for grouping keywords. */
static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
 [TOMOYO_PATH_GROUP]    = "path_group ",
 [TOMOYO_NUMBER_GROUP]  = "number_group ",
 [TOMOYO_ADDRESS_GROUP] = "address_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)
{
 const bool is_delete = head->w.is_delete;
 struct tomoyo_acl_param param = {
  .ns = head->w.ns,
  .is_delete = is_delete,
  .data = head->write_buf,
 };
 u8 i;

 if (tomoyo_str_starts(¶m.data, "aggregator "))
  return tomoyo_write_aggregator(¶m);
 for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
  if (tomoyo_str_starts(¶m.data, tomoyo_transition_type[i]))
   return tomoyo_write_transition_control(¶m, i);
 for (i = 0; i < TOMOYO_MAX_GROUP; i++)
  if (tomoyo_str_starts(¶m.data, tomoyo_group_name[i]))
   return tomoyo_write_group(¶m, i);
 if (tomoyo_str_starts(¶m.data, "acl_group ")) {
  unsigned int group;
  char *data;

  group = simple_strtoul(param.data, &data, 10);
  if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
   return tomoyo_write_domain2
    (head->w.ns, &head->w.ns->acl_group[group],
     data, is_delete);
 }
 return -EINVAL;
}

/**
 * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_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)
{
 struct tomoyo_policy_namespace *ns =
  container_of(head->r.ns, typeof(*ns), namespace_list);
 struct list_head *list = &ns->group_list[idx];

 list_for_each_cookie(head->r.group, list) {
  struct tomoyo_group *group =
   list_entry(head->r.group, typeof(*group), head.list);

  list_for_each_cookie(head->r.acl, &group->member_list) {
   struct tomoyo_acl_head *ptr =
    list_entry(head->r.acl, typeof(*ptr), list);

   if (ptr->is_deleted)
    continue;
   if (!tomoyo_flush(head))
    return false;
   tomoyo_print_namespace(head);
   tomoyo_set_string(head, tomoyo_group_name[idx]);
   tomoyo_set_string(head, group->group_name->name);
   if (idx == TOMOYO_PATH_GROUP) {
    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);
   } else if (idx == TOMOYO_ADDRESS_GROUP) {
    char buffer[128];
    struct tomoyo_address_group *member =
     container_of(ptr, typeof(*member),
           head);

    tomoyo_print_ip(buffer, sizeof(buffer),
      &member->address);
    tomoyo_io_printf(head, " %s", buffer);
   }
   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)
{
 struct tomoyo_policy_namespace *ns =
  container_of(head->r.ns, typeof(*ns), namespace_list);
 struct list_head *list = &ns->policy_list[idx];

 list_for_each_cookie(head->r.acl, list) {
  struct tomoyo_acl_head *acl =
   container_of(head->r.acl, typeof(*acl), list);
  if (acl->is_deleted)
   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_print_namespace(head);
    tomoyo_set_string(head, tomoyo_transition_type
        [ptr->type]);
    tomoyo_set_string(head, ptr->program ?
        ptr->program->name : "any");
    tomoyo_set_string(head, " from ");
    tomoyo_set_string(head, ptr->domainname ?
        ptr->domainname->name :
        "any");
   }
   break;
  case TOMOYO_ID_AGGREGATOR:
   {
    struct tomoyo_aggregator *ptr =
     container_of(acl, typeof(*ptr), head);

    tomoyo_print_namespace(head);
    tomoyo_set_string(head, "aggregator ");
    tomoyo_set_string(head,
        ptr->original_name->name);
    tomoyo_set_space(head);
    tomoyo_set_string(head,
            ptr->aggregated_name->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)
{
 struct tomoyo_policy_namespace *ns =
  container_of(head->r.ns, typeof(*ns), namespace_list);

 if (head->r.eof)
  return;
 while (head->r.step < TOMOYO_MAX_POLICY &&
        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;
 while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP
        + TOMOYO_MAX_ACL_GROUPS) {
  head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY
   - TOMOYO_MAX_GROUP;
  if (!tomoyo_read_domain2(head, &ns->acl_group
      [head->r.acl_group_index]))
   return;
  head->r.step++;
 }
 head->r.eof = true;
}

/* Wait queue for kernel -> userspace notification. */
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
/* Wait queue for userspace -> kernel notification. */
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait);

/* Structure for query. */
struct tomoyo_query {
 struct list_head list;
 struct tomoyo_domain_info *domain;
 char *query;
 size_t query_len;
 unsigned int serial;
 u8 timer;
 u8 answer;
 u8 retry;
};

/* The list for "struct tomoyo_query". */
static LIST_HEAD(tomoyo_query_list);

/* Lock for manipulating tomoyo_query_list. */
static DEFINE_SPINLOCK(tomoyo_query_list_lock);

/*
 * Number of "struct file" referring /sys/kernel/security/tomoyo/query
 * interface.
 */

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_numscan - sscanf() which stores the length of a decimal integer value.
 *
 * @str:   String to scan.
 * @head:  Leading string that must start with.
 * @width: Pointer to "int" for storing length of a decimal integer value after @head.
 * @tail:  Optional character that must match after a decimal integer value.
 *
 * Returns whether @str starts with @head and a decimal value follows @head.
 */

static bool tomoyo_numscan(const char *str, const char *head, int *width, const char tail)
{
 const char *cp;
 const int n = strlen(head);

 if (!strncmp(str, head, n)) {
  cp = str + n;
  while (*cp && *cp >= '0' && *cp <= '9')
   cp++;
  if (*cp == tail || !tail) {
   *width = cp - (str + n);
   return *width != 0;
  }
 }
 *width = 0;
 return 0;
}

/**
 * tomoyo_patternize_path - Make patterns for file path. Used by learning mode.
 *
 * @buffer: Destination buffer.
 * @len:    Size of @buffer.
 * @entry:  Original line.
 *
 * Returns nothing.
 */

static void tomoyo_patternize_path(char *buffer, const int len, char *entry)
{
 int width;
 char *cp = entry;

 /* Nothing to do if this line is not for "file" related entry. */
 if (strncmp(entry, "file ", 5))
  goto flush;
 /*
 * Nothing to do if there is no colon in this line, for this rewriting
 * applies to only filesystems where numeric values in the path are volatile.
 */

 cp = strchr(entry + 5, ':');
 if (!cp) {
  cp = entry;
  goto flush;
 }
 /* Flush e.g. "file ioctl" part. */
 while (*cp != ' ')
  cp--;
 *cp++ = '\0';
 tomoyo_addprintf(buffer, len, "%s ", entry);
 /* e.g. file ioctl pipe:[$INO] $CMD */
 if (tomoyo_numscan(cp, "pipe:[", &width, ']')) {
  cp += width + 7;
  tomoyo_addprintf(buffer, len, "pipe:[\\$]");
  goto flush;
 }
 /* e.g. file ioctl socket:[$INO] $CMD */
 if (tomoyo_numscan(cp, "socket:[", &width, ']')) {
  cp += width + 9;
  tomoyo_addprintf(buffer, len, "socket:[\\$]");
  goto flush;
 }
 if (!strncmp(cp, "proc:/self", 10)) {
  /* e.g. file read proc:/self/task/$TID/fdinfo/$FD */
  cp += 10;
  tomoyo_addprintf(buffer, len, "proc:/self");
 } else if (tomoyo_numscan(cp, "proc:/", &width, 0)) {
  /* e.g. file read proc:/$PID/task/$TID/fdinfo/$FD */
  /*
 * Don't patternize $PID part if $PID == 1, for several
 * programs access only files in /proc/1/ directory.
 */

  cp += width + 6;
  if (width == 1 && *(cp - 1) == '1')
   tomoyo_addprintf(buffer, len, "proc:/1");
  else
   tomoyo_addprintf(buffer, len, "proc:/\\$");
 } else {
  goto flush;
 }
 /* Patternize $TID part if "/task/" follows. */
 if (tomoyo_numscan(cp, "/task/", &width, 0)) {
  cp += width + 6;
  tomoyo_addprintf(buffer, len, "/task/\\$");
 }
 /* Patternize $FD part if "/fd/" or "/fdinfo/" follows. */
 if (tomoyo_numscan(cp, "/fd/", &width, 0)) {
  cp += width + 4;
  tomoyo_addprintf(buffer, len, "/fd/\\$");
 } else if (tomoyo_numscan(cp, "/fdinfo/", &width, 0)) {
  cp += width + 8;
  tomoyo_addprintf(buffer, len, "/fdinfo/\\$");
 }
flush:
 /* Flush remaining part if any. */
 if (*cp)
  tomoyo_addprintf(buffer, len, "%s", cp);
}

/**
 * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
 *
 * @domain: Pointer to "struct tomoyo_domain_info".
 * @header: Lines containing ACL.
 *
 * Returns nothing.
 */

static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
{
 char *buffer;
 char *realpath = NULL;
 char *argv0 = NULL;
 char *symlink = NULL;
 char *cp = strchr(header, '\n');
 int len;

 if (!cp)
  return;
 cp = strchr(cp + 1, '\n');
 if (!cp)
  return;
 *cp++ = '\0';
 /* Reserve some space for potentially using patterns. */
 len = strlen(cp) + 16;
 /* 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 | __GFP_ZERO);
 if (!buffer)
  return;
 tomoyo_patternize_path(buffer, len, cp);
 if (realpath)
  tomoyo_addprintf(buffer, len, " exec.%s", realpath);
 if (argv0)
  tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0);
 if (symlink)
  tomoyo_addprintf(buffer, len, "%s", symlink);
 tomoyo_normalize_line(buffer);
 if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
      false))
  tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
 kfree(buffer);
}

/**
 * tomoyo_supervisor - Ask for the supervisor's decision.
 *
 * @r:   Pointer to "struct tomoyo_request_info".
 * @fmt: The printf()'s format string, followed by parameters.
 *
 * 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;
 int len;
 static unsigned int tomoyo_serial;
 struct tomoyo_query entry = { };
 bool quota_exceeded = false;

 va_start(args, fmt);
 len = vsnprintf(NULL, 0, fmt, args) + 1;
 va_end(args);
 /* Write /sys/kernel/security/tomoyo/audit. */
 va_start(args, fmt);
 tomoyo_write_log2(r, len, fmt, args);
 va_end(args);
 /* Nothing more to do if granted. */
 if (r->granted)
  return 0;
 if (r->mode)
  tomoyo_update_stat(r->mode);
 switch (r->mode) {
 case TOMOYO_CONFIG_ENFORCING:
  error = -EPERM;
  if (atomic_read(&tomoyo_query_observers))
   break;
  goto out;
 case TOMOYO_CONFIG_LEARNING:
  error = 0;
  /* Check max_learning_entry parameter. */
  if (tomoyo_domain_quota_is_ok(r))
   break;
  fallthrough;
 default:
  return 0;
 }
 /* Get message. */
 va_start(args, fmt);
 entry.query = tomoyo_init_log(r, len, fmt, args);
 va_end(args);
 if (!entry.query)
  goto out;
 entry.query_len = strlen(entry.query) + 1;
 if (!error) {
  tomoyo_add_entry(r->domain, entry.query);
  goto out;
 }
 len = kmalloc_size_roundup(entry.query_len);
 entry.domain = r->domain;
 spin_lock(&tomoyo_query_list_lock);
 if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] &&
     tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len
     >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {
  quota_exceeded = true;
 } else {
  entry.serial = tomoyo_serial++;
  entry.retry = r->retry;
  tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len;
  list_add_tail(&entry.list, &tomoyo_query_list);
 }
 spin_unlock(&tomoyo_query_list_lock);
 if (quota_exceeded)
  goto out;
 /* Give 10 seconds for supervisor's opinion. */
 while (entry.timer < 10) {
  wake_up_all(&tomoyo_query_wait);
  if (wait_event_interruptible_timeout
      (tomoyo_answer_wait, entry.answer ||
       !atomic_read(&tomoyo_query_observers), HZ))
   break;
  entry.timer++;
 }
 spin_lock(&tomoyo_query_list_lock);
 list_del(&entry.list);
 tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;
 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;
 default:
  /* Timed out or rejected by administrator. */
  break;
 }
out:
 kfree(entry.query);
 return error;
}

/**
 * tomoyo_find_domain_by_qid - Get domain by query id.
 *
 * @serial: Query ID assigned by tomoyo_supervisor().
 *
 * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
 */

static struct tomoyo_domain_info *tomoyo_find_domain_by_qid
(unsigned int serial)
{
 struct tomoyo_query *ptr;
 struct tomoyo_domain_info *domain = NULL;

 spin_lock(&tomoyo_query_list_lock);
 list_for_each_entry(ptr, &tomoyo_query_list, list) {
  if (ptr->serial != serial)
   continue;
  domain = ptr->domain;
  break;
 }
 spin_unlock(&tomoyo_query_list_lock);
 return domain;
}

/**
 * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query.
 *
 * @file: Pointer to "struct file".
 * @wait: Pointer to "poll_table".
 *
 * Returns EPOLLIN | EPOLLRDNORM when ready to read, 0 otherwise.
 *
 * Waits for access requests which violated policy in enforcing mode.
 */

static __poll_t tomoyo_poll_query(struct file *file, poll_table *wait)
{
 if (!list_empty(&tomoyo_query_list))
  return EPOLLIN | EPOLLRDNORM;
 poll_wait(file, &tomoyo_query_wait, wait);
 if (!list_empty(&tomoyo_query_list))
  return EPOLLIN | EPOLLRDNORM;
 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;
 unsigned int pos = 0;
 size_t len = 0;
 char *buf;

 if (head->r.w_pos)
  return;
 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 (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 + 32, 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 (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)
   snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial,
     ptr->retry, ptr->query);
  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;
  ptr->answer = answer;
  /* Remove from tomoyo_query_list. */
  if (ptr->answer)
   list_del_init(&ptr->list);
  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.6.0");
  head->r.eof = true;
 }
}

/* String table for /sys/kernel/security/tomoyo/stat interface. */
static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=93 G=94

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.20Angebot  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge