// 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 "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_yesno - Return "yes" or "no". 
 | 
 * 
 | 
 * @value: Bool value. 
 | 
 */ 
 | 
const char *tomoyo_yesno(const unsigned int value) 
 | 
{ 
 | 
    return value ? "yes" : "no"; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_addprintf - strncat()-like-snprintf(). 
 | 
 * 
 | 
 * @buffer: Buffer to write to. Must be '\0'-terminated. 
 | 
 * @len:    Size of @buffer. 
 | 
 * @fmt:    The printf()'s format string, followed by parameters. 
 | 
 * 
 | 
 * Returns nothing. 
 | 
 */ 
 | 
static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) 
 | 
{ 
 | 
    va_list args; 
 | 
    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); 
 | 
    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_DEAFULT'. 
 | 
                 */ 
 | 
                config = (config & ~7) | mode; 
 | 
        if (config != TOMOYO_CONFIG_USE_DEFAULT) { 
 | 
            switch (tomoyo_find_yesno(value, "grant_log")) { 
 | 
            case 1: 
 | 
                config |= TOMOYO_CONFIG_WANT_GRANT_LOG; 
 | 
                break; 
 | 
            case 0: 
 | 
                config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG; 
 | 
                break; 
 | 
            } 
 | 
            switch (tomoyo_find_yesno(value, "reject_log")) { 
 | 
            case 1: 
 | 
                config |= TOMOYO_CONFIG_WANT_REJECT_LOG; 
 | 
                break; 
 | 
            case 0: 
 | 
                config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG; 
 | 
                break; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) 
 | 
        profile->config[i] = config; 
 | 
    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], 
 | 
             tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG), 
 | 
             tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG)); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_read_profile - Read profile table. 
 | 
 * 
 | 
 * @head: Pointer to "struct tomoyo_io_buffer". 
 | 
 * 
 | 
 * Returns nothing. 
 | 
 */ 
 | 
static void tomoyo_read_profile(struct tomoyo_io_buffer *head) 
 | 
{ 
 | 
    u8 index; 
 | 
    struct tomoyo_policy_namespace *ns = 
 | 
        container_of(head->r.ns, typeof(*ns), namespace_list); 
 | 
    const struct tomoyo_profile *profile; 
 | 
  
 | 
    if (head->r.eof) 
 | 
        return; 
 | 
 next: 
 | 
    index = head->r.index; 
 | 
    profile = 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", 
 | 
                     tomoyo_yesno(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_add_entry - Add an ACL to current thread's domain. Used by learning mode. 
 | 
 * 
 | 
 * @domain: Pointer to "struct tomoyo_domain_info". 
 | 
 * @header: Lines containing ACL. 
 | 
 * 
 | 
 * Returns nothing. 
 | 
 */ 
 | 
static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) 
 | 
{ 
 | 
    char *buffer; 
 | 
    char *realpath = NULL; 
 | 
    char *argv0 = NULL; 
 | 
    char *symlink = NULL; 
 | 
    char *cp = strchr(header, '\n'); 
 | 
    int len; 
 | 
  
 | 
    if (!cp) 
 | 
        return; 
 | 
    cp = strchr(cp + 1, '\n'); 
 | 
    if (!cp) 
 | 
        return; 
 | 
    *cp++ = '\0'; 
 | 
    len = strlen(cp) + 1; 
 | 
    /* strstr() will return NULL if ordering is wrong. */ 
 | 
    if (*cp == 'f') { 
 | 
        argv0 = strstr(header, " argv[]={ \""); 
 | 
        if (argv0) { 
 | 
            argv0 += 10; 
 | 
            len += tomoyo_truncate(argv0) + 14; 
 | 
        } 
 | 
        realpath = strstr(header, " exec={ realpath=\""); 
 | 
        if (realpath) { 
 | 
            realpath += 8; 
 | 
            len += tomoyo_truncate(realpath) + 6; 
 | 
        } 
 | 
        symlink = strstr(header, " symlink.target=\""); 
 | 
        if (symlink) 
 | 
            len += tomoyo_truncate(symlink + 1) + 1; 
 | 
    } 
 | 
    buffer = kmalloc(len, GFP_NOFS); 
 | 
    if (!buffer) 
 | 
        return; 
 | 
    snprintf(buffer, len - 1, "%s", cp); 
 | 
    if (realpath) 
 | 
        tomoyo_addprintf(buffer, len, " exec.%s", realpath); 
 | 
    if (argv0) 
 | 
        tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0); 
 | 
    if (symlink) 
 | 
        tomoyo_addprintf(buffer, len, "%s", symlink); 
 | 
    tomoyo_normalize_line(buffer); 
 | 
    if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, 
 | 
                  false)) 
 | 
        tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); 
 | 
    kfree(buffer); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_supervisor - Ask for the supervisor's decision. 
 | 
 * 
 | 
 * @r:   Pointer to "struct tomoyo_request_info". 
 | 
 * @fmt: The printf()'s format string, followed by parameters. 
 | 
 * 
 | 
 * 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((char *) &len, 1, fmt, args) + 1; 
 | 
    va_end(args); 
 | 
    /* Write /sys/kernel/security/tomoyo/audit. */ 
 | 
    va_start(args, fmt); 
 | 
    tomoyo_write_log2(r, len, fmt, args); 
 | 
    va_end(args); 
 | 
    /* Nothing more to do if granted. */ 
 | 
    if (r->granted) 
 | 
        return 0; 
 | 
    if (r->mode) 
 | 
        tomoyo_update_stat(r->mode); 
 | 
    switch (r->mode) { 
 | 
    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 = tomoyo_round2(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] = { 
 | 
    [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:", 
 | 
}; 
 | 
  
 | 
/* Counter for number of updates. */ 
 | 
static atomic_t tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; 
 | 
/* Timestamp counter for last updated. */ 
 | 
static time64_t 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) 
 | 
{ 
 | 
    atomic_inc(&tomoyo_stat_updated[index]); 
 | 
    tomoyo_stat_modified[index] = ktime_get_real_seconds(); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * 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], 
 | 
                 atomic_read(&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". 
 | 
 * 
 | 
 * Returns 0 on success, negative value otherwise. 
 | 
 */ 
 | 
int tomoyo_open_control(const u8 type, struct file *file) 
 | 
{ 
 | 
    struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); 
 | 
  
 | 
    if (!head) 
 | 
        return -ENOMEM; 
 | 
    mutex_init(&head->io_sem); 
 | 
    head->type = type; 
 | 
    switch (type) { 
 | 
    case TOMOYO_DOMAINPOLICY: 
 | 
        /* /sys/kernel/security/tomoyo/domain_policy */ 
 | 
        head->write = tomoyo_write_domain; 
 | 
        head->read = tomoyo_read_domain; 
 | 
        break; 
 | 
    case TOMOYO_EXCEPTIONPOLICY: 
 | 
        /* /sys/kernel/security/tomoyo/exception_policy */ 
 | 
        head->write = tomoyo_write_exception; 
 | 
        head->read = tomoyo_read_exception; 
 | 
        break; 
 | 
    case TOMOYO_AUDIT: 
 | 
        /* /sys/kernel/security/tomoyo/audit */ 
 | 
        head->poll = tomoyo_poll_log; 
 | 
        head->read = tomoyo_read_log; 
 | 
        break; 
 | 
    case TOMOYO_PROCESS_STATUS: 
 | 
        /* /sys/kernel/security/tomoyo/.process_status */ 
 | 
        head->write = tomoyo_write_pid; 
 | 
        head->read = tomoyo_read_pid; 
 | 
        break; 
 | 
    case TOMOYO_VERSION: 
 | 
        /* /sys/kernel/security/tomoyo/version */ 
 | 
        head->read = tomoyo_read_version; 
 | 
        head->readbuf_size = 128; 
 | 
        break; 
 | 
    case TOMOYO_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 */ 
 | 
        head->write = tomoyo_write_profile; 
 | 
        head->read = tomoyo_read_profile; 
 | 
        break; 
 | 
    case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */ 
 | 
        head->poll = tomoyo_poll_query; 
 | 
        head->write = tomoyo_write_answer; 
 | 
        head->read = tomoyo_read_query; 
 | 
        break; 
 | 
    case TOMOYO_MANAGER: 
 | 
        /* /sys/kernel/security/tomoyo/manager */ 
 | 
        head->write = tomoyo_write_manager; 
 | 
        head->read = tomoyo_read_manager; 
 | 
        break; 
 | 
    } 
 | 
    if (!(file->f_mode & FMODE_READ)) { 
 | 
        /* 
 | 
         * No need to allocate read_buf since it is not opened 
 | 
         * for reading. 
 | 
         */ 
 | 
        head->read = NULL; 
 | 
        head->poll = NULL; 
 | 
    } else if (!head->poll) { 
 | 
        /* Don't allocate read_buf for poll() access. */ 
 | 
        if (!head->readbuf_size) 
 | 
            head->readbuf_size = 4096 * 2; 
 | 
        head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); 
 | 
        if (!head->read_buf) { 
 | 
            kfree(head); 
 | 
            return -ENOMEM; 
 | 
        } 
 | 
    } 
 | 
    if (!(file->f_mode & FMODE_WRITE)) { 
 | 
        /* 
 | 
         * No need to allocate write_buf since it is not opened 
 | 
         * for writing. 
 | 
         */ 
 | 
        head->write = NULL; 
 | 
    } else if (head->write) { 
 | 
        head->writebuf_size = 4096 * 2; 
 | 
        head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS); 
 | 
        if (!head->write_buf) { 
 | 
            kfree(head->read_buf); 
 | 
            kfree(head); 
 | 
            return -ENOMEM; 
 | 
        } 
 | 
    } 
 | 
    /* 
 | 
     * If the file is /sys/kernel/security/tomoyo/query , increment the 
 | 
     * observer counter. 
 | 
     * The obserber counter is used by tomoyo_supervisor() to see if 
 | 
     * there is some process monitoring /sys/kernel/security/tomoyo/query. 
 | 
     */ 
 | 
    if (type == TOMOYO_QUERY) 
 | 
        atomic_inc(&tomoyo_query_observers); 
 | 
    file->private_data = head; 
 | 
    tomoyo_notify_gc(head, true); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. 
 | 
 * 
 | 
 * @file: Pointer to "struct file". 
 | 
 * @wait: Pointer to "poll_table". Maybe NULL. 
 | 
 * 
 | 
 * Returns EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM if ready to read/write, 
 | 
 * EPOLLOUT | EPOLLWRNORM otherwise. 
 | 
 */ 
 | 
__poll_t tomoyo_poll_control(struct file *file, poll_table *wait) 
 | 
{ 
 | 
    struct tomoyo_io_buffer *head = file->private_data; 
 | 
  
 | 
    if (head->poll) 
 | 
        return head->poll(file, wait) | EPOLLOUT | EPOLLWRNORM; 
 | 
    return EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_set_namespace_cursor - Set namespace to read. 
 | 
 * 
 | 
 * @head: Pointer to "struct tomoyo_io_buffer". 
 | 
 * 
 | 
 * Returns nothing. 
 | 
 */ 
 | 
static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head) 
 | 
{ 
 | 
    struct list_head *ns; 
 | 
  
 | 
    if (head->type != TOMOYO_EXCEPTIONPOLICY && 
 | 
        head->type != TOMOYO_PROFILE) 
 | 
        return; 
 | 
    /* 
 | 
     * If this is the first read, or reading previous namespace finished 
 | 
     * and has more namespaces to read, update the namespace cursor. 
 | 
     */ 
 | 
    ns = head->r.ns; 
 | 
    if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) { 
 | 
        /* Clearing is OK because tomoyo_flush() returned true. */ 
 | 
        memset(&head->r, 0, sizeof(head->r)); 
 | 
        head->r.ns = ns ? ns->next : tomoyo_namespace_list.next; 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_has_more_namespace - Check for unread namespaces. 
 | 
 * 
 | 
 * @head: Pointer to "struct tomoyo_io_buffer". 
 | 
 * 
 | 
 * Returns true if we have more entries to print, false otherwise. 
 | 
 */ 
 | 
static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) 
 | 
{ 
 | 
    return (head->type == TOMOYO_EXCEPTIONPOLICY || 
 | 
        head->type == TOMOYO_PROFILE) && head->r.eof && 
 | 
        head->r.ns->next != &tomoyo_namespace_list; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. 
 | 
 * 
 | 
 * @head:       Pointer to "struct tomoyo_io_buffer". 
 | 
 * @buffer:     Poiner to buffer to write to. 
 | 
 * @buffer_len: Size of @buffer. 
 | 
 * 
 | 
 * Returns bytes read on success, negative value otherwise. 
 | 
 */ 
 | 
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 -EINVAL; 
 | 
    if (mutex_lock_interruptible(&head->io_sem)) 
 | 
        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 { 
 | 
            tomoyo_set_namespace_cursor(head); 
 | 
            head->read(head); 
 | 
        } while (tomoyo_flush(head) && 
 | 
             tomoyo_has_more_namespace(head)); 
 | 
    tomoyo_read_unlock(idx); 
 | 
    len = head->read_user_buf - buffer; 
 | 
    mutex_unlock(&head->io_sem); 
 | 
    return len; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_parse_policy - Parse a policy line. 
 | 
 * 
 | 
 * @head: Poiter to "struct tomoyo_io_buffer". 
 | 
 * @line: Line to parse. 
 | 
 * 
 | 
 * Returns 0 on success, negative value otherwise. 
 | 
 * 
 | 
 * Caller holds tomoyo_read_lock(). 
 | 
 */ 
 | 
static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) 
 | 
{ 
 | 
    /* Delete request? */ 
 | 
    head->w.is_delete = !strncmp(line, "delete ", 7); 
 | 
    if (head->w.is_delete) 
 | 
        memmove(line, line + 7, strlen(line + 7) + 1); 
 | 
    /* Selecting namespace to update. */ 
 | 
    if (head->type == TOMOYO_EXCEPTIONPOLICY || 
 | 
        head->type == TOMOYO_PROFILE) { 
 | 
        if (*line == '<') { 
 | 
            char *cp = strchr(line, ' '); 
 | 
  
 | 
            if (cp) { 
 | 
                *cp++ = '\0'; 
 | 
                head->w.ns = tomoyo_assign_namespace(line); 
 | 
                memmove(line, cp, strlen(cp) + 1); 
 | 
            } else 
 | 
                head->w.ns = NULL; 
 | 
        } else 
 | 
            head->w.ns = &tomoyo_kernel_namespace; 
 | 
        /* Don't allow updating if namespace is invalid. */ 
 | 
        if (!head->w.ns) 
 | 
            return -ENOENT; 
 | 
    } 
 | 
    /* Do the update. */ 
 | 
    return head->write(head); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface. 
 | 
 * 
 | 
 * @head:       Pointer to "struct tomoyo_io_buffer". 
 | 
 * @buffer:     Pointer to buffer to read from. 
 | 
 * @buffer_len: Size of @buffer. 
 | 
 * 
 | 
 * Returns @buffer_len on success, negative value otherwise. 
 | 
 */ 
 | 
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 -EINVAL; 
 | 
    if (mutex_lock_interruptible(&head->io_sem)) 
 | 
        return -EINTR; 
 | 
    head->read_user_buf_avail = 0; 
 | 
    idx = tomoyo_read_lock(); 
 | 
    /* Read a line and dispatch it to the policy handler. */ 
 | 
    while (avail_len > 0) { 
 | 
        char c; 
 | 
  
 | 
        if (head->w.avail >= head->writebuf_size - 1) { 
 | 
            const int len = head->writebuf_size * 2; 
 | 
            char *cp = kzalloc(len, GFP_NOFS); 
 | 
  
 | 
            if (!cp) { 
 | 
                error = -ENOMEM; 
 | 
                break; 
 | 
            } 
 | 
            memmove(cp, cp0, head->w.avail); 
 | 
            kfree(cp0); 
 | 
            head->write_buf = cp; 
 | 
            cp0 = cp; 
 | 
            head->writebuf_size = len; 
 | 
        } 
 | 
        if (get_user(c, buffer)) { 
 | 
            error = -EFAULT; 
 | 
            break; 
 | 
        } 
 | 
        buffer++; 
 | 
        avail_len--; 
 | 
        cp0[head->w.avail++] = c; 
 | 
        if (c != '\n') 
 | 
            continue; 
 | 
        cp0[head->w.avail - 1] = '\0'; 
 | 
        head->w.avail = 0; 
 | 
        tomoyo_normalize_line(cp0); 
 | 
        if (!strcmp(cp0, "reset")) { 
 | 
            head->w.ns = &tomoyo_kernel_namespace; 
 | 
            head->w.domain = NULL; 
 | 
            memset(&head->r, 0, sizeof(head->r)); 
 | 
            continue; 
 | 
        } 
 | 
        /* Don't allow updating policies by non manager programs. */ 
 | 
        switch (head->type) { 
 | 
        case TOMOYO_PROCESS_STATUS: 
 | 
            /* This does not write anything. */ 
 | 
            break; 
 | 
        case TOMOYO_DOMAINPOLICY: 
 | 
            if (tomoyo_select_domain(head, cp0)) 
 | 
                continue; 
 | 
            fallthrough; 
 | 
        case TOMOYO_EXCEPTIONPOLICY: 
 | 
            if (!strcmp(cp0, "select transition_only")) { 
 | 
                head->r.print_transition_related_only = true; 
 | 
                continue; 
 | 
            } 
 | 
            fallthrough; 
 | 
        default: 
 | 
            if (!tomoyo_manager()) { 
 | 
                error = -EPERM; 
 | 
                goto out; 
 | 
            } 
 | 
        } 
 | 
        switch (tomoyo_parse_policy(head, cp0)) { 
 | 
        case -EPERM: 
 | 
            error = -EPERM; 
 | 
            goto out; 
 | 
        case 0: 
 | 
            switch (head->type) { 
 | 
            case TOMOYO_DOMAINPOLICY: 
 | 
            case TOMOYO_EXCEPTIONPOLICY: 
 | 
            case TOMOYO_STAT: 
 | 
            case TOMOYO_PROFILE: 
 | 
            case TOMOYO_MANAGER: 
 | 
                tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); 
 | 
                break; 
 | 
            default: 
 | 
                break; 
 | 
            } 
 | 
            break; 
 | 
        } 
 | 
    } 
 | 
out: 
 | 
    tomoyo_read_unlock(idx); 
 | 
    mutex_unlock(&head->io_sem); 
 | 
    return error; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface. 
 | 
 * 
 | 
 * @head: Pointer to "struct tomoyo_io_buffer". 
 | 
 */ 
 | 
void tomoyo_close_control(struct tomoyo_io_buffer *head) 
 | 
{ 
 | 
    /* 
 | 
     * If the file is /sys/kernel/security/tomoyo/query , decrement the 
 | 
     * observer counter. 
 | 
     */ 
 | 
    if (head->type == TOMOYO_QUERY && 
 | 
        atomic_dec_and_test(&tomoyo_query_observers)) 
 | 
        wake_up_all(&tomoyo_answer_wait); 
 | 
    tomoyo_notify_gc(head, false); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_check_profile - Check all profiles currently assigned to domains are defined. 
 | 
 */ 
 | 
void tomoyo_check_profile(void) 
 | 
{ 
 | 
    struct tomoyo_domain_info *domain; 
 | 
    const int idx = tomoyo_read_lock(); 
 | 
  
 | 
    tomoyo_policy_loaded = true; 
 | 
    pr_info("TOMOYO: 2.6.0\n"); 
 | 
    list_for_each_entry_rcu(domain, &tomoyo_domain_list, list, 
 | 
                srcu_read_lock_held(&tomoyo_ss)) { 
 | 
        const u8 profile = domain->profile; 
 | 
        struct tomoyo_policy_namespace *ns = domain->ns; 
 | 
  
 | 
        if (ns->profile_version == 20110903) { 
 | 
            pr_info_once("Converting profile version from %u to %u.\n", 
 | 
                     20110903, 20150505); 
 | 
            ns->profile_version = 20150505; 
 | 
        } 
 | 
        if (ns->profile_version != 20150505) 
 | 
            pr_err("Profile version %u is not supported.\n", 
 | 
                   ns->profile_version); 
 | 
        else if (!ns->profile_ptr[profile]) 
 | 
            pr_err("Profile %u (used by '%s') is not defined.\n", 
 | 
                   profile, domain->domainname->name); 
 | 
        else 
 | 
            continue; 
 | 
        pr_err("Userland tools for TOMOYO 2.6 must be installed and policy must be initialized.\n"); 
 | 
        pr_err("Please see https://tomoyo.osdn.jp/2.6/ for more information.\n"); 
 | 
        panic("STOP!"); 
 | 
    } 
 | 
    tomoyo_read_unlock(idx); 
 | 
    pr_info("Mandatory Access Control activated.\n"); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * tomoyo_load_builtin_policy - Load built-in policy. 
 | 
 * 
 | 
 * Returns nothing. 
 | 
 */ 
 | 
void __init tomoyo_load_builtin_policy(void) 
 | 
{ 
 | 
#ifdef CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING 
 | 
    static char tomoyo_builtin_profile[] __initdata = 
 | 
        "PROFILE_VERSION=20150505\n" 
 | 
        "0-CONFIG={ mode=learning grant_log=no reject_log=yes }\n"; 
 | 
    static char tomoyo_builtin_exception_policy[] __initdata = 
 | 
        "aggregator proc:/self/exe /proc/self/exe\n"; 
 | 
    static char tomoyo_builtin_domain_policy[] __initdata = ""; 
 | 
    static char tomoyo_builtin_manager[] __initdata = ""; 
 | 
    static char tomoyo_builtin_stat[] __initdata = ""; 
 | 
#else 
 | 
    /* 
 | 
     * 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" 
 | 
#endif 
 | 
    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 
 | 
} 
 |