Merge branch 'next' into for-linus
authorJames Morris <jmorris@namei.org>
Sun, 28 Feb 2010 22:36:31 +0000 (09:36 +1100)
committerJames Morris <jmorris@namei.org>
Sun, 28 Feb 2010 22:36:31 +0000 (09:36 +1100)
30 files changed:
fs/proc/kmsg.c
include/linux/security.h
include/linux/syslog.h [new file with mode: 0644]
kernel/capability.c
kernel/printk.c
security/capability.c
security/commoncap.c
security/security.c
security/selinux/avc.c
security/selinux/hooks.c
security/selinux/include/security.h
security/selinux/selinuxfs.c
security/selinux/ss/context.h
security/selinux/ss/mls.c
security/selinux/ss/mls.h
security/selinux/ss/mls_types.h
security/selinux/ss/policydb.c
security/selinux/ss/policydb.h
security/selinux/ss/services.c
security/smack/smack_lsm.c
security/tomoyo/Makefile
security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/domain.c
security/tomoyo/file.c
security/tomoyo/gc.c [new file with mode: 0644]
security/tomoyo/realpath.c
security/tomoyo/realpath.h [deleted file]
security/tomoyo/tomoyo.c
security/tomoyo/tomoyo.h [deleted file]

index 7ca78346d3f0f9ceed2014a42c2c03b774f1393f..cfe90a48a6e81df983eb50fa700f22463ad6e4bc 100644 (file)
 #include <linux/poll.h>
 #include <linux/proc_fs.h>
 #include <linux/fs.h>
+#include <linux/syslog.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
 extern wait_queue_head_t log_wait;
 
-extern int do_syslog(int type, char __user *bug, int count);
-
 static int kmsg_open(struct inode * inode, struct file * file)
 {
-       return do_syslog(1,NULL,0);
+       return do_syslog(SYSLOG_ACTION_OPEN, NULL, 0, SYSLOG_FROM_FILE);
 }
 
 static int kmsg_release(struct inode * inode, struct file * file)
 {
-       (void) do_syslog(0,NULL,0);
+       (void) do_syslog(SYSLOG_ACTION_CLOSE, NULL, 0, SYSLOG_FROM_FILE);
        return 0;
 }
 
 static ssize_t kmsg_read(struct file *file, char __user *buf,
                         size_t count, loff_t *ppos)
 {
-       if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))
+       if ((file->f_flags & O_NONBLOCK) &&
+           !do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_FILE))
                return -EAGAIN;
-       return do_syslog(2, buf, count);
+       return do_syslog(SYSLOG_ACTION_READ, buf, count, SYSLOG_FROM_FILE);
 }
 
 static unsigned int kmsg_poll(struct file *file, poll_table *wait)
 {
        poll_wait(file, &log_wait, wait);
-       if (do_syslog(9, NULL, 0))
+       if (do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_FILE))
                return POLLIN | POLLRDNORM;
        return 0;
 }
index 2c627d361c023570a881b0b19adb509beadd2552..233d20b52c1b2506debd5a0585f9a7810329c9fd 100644 (file)
@@ -76,7 +76,7 @@ extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
 extern int cap_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp);
 extern int cap_task_setioprio(struct task_struct *p, int ioprio);
 extern int cap_task_setnice(struct task_struct *p, int nice);
-extern int cap_syslog(int type);
+extern int cap_syslog(int type, bool from_file);
 extern int cap_vm_enough_memory(struct mm_struct *mm, long pages);
 
 struct msghdr;
@@ -95,6 +95,8 @@ struct seq_file;
 extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
 extern int cap_netlink_recv(struct sk_buff *skb, int cap);
 
+void reset_security_ops(void);
+
 #ifdef CONFIG_MMU
 extern unsigned long mmap_min_addr;
 extern unsigned long dac_mmap_min_addr;
@@ -985,6 +987,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     Check permissions on incoming network packets.  This hook is distinct
  *     from Netfilter's IP input hooks since it is the first time that the
  *     incoming sk_buff @skb has been associated with a particular socket, @sk.
+ *     Must not sleep inside this hook because some callers hold spinlocks.
  *     @sk contains the sock (not socket) associated with the incoming sk_buff.
  *     @skb contains the incoming network data.
  * @socket_getpeersec_stream:
@@ -1348,6 +1351,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     logging to the console.
  *     See the syslog(2) manual page for an explanation of the @type values.
  *     @type contains the type of action.
+ *     @from_file indicates the context of action (if it came from /proc).
  *     Return 0 if permission is granted.
  * @settime:
  *     Check permission to change the system time.
@@ -1462,7 +1466,7 @@ struct security_operations {
        int (*sysctl) (struct ctl_table *table, int op);
        int (*quotactl) (int cmds, int type, int id, struct super_block *sb);
        int (*quota_on) (struct dentry *dentry);
-       int (*syslog) (int type);
+       int (*syslog) (int type, bool from_file);
        int (*settime) (struct timespec *ts, struct timezone *tz);
        int (*vm_enough_memory) (struct mm_struct *mm, long pages);
 
@@ -1761,7 +1765,7 @@ int security_acct(struct file *file);
 int security_sysctl(struct ctl_table *table, int op);
 int security_quotactl(int cmds, int type, int id, struct super_block *sb);
 int security_quota_on(struct dentry *dentry);
-int security_syslog(int type);
+int security_syslog(int type, bool from_file);
 int security_settime(struct timespec *ts, struct timezone *tz);
 int security_vm_enough_memory(long pages);
 int security_vm_enough_memory_mm(struct mm_struct *mm, long pages);
@@ -2007,9 +2011,9 @@ static inline int security_quota_on(struct dentry *dentry)
        return 0;
 }
 
-static inline int security_syslog(int type)
+static inline int security_syslog(int type, bool from_file)
 {
-       return cap_syslog(type);
+       return cap_syslog(type, from_file);
 }
 
 static inline int security_settime(struct timespec *ts, struct timezone *tz)
diff --git a/include/linux/syslog.h b/include/linux/syslog.h
new file mode 100644 (file)
index 0000000..3891139
--- /dev/null
@@ -0,0 +1,52 @@
+/*  Syslog internals
+ *
+ *  Copyright 2010 Canonical, Ltd.
+ *  Author: Kees Cook <kees.cook@canonical.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _LINUX_SYSLOG_H
+#define _LINUX_SYSLOG_H
+
+/* Close the log.  Currently a NOP. */
+#define SYSLOG_ACTION_CLOSE          0
+/* Open the log. Currently a NOP. */
+#define SYSLOG_ACTION_OPEN           1
+/* Read from the log. */
+#define SYSLOG_ACTION_READ           2
+/* Read all messages remaining in the ring buffer. */
+#define SYSLOG_ACTION_READ_ALL       3
+/* Read and clear all messages remaining in the ring buffer */
+#define SYSLOG_ACTION_READ_CLEAR     4
+/* Clear ring buffer. */
+#define SYSLOG_ACTION_CLEAR          5
+/* Disable printk's to console */
+#define SYSLOG_ACTION_CONSOLE_OFF    6
+/* Enable printk's to console */
+#define SYSLOG_ACTION_CONSOLE_ON     7
+/* Set level of messages printed to console */
+#define SYSLOG_ACTION_CONSOLE_LEVEL  8
+/* Return number of unread characters in the log buffer */
+#define SYSLOG_ACTION_SIZE_UNREAD    9
+/* Return size of the log buffer */
+#define SYSLOG_ACTION_SIZE_BUFFER   10
+
+#define SYSLOG_FROM_CALL 0
+#define SYSLOG_FROM_FILE 1
+
+int do_syslog(int type, char __user *buf, int count, bool from_file);
+
+#endif /* _LINUX_SYSLOG_H */
index 7f876e60521f0f3f5cda063db39be0177bf50f3f..9e4697e9b276e7429fed888b76c8e4ee19562e4c 100644 (file)
@@ -135,7 +135,7 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
        if (pid && (pid != task_pid_vnr(current))) {
                struct task_struct *target;
 
-               read_lock(&tasklist_lock);
+               rcu_read_lock();
 
                target = find_task_by_vpid(pid);
                if (!target)
@@ -143,7 +143,7 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
                else
                        ret = security_capget(target, pEp, pIp, pPp);
 
-               read_unlock(&tasklist_lock);
+               rcu_read_unlock();
        } else
                ret = security_capget(current, pEp, pIp, pPp);
 
index 1751c456b71ff6ee071f729a8375009e979faf7e..40674122ecf23aed335a67d88ff4444de8970e9c 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/kexec.h>
 #include <linux/ratelimit.h>
 #include <linux/kmsg_dump.h>
+#include <linux/syslog.h>
 
 #include <asm/uaccess.h>
 
@@ -258,38 +259,23 @@ static inline void boot_delay_msec(void)
 }
 #endif
 
-/*
- * Commands to do_syslog:
- *
- *     0 -- Close the log.  Currently a NOP.
- *     1 -- Open the log. Currently a NOP.
- *     2 -- Read from the log.
- *     3 -- Read all messages remaining in the ring buffer.
- *     4 -- Read and clear all messages remaining in the ring buffer
- *     5 -- Clear ring buffer.
- *     6 -- Disable printk's to console
- *     7 -- Enable printk's to console
- *     8 -- Set level of messages printed to console
- *     9 -- Return number of unread characters in the log buffer
- *     10 -- Return size of the log buffer
- */
-int do_syslog(int type, char __user *buf, int len)
+int do_syslog(int type, char __user *buf, int len, bool from_file)
 {
        unsigned i, j, limit, count;
        int do_clear = 0;
        char c;
        int error = 0;
 
-       error = security_syslog(type);
+       error = security_syslog(type, from_file);
        if (error)
                return error;
 
        switch (type) {
-       case 0:         /* Close log */
+       case SYSLOG_ACTION_CLOSE:       /* Close log */
                break;
-       case 1:         /* Open log */
+       case SYSLOG_ACTION_OPEN:        /* Open log */
                break;
-       case 2:         /* Read from log */
+       case SYSLOG_ACTION_READ:        /* Read from log */
                error = -EINVAL;
                if (!buf || len < 0)
                        goto out;
@@ -320,10 +306,12 @@ int do_syslog(int type, char __user *buf, int len)
                if (!error)
                        error = i;
                break;
-       case 4:         /* Read/clear last kernel messages */
+       /* Read/clear last kernel messages */
+       case SYSLOG_ACTION_READ_CLEAR:
                do_clear = 1;
                /* FALL THRU */
-       case 3:         /* Read last kernel messages */
+       /* Read last kernel messages */
+       case SYSLOG_ACTION_READ_ALL:
                error = -EINVAL;
                if (!buf || len < 0)
                        goto out;
@@ -376,21 +364,25 @@ int do_syslog(int type, char __user *buf, int len)
                        }
                }
                break;
-       case 5:         /* Clear ring buffer */
+       /* Clear ring buffer */
+       case SYSLOG_ACTION_CLEAR:
                logged_chars = 0;
                break;
-       case 6:         /* Disable logging to console */
+       /* Disable logging to console */
+       case SYSLOG_ACTION_CONSOLE_OFF:
                if (saved_console_loglevel == -1)
                        saved_console_loglevel = console_loglevel;
                console_loglevel = minimum_console_loglevel;
                break;
-       case 7:         /* Enable logging to console */
+       /* Enable logging to console */
+       case SYSLOG_ACTION_CONSOLE_ON:
                if (saved_console_loglevel != -1) {
                        console_loglevel = saved_console_loglevel;
                        saved_console_loglevel = -1;
                }
                break;
-       case 8:         /* Set level of messages printed to console */
+       /* Set level of messages printed to console */
+       case SYSLOG_ACTION_CONSOLE_LEVEL:
                error = -EINVAL;
                if (len < 1 || len > 8)
                        goto out;
@@ -401,10 +393,12 @@ int do_syslog(int type, char __user *buf, int len)
                saved_console_loglevel = -1;
                error = 0;
                break;
-       case 9:         /* Number of chars in the log buffer */
+       /* Number of chars in the log buffer */
+       case SYSLOG_ACTION_SIZE_UNREAD:
                error = log_end - log_start;
                break;
-       case 10:        /* Size of the log buffer */
+       /* Size of the log buffer */
+       case SYSLOG_ACTION_SIZE_BUFFER:
                error = log_buf_len;
                break;
        default:
@@ -417,7 +411,7 @@ out:
 
 SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
 {
-       return do_syslog(type, buf, len);
+       return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
 }
 
 /*
index 5c700e1a4fd377478fbcdc8a048022c1c6fb9ac7..4875142b858d65e0dea6c2faaf7adf56365ec93a 100644 (file)
@@ -906,10 +906,6 @@ static void cap_audit_rule_free(void *lsmrule)
 }
 #endif /* CONFIG_AUDIT */
 
-struct security_operations default_security_ops = {
-       .name   = "default",
-};
-
 #define set_to_cap_if_null(ops, function)                              \
        do {                                                            \
                if (!ops->function) {                                   \
index f800fdb3de94136a093f51b11d0ca5e9eaf686e8..61669730da98e05c77d18c77e853fc922f3031a5 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/sched.h>
 #include <linux/prctl.h>
 #include <linux/securebits.h>
+#include <linux/syslog.h>
 
 /*
  * If a non-root user executes a setuid-root binary in
@@ -888,13 +889,17 @@ error:
 /**
  * cap_syslog - Determine whether syslog function is permitted
  * @type: Function requested
+ * @from_file: Whether this request came from an open file (i.e. /proc)
  *
  * Determine whether the current process is permitted to use a particular
  * syslog function, returning 0 if permission is granted, -ve if not.
  */
-int cap_syslog(int type)
+int cap_syslog(int type, bool from_file)
 {
-       if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
+       if (type != SYSLOG_ACTION_OPEN && from_file)
+               return 0;
+       if ((type != SYSLOG_ACTION_READ_ALL &&
+            type != SYSLOG_ACTION_SIZE_BUFFER) && !capable(CAP_SYS_ADMIN))
                return -EPERM;
        return 0;
 }
index 122b748d0f4c5cbb11b04fd11a954429136586c2..687c6fd14bb6db68695d6d207edb8ad1db195c00 100644 (file)
@@ -23,10 +23,12 @@ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
        CONFIG_DEFAULT_SECURITY;
 
 /* things that live in capability.c */
-extern struct security_operations default_security_ops;
 extern void security_fixup_ops(struct security_operations *ops);
 
-struct security_operations *security_ops;      /* Initialized to NULL */
+static struct security_operations *security_ops;
+static struct security_operations default_security_ops = {
+       .name   = "default",
+};
 
 static inline int verify(struct security_operations *ops)
 {
@@ -63,6 +65,11 @@ int __init security_init(void)
        return 0;
 }
 
+void reset_security_ops(void)
+{
+       security_ops = &default_security_ops;
+}
+
 /* Save user chosen LSM */
 static int __init choose_lsm(char *str)
 {
@@ -203,9 +210,9 @@ int security_quota_on(struct dentry *dentry)
        return security_ops->quota_on(dentry);
 }
 
-int security_syslog(int type)
+int security_syslog(int type, bool from_file)
 {
-       return security_ops->syslog(type);
+       return security_ops->syslog(type, from_file);
 }
 
 int security_settime(struct timespec *ts, struct timezone *tz)
@@ -389,42 +396,42 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 EXPORT_SYMBOL(security_inode_init_security);
 
 #ifdef CONFIG_SECURITY_PATH
-int security_path_mknod(struct path *path, struct dentry *dentry, int mode,
+int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
                        unsigned int dev)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_mknod(path, dentry, mode, dev);
+       return security_ops->path_mknod(dir, dentry, mode, dev);
 }
 EXPORT_SYMBOL(security_path_mknod);
 
-int security_path_mkdir(struct path *path, struct dentry *dentry, int mode)
+int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_mkdir(path, dentry, mode);
+       return security_ops->path_mkdir(dir, dentry, mode);
 }
 
-int security_path_rmdir(struct path *path, struct dentry *dentry)
+int security_path_rmdir(struct path *dir, struct dentry *dentry)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_rmdir(path, dentry);
+       return security_ops->path_rmdir(dir, dentry);
 }
 
-int security_path_unlink(struct path *path, struct dentry *dentry)
+int security_path_unlink(struct path *dir, struct dentry *dentry)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_unlink(path, dentry);
+       return security_ops->path_unlink(dir, dentry);
 }
 
-int security_path_symlink(struct path *path, struct dentry *dentry,
+int security_path_symlink(struct path *dir, struct dentry *dentry,
                          const char *old_name)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_symlink(path, dentry, old_name);
+       return security_ops->path_symlink(dir, dentry, old_name);
 }
 
 int security_path_link(struct dentry *old_dentry, struct path *new_dir,
@@ -630,14 +637,14 @@ int security_inode_killpriv(struct dentry *dentry)
 int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
 {
        if (unlikely(IS_PRIVATE(inode)))
-               return 0;
+               return -EOPNOTSUPP;
        return security_ops->inode_getsecurity(inode, name, buffer, alloc);
 }
 
 int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
 {
        if (unlikely(IS_PRIVATE(inode)))
-               return 0;
+               return -EOPNOTSUPP;
        return security_ops->inode_setsecurity(inode, name, value, size, flags);
 }
 
index f2dde268165ab92ec7d9373db532616f5744069c..db0fd9f334994fe5a29ae7dfc31f1cef6fd263bb 100644 (file)
@@ -489,17 +489,14 @@ void avc_audit(u32 ssid, u32 tsid,
        struct common_audit_data stack_data;
        u32 denied, audited;
        denied = requested & ~avd->allowed;
-       if (denied) {
-               audited = denied;
-               if (!(audited & avd->auditdeny))
-                       return;
-       } else if (result) {
+       if (denied)
+               audited = denied & avd->auditdeny;
+       else if (result)
                audited = denied = requested;
-       } else {
-               audited = requested;
-               if (!(audited & avd->auditallow))
-                       return;
-       }
+       else
+               audited = requested & avd->auditallow;
+       if (!audited)
+               return;
        if (!a) {
                a = &stack_data;
                memset(a, 0, sizeof(*a));
@@ -746,9 +743,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                else
                        avd = &avd_entry;
 
-               rc = security_compute_av(ssid, tsid, tclass, requested, avd);
-               if (rc)
-                       goto out;
+               security_compute_av(ssid, tsid, tclass, avd);
                rcu_read_lock();
                node = avc_insert(ssid, tsid, tclass, avd);
        } else {
@@ -770,7 +765,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
        }
 
        rcu_read_unlock();
-out:
        return rc;
 }
 
index 9a2ee845e9d4c4b40bc29a9f33c67d591f2e8c30..5feecb41009d58420d5c702cbde9455d64b112fb 100644 (file)
@@ -76,6 +76,7 @@
 #include <linux/selinux.h>
 #include <linux/mutex.h>
 #include <linux/posix-timers.h>
+#include <linux/syslog.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -125,13 +126,6 @@ __setup("selinux=", selinux_enabled_setup);
 int selinux_enabled = 1;
 #endif
 
-
-/*
- * Minimal support for a secondary security module,
- * just to allow the use of the capability module.
- */
-static struct security_operations *secondary_ops;
-
 /* Lists of inode and superblock security structures initialized
    before the policy was loaded. */
 static LIST_HEAD(superblock_security_head);
@@ -2049,29 +2043,30 @@ static int selinux_quota_on(struct dentry *dentry)
        return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
 }
 
-static int selinux_syslog(int type)
+static int selinux_syslog(int type, bool from_file)
 {
        int rc;
 
-       rc = cap_syslog(type);
+       rc = cap_syslog(type, from_file);
        if (rc)
                return rc;
 
        switch (type) {
-       case 3:         /* Read last kernel messages */
-       case 10:        /* Return size of the log buffer */
+       case SYSLOG_ACTION_READ_ALL:    /* Read last kernel messages */
+       case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */
                rc = task_has_system(current, SYSTEM__SYSLOG_READ);
                break;
-       case 6:         /* Disable logging to console */
-       case 7:         /* Enable logging to console */
-       case 8:         /* Set level of messages printed to console */
+       case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */
+       case SYSLOG_ACTION_CONSOLE_ON:  /* Enable logging to console */
+       /* Set level of messages printed to console */
+       case SYSLOG_ACTION_CONSOLE_LEVEL:
                rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE);
                break;
-       case 0:         /* Close log */
-       case 1:         /* Open log */
-       case 2:         /* Read from log */
-       case 4:         /* Read/clear last kernel messages */
-       case 5:         /* Clear ring buffer */
+       case SYSLOG_ACTION_CLOSE:       /* Close log */
+       case SYSLOG_ACTION_OPEN:        /* Open log */
+       case SYSLOG_ACTION_READ:        /* Read from log */
+       case SYSLOG_ACTION_READ_CLEAR:  /* Read/clear last kernel messages */
+       case SYSLOG_ACTION_CLEAR:       /* Clear ring buffer */
        default:
                rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
                break;
@@ -3334,7 +3329,7 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
 
        if (ret == 0)
                tsec->create_sid = isec->sid;
-       return 0;
+       return ret;
 }
 
 static int selinux_kernel_module_request(char *kmod_name)
@@ -5672,9 +5667,6 @@ static __init int selinux_init(void)
                                            0, SLAB_PANIC, NULL);
        avc_init();
 
-       secondary_ops = security_ops;
-       if (!secondary_ops)
-               panic("SELinux: No initial security operations\n");
        if (register_security(&selinux_ops))
                panic("SELinux: Unable to register with kernel.\n");
 
@@ -5835,8 +5827,7 @@ int selinux_disable(void)
        selinux_disabled = 1;
        selinux_enabled = 0;
 
-       /* Reset security_ops to the secondary module, dummy or capability. */
-       security_ops = secondary_ops;
+       reset_security_ops();
 
        /* Try to destroy the avc node cache */
        avc_disable();
index 2553266ad793ff76c6cf30d4a4383f285e66a2e4..1f7c2491d3dccbc54769a6ccaf509d50255cfe3f 100644 (file)
@@ -57,7 +57,6 @@
 struct netlbl_lsm_secattr;
 
 extern int selinux_enabled;
-extern int selinux_mls_enabled;
 
 /* Policy capabilities */
 enum {
@@ -80,6 +79,8 @@ extern int selinux_policycap_openperm;
 /* limitation of boundary depth  */
 #define POLICYDB_BOUNDS_MAXDEPTH       4
 
+int security_mls_enabled(void);
+
 int security_load_policy(void *data, size_t len);
 
 int security_policycap_supported(unsigned int req_cap);
@@ -96,13 +97,11 @@ struct av_decision {
 /* definitions of av_decision.flags */
 #define AVD_FLAGS_PERMISSIVE   0x0001
 
-int security_compute_av(u32 ssid, u32 tsid,
-                       u16 tclass, u32 requested,
-                       struct av_decision *avd);
+void security_compute_av(u32 ssid, u32 tsid,
+                        u16 tclass, struct av_decision *avd);
 
-int security_compute_av_user(u32 ssid, u32 tsid,
-                            u16 tclass, u32 requested,
-                            struct av_decision *avd);
+void security_compute_av_user(u32 ssid, u32 tsid,
+                            u16 tclass, struct av_decision *avd);
 
 int security_transition_sid(u32 ssid, u32 tsid,
                            u16 tclass, u32 *out_sid);
index fab36fdf2769f0e0bce984bb0f40f84c63fff144..cd191bbec03c257ed050d27bd4881cd3e68d19d0 100644 (file)
@@ -282,7 +282,8 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf,
        char tmpbuf[TMPBUFLEN];
        ssize_t length;
 
-       length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled);
+       length = scnprintf(tmpbuf, TMPBUFLEN, "%d",
+                          security_mls_enabled());
        return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
 }
 
@@ -494,7 +495,6 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
        char *scon, *tcon;
        u32 ssid, tsid;
        u16 tclass;
-       u32 req;
        struct av_decision avd;
        ssize_t length;
 
@@ -512,7 +512,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
                goto out;
 
        length = -EINVAL;
-       if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
+       if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
                goto out2;
 
        length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
@@ -522,9 +522,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
        if (length < 0)
                goto out2;
 
-       length = security_compute_av_user(ssid, tsid, tclass, req, &avd);
-       if (length < 0)
-               goto out2;
+       security_compute_av_user(ssid, tsid, tclass, &avd);
 
        length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
                          "%x %x %x %x %u %x",
@@ -979,6 +977,8 @@ static int sel_make_bools(void)
        u32 sid;
 
        /* remove any existing files */
+       for (i = 0; i < bool_num; i++)
+               kfree(bool_pending_names[i]);
        kfree(bool_pending_names);
        kfree(bool_pending_values);
        bool_pending_names = NULL;
index d9dd7a2f6a8aff91029abc0ae41d7ee558ecdbd2..45e8fb0515f8793c18cb665cca245988b61517e0 100644 (file)
@@ -41,9 +41,6 @@ static inline int mls_context_cpy(struct context *dst, struct context *src)
 {
        int rc;
 
-       if (!selinux_mls_enabled)
-               return 0;
-
        dst->range.level[0].sens = src->range.level[0].sens;
        rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
        if (rc)
@@ -64,9 +61,6 @@ static inline int mls_context_cpy_low(struct context *dst, struct context *src)
 {
        int rc;
 
-       if (!selinux_mls_enabled)
-               return 0;
-
        dst->range.level[0].sens = src->range.level[0].sens;
        rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
        if (rc)
@@ -82,9 +76,6 @@ out:
 
 static inline int mls_context_cmp(struct context *c1, struct context *c2)
 {
-       if (!selinux_mls_enabled)
-               return 1;
-
        return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
                ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) &&
                (c1->range.level[1].sens == c2->range.level[1].sens) &&
@@ -93,9 +84,6 @@ static inline int mls_context_cmp(struct context *c1, struct context *c2)
 
 static inline void mls_context_destroy(struct context *c)
 {
-       if (!selinux_mls_enabled)
-               return;
-
        ebitmap_destroy(&c->range.level[0].cat);
        ebitmap_destroy(&c->range.level[1].cat);
        mls_context_init(c);
index 3f2b2706b5bbc9226b9e211c9fd46191c5856e23..372b773f8210ae6ade68f05cf902b8d765eae49a 100644 (file)
@@ -39,7 +39,7 @@ int mls_compute_context_len(struct context *context)
        struct ebitmap *e;
        struct ebitmap_node *node;
 
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return 0;
 
        len = 1; /* for the beginning ":" */
@@ -93,7 +93,7 @@ void mls_sid_to_context(struct context *context,
        struct ebitmap *e;
        struct ebitmap_node *node;
 
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return;
 
        scontextp = *scontext;
@@ -200,7 +200,7 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
 {
        struct user_datum *usrdatum;
 
-       if (!selinux_mls_enabled)
+       if (!p->mls_enabled)
                return 1;
 
        if (!mls_range_isvalid(p, &c->range))
@@ -253,7 +253,7 @@ int mls_context_to_sid(struct policydb *pol,
        struct cat_datum *catdatum, *rngdatum;
        int l, rc = -EINVAL;
 
-       if (!selinux_mls_enabled) {
+       if (!pol->mls_enabled) {
                if (def_sid != SECSID_NULL && oldc)
                        *scontext += strlen(*scontext)+1;
                return 0;
@@ -387,7 +387,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
        char *tmpstr, *freestr;
        int rc;
 
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return -EINVAL;
 
        /* we need freestr because mls_context_to_sid will change
@@ -407,7 +407,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
 /*
  * Copies the MLS range `range' into `context'.
  */
-static inline int mls_range_set(struct context *context,
+int mls_range_set(struct context *context,
                                struct mls_range *range)
 {
        int l, rc = 0;
@@ -427,7 +427,7 @@ static inline int mls_range_set(struct context *context,
 int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
                         struct context *usercon)
 {
-       if (selinux_mls_enabled) {
+       if (policydb.mls_enabled) {
                struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
                struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
                struct mls_level *user_low = &(user->range.level[0]);
@@ -477,7 +477,7 @@ int mls_convert_context(struct policydb *oldp,
        struct ebitmap_node *node;
        int l, i;
 
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return 0;
 
        for (l = 0; l < 2; l++) {
@@ -513,23 +513,21 @@ int mls_compute_sid(struct context *scontext,
                    u32 specified,
                    struct context *newcontext)
 {
-       struct range_trans *rtr;
+       struct range_trans rtr;
+       struct mls_range *r;
 
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return 0;
 
        switch (specified) {
        case AVTAB_TRANSITION:
                /* Look for a range transition rule. */
-               for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
-                       if (rtr->source_type == scontext->type &&
-                           rtr->target_type == tcontext->type &&
-                           rtr->target_class == tclass) {
-                               /* Set the range from the rule */
-                               return mls_range_set(newcontext,
-                                                    &rtr->target_range);
-                       }
-               }
+               rtr.source_type = scontext->type;
+               rtr.target_type = tcontext->type;
+               rtr.target_class = tclass;
+               r = hashtab_search(policydb.range_tr, &rtr);
+               if (r)
+                       return mls_range_set(newcontext, r);
                /* Fallthrough */
        case AVTAB_CHANGE:
                if (tclass == policydb.process_class)
@@ -541,8 +539,8 @@ int mls_compute_sid(struct context *scontext,
        case AVTAB_MEMBER:
                /* Use the process effective MLS attributes. */
                return mls_context_cpy_low(newcontext, scontext);
-       default:
-               return -EINVAL;
+
+       /* fall through */
        }
        return -EINVAL;
 }
@@ -561,7 +559,7 @@ int mls_compute_sid(struct context *scontext,
 void mls_export_netlbl_lvl(struct context *context,
                           struct netlbl_lsm_secattr *secattr)
 {
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return;
 
        secattr->attr.mls.lvl = context->range.level[0].sens - 1;
@@ -581,7 +579,7 @@ void mls_export_netlbl_lvl(struct context *context,
 void mls_import_netlbl_lvl(struct context *context,
                           struct netlbl_lsm_secattr *secattr)
 {
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return;
 
        context->range.level[0].sens = secattr->attr.mls.lvl + 1;
@@ -603,7 +601,7 @@ int mls_export_netlbl_cat(struct context *context,
 {
        int rc;
 
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return 0;
 
        rc = ebitmap_netlbl_export(&context->range.level[0].cat,
@@ -631,7 +629,7 @@ int mls_import_netlbl_cat(struct context *context,
 {
        int rc;
 
-       if (!selinux_mls_enabled)
+       if (!policydb.mls_enabled)
                return 0;
 
        rc = ebitmap_netlbl_import(&context->range.level[0].cat,
index 1276715aaa8bf3f0aac5973022f38e98388b0605..cd9152632e54f09ef424862540ece0b404a78c4d 100644 (file)
@@ -39,6 +39,8 @@ int mls_context_to_sid(struct policydb *p,
 
 int mls_from_string(char *str, struct context *context, gfp_t gfp_mask);
 
+int mls_range_set(struct context *context, struct mls_range *range);
+
 int mls_convert_context(struct policydb *oldp,
                        struct policydb *newp,
                        struct context *context);
index b6e943a210610db265022c1b6af9dfddc8f57021..03bed52a80526abfbda766a33859595cc1d8bfa5 100644 (file)
@@ -15,6 +15,7 @@
 #define _SS_MLS_TYPES_H_
 
 #include "security.h"
+#include "ebitmap.h"
 
 struct mls_level {
        u32 sens;               /* sensitivity */
@@ -27,18 +28,12 @@ struct mls_range {
 
 static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)
 {
-       if (!selinux_mls_enabled)
-               return 1;
-
        return ((l1->sens == l2->sens) &&
                ebitmap_cmp(&l1->cat, &l2->cat));
 }
 
 static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)
 {
-       if (!selinux_mls_enabled)
-               return 1;
-
        return ((l1->sens >= l2->sens) &&
                ebitmap_contains(&l1->cat, &l2->cat));
 }
index f03667213ea8d4c1d0cb94ed270c4da8e8752dea..23c6e53c102c1c5b8ff6265e1c9bc77e5671b4a0 100644 (file)
@@ -52,8 +52,6 @@ static char *symtab_name[SYM_NUM] = {
 };
 #endif
 
-int selinux_mls_enabled;
-
 static unsigned int symtab_sizes[SYM_NUM] = {
        2,
        32,
@@ -177,6 +175,21 @@ out_free_role:
        goto out;
 }
 
+static u32 rangetr_hash(struct hashtab *h, const void *k)
+{
+       const struct range_trans *key = k;
+       return (key->source_type + (key->target_type << 3) +
+               (key->target_class << 5)) & (h->size - 1);
+}
+
+static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
+{
+       const struct range_trans *key1 = k1, *key2 = k2;
+       return (key1->source_type != key2->source_type ||
+               key1->target_type != key2->target_type ||
+               key1->target_class != key2->target_class);
+}
+
 /*
  * Initialize a policy database structure.
  */
@@ -204,6 +217,10 @@ static int policydb_init(struct policydb *p)
        if (rc)
                goto out_free_symtab;
 
+       p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
+       if (!p->range_tr)
+               goto out_free_symtab;
+
        ebitmap_init(&p->policycaps);
        ebitmap_init(&p->permissive_map);
 
@@ -408,6 +425,20 @@ static void symtab_hash_eval(struct symtab *s)
                       info.slots_used, h->size, info.max_chain_len);
        }
 }
+
+static void rangetr_hash_eval(struct hashtab *h)
+{
+       struct hashtab_info info;
+
+       hashtab_stat(h, &info);
+       printk(KERN_DEBUG "SELinux: rangetr:  %d entries and %d/%d buckets used, "
+              "longest chain length %d\n", h->nel,
+              info.slots_used, h->size, info.max_chain_len);
+}
+#else
+static inline void rangetr_hash_eval(struct hashtab *h)
+{
+}
 #endif
 
 /*
@@ -422,7 +453,7 @@ static int policydb_index_others(struct policydb *p)
 
        printk(KERN_DEBUG "SELinux:  %d users, %d roles, %d types, %d bools",
               p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim);
-       if (selinux_mls_enabled)
+       if (p->mls_enabled)
                printk(", %d sens, %d cats", p->p_levels.nprim,
                       p->p_cats.nprim);
        printk("\n");
@@ -612,6 +643,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
        cat_destroy,
 };
 
+static int range_tr_destroy(void *key, void *datum, void *p)
+{
+       struct mls_range *rt = datum;
+       kfree(key);
+       ebitmap_destroy(&rt->level[0].cat);
+       ebitmap_destroy(&rt->level[1].cat);
+       kfree(datum);
+       cond_resched();
+       return 0;
+}
+
 static void ocontext_destroy(struct ocontext *c, int i)
 {
        context_destroy(&c->context[0]);
@@ -632,7 +674,6 @@ void policydb_destroy(struct policydb *p)
        int i;
        struct role_allow *ra, *lra = NULL;
        struct role_trans *tr, *ltr = NULL;
-       struct range_trans *rt, *lrt = NULL;
 
        for (i = 0; i < SYM_NUM; i++) {
                cond_resched();
@@ -693,20 +734,8 @@ void policydb_destroy(struct policydb *p)
        }
        kfree(lra);
 
-       for (rt = p->range_tr; rt; rt = rt->next) {
-               cond_resched();
-               if (lrt) {
-                       ebitmap_destroy(&lrt->target_range.level[0].cat);
-                       ebitmap_destroy(&lrt->target_range.level[1].cat);
-                       kfree(lrt);
-               }
-               lrt = rt;
-       }
-       if (lrt) {
-               ebitmap_destroy(&lrt->target_range.level[0].cat);
-               ebitmap_destroy(&lrt->target_range.level[1].cat);
-               kfree(lrt);
-       }
+       hashtab_map(p->range_tr, range_tr_destroy, NULL);
+       hashtab_destroy(p->range_tr);
 
        if (p->type_attr_map) {
                for (i = 0; i < p->p_types.nprim; i++)
@@ -1686,12 +1715,11 @@ int policydb_read(struct policydb *p, void *fp)
        int i, j, rc;
        __le32 buf[4];
        u32 nodebuf[8];
-       u32 len, len2, config, nprim, nel, nel2;
+       u32 len, len2, nprim, nel, nel2;
        char *policydb_str;
        struct policydb_compat_info *info;
-       struct range_trans *rt, *lrt;
-
-       config = 0;
+       struct range_trans *rt;
+       struct mls_range *r;
 
        rc = policydb_init(p);
        if (rc)
@@ -1740,7 +1768,7 @@ int policydb_read(struct policydb *p, void *fp)
        kfree(policydb_str);
        policydb_str = NULL;
 
-       /* Read the version, config, and table sizes. */
+       /* Read the version and table sizes. */
        rc = next_entry(buf, fp, sizeof(u32)*4);
        if (rc < 0)
                goto bad;
@@ -1755,13 +1783,7 @@ int policydb_read(struct policydb *p, void *fp)
        }
 
        if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) {
-               if (ss_initialized && !selinux_mls_enabled) {
-                       printk(KERN_ERR "SELinux: Cannot switch between non-MLS"
-                               " and MLS policies\n");
-                       goto bad;
-               }
-               selinux_mls_enabled = 1;
-               config |= POLICYDB_CONFIG_MLS;
+               p->mls_enabled = 1;
 
                if (p->policyvers < POLICYDB_VERSION_MLS) {
                        printk(KERN_ERR "SELinux: security policydb version %d "
@@ -1769,12 +1791,6 @@ int policydb_read(struct policydb *p, void *fp)
                                p->policyvers);
                        goto bad;
                }
-       } else {
-               if (ss_initialized && selinux_mls_enabled) {
-                       printk(KERN_ERR "SELinux: Cannot switch between MLS and"
-                               " non-MLS policies\n");
-                       goto bad;
-               }
        }
        p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
        p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
@@ -2122,44 +2138,61 @@ int policydb_read(struct policydb *p, void *fp)
                if (rc < 0)
                        goto bad;
                nel = le32_to_cpu(buf[0]);
-               lrt = NULL;
                for (i = 0; i < nel; i++) {
                        rt = kzalloc(sizeof(*rt), GFP_KERNEL);
                        if (!rt) {
                                rc = -ENOMEM;
                                goto bad;
                        }
-                       if (lrt)
-                               lrt->next = rt;
-                       else
-                               p->range_tr = rt;
                        rc = next_entry(buf, fp, (sizeof(u32) * 2));
-                       if (rc < 0)
+                       if (rc < 0) {
+                               kfree(rt);
                                goto bad;
+                       }
                        rt->source_type = le32_to_cpu(buf[0]);
                        rt->target_type = le32_to_cpu(buf[1]);
                        if (new_rangetr) {
                                rc = next_entry(buf, fp, sizeof(u32));
-                               if (rc < 0)
+                               if (rc < 0) {
+                                       kfree(rt);
                                        goto bad;
+                               }
                                rt->target_class = le32_to_cpu(buf[0]);
                        } else
                                rt->target_class = p->process_class;
                        if (!policydb_type_isvalid(p, rt->source_type) ||
                            !policydb_type_isvalid(p, rt->target_type) ||
                            !policydb_class_isvalid(p, rt->target_class)) {
+                               kfree(rt);
                                rc = -EINVAL;
                                goto bad;
                        }
-                       rc = mls_read_range_helper(&rt->target_range, fp);
-                       if (rc)
+                       r = kzalloc(sizeof(*r), GFP_KERNEL);
+                       if (!r) {
+                               kfree(rt);
+                               rc = -ENOMEM;
                                goto bad;
-                       if (!mls_range_isvalid(p, &rt->target_range)) {
+                       }
+                       rc = mls_read_range_helper(r, fp);
+                       if (rc) {
+                               kfree(rt);
+                               kfree(r);
+                               goto bad;
+                       }
+                       if (!mls_range_isvalid(p, r)) {
                                printk(KERN_WARNING "SELinux:  rangetrans:  invalid range\n");
+                               kfree(rt);
+                               kfree(r);
+                               goto bad;
+                       }
+                       rc = hashtab_insert(p->range_tr, rt, r);
+                       if (rc) {
+                               kfree(rt);
+                               kfree(r);
                                goto bad;
                        }
-                       lrt = rt;
                }
+               rangetr_hash_eval(p->range_tr);
        }
 
        p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL);
index cdcc5700946f7f850dfb251f8c8ccb0596ac3c74..26d9adf8542b313982df291d6357d04415192b03 100644 (file)
@@ -27,6 +27,8 @@
 #include "symtab.h"
 #include "avtab.h"
 #include "sidtab.h"
+#include "ebitmap.h"
+#include "mls_types.h"
 #include "context.h"
 #include "constraint.h"
 
@@ -113,8 +115,6 @@ struct range_trans {
        u32 source_type;
        u32 target_type;
        u32 target_class;
-       struct mls_range target_range;
-       struct range_trans *next;
 };
 
 /* Boolean data type */
@@ -187,6 +187,8 @@ struct genfs {
 
 /* The policy database */
 struct policydb {
+       int mls_enabled;
+
        /* symbol tables */
        struct symtab symtab[SYM_NUM];
 #define p_commons symtab[SYM_COMMONS]
@@ -240,8 +242,8 @@ struct policydb {
           fixed labeling behavior. */
        struct genfs *genfs;
 
-       /* range transitions */
-       struct range_trans *range_tr;
+       /* range transitions table (range_trans_key -> mls_range) */
+       struct hashtab *range_tr;
 
        /* type -> attribute reverse mapping */
        struct ebitmap *type_attr_map;
index b3efae204ac7cd40c68fd3c85d066997f74fcbc2..cf27b3ee1a95b45cfa5517d311dacf9535d823c1 100644 (file)
  *
  *  Added support for bounds domain and audit messaged on masked permissions
  *
+ * Updated: Guido Trentalancia <guido@trentalancia.com>
+ *
+ *  Added support for runtime switching of the policy type
+ *
  * Copyright (C) 2008, 2009 NEC Corporation
  * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
@@ -87,11 +91,10 @@ static u32 latest_granting;
 static int context_struct_to_string(struct context *context, char **scontext,
                                    u32 *scontext_len);
 
-static int context_struct_compute_av(struct context *scontext,
-                                    struct context *tcontext,
-                                    u16 tclass,
-                                    u32 requested,
-                                    struct av_decision *avd);
+static void context_struct_compute_av(struct context *scontext,
+                                     struct context *tcontext,
+                                     u16 tclass,
+                                     struct av_decision *avd);
 
 struct selinux_mapping {
        u16 value; /* policy value */
@@ -196,23 +199,6 @@ static u16 unmap_class(u16 tclass)
        return tclass;
 }
 
-static u32 unmap_perm(u16 tclass, u32 tperm)
-{
-       if (tclass < current_mapping_size) {
-               unsigned i;
-               u32 kperm = 0;
-
-               for (i = 0; i < current_mapping[tclass].num_perms; i++)
-                       if (tperm & (1<<i)) {
-                               kperm |= current_mapping[tclass].perms[i];
-                               tperm &= ~(1<<i);
-                       }
-               return kperm;
-       }
-
-       return tperm;
-}
-
 static void map_decision(u16 tclass, struct av_decision *avd,
                         int allow_unknown)
 {
@@ -250,6 +236,10 @@ static void map_decision(u16 tclass, struct av_decision *avd,
        }
 }
 
+int security_mls_enabled(void)
+{
+       return policydb.mls_enabled;
+}
 
 /*
  * Return the boolean value of a constraint expression
@@ -465,7 +455,8 @@ static void security_dump_masked_av(struct context *scontext,
        char *scontext_name = NULL;
        char *tcontext_name = NULL;
        char *permission_names[32];
-       int index, length;
+       int index;
+       u32 length;
        bool need_comma = false;
 
        if (!permissions)
@@ -532,7 +523,6 @@ out:
 static void type_attribute_bounds_av(struct context *scontext,
                                     struct context *tcontext,
                                     u16 tclass,
-                                    u32 requested,
                                     struct av_decision *avd)
 {
        struct context lo_scontext;
@@ -553,7 +543,6 @@ static void type_attribute_bounds_av(struct context *scontext,
                context_struct_compute_av(&lo_scontext,
                                          tcontext,
                                          tclass,
-                                         requested,
                                          &lo_avd);
                if ((lo_avd.allowed & avd->allowed) == avd->allowed)
                        return;         /* no masked permission */
@@ -569,7 +558,6 @@ static void type_attribute_bounds_av(struct context *scontext,
                context_struct_compute_av(scontext,
                                          &lo_tcontext,
                                          tclass,
-                                         requested,
                                          &lo_avd);
                if ((lo_avd.allowed & avd->allowed) == avd->allowed)
                        return;         /* no masked permission */
@@ -586,7 +574,6 @@ static void type_attribute_bounds_av(struct context *scontext,
                context_struct_compute_av(&lo_scontext,
                                          &lo_tcontext,
                                          tclass,
-                                         requested,
                                          &lo_avd);
                if ((lo_avd.allowed & avd->allowed) == avd->allowed)
                        return;         /* no masked permission */
@@ -607,11 +594,10 @@ static void type_attribute_bounds_av(struct context *scontext,
  * Compute access vectors based on a context structure pair for
  * the permissions in a particular class.
  */
-static int context_struct_compute_av(struct context *scontext,
-                                    struct context *tcontext,
-                                    u16 tclass,
-                                    u32 requested,
-                                    struct av_decision *avd)
+static void context_struct_compute_av(struct context *scontext,
+                                     struct context *tcontext,
+                                     u16 tclass,
+                                     struct av_decision *avd)
 {
        struct constraint_node *constraint;
        struct role_allow *ra;
@@ -622,19 +608,14 @@ static int context_struct_compute_av(struct context *scontext,
        struct ebitmap_node *snode, *tnode;
        unsigned int i, j;
 
-       /*
-        * Initialize the access vectors to the default values.
-        */
        avd->allowed = 0;
        avd->auditallow = 0;
        avd->auditdeny = 0xffffffff;
-       avd->seqno = latest_granting;
-       avd->flags = 0;
 
        if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
                if (printk_ratelimit())
                        printk(KERN_WARNING "SELinux:  Invalid class %hu\n", tclass);
-               return -EINVAL;
+               return;
        }
 
        tclass_datum = policydb.class_val_to_struct[tclass - 1];
@@ -705,9 +686,7 @@ static int context_struct_compute_av(struct context *scontext,
         * permission and notice it to userspace via audit.
         */
        type_attribute_bounds_av(scontext, tcontext,
-                                tclass, requested, avd);
-
-       return 0;
+                                tclass, avd);
 }
 
 static int security_validtrans_handle_fail(struct context *ocontext,
@@ -864,7 +843,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
        if (rc) {
                char *old_name = NULL;
                char *new_name = NULL;
-               int length;
+               u32 length;
 
                if (!context_struct_to_string(old_context,
                                              &old_name, &length) &&
@@ -886,110 +865,116 @@ out:
        return rc;
 }
 
-
-static int security_compute_av_core(u32 ssid,
-                                   u32 tsid,
-                                   u16 tclass,
-                                   u32 requested,
-                                   struct av_decision *avd)
+static void avd_init(struct av_decision *avd)
 {
-       struct context *scontext = NULL, *tcontext = NULL;
-       int rc = 0;
-
-       scontext = sidtab_search(&sidtab, ssid);
-       if (!scontext) {
-               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
-                      __func__, ssid);
-               return -EINVAL;
-       }
-       tcontext = sidtab_search(&sidtab, tsid);
-       if (!tcontext) {
-               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
-                      __func__, tsid);
-               return -EINVAL;
-       }
-
-       rc = context_struct_compute_av(scontext, tcontext, tclass,
-                                      requested, avd);
-
-       /* permissive domain? */
-       if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
-               avd->flags |= AVD_FLAGS_PERMISSIVE;
-
-       return rc;
+       avd->allowed = 0;
+       avd->auditallow = 0;
+       avd->auditdeny = 0xffffffff;
+       avd->seqno = latest_granting;
+       avd->flags = 0;
 }
 
+
 /**
  * security_compute_av - Compute access vector decisions.
  * @ssid: source security identifier
  * @tsid: target security identifier
  * @tclass: target security class
- * @requested: requested permissions
  * @avd: access vector decisions
  *
  * Compute a set of access vector decisions based on the
  * SID pair (@ssid, @tsid) for the permissions in @tclass.
- * Return -%EINVAL if any of the parameters are invalid or %0
- * if the access vector decisions were computed successfully.
  */
-int security_compute_av(u32 ssid,
-                       u32 tsid,
-                       u16 orig_tclass,
-                       u32 orig_requested,
-                       struct av_decision *avd)
+void security_compute_av(u32 ssid,
+                        u32 tsid,
+                        u16 orig_tclass,
+                        struct av_decision *avd)
 {
        u16 tclass;
-       u32 requested;
-       int rc;
+       struct context *scontext = NULL, *tcontext = NULL;
 
        read_lock(&policy_rwlock);
-
+       avd_init(avd);
        if (!ss_initialized)
                goto allow;
 
-       requested = unmap_perm(orig_tclass, orig_requested);
+       scontext = sidtab_search(&sidtab, ssid);
+       if (!scontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, ssid);
+               goto out;
+       }
+
+       /* permissive domain? */
+       if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+               avd->flags |= AVD_FLAGS_PERMISSIVE;
+
+       tcontext = sidtab_search(&sidtab, tsid);
+       if (!tcontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, tsid);
+               goto out;
+       }
+
        tclass = unmap_class(orig_tclass);
        if (unlikely(orig_tclass && !tclass)) {
                if (policydb.allow_unknown)
                        goto allow;
-               rc = -EINVAL;
                goto out;
        }
-       rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
+       context_struct_compute_av(scontext, tcontext, tclass, avd);
        map_decision(orig_tclass, avd, policydb.allow_unknown);
 out:
        read_unlock(&policy_rwlock);
-       return rc;
+       return;
 allow:
        avd->allowed = 0xffffffff;
-       avd->auditallow = 0;
-       avd->auditdeny = 0xffffffff;
-       avd->seqno = latest_granting;
-       avd->flags = 0;
-       rc = 0;
        goto out;
 }
 
-int security_compute_av_user(u32 ssid,
-                            u32 tsid,
-                            u16 tclass,
-                            u32 requested,
-                            struct av_decision *avd)
+void security_compute_av_user(u32 ssid,
+                             u32 tsid,
+                             u16 tclass,
+                             struct av_decision *avd)
 {
-       int rc;
+       struct context *scontext = NULL, *tcontext = NULL;
 
-       if (!ss_initialized) {
-               avd->allowed = 0xffffffff;
-               avd->auditallow = 0;
-               avd->auditdeny = 0xffffffff;
-               avd->seqno = latest_granting;
-               return 0;
+       read_lock(&policy_rwlock);
+       avd_init(avd);
+       if (!ss_initialized)
+               goto allow;
+
+       scontext = sidtab_search(&sidtab, ssid);
+       if (!scontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, ssid);
+               goto out;
        }
 
-       read_lock(&policy_rwlock);
-       rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
+       /* permissive domain? */
+       if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+               avd->flags |= AVD_FLAGS_PERMISSIVE;
+
+       tcontext = sidtab_search(&sidtab, tsid);
+       if (!tcontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, tsid);
+               goto out;
+       }
+
+       if (unlikely(!tclass)) {
+               if (policydb.allow_unknown)
+                       goto allow;
+               goto out;
+       }
+
+       context_struct_compute_av(scontext, tcontext, tclass, avd);
+ out:
        read_unlock(&policy_rwlock);
-       return rc;
+       return;
+allow:
+       avd->allowed = 0xffffffff;
+       goto out;
 }
 
 /*
@@ -1565,7 +1550,10 @@ static int clone_sid(u32 sid,
 {
        struct sidtab *s = arg;
 
-       return sidtab_insert(s, sid, context);
+       if (sid > SECINITSID_NUM)
+               return sidtab_insert(s, sid, context);
+       else
+               return 0;
 }
 
 static inline int convert_context_handle_invalid_context(struct context *context)
@@ -1606,12 +1594,17 @@ static int convert_context(u32 key,
 {
        struct convert_context_args *args;
        struct context oldc;
+       struct ocontext *oc;
+       struct mls_range *range;
        struct role_datum *role;
        struct type_datum *typdatum;
        struct user_datum *usrdatum;
        char *s;
        u32 len;
-       int rc;
+       int rc = 0;
+
+       if (key <= SECINITSID_NUM)
+               goto out;
 
        args = p;
 
@@ -1673,9 +1666,39 @@ static int convert_context(u32 key,
                goto bad;
        c->type = typdatum->value;
 
-       rc = mls_convert_context(args->oldp, args->newp, c);
-       if (rc)
-               goto bad;
+       /* Convert the MLS fields if dealing with MLS policies */
+       if (args->oldp->mls_enabled && args->newp->mls_enabled) {
+               rc = mls_convert_context(args->oldp, args->newp, c);
+               if (rc)
+                       goto bad;
+       } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) {
+               /*
+                * Switching between MLS and non-MLS policy:
+                * free any storage used by the MLS fields in the
+                * context for all existing entries in the sidtab.
+                */
+               mls_context_destroy(c);
+       } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) {
+               /*
+                * Switching between non-MLS and MLS policy:
+                * ensure that the MLS fields of the context for all
+                * existing entries in the sidtab are filled in with a
+                * suitable default value, likely taken from one of the
+                * initial SIDs.
+                */
+               oc = args->newp->ocontexts[OCON_ISID];
+               while (oc && oc->sid[0] != SECINITSID_UNLABELED)
+                       oc = oc->next;
+               if (!oc) {
+                       printk(KERN_ERR "SELinux:  unable to look up"
+                               " the initial SIDs list\n");
+                       goto bad;
+               }
+               range = &oc->context[0].range;
+               rc = mls_range_set(c, range);
+               if (rc)
+                       goto bad;
+       }
 
        /* Check the validity of the new context. */
        if (!policydb_context_isvalid(args->newp, c)) {
@@ -1771,9 +1794,17 @@ int security_load_policy(void *data, size_t len)
        if (policydb_read(&newpolicydb, fp))
                return -EINVAL;
 
-       if (sidtab_init(&newsidtab)) {
+       /* If switching between different policy types, log MLS status */
+       if (policydb.mls_enabled && !newpolicydb.mls_enabled)
+               printk(KERN_INFO "SELinux: Disabling MLS support...\n");
+       else if (!policydb.mls_enabled && newpolicydb.mls_enabled)
+               printk(KERN_INFO "SELinux: Enabling MLS support...\n");
+
+       rc = policydb_load_isids(&newpolicydb, &newsidtab);
+       if (rc) {
+               printk(KERN_ERR "SELinux:  unable to load the initial SIDs\n");
                policydb_destroy(&newpolicydb);
-               return -ENOMEM;
+               return rc;
        }
 
        if (selinux_set_mapping(&newpolicydb, secclass_map,
@@ -1800,8 +1831,12 @@ int security_load_policy(void *data, size_t len)
        args.oldp = &policydb;
        args.newp = &newpolicydb;
        rc = sidtab_map(&newsidtab, convert_context, &args);
-       if (rc)
+       if (rc) {
+               printk(KERN_ERR "SELinux:  unable to convert the internal"
+                       " representation of contexts in the new SID"
+                       " table\n");
                goto err;
+       }
 
        /* Save the old policydb and SID table to free later. */
        memcpy(&oldpolicydb, &policydb, sizeof policydb);
@@ -2397,7 +2432,7 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
        u32 len;
        int rc = 0;
 
-       if (!ss_initialized || !selinux_mls_enabled) {
+       if (!ss_initialized || !policydb.mls_enabled) {
                *new_sid = sid;
                goto out;
        }
@@ -2498,7 +2533,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
        /* we don't need to check ss_initialized here since the only way both
         * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
         * security server was initialized and ss_initialized was true */
-       if (!selinux_mls_enabled) {
+       if (!policydb.mls_enabled) {
                *peer_sid = SECSID_NULL;
                return 0;
        }
@@ -2555,7 +2590,7 @@ int security_get_classes(char ***classes, int *nclasses)
        read_lock(&policy_rwlock);
 
        *nclasses = policydb.p_classes.nprim;
-       *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
+       *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC);
        if (!*classes)
                goto out;
 
@@ -2602,7 +2637,7 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
        }
 
        *nperms = match->permissions.nprim;
-       *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
+       *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC);
        if (!*perms)
                goto out;
 
index 529c9ca65878a0139303362316f3e0936b0da8c8..a5721b373f53935601c63687acc5b4454acc54b4 100644 (file)
@@ -157,12 +157,12 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
  *
  * Returns 0 on success, error code otherwise.
  */
-static int smack_syslog(int type)
+static int smack_syslog(int type, bool from_file)
 {
        int rc;
        char *sp = current_security();
 
-       rc = cap_syslog(type);
+       rc = cap_syslog(type, from_file);
        if (rc != 0)
                return rc;
 
index 10ccd686b290959c6316e1b451e433809348421e..60a9e2002da1c88f372ab6d29b3abf651a6d5d10 100644 (file)
@@ -1 +1 @@
-obj-y = common.o realpath.o tomoyo.o domain.o file.o
+obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o
index e0d0354008b75a041887b2ae59a50fbd1e572a2b..ff51f1026b576af78b0ba5138bd8404822a6a130 100644 (file)
 #include <linux/uaccess.h>
 #include <linux/security.h>
 #include <linux/hardirq.h>
-#include "realpath.h"
 #include "common.h"
-#include "tomoyo.h"
+
+/* Lock for protecting policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);
 
 /* Has loading policy done? */
 bool tomoyo_policy_loaded;
@@ -178,14 +179,12 @@ static void tomoyo_normalize_line(unsigned char *buffer)
  *                1 = must / -1 = must not / 0 = don't care
  * @end_type:     Should the pathname end with '/'?
  *                1 = must / -1 = must not / 0 = don't care
- * @function:     The name of function calling me.
  *
  * Check whether the given filename follows the naming rules.
  * Returns true if @filename follows the naming rules, false otherwise.
  */
 bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
-                           const s8 pattern_type, const s8 end_type,
-                           const char *function)
+                           const s8 pattern_type, const s8 end_type)
 {
        const char *const start = filename;
        bool in_repetition = false;
@@ -193,7 +192,6 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
        unsigned char c;
        unsigned char d;
        unsigned char e;
-       const char *original_filename = filename;
 
        if (!filename)
                goto out;
@@ -282,25 +280,20 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
                goto out;
        return true;
  out:
-       printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
-              original_filename);
        return false;
 }
 
 /**
  * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
  * @domainname:   The domainname to check.
- * @function:     The name of function calling me.
  *
  * Returns true if @domainname follows the naming rules, false otherwise.
  */
-bool tomoyo_is_correct_domain(const unsigned char *domainname,
-                             const char *function)
+bool tomoyo_is_correct_domain(const unsigned char *domainname)
 {
        unsigned char c;
        unsigned char d;
        unsigned char e;
-       const char *org_domainname = domainname;
 
        if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
                                   TOMOYO_ROOT_NAME_LEN))
@@ -343,8 +336,6 @@ bool tomoyo_is_correct_domain(const unsigned char *domainname,
        } while (*domainname);
        return true;
  out:
-       printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,
-              org_domainname);
        return false;
 }
 
@@ -365,10 +356,9 @@ bool tomoyo_is_domain_def(const unsigned char *buffer)
  *
  * @domainname: The domainname to find.
  *
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
  * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
 {
@@ -377,7 +367,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
 
        name.name = domainname;
        tomoyo_fill_path_info(&name);
-       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+       list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
                if (!domain->is_deleted &&
                    !tomoyo_pathcmp(&name, domain->domainname))
                        return domain;
@@ -748,7 +738,7 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
  *
  * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
  *
- * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * This function uses kzalloc(), so the caller must call kfree()
  * if this function didn't return NULL.
  */
 static const char *tomoyo_get_exe(void)
@@ -829,6 +819,8 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
  * @domain: Pointer to "struct tomoyo_domain_info".
  *
  * Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
 {
@@ -837,61 +829,29 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
 
        if (!domain)
                return true;
-       down_read(&tomoyo_domain_acl_info_list_lock);
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
-               if (ptr->type & TOMOYO_ACL_DELETED)
-                       continue;
-               switch (tomoyo_acl_type2(ptr)) {
-                       struct tomoyo_single_path_acl_record *acl1;
-                       struct tomoyo_double_path_acl_record *acl2;
-                       u16 perm;
-               case TOMOYO_TYPE_SINGLE_PATH_ACL:
-                       acl1 = container_of(ptr,
-                                   struct tomoyo_single_path_acl_record,
-                                           head);
-                       perm = acl1->perm;
-                       if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
-                               count++;
-                       if (perm &
-                           ((1 << TOMOYO_TYPE_READ_ACL) |
-                            (1 << TOMOYO_TYPE_WRITE_ACL)))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
-                               count++;
+       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+               switch (ptr->type) {
+                       struct tomoyo_path_acl *acl;
+                       u32 perm;
+                       u8 i;
+               case TOMOYO_TYPE_PATH_ACL:
+                       acl = container_of(ptr, struct tomoyo_path_acl, head);
+                       perm = acl->perm | (((u32) acl->perm_high) << 16);
+                       for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++)
+                               if (perm & (1 << i))
+                                       count++;
+                       if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
+                               count -= 2;
                        break;
-               case TOMOYO_TYPE_DOUBLE_PATH_ACL:
-                       acl2 = container_of(ptr,
-                                   struct tomoyo_double_path_acl_record,
-                                           head);
-                       perm = acl2->perm;
-                       if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
-                               count++;
+               case TOMOYO_TYPE_PATH2_ACL:
+                       perm = container_of(ptr, struct tomoyo_path2_acl, head)
+                               ->perm;
+                       for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++)
+                               if (perm & (1 << i))
+                                       count++;
                        break;
                }
        }
-       up_read(&tomoyo_domain_acl_info_list_lock);
        if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
                return true;
        if (!domain->quota_warned) {
@@ -923,9 +883,11 @@ static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
        ptr = tomoyo_profile_ptr[profile];
        if (ptr)
                goto ok;
-       ptr = tomoyo_alloc_element(sizeof(*ptr));
-       if (!ptr)
+       ptr = kmalloc(sizeof(*ptr), GFP_KERNEL);
+       if (!tomoyo_memory_ok(ptr)) {
+               kfree(ptr);
                goto ok;
+       }
        for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
                ptr->value[i] = tomoyo_control_array[i].current_value;
        mb(); /* Avoid out-of-order execution. */
@@ -966,7 +928,9 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
                return -EINVAL;
        *cp = '\0';
        if (!strcmp(data, "COMMENT")) {
-               profile->comment = tomoyo_save_name(cp + 1);
+               const struct tomoyo_path_info *old_comment = profile->comment;
+               profile->comment = tomoyo_get_name(cp + 1);
+               tomoyo_put_name(old_comment);
                return 0;
        }
        for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
@@ -1060,27 +1024,6 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
        return 0;
 }
 
-/*
- * tomoyo_policy_manager_entry is a structure which is used for holding list of
- * domainnames or programs which are permitted to modify configuration via
- * /sys/kernel/security/tomoyo/ interface.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_policy_manager_list .
- *  (2) "manager" is a domainname or a program's pathname.
- *  (3) "is_domain" is a bool which is true if "manager" is a domainname, false
- *      otherwise.
- *  (4) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- */
-struct tomoyo_policy_manager_entry {
-       struct list_head list;
-       /* A path to program or a domainname. */
-       const struct tomoyo_path_info *manager;
-       bool is_domain;  /* True if manager is a domainname. */
-       bool is_deleted; /* True if this entry is deleted. */
-};
-
 /*
  * tomoyo_policy_manager_list is used for holding list of domainnames or
  * programs which are permitted to modify configuration via
@@ -1111,8 +1054,7 @@ struct tomoyo_policy_manager_entry {
  *
  * # cat /sys/kernel/security/tomoyo/manager
  */
-static LIST_HEAD(tomoyo_policy_manager_list);
-static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
+LIST_HEAD(tomoyo_policy_manager_list);
 
 /**
  * tomoyo_update_manager_entry - Add a manager entry.
@@ -1121,48 +1063,50 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
  * @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_policy_manager_entry *new_entry;
+       struct tomoyo_policy_manager_entry *entry = NULL;
        struct tomoyo_policy_manager_entry *ptr;
        const struct tomoyo_path_info *saved_manager;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
        bool is_domain = false;
 
        if (tomoyo_is_domain_def(manager)) {
-               if (!tomoyo_is_correct_domain(manager, __func__))
+               if (!tomoyo_is_correct_domain(manager))
                        return -EINVAL;
                is_domain = true;
        } else {
-               if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__))
+               if (!tomoyo_is_correct_path(manager, 1, -1, -1))
                        return -EINVAL;
        }
-       saved_manager = tomoyo_save_name(manager);
+       saved_manager = tomoyo_get_name(manager);
        if (!saved_manager)
                return -ENOMEM;
-       down_write(&tomoyo_policy_manager_list_lock);
-       list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
                if (ptr->manager != saved_manager)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->manager = saved_manager;
+               saved_manager = NULL;
+               entry->is_domain = is_domain;
+               list_add_tail_rcu(&entry->list, &tomoyo_policy_manager_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->manager = saved_manager;
-       new_entry->is_domain = is_domain;
-       list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
-       error = 0;
- out:
-       up_write(&tomoyo_policy_manager_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       tomoyo_put_name(saved_manager);
+       kfree(entry);
        return error;
 }
 
@@ -1172,6 +1116,8 @@ static int tomoyo_update_manager_entry(const char *manager,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
 {
@@ -1191,6 +1137,8 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
 {
@@ -1199,7 +1147,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
 
        if (head->read_eof)
                return 0;
-       down_read(&tomoyo_policy_manager_list_lock);
        list_for_each_cookie(pos, head->read_var2,
                             &tomoyo_policy_manager_list) {
                struct tomoyo_policy_manager_entry *ptr;
@@ -1211,7 +1158,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_policy_manager_list_lock);
        head->read_eof = done;
        return 0;
 }
@@ -1221,6 +1167,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
  *
  * 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_is_policy_manager(void)
 {
@@ -1234,29 +1182,25 @@ static bool tomoyo_is_policy_manager(void)
                return true;
        if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
                return false;
-       down_read(&tomoyo_policy_manager_list_lock);
-       list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
                if (!ptr->is_deleted && ptr->is_domain
                    && !tomoyo_pathcmp(domainname, ptr->manager)) {
                        found = true;
                        break;
                }
        }
-       up_read(&tomoyo_policy_manager_list_lock);
        if (found)
                return true;
        exe = tomoyo_get_exe();
        if (!exe)
                return false;
-       down_read(&tomoyo_policy_manager_list_lock);
-       list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
                if (!ptr->is_deleted && !ptr->is_domain
                    && !strcmp(exe, ptr->manager->name)) {
                        found = true;
                        break;
                }
        }
-       up_read(&tomoyo_policy_manager_list_lock);
        if (!found) { /* Reduce error messages. */
                static pid_t last_pid;
                const pid_t pid = current->pid;
@@ -1266,7 +1210,7 @@ static bool tomoyo_is_policy_manager(void)
                        last_pid = pid;
                }
        }
-       tomoyo_free(exe);
+       kfree(exe);
        return found;
 }
 
@@ -1277,6 +1221,8 @@ static bool tomoyo_is_policy_manager(void)
  * @data: String to parse.
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
                                 const char *data)
@@ -1286,17 +1232,16 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
 
        if (sscanf(data, "pid=%u", &pid) == 1) {
                struct task_struct *p;
+               rcu_read_lock();
                read_lock(&tasklist_lock);
                p = find_task_by_vpid(pid);
                if (p)
                        domain = tomoyo_real_domain(p);
                read_unlock(&tasklist_lock);
+               rcu_read_unlock();
        } else if (!strncmp(data, "domain=", 7)) {
-               if (tomoyo_is_domain_def(data + 7)) {
-                       down_read(&tomoyo_domain_list_lock);
+               if (tomoyo_is_domain_def(data + 7))
                        domain = tomoyo_find_domain(data + 7);
-                       up_read(&tomoyo_domain_list_lock);
-               }
        } else
                return false;
        head->write_var1 = domain;
@@ -1310,13 +1255,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
        if (domain) {
                struct tomoyo_domain_info *d;
                head->read_var1 = NULL;
-               down_read(&tomoyo_domain_list_lock);
-               list_for_each_entry(d, &tomoyo_domain_list, list) {
+               list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
                        if (d == domain)
                                break;
                        head->read_var1 = &d->list;
                }
-               up_read(&tomoyo_domain_list_lock);
                head->read_var2 = NULL;
                head->read_bit = 0;
                head->read_step = 0;
@@ -1332,6 +1275,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
  * @domainname: The name of domain.
  *
  * Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_delete_domain(char *domainname)
 {
@@ -1340,9 +1285,9 @@ static int tomoyo_delete_domain(char *domainname)
 
        name.name = domainname;
        tomoyo_fill_path_info(&name);
-       down_write(&tomoyo_domain_list_lock);
+       mutex_lock(&tomoyo_policy_lock);
        /* Is there an active domain? */
-       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+       list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
                /* Never delete tomoyo_kernel_domain */
                if (domain == &tomoyo_kernel_domain)
                        continue;
@@ -1352,7 +1297,7 @@ static int tomoyo_delete_domain(char *domainname)
                domain->is_deleted = true;
                break;
        }
-       up_write(&tomoyo_domain_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
        return 0;
 }
 
@@ -1362,6 +1307,8 @@ static int tomoyo_delete_domain(char *domainname)
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
 {
@@ -1384,11 +1331,9 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
                domain = NULL;
                if (is_delete)
                        tomoyo_delete_domain(data);
-               else if (is_select) {
-                       down_read(&tomoyo_domain_list_lock);
+               else if (is_select)
                        domain = tomoyo_find_domain(data);
-                       up_read(&tomoyo_domain_list_lock);
-               } else
+               else
                        domain = tomoyo_find_or_assign_new_domain(data, 0);
                head->write_var1 = domain;
                return 0;
@@ -1403,43 +1348,39 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
                return 0;
        }
        if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
-               tomoyo_set_domain_flag(domain, is_delete,
-                              TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);
+               domain->ignore_global_allow_read = !is_delete;
                return 0;
        }
        return tomoyo_write_file_policy(data, domain, is_delete);
 }
 
 /**
- * tomoyo_print_single_path_acl - Print a single path ACL entry.
+ * tomoyo_print_path_acl - Print a single path ACL entry.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
- * @ptr:  Pointer to "struct tomoyo_single_path_acl_record".
+ * @ptr:  Pointer to "struct tomoyo_path_acl".
  *
  * Returns true on success, false otherwise.
  */
-static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
-                                        struct tomoyo_single_path_acl_record *
-                                        ptr)
+static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head,
+                                 struct tomoyo_path_acl *ptr)
 {
        int pos;
        u8 bit;
        const char *atmark = "";
        const char *filename;
-       const u16 perm = ptr->perm;
+       const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16);
 
        filename = ptr->filename->name;
-       for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
-            bit++) {
+       for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
                const char *msg;
                if (!(perm & (1 << bit)))
                        continue;
                /* Print "read/write" instead of "read" and "write". */
-               if ((bit == TOMOYO_TYPE_READ_ACL ||
-                    bit == TOMOYO_TYPE_WRITE_ACL)
-                   && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+               if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE)
+                   && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
                        continue;
-               msg = tomoyo_sp2keyword(bit);
+               msg = tomoyo_path2keyword(bit);
                pos = head->read_avail;
                if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg,
                                      atmark, filename))
@@ -1454,16 +1395,15 @@ static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
 }
 
 /**
- * tomoyo_print_double_path_acl - Print a double path ACL entry.
+ * tomoyo_print_path2_acl - Print a double path ACL entry.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
- * @ptr:  Pointer to "struct tomoyo_double_path_acl_record".
+ * @ptr:  Pointer to "struct tomoyo_path2_acl".
  *
  * Returns true on success, false otherwise.
  */
-static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
-                                        struct tomoyo_double_path_acl_record *
-                                        ptr)
+static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head,
+                                  struct tomoyo_path2_acl *ptr)
 {
        int pos;
        const char *atmark1 = "";
@@ -1475,12 +1415,11 @@ static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
 
        filename1 = ptr->filename1->name;
        filename2 = ptr->filename2->name;
-       for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION;
-            bit++) {
+       for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
                const char *msg;
                if (!(perm & (1 << bit)))
                        continue;
-               msg = tomoyo_dp2keyword(bit);
+               msg = tomoyo_path22keyword(bit);
                pos = head->read_avail;
                if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg,
                                      atmark1, filename1, atmark2, filename2))
@@ -1505,23 +1444,17 @@ static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
 static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
                               struct tomoyo_acl_info *ptr)
 {
-       const u8 acl_type = tomoyo_acl_type2(ptr);
+       const u8 acl_type = ptr->type;
 
-       if (acl_type & TOMOYO_ACL_DELETED)
-               return true;
-       if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) {
-               struct tomoyo_single_path_acl_record *acl
-                       = container_of(ptr,
-                                      struct tomoyo_single_path_acl_record,
-                                      head);
-               return tomoyo_print_single_path_acl(head, acl);
+       if (acl_type == TOMOYO_TYPE_PATH_ACL) {
+               struct tomoyo_path_acl *acl
+                       = container_of(ptr, struct tomoyo_path_acl, head);
+               return tomoyo_print_path_acl(head, acl);
        }
-       if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) {
-               struct tomoyo_double_path_acl_record *acl
-                       = container_of(ptr,
-                                      struct tomoyo_double_path_acl_record,
-                                      head);
-               return tomoyo_print_double_path_acl(head, acl);
+       if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
+               struct tomoyo_path2_acl *acl
+                       = container_of(ptr, struct tomoyo_path2_acl, head);
+               return tomoyo_print_path2_acl(head, acl);
        }
        BUG(); /* This must not happen. */
        return false;
@@ -1533,6 +1466,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
 {
@@ -1544,7 +1479,6 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
                return 0;
        if (head->read_step == 0)
                head->read_step = 1;
-       down_read(&tomoyo_domain_list_lock);
        list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
                struct tomoyo_domain_info *domain;
                const char *quota_exceeded = "";
@@ -1558,10 +1492,9 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
                /* Print domainname and flags. */
                if (domain->quota_warned)
                        quota_exceeded = "quota_exceeded\n";
-               if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED)
+               if (domain->transition_failed)
                        transition_failed = "transition_failed\n";
-               if (domain->flags &
-                   TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
+               if (domain->ignore_global_allow_read)
                        ignore_global_allow_read
                                = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
                done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE
@@ -1577,7 +1510,6 @@ acl_loop:
                if (head->read_step == 3)
                        goto tail_mark;
                /* Print ACL entries in the domain. */
-               down_read(&tomoyo_domain_acl_info_list_lock);
                list_for_each_cookie(apos, head->read_var2,
                                     &domain->acl_info_list) {
                        struct tomoyo_acl_info *ptr
@@ -1587,7 +1519,6 @@ acl_loop:
                        if (!done)
                                break;
                }
-               up_read(&tomoyo_domain_acl_info_list_lock);
                if (!done)
                        break;
                head->read_step = 3;
@@ -1599,7 +1530,6 @@ tail_mark:
                if (head->read_single_domain)
                        break;
        }
-       up_read(&tomoyo_domain_list_lock);
        head->read_eof = done;
        return 0;
 }
@@ -1615,6 +1545,8 @@ tail_mark:
  *
  *     ( echo "select " $domainname; echo "use_profile " $profile ) |
  *     /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
 {
@@ -1626,9 +1558,7 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
        if (!cp)
                return -EINVAL;
        *cp = '\0';
-       down_read(&tomoyo_domain_list_lock);
        domain = tomoyo_find_domain(cp + 1);
-       up_read(&tomoyo_domain_list_lock);
        if (strict_strtoul(data, 10, &profile))
                return -EINVAL;
        if (domain && profile < TOMOYO_MAX_PROFILES
@@ -1650,6 +1580,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
  *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
  *     domainname = $0; } else if ( $1 == "use_profile" ) {
  *     print $2 " " domainname; domainname = ""; } } ; '
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
 {
@@ -1658,7 +1590,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
 
        if (head->read_eof)
                return 0;
-       down_read(&tomoyo_domain_list_lock);
        list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
                struct tomoyo_domain_info *domain;
                domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1669,7 +1600,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_domain_list_lock);
        head->read_eof = done;
        return 0;
 }
@@ -1707,11 +1637,13 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
                const int pid = head->read_step;
                struct task_struct *p;
                struct tomoyo_domain_info *domain = NULL;
+               rcu_read_lock();
                read_lock(&tasklist_lock);
                p = find_task_by_vpid(pid);
                if (p)
                        domain = tomoyo_real_domain(p);
                read_unlock(&tasklist_lock);
+               rcu_read_unlock();
                if (domain)
                        tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
                                         domain->domainname->name);
@@ -1726,6 +1658,8 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
 {
@@ -1760,6 +1694,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns 0 on success, -EINVAL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
 {
@@ -1889,15 +1825,13 @@ void tomoyo_load_policy(const char *filename)
        tomoyo_policy_loaded = true;
        { /* Check all profiles currently assigned to domains are defined. */
                struct tomoyo_domain_info *domain;
-               down_read(&tomoyo_domain_list_lock);
-               list_for_each_entry(domain, &tomoyo_domain_list, list) {
+               list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
                        const u8 profile = domain->profile;
                        if (tomoyo_profile_ptr[profile])
                                continue;
                        panic("Profile %u (used by '%s') not defined.\n",
                              profile, domain->domainname->name);
                }
-               up_read(&tomoyo_domain_list_lock);
        }
 }
 
@@ -1945,10 +1879,12 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
  * @file: Pointer to "struct file".
  *
  * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ *
+ * Caller acquires tomoyo_read_lock().
  */
 static int tomoyo_open_control(const u8 type, struct file *file)
 {
-       struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head));
+       struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_KERNEL);
 
        if (!head)
                return -ENOMEM;
@@ -2009,9 +1945,9 @@ static int tomoyo_open_control(const u8 type, struct file *file)
        } else {
                if (!head->readbuf_size)
                        head->readbuf_size = 4096 * 2;
-               head->read_buf = tomoyo_alloc(head->readbuf_size);
+               head->read_buf = kzalloc(head->readbuf_size, GFP_KERNEL);
                if (!head->read_buf) {
-                       tomoyo_free(head);
+                       kfree(head);
                        return -ENOMEM;
                }
        }
@@ -2023,13 +1959,14 @@ static int tomoyo_open_control(const u8 type, struct file *file)
                head->write = NULL;
        } else if (head->write) {
                head->writebuf_size = 4096 * 2;
-               head->write_buf = tomoyo_alloc(head->writebuf_size);
+               head->write_buf = kzalloc(head->writebuf_size, GFP_KERNEL);
                if (!head->write_buf) {
-                       tomoyo_free(head->read_buf);
-                       tomoyo_free(head);
+                       kfree(head->read_buf);
+                       kfree(head);
                        return -ENOMEM;
                }
        }
+       head->reader_idx = tomoyo_read_lock();
        file->private_data = head;
        /*
         * Call the handler now if the file is
@@ -2051,6 +1988,8 @@ static int tomoyo_open_control(const u8 type, struct file *file)
  * @buffer_len: Size of @buffer.
  *
  * Returns bytes read on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_read_control(struct file *file, char __user *buffer,
                               const int buffer_len)
@@ -2094,6 +2033,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer,
  * @buffer_len: Size of @buffer.
  *
  * Returns @buffer_len on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_write_control(struct file *file, const char __user *buffer,
                                const int buffer_len)
@@ -2144,51 +2085,28 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer,
  * @file: Pointer to "struct file".
  *
  * Releases memory and returns 0.
+ *
+ * Caller looses tomoyo_read_lock().
  */
 static int tomoyo_close_control(struct file *file)
 {
        struct tomoyo_io_buffer *head = file->private_data;
+       const bool is_write = !!head->write_buf;
 
+       tomoyo_read_unlock(head->reader_idx);
        /* Release memory used for policy I/O. */
-       tomoyo_free(head->read_buf);
+       kfree(head->read_buf);
        head->read_buf = NULL;
-       tomoyo_free(head->write_buf);
+       kfree(head->write_buf);
        head->write_buf = NULL;
-       tomoyo_free(head);
+       kfree(head);
        head = NULL;
        file->private_data = NULL;
+       if (is_write)
+               tomoyo_run_gc();
        return 0;
 }
 
-/**
- * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
- *
- * @acl_type:  Type of ACL entry.
- *
- * Returns pointer to the ACL entry on success, NULL otherwise.
- */
-void *tomoyo_alloc_acl_element(const u8 acl_type)
-{
-       int len;
-       struct tomoyo_acl_info *ptr;
-
-       switch (acl_type) {
-       case TOMOYO_TYPE_SINGLE_PATH_ACL:
-               len = sizeof(struct tomoyo_single_path_acl_record);
-               break;
-       case TOMOYO_TYPE_DOUBLE_PATH_ACL:
-               len = sizeof(struct tomoyo_double_path_acl_record);
-               break;
-       default:
-               return NULL;
-       }
-       ptr = tomoyo_alloc_element(len);
-       if (!ptr)
-               return NULL;
-       ptr->type = acl_type;
-       return ptr;
-}
-
 /**
  * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
  *
index 92169d29b2db4c1e71ea5f89d142da5b02ca0ec3..67bd22dd3e6896ffdbb2b8c58036e8d22a039c5c 100644 (file)
@@ -1,12 +1,9 @@
 /*
  * security/tomoyo/common.h
  *
- * Common functions for TOMOYO.
- *
- * Copyright (C) 2005-2009  NTT DATA CORPORATION
- *
- * Version: 2.2.0   2009/04/01
+ * Header file for TOMOYO.
  *
+ * Copyright (C) 2005-2010  NTT DATA CORPORATION
  */
 
 #ifndef _SECURITY_TOMOYO_COMMON_H
 #include <linux/namei.h>
 #include <linux/mount.h>
 #include <linux/list.h>
+#include <linux/cred.h>
+struct linux_binprm;
+
+/********** Constants definitions. **********/
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_HASH_BITS  8
+#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
+
+/*
+ * This is the max length of a token.
+ *
+ * A token consists of only ASCII printable characters.
+ * Non printable characters in a token is represented in \ooo style
+ * octal string. Thus, \ itself is represented as \\.
+ */
+#define TOMOYO_MAX_PATHNAME_LEN 4000
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_ALIAS                     "alias "
+#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read "
+#define TOMOYO_KEYWORD_DELETE                    "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
+#define TOMOYO_KEYWORD_SELECT                    "select "
+#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
+/* A domain definition starts with <kernel>. */
+#define TOMOYO_ROOT_NAME                         "<kernel>"
+#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/* Index numbers for Access Controls. */
+enum tomoyo_mac_index {
+       TOMOYO_MAC_FOR_FILE,  /* domain_policy.conf */
+       TOMOYO_MAX_ACCEPT_ENTRY,
+       TOMOYO_VERBOSE,
+       TOMOYO_MAX_CONTROL_INDEX
+};
+
+/* Index numbers for Access Controls. */
+enum tomoyo_acl_entry_type_index {
+       TOMOYO_TYPE_PATH_ACL,
+       TOMOYO_TYPE_PATH2_ACL,
+};
+
+/* Index numbers for File Controls. */
+
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+enum tomoyo_path_acl_index {
+       TOMOYO_TYPE_READ_WRITE,
+       TOMOYO_TYPE_EXECUTE,
+       TOMOYO_TYPE_READ,
+       TOMOYO_TYPE_WRITE,
+       TOMOYO_TYPE_CREATE,
+       TOMOYO_TYPE_UNLINK,
+       TOMOYO_TYPE_MKDIR,
+       TOMOYO_TYPE_RMDIR,
+       TOMOYO_TYPE_MKFIFO,
+       TOMOYO_TYPE_MKSOCK,
+       TOMOYO_TYPE_MKBLOCK,
+       TOMOYO_TYPE_MKCHAR,
+       TOMOYO_TYPE_TRUNCATE,
+       TOMOYO_TYPE_SYMLINK,
+       TOMOYO_TYPE_REWRITE,
+       TOMOYO_TYPE_IOCTL,
+       TOMOYO_TYPE_CHMOD,
+       TOMOYO_TYPE_CHOWN,
+       TOMOYO_TYPE_CHGRP,
+       TOMOYO_TYPE_CHROOT,
+       TOMOYO_TYPE_MOUNT,
+       TOMOYO_TYPE_UMOUNT,
+       TOMOYO_MAX_PATH_OPERATION
+};
 
-struct dentry;
-struct vfsmount;
+enum tomoyo_path2_acl_index {
+       TOMOYO_TYPE_LINK,
+       TOMOYO_TYPE_RENAME,
+       TOMOYO_TYPE_PIVOT_ROOT,
+       TOMOYO_MAX_PATH2_OPERATION
+};
+
+enum tomoyo_securityfs_interface_index {
+       TOMOYO_DOMAINPOLICY,
+       TOMOYO_EXCEPTIONPOLICY,
+       TOMOYO_DOMAIN_STATUS,
+       TOMOYO_PROCESS_STATUS,
+       TOMOYO_MEMINFO,
+       TOMOYO_SELFDOMAIN,
+       TOMOYO_VERSION,
+       TOMOYO_PROFILE,
+       TOMOYO_MANAGER
+};
+
+/********** Structure definitions. **********/
 
 /*
  * tomoyo_page_buffer is a structure which is used for holding a pathname
@@ -66,13 +173,14 @@ struct tomoyo_path_info {
 };
 
 /*
- * This is the max length of a token.
- *
- * A token consists of only ASCII printable characters.
- * Non printable characters in a token is represented in \ooo style
- * octal string. Thus, \ itself is represented as \\.
+ * tomoyo_name_entry is a structure which is used for linking
+ * "struct tomoyo_path_info" into tomoyo_name_list .
  */
-#define TOMOYO_MAX_PATHNAME_LEN 4000
+struct tomoyo_name_entry {
+       struct list_head list;
+       atomic_t users;
+       struct tomoyo_path_info entry;
+};
 
 /*
  * tomoyo_path_info_with_data is a structure which is used for holding a
@@ -89,7 +197,7 @@ struct tomoyo_path_info {
  * "struct tomoyo_path_info_with_data".
  */
 struct tomoyo_path_info_with_data {
-       /* Keep "head" first, for this pointer is passed to tomoyo_free(). */
+       /* Keep "head" first, for this pointer is passed to kfree(). */
        struct tomoyo_path_info head;
        char barrier1[16]; /* Safeguard for overrun. */
        char body[TOMOYO_MAX_PATHNAME_LEN];
@@ -101,30 +209,19 @@ struct tomoyo_path_info_with_data {
  *
  *  (1) "list" which is linked to the ->acl_info_list of
  *      "struct tomoyo_domain_info"
- *  (2) "type" which tells
- *      (a) type & 0x7F : type of the entry (either
- *          "struct tomoyo_single_path_acl_record" or
- *          "struct tomoyo_double_path_acl_record")
- *      (b) type & 0x80 : whether the entry is marked as "deleted".
+ *  (2) "type" which tells type of the entry (either
+ *      "struct tomoyo_path_acl" or "struct tomoyo_path2_acl").
  *
  * Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_single_path_acl_record" to embed "u16" and
- * "struct tomoyo_double_path_acl_record" to embed "u8"
+ * "struct tomoyo_path_acl" to embed "u8" + "u16" and
+ * "struct tomoyo_path2_acl" to embed "u8"
  * without enlarging their structure size.
  */
 struct tomoyo_acl_info {
        struct list_head list;
-       /*
-        * Type of this ACL entry.
-        *
-        * MSB is is_deleted flag.
-        */
        u8 type;
 } __packed;
 
-/* This ACL entry is deleted.           */
-#define TOMOYO_ACL_DELETED        0x80
-
 /*
  * tomoyo_domain_info is a structure which is used for holding permissions
  * (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
@@ -138,7 +235,17 @@ struct tomoyo_acl_info {
  *      "deleted", false otherwise.
  *  (6) "quota_warned" is a bool which is used for suppressing warning message
  *      when learning mode learned too much entries.
- *  (7) "flags" which remembers this domain's attributes.
+ *  (7) "ignore_global_allow_read" is a bool which is true if this domain
+ *      should ignore "allow_read" directive in exception policy.
+ *  (8) "transition_failed" is a bool which is set to true when this domain was
+ *      unable to create a new domain at tomoyo_find_next_domain() because the
+ *      name of the domain to be created was too long or it could not allocate
+ *      memory. If set to true, more than one process continued execve()
+ *      without domain transition.
+ *  (9) "users" is an atomic_t that holds how many "struct cred"->security
+ *      are referring this "struct tomoyo_domain_info". If is_deleted == true
+ *      and users == 0, this struct will be kfree()d upon next garbage
+ *      collection.
  *
  * A domain's lifecycle is an analogy of files on / directory.
  * Multiple domains with the same domainname cannot be created (as with
@@ -155,25 +262,13 @@ struct tomoyo_domain_info {
        u8 profile;        /* Profile number to use. */
        bool is_deleted;   /* Delete flag.           */
        bool quota_warned; /* Quota warnning flag.   */
-       /* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */
-       u8 flags;
+       bool ignore_global_allow_read; /* Ignore "allow_read" flag. */
+       bool transition_failed; /* Domain transition failed flag. */
+       atomic_t users; /* Number of referring credentials. */
 };
 
-/* Profile number is an integer between 0 and 255. */
-#define TOMOYO_MAX_PROFILES 256
-
-/* Ignore "allow_read" directive in exception policy. */
-#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1
-/*
- * This domain was unable to create a new domain at tomoyo_find_next_domain()
- * because the name of the domain to be created was too long or
- * it could not allocate memory.
- * More than one process continued execve() without domain transition.
- */
-#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED        2
-
 /*
- * tomoyo_single_path_acl_record is a structure which is used for holding an
+ * tomoyo_path_acl is a structure which is used for holding an
  * entry with one pathname operation (e.g. open(), mkdir()).
  * It has following fields.
  *
@@ -184,18 +279,21 @@ struct tomoyo_domain_info {
  * Directives held by this structure are "allow_read/write", "allow_execute",
  * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir",
  * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock",
- * "allow_mkchar", "allow_truncate", "allow_symlink" and "allow_rewrite".
+ * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite",
+ * "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", "allow_mount"
+ * and "allow_unmount".
  */
-struct tomoyo_single_path_acl_record {
-       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
+struct tomoyo_path_acl {
+       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
+       u8 perm_high;
        u16 perm;
        /* Pointer to single pathname. */
        const struct tomoyo_path_info *filename;
 };
 
 /*
- * tomoyo_double_path_acl_record is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link() and rename()).
+ * tomoyo_path2_acl is a structure which is used for holding an
+ * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
  * It has following fields.
  *
  *  (1) "head" which is a "struct tomoyo_acl_info".
@@ -203,10 +301,11 @@ struct tomoyo_single_path_acl_record {
  *  (3) "filename1" is the source/old pathname.
  *  (4) "filename2" is the destination/new pathname.
  *
- * Directives held by this structure are "allow_rename" and "allow_link".
+ * Directives held by this structure are "allow_rename", "allow_link" and
+ * "allow_pivot_root".
  */
-struct tomoyo_double_path_acl_record {
-       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
+struct tomoyo_path2_acl {
+       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
        u8 perm;
        /* Pointer to single pathname. */
        const struct tomoyo_path_info *filename1;
@@ -214,29 +313,6 @@ struct tomoyo_double_path_acl_record {
        const struct tomoyo_path_info *filename2;
 };
 
-/* Keywords for ACLs. */
-#define TOMOYO_KEYWORD_ALIAS                     "alias "
-#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read "
-#define TOMOYO_KEYWORD_DELETE                    "delete "
-#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite "
-#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern "
-#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain "
-#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
-#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
-#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
-#define TOMOYO_KEYWORD_SELECT                    "select "
-#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
-#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
-/* A domain definition starts with <kernel>. */
-#define TOMOYO_ROOT_NAME                         "<kernel>"
-#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
-
-/* Index numbers for Access Controls. */
-#define TOMOYO_MAC_FOR_FILE                  0  /* domain_policy.conf */
-#define TOMOYO_MAX_ACCEPT_ENTRY              1
-#define TOMOYO_VERBOSE                       2
-#define TOMOYO_MAX_CONTROL_INDEX             3
-
 /*
  * tomoyo_io_buffer is a structure which is used for reading and modifying
  * configuration via /sys/kernel/security/tomoyo/ interface.
@@ -265,6 +341,8 @@ struct tomoyo_io_buffer {
        int (*write) (struct tomoyo_io_buffer *);
        /* Exclusive lock for this structure.   */
        struct mutex io_sem;
+       /* Index returned by tomoyo_read_lock(). */
+       int reader_idx;
        /* The position currently reading from. */
        struct list_head *read_var1;
        /* Extra variables for reading.         */
@@ -293,18 +371,159 @@ struct tomoyo_io_buffer {
        int writebuf_size;
 };
 
+/*
+ * tomoyo_globally_readable_file_entry is a structure which is used for holding
+ * "allow_read" entries.
+ * It has following fields.
+ *
+ *  (1) "list" which is linked to tomoyo_globally_readable_list .
+ *  (2) "filename" is a pathname which is allowed to open(O_RDONLY).
+ *  (3) "is_deleted" is a bool which is true if marked as deleted, false
+ *      otherwise.
+ */
+struct tomoyo_globally_readable_file_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *filename;
+       bool is_deleted;
+};
+
+/*
+ * tomoyo_pattern_entry is a structure which is used for holding
+ * "tomoyo_pattern_list" entries.
+ * It has following fields.
+ *
+ *  (1) "list" which is linked to tomoyo_pattern_list .
+ *  (2) "pattern" is a pathname pattern which is used for converting pathnames
+ *      to pathname patterns during learning mode.
+ *  (3) "is_deleted" is a bool which is true if marked as deleted, false
+ *      otherwise.
+ */
+struct tomoyo_pattern_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *pattern;
+       bool is_deleted;
+};
+
+/*
+ * tomoyo_no_rewrite_entry is a structure which is used for holding
+ * "deny_rewrite" entries.
+ * It has following fields.
+ *
+ *  (1) "list" which is linked to tomoyo_no_rewrite_list .
+ *  (2) "pattern" is a pathname which is by default not permitted to modify
+ *      already existing content.
+ *  (3) "is_deleted" is a bool which is true if marked as deleted, false
+ *      otherwise.
+ */
+struct tomoyo_no_rewrite_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *pattern;
+       bool is_deleted;
+};
+
+/*
+ * tomoyo_domain_initializer_entry is a structure which is used for holding
+ * "initialize_domain" and "no_initialize_domain" entries.
+ * It has following fields.
+ *
+ *  (1) "list" which is linked to tomoyo_domain_initializer_list .
+ *  (2) "domainname" which is "a domainname" or "the last component of a
+ *      domainname". This field is NULL if "from" clause is not specified.
+ *  (3) "program" which is a program's pathname.
+ *  (4) "is_deleted" is a bool which is true if marked as deleted, false
+ *      otherwise.
+ *  (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ *      otherwise.
+ *  (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ *      component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_initializer_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *domainname;    /* This may be NULL */
+       const struct tomoyo_path_info *program;
+       bool is_deleted;
+       bool is_not;       /* True if this entry is "no_initialize_domain".  */
+       /* True if the domainname is tomoyo_get_last_name(). */
+       bool is_last_name;
+};
+
+/*
+ * tomoyo_domain_keeper_entry is a structure which is used for holding
+ * "keep_domain" and "no_keep_domain" entries.
+ * It has following fields.
+ *
+ *  (1) "list" which is linked to tomoyo_domain_keeper_list .
+ *  (2) "domainname" which is "a domainname" or "the last component of a
+ *      domainname".
+ *  (3) "program" which is a program's pathname.
+ *      This field is NULL if "from" clause is not specified.
+ *  (4) "is_deleted" is a bool which is true if marked as deleted, false
+ *      otherwise.
+ *  (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ *      otherwise.
+ *  (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ *      component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_keeper_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *domainname;
+       const struct tomoyo_path_info *program;       /* This may be NULL */
+       bool is_deleted;
+       bool is_not;       /* True if this entry is "no_keep_domain".        */
+       /* True if the domainname is tomoyo_get_last_name(). */
+       bool is_last_name;
+};
+
+/*
+ * tomoyo_alias_entry is a structure which is used for holding "alias" entries.
+ * It has following fields.
+ *
+ *  (1) "list" which is linked to tomoyo_alias_list .
+ *  (2) "original_name" which is a dereferenced pathname.
+ *  (3) "aliased_name" which is a symlink's pathname.
+ *  (4) "is_deleted" is a bool which is true if marked as deleted, false
+ *      otherwise.
+ */
+struct tomoyo_alias_entry {
+       struct list_head list;
+       const struct tomoyo_path_info *original_name;
+       const struct tomoyo_path_info *aliased_name;
+       bool is_deleted;
+};
+
+/*
+ * tomoyo_policy_manager_entry is a structure which is used for holding list of
+ * domainnames or programs which are permitted to modify configuration via
+ * /sys/kernel/security/tomoyo/ interface.
+ * It has following fields.
+ *
+ *  (1) "list" which is linked to tomoyo_policy_manager_list .
+ *  (2) "manager" is a domainname or a program's pathname.
+ *  (3) "is_domain" is a bool which is true if "manager" is a domainname, false
+ *      otherwise.
+ *  (4) "is_deleted" is a bool which is true if marked as deleted, false
+ *      otherwise.
+ */
+struct tomoyo_policy_manager_entry {
+       struct list_head list;
+       /* A path to program or a domainname. */
+       const struct tomoyo_path_info *manager;
+       bool is_domain;  /* True if manager is a domainname. */
+       bool is_deleted; /* True if this entry is deleted. */
+};
+
+/********** Function prototypes. **********/
+
 /* Check whether the domain has too many ACL entries to hold. */
 bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain);
 /* Transactional sprintf() for policy dump. */
 bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
        __attribute__ ((format(printf, 2, 3)));
 /* Check whether the domainname is correct. */
-bool tomoyo_is_correct_domain(const unsigned char *domainname,
-                             const char *function);
+bool tomoyo_is_correct_domain(const unsigned char *domainname);
 /* Check whether the token is correct. */
 bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
-                           const s8 pattern_type, const s8 end_type,
-                           const char *function);
+                           const s8 pattern_type, const s8 end_type);
 /* Check whether the token can be a domainname. */
 bool tomoyo_is_domain_def(const unsigned char *buffer);
 /* Check whether the given filename matches the given pattern. */
@@ -328,13 +547,13 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
 /* Write domain policy violation warning message to console? */
 bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
 /* Convert double path operation to operation name. */
-const char *tomoyo_dp2keyword(const u8 operation);
+const char *tomoyo_path22keyword(const u8 operation);
 /* Get the last component of the given domainname. */
 const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
 /* Get warning message. */
 const char *tomoyo_get_msg(const bool is_enforce);
 /* Convert single path operation to operation name. */
-const char *tomoyo_sp2keyword(const u8 operation);
+const char *tomoyo_path2keyword(const u8 operation);
 /* Create "alias" entry in exception policy. */
 int tomoyo_write_alias_policy(char *data, const bool is_delete);
 /*
@@ -370,33 +589,107 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
 /* Check mode for specified functionality. */
 unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
                                const u8 index);
-/* Allocate memory for structures. */
-void *tomoyo_alloc_acl_element(const u8 acl_type);
 /* Fill in "struct tomoyo_path_info" members. */
 void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
 /* Run policy loader when /sbin/init starts. */
 void tomoyo_load_policy(const char *filename);
-/* Change "struct tomoyo_domain_info"->flags. */
-void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
-                           const bool is_delete, const u8 flags);
 
-/* strcmp() for "struct tomoyo_path_info" structure. */
-static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
-                                 const struct tomoyo_path_info *b)
+/* Convert binary string to ascii string. */
+int tomoyo_encode(char *buffer, int buflen, const char *str);
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+                              int newname_len);
+
+/*
+ * Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ * These functions use kzalloc(), so the caller must call kfree()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath(const char *pathname);
+/*
+ * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname);
+/* Same with tomoyo_realpath() except that the pathname is already solved. */
+char *tomoyo_realpath_from_path(struct path *path);
+
+/* Check memory quota. */
+bool tomoyo_memory_ok(void *ptr);
+
+/*
+ * Keep the given name on the RAM.
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_get_name(const char *name);
+
+/* Check for memory usage. */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+
+/* Set memory quota. */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+
+/* Initialize realpath related code. */
+void __init tomoyo_realpath_init(void);
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+                          const struct tomoyo_path_info *filename);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+                                struct path *path, const int flag);
+int tomoyo_path_perm(const u8 operation, struct path *path);
+int tomoyo_path2_perm(const u8 operation, struct path *path1,
+                     struct path *path2);
+int tomoyo_check_rewrite_permission(struct file *filp);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
+
+/* Run garbage collector. */
+void tomoyo_run_gc(void);
+
+void tomoyo_memory_free(void *ptr);
+
+/********** External variable definitions. **********/
+
+/* Lock for GC. */
+extern struct srcu_struct tomoyo_ss;
+
+/* The list for "struct tomoyo_domain_info". */
+extern struct list_head tomoyo_domain_list;
+
+extern struct list_head tomoyo_domain_initializer_list;
+extern struct list_head tomoyo_domain_keeper_list;
+extern struct list_head tomoyo_alias_list;
+extern struct list_head tomoyo_globally_readable_list;
+extern struct list_head tomoyo_pattern_list;
+extern struct list_head tomoyo_no_rewrite_list;
+extern struct list_head tomoyo_policy_manager_list;
+extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+extern struct mutex tomoyo_name_list_lock;
+
+/* Lock for protecting policy. */
+extern struct mutex tomoyo_policy_lock;
+
+/* Has /sbin/init started? */
+extern bool tomoyo_policy_loaded;
+
+/* The kernel's domain. */
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/********** Inlined functions. **********/
+
+static inline int tomoyo_read_lock(void)
 {
-       return a->hash != b->hash || strcmp(a->name, b->name);
+       return srcu_read_lock(&tomoyo_ss);
 }
 
-/* Get type of an ACL entry. */
-static inline u8 tomoyo_acl_type1(struct tomoyo_acl_info *ptr)
+static inline void tomoyo_read_unlock(int idx)
 {
-       return ptr->type & ~TOMOYO_ACL_DELETED;
+       srcu_read_unlock(&tomoyo_ss, idx);
 }
 
-/* Get type of an ACL entry. */
-static inline u8 tomoyo_acl_type2(struct tomoyo_acl_info *ptr)
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+                                 const struct tomoyo_path_info *b)
 {
-       return ptr->type;
+       return a->hash != b->hash || strcmp(a->name, b->name);
 }
 
 /**
@@ -423,18 +716,25 @@ static inline bool tomoyo_is_invalid(const unsigned char c)
        return c && (c <= ' ' || c >= 127);
 }
 
-/* The list for "struct tomoyo_domain_info". */
-extern struct list_head tomoyo_domain_list;
-extern struct rw_semaphore tomoyo_domain_list_lock;
-
-/* Lock for domain->acl_info_list. */
-extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
+{
+       if (name) {
+               struct tomoyo_name_entry *ptr =
+                       container_of(name, struct tomoyo_name_entry, entry);
+               atomic_dec(&ptr->users);
+       }
+}
 
-/* Has /sbin/init started? */
-extern bool tomoyo_policy_loaded;
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+       return current_cred()->security;
+}
 
-/* The kernel's domain. */
-extern struct tomoyo_domain_info tomoyo_kernel_domain;
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+                                                           *task)
+{
+       return task_cred_xxx(task, security);
+}
 
 /**
  * list_for_each_cookie - iterate over a list with cookie.
@@ -442,16 +742,16 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
  * @cookie:     the &struct list_head to use as a cookie.
  * @head:       the head for your list.
  *
- * Same with list_for_each() except that this primitive uses @cookie
+ * Same with list_for_each_rcu() except that this primitive uses @cookie
  * so that we can continue iteration.
  * @cookie must be NULL when iteration starts, and @cookie will become
  * NULL when iteration finishes.
  */
-#define list_for_each_cookie(pos, cookie, head)                       \
-       for (({ if (!cookie)                                          \
-                                    cookie = head; }),               \
-            pos = (cookie)->next;                                    \
-            prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
-            (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head)                                \
+       for (({ if (!cookie)                                            \
+                                    cookie = head; }),                 \
+                    pos = rcu_dereference((cookie)->next);             \
+            prefetch(pos->next), pos != (head) || ((cookie) = NULL);   \
+            (cookie) = pos, pos = rcu_dereference(pos->next))
 
 #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
index fcf52accce2b107798b7be675337eb53f9279a38..66caaa1b842a2a7e102313a2dcc199bd69debec2 100644 (file)
@@ -10,8 +10,6 @@
  */
 
 #include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
 #include <linux/binfmts.h>
 
 /* Variables definitions.*/
@@ -58,99 +56,6 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
  * exceptions.
  */
 LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
-
-/*
- * tomoyo_domain_initializer_entry is a structure which is used for holding
- * "initialize_domain" and "no_initialize_domain" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_domain_initializer_list .
- *  (2) "domainname" which is "a domainname" or "the last component of a
- *      domainname". This field is NULL if "from" clause is not specified.
- *  (3) "program" which is a program's pathname.
- *  (4) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- *  (5) "is_not" is a bool which is true if "no_initialize_domain", false
- *      otherwise.
- *  (6) "is_last_name" is a bool which is true if "domainname" is "the last
- *      component of a domainname", false otherwise.
- */
-struct tomoyo_domain_initializer_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *domainname;    /* This may be NULL */
-       const struct tomoyo_path_info *program;
-       bool is_deleted;
-       bool is_not;       /* True if this entry is "no_initialize_domain".  */
-       /* True if the domainname is tomoyo_get_last_name(). */
-       bool is_last_name;
-};
-
-/*
- * tomoyo_domain_keeper_entry is a structure which is used for holding
- * "keep_domain" and "no_keep_domain" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_domain_keeper_list .
- *  (2) "domainname" which is "a domainname" or "the last component of a
- *      domainname".
- *  (3) "program" which is a program's pathname.
- *      This field is NULL if "from" clause is not specified.
- *  (4) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- *  (5) "is_not" is a bool which is true if "no_initialize_domain", false
- *      otherwise.
- *  (6) "is_last_name" is a bool which is true if "domainname" is "the last
- *      component of a domainname", false otherwise.
- */
-struct tomoyo_domain_keeper_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *domainname;
-       const struct tomoyo_path_info *program;       /* This may be NULL */
-       bool is_deleted;
-       bool is_not;       /* True if this entry is "no_keep_domain".        */
-       /* True if the domainname is tomoyo_get_last_name(). */
-       bool is_last_name;
-};
-
-/*
- * tomoyo_alias_entry is a structure which is used for holding "alias" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_alias_list .
- *  (2) "original_name" which is a dereferenced pathname.
- *  (3) "aliased_name" which is a symlink's pathname.
- *  (4) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- */
-struct tomoyo_alias_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *original_name;
-       const struct tomoyo_path_info *aliased_name;
-       bool is_deleted;
-};
-
-/**
- * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
- *
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- * @flags:     Flags to set or clear.
- *
- * Returns nothing.
- */
-void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
-                           const bool is_delete, const u8 flags)
-{
-       /* We need to serialize because this is bitfield operation. */
-       static DEFINE_SPINLOCK(lock);
-       spin_lock(&lock);
-       if (!is_delete)
-               domain->flags |= flags;
-       else
-               domain->flags &= ~flags;
-       spin_unlock(&lock);
-}
 
 /**
  * tomoyo_get_last_name - Get last component of a domainname.
@@ -205,8 +110,7 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
  * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
  * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
  */
-static LIST_HEAD(tomoyo_domain_initializer_list);
-static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
+LIST_HEAD(tomoyo_domain_initializer_list);
 
 /**
  * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -217,59 +121,65 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
  * @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_domain_initializer_entry(const char *domainname,
                                                  const char *program,
                                                  const bool is_not,
                                                  const bool is_delete)
 {
-       struct tomoyo_domain_initializer_entry *new_entry;
+       struct tomoyo_domain_initializer_entry *entry = NULL;
        struct tomoyo_domain_initializer_entry *ptr;
-       const struct tomoyo_path_info *saved_program;
+       const struct tomoyo_path_info *saved_program = NULL;
        const struct tomoyo_path_info *saved_domainname = NULL;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
        bool is_last_name = false;
 
-       if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+       if (!tomoyo_is_correct_path(program, 1, -1, -1))
                return -EINVAL; /* No patterns allowed. */
        if (domainname) {
                if (!tomoyo_is_domain_def(domainname) &&
-                   tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+                   tomoyo_is_correct_path(domainname, 1, -1, -1))
                        is_last_name = true;
-               else if (!tomoyo_is_correct_domain(domainname, __func__))
+               else if (!tomoyo_is_correct_domain(domainname))
                        return -EINVAL;
-               saved_domainname = tomoyo_save_name(domainname);
+               saved_domainname = tomoyo_get_name(domainname);
                if (!saved_domainname)
-                       return -ENOMEM;
+                       goto out;
        }
-       saved_program = tomoyo_save_name(program);
+       saved_program = tomoyo_get_name(program);
        if (!saved_program)
-               return -ENOMEM;
-       down_write(&tomoyo_domain_initializer_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+               goto out;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
                if (ptr->is_not != is_not ||
                    ptr->domainname != saved_domainname ||
                    ptr->program != saved_program)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->domainname = saved_domainname;
+               saved_domainname = NULL;
+               entry->program = saved_program;
+               saved_program = NULL;
+               entry->is_not = is_not;
+               entry->is_last_name = is_last_name;
+               list_add_tail_rcu(&entry->list,
+                                 &tomoyo_domain_initializer_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->domainname = saved_domainname;
-       new_entry->program = saved_program;
-       new_entry->is_not = is_not;
-       new_entry->is_last_name = is_last_name;
-       list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
-       error = 0;
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_domain_initializer_list_lock);
+       tomoyo_put_name(saved_domainname);
+       tomoyo_put_name(saved_program);
+       kfree(entry);
        return error;
 }
 
@@ -279,13 +189,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_domain_initializer_list_lock);
        list_for_each_cookie(pos, head->read_var2,
                             &tomoyo_domain_initializer_list) {
                const char *no;
@@ -308,7 +219,6 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_domain_initializer_list_lock);
        return done;
 }
 
@@ -320,6 +230,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
                                           const bool is_delete)
@@ -345,6 +257,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
  *
  * Returns true if executing @program reinitializes domain transition,
  * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
                                         domainname,
@@ -355,8 +269,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
        struct tomoyo_domain_initializer_entry *ptr;
        bool flag = false;
 
-       down_read(&tomoyo_domain_initializer_list_lock);
-       list_for_each_entry(ptr,  &tomoyo_domain_initializer_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
                if (ptr->is_deleted)
                        continue;
                if (ptr->domainname) {
@@ -376,7 +289,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
                }
                flag = true;
        }
-       up_read(&tomoyo_domain_initializer_list_lock);
        return flag;
 }
 
@@ -418,8 +330,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
  * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
  * explicitly specified by "initialize_domain".
  */
-static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
+LIST_HEAD(tomoyo_domain_keeper_list);
 
 /**
  * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -430,59 +341,64 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
  * @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_domain_keeper_entry(const char *domainname,
                                             const char *program,
                                             const bool is_not,
                                             const bool is_delete)
 {
-       struct tomoyo_domain_keeper_entry *new_entry;
+       struct tomoyo_domain_keeper_entry *entry = NULL;
        struct tomoyo_domain_keeper_entry *ptr;
-       const struct tomoyo_path_info *saved_domainname;
+       const struct tomoyo_path_info *saved_domainname = NULL;
        const struct tomoyo_path_info *saved_program = NULL;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
        bool is_last_name = false;
 
        if (!tomoyo_is_domain_def(domainname) &&
-           tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+           tomoyo_is_correct_path(domainname, 1, -1, -1))
                is_last_name = true;
-       else if (!tomoyo_is_correct_domain(domainname, __func__))
+       else if (!tomoyo_is_correct_domain(domainname))
                return -EINVAL;
        if (program) {
-               if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+               if (!tomoyo_is_correct_path(program, 1, -1, -1))
                        return -EINVAL;
-               saved_program = tomoyo_save_name(program);
+               saved_program = tomoyo_get_name(program);
                if (!saved_program)
-                       return -ENOMEM;
+                       goto out;
        }
-       saved_domainname = tomoyo_save_name(domainname);
+       saved_domainname = tomoyo_get_name(domainname);
        if (!saved_domainname)
-               return -ENOMEM;
-       down_write(&tomoyo_domain_keeper_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+               goto out;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
                if (ptr->is_not != is_not ||
                    ptr->domainname != saved_domainname ||
                    ptr->program != saved_program)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->domainname = saved_domainname;
+               saved_domainname = NULL;
+               entry->program = saved_program;
+               saved_program = NULL;
+               entry->is_not = is_not;
+               entry->is_last_name = is_last_name;
+               list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->domainname = saved_domainname;
-       new_entry->program = saved_program;
-       new_entry->is_not = is_not;
-       new_entry->is_last_name = is_last_name;
-       list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
-       error = 0;
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_domain_keeper_list_lock);
+       tomoyo_put_name(saved_domainname);
+       tomoyo_put_name(saved_program);
+       kfree(entry);
        return error;
 }
 
@@ -493,6 +409,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
  * @is_not:    True if it is "no_keep_domain" entry.
  * @is_delete: True if it is a delete request.
  *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
                                      const bool is_delete)
@@ -513,13 +430,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_domain_keeper_list_lock);
        list_for_each_cookie(pos, head->read_var2,
                             &tomoyo_domain_keeper_list) {
                struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +460,6 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_domain_keeper_list_lock);
        return done;
 }
 
@@ -555,6 +472,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
  *
  * Returns true if executing @program supresses domain transition,
  * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
                                    const struct tomoyo_path_info *program,
@@ -563,8 +482,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
        struct tomoyo_domain_keeper_entry *ptr;
        bool flag = false;
 
-       down_read(&tomoyo_domain_keeper_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
                if (ptr->is_deleted)
                        continue;
                if (!ptr->is_last_name) {
@@ -582,7 +500,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
                }
                flag = true;
        }
-       up_read(&tomoyo_domain_keeper_list_lock);
        return flag;
 }
 
@@ -616,8 +533,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
  * /bin/busybox and domainname which the current process will belong to after
  * execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
  */
-static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);
+LIST_HEAD(tomoyo_alias_list);
 
 /**
  * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -627,46 +543,51 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock);
  * @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_alias_entry(const char *original_name,
                                     const char *aliased_name,
                                     const bool is_delete)
 {
-       struct tomoyo_alias_entry *new_entry;
+       struct tomoyo_alias_entry *entry = NULL;
        struct tomoyo_alias_entry *ptr;
        const struct tomoyo_path_info *saved_original_name;
        const struct tomoyo_path_info *saved_aliased_name;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
 
-       if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
-           !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
+       if (!tomoyo_is_correct_path(original_name, 1, -1, -1) ||
+           !tomoyo_is_correct_path(aliased_name, 1, -1, -1))
                return -EINVAL; /* No patterns allowed. */
-       saved_original_name = tomoyo_save_name(original_name);
-       saved_aliased_name = tomoyo_save_name(aliased_name);
+       saved_original_name = tomoyo_get_name(original_name);
+       saved_aliased_name = tomoyo_get_name(aliased_name);
        if (!saved_original_name || !saved_aliased_name)
-               return -ENOMEM;
-       down_write(&tomoyo_alias_list_lock);
-       list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+               goto out;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
                if (ptr->original_name != saved_original_name ||
                    ptr->aliased_name != saved_aliased_name)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->original_name = saved_original_name;
+               saved_original_name = NULL;
+               entry->aliased_name = saved_aliased_name;
+               saved_aliased_name = NULL;
+               list_add_tail_rcu(&entry->list, &tomoyo_alias_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->original_name = saved_original_name;
-       new_entry->aliased_name = saved_aliased_name;
-       list_add_tail(&new_entry->list, &tomoyo_alias_list);
-       error = 0;
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_alias_list_lock);
+       tomoyo_put_name(saved_original_name);
+       tomoyo_put_name(saved_aliased_name);
+       kfree(entry);
        return error;
 }
 
@@ -676,13 +597,14 @@ static int tomoyo_update_alias_entry(const char *original_name,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_alias_list_lock);
        list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
                struct tomoyo_alias_entry *ptr;
 
@@ -695,7 +617,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_alias_list_lock);
        return done;
 }
 
@@ -706,6 +627,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_alias_policy(char *data, const bool is_delete)
 {
@@ -724,63 +647,46 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
  * @profile:    Profile number to assign if the domain was newly created.
  *
  * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
                                                            domainname,
                                                            const u8 profile)
 {
-       struct tomoyo_domain_info *domain = NULL;
+       struct tomoyo_domain_info *entry;
+       struct tomoyo_domain_info *domain;
        const struct tomoyo_path_info *saved_domainname;
+       bool found = false;
 
-       down_write(&tomoyo_domain_list_lock);
-       domain = tomoyo_find_domain(domainname);
-       if (domain)
-               goto out;
-       if (!tomoyo_is_correct_domain(domainname, __func__))
-               goto out;
-       saved_domainname = tomoyo_save_name(domainname);
+       if (!tomoyo_is_correct_domain(domainname))
+               return NULL;
+       saved_domainname = tomoyo_get_name(domainname);
        if (!saved_domainname)
-               goto out;
-       /* Can I reuse memory of deleted domain? */
-       list_for_each_entry(domain, &tomoyo_domain_list, list) {
-               struct task_struct *p;
-               struct tomoyo_acl_info *ptr;
-               bool flag;
-               if (!domain->is_deleted ||
-                   domain->domainname != saved_domainname)
+               return NULL;
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+               if (domain->is_deleted ||
+                   tomoyo_pathcmp(saved_domainname, domain->domainname))
                        continue;
-               flag = false;
-               read_lock(&tasklist_lock);
-               for_each_process(p) {
-                       if (tomoyo_real_domain(p) != domain)
-                               continue;
-                       flag = true;
-                       break;
-               }
-               read_unlock(&tasklist_lock);
-               if (flag)
-                       continue;
-               list_for_each_entry(ptr, &domain->acl_info_list, list) {
-                       ptr->type |= TOMOYO_ACL_DELETED;
-               }
-               tomoyo_set_domain_flag(domain, true, domain->flags);
-               domain->profile = profile;
-               domain->quota_warned = false;
-               mb(); /* Avoid out-of-order execution. */
-               domain->is_deleted = false;
-               goto out;
+               found = true;
+               break;
        }
-       /* No memory reusable. Create using new memory. */
-       domain = tomoyo_alloc_element(sizeof(*domain));
-       if (domain) {
-               INIT_LIST_HEAD(&domain->acl_info_list);
-               domain->domainname = saved_domainname;
-               domain->profile = profile;
-               list_add_tail(&domain->list, &tomoyo_domain_list);
+       if (!found && tomoyo_memory_ok(entry)) {
+               INIT_LIST_HEAD(&entry->acl_info_list);
+               entry->domainname = saved_domainname;
+               saved_domainname = NULL;
+               entry->profile = profile;
+               list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
+               domain = entry;
+               entry = NULL;
+               found = true;
        }
- out:
-       up_write(&tomoyo_domain_list_lock);
-       return domain;
+       mutex_unlock(&tomoyo_policy_lock);
+       tomoyo_put_name(saved_domainname);
+       kfree(entry);
+       return found ? domain : NULL;
 }
 
 /**
@@ -789,6 +695,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
  * @bprm: Pointer to "struct linux_binprm".
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_find_next_domain(struct linux_binprm *bprm)
 {
@@ -796,7 +704,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
         * This function assumes that the size of buffer returned by
         * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
         */
-       struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
+       struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
        struct tomoyo_domain_info *old_domain = tomoyo_domain();
        struct tomoyo_domain_info *domain = NULL;
        const char *old_domain_name = old_domain->domainname->name;
@@ -849,8 +757,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        if (tomoyo_pathcmp(&r, &s)) {
                struct tomoyo_alias_entry *ptr;
                /* Is this program allowed to be called via symbolic links? */
-               down_read(&tomoyo_alias_list_lock);
-               list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+               list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
                        if (ptr->is_deleted ||
                            tomoyo_pathcmp(&r, ptr->original_name) ||
                            tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -861,7 +768,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
                        tomoyo_fill_path_info(&r);
                        break;
                }
-               up_read(&tomoyo_alias_list_lock);
        }
 
        /* Check execute permission. */
@@ -892,9 +798,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        }
        if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
                goto done;
-       down_read(&tomoyo_domain_list_lock);
        domain = tomoyo_find_domain(new_domain_name);
-       up_read(&tomoyo_domain_list_lock);
        if (domain)
                goto done;
        if (is_enforce)
@@ -909,14 +813,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        if (is_enforce)
                retval = -EPERM;
        else
-               tomoyo_set_domain_flag(old_domain, false,
-                                      TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
+               old_domain->transition_failed = true;
  out:
        if (!domain)
                domain = old_domain;
+       /* Update reference count on "struct tomoyo_domain_info". */
+       atomic_inc(&domain->users);
        bprm->cred->security = domain;
-       tomoyo_free(real_program_name);
-       tomoyo_free(symlink_program_name);
-       tomoyo_free(tmp);
+       kfree(real_program_name);
+       kfree(symlink_program_name);
+       kfree(tmp);
        return retval;
 }
index 9a6c58881c0a37562090a345f91b86afab76248a..1b24304edb7de3135a2febcd0b15f35d66582dd9 100644 (file)
  */
 
 #include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
-
-/*
- * tomoyo_globally_readable_file_entry is a structure which is used for holding
- * "allow_read" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_globally_readable_list .
- *  (2) "filename" is a pathname which is allowed to open(O_RDONLY).
- *  (3) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- */
-struct tomoyo_globally_readable_file_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *filename;
-       bool is_deleted;
-};
-
-/*
- * tomoyo_pattern_entry is a structure which is used for holding
- * "tomoyo_pattern_list" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_pattern_list .
- *  (2) "pattern" is a pathname pattern which is used for converting pathnames
- *      to pathname patterns during learning mode.
- *  (3) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- */
-struct tomoyo_pattern_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *pattern;
-       bool is_deleted;
-};
-
-/*
- * tomoyo_no_rewrite_entry is a structure which is used for holding
- * "deny_rewrite" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_no_rewrite_list .
- *  (2) "pattern" is a pathname which is by default not permitted to modify
- *      already existing content.
- *  (3) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- */
-struct tomoyo_no_rewrite_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *pattern;
-       bool is_deleted;
-};
 
 /* Keyword array for single path operations. */
-static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
-       [TOMOYO_TYPE_READ_WRITE_ACL] = "read/write",
-       [TOMOYO_TYPE_EXECUTE_ACL]    = "execute",
-       [TOMOYO_TYPE_READ_ACL]       = "read",
-       [TOMOYO_TYPE_WRITE_ACL]      = "write",
-       [TOMOYO_TYPE_CREATE_ACL]     = "create",
-       [TOMOYO_TYPE_UNLINK_ACL]     = "unlink",
-       [TOMOYO_TYPE_MKDIR_ACL]      = "mkdir",
-       [TOMOYO_TYPE_RMDIR_ACL]      = "rmdir",
-       [TOMOYO_TYPE_MKFIFO_ACL]     = "mkfifo",
-       [TOMOYO_TYPE_MKSOCK_ACL]     = "mksock",
-       [TOMOYO_TYPE_MKBLOCK_ACL]    = "mkblock",
-       [TOMOYO_TYPE_MKCHAR_ACL]     = "mkchar",
-       [TOMOYO_TYPE_TRUNCATE_ACL]   = "truncate",
-       [TOMOYO_TYPE_SYMLINK_ACL]    = "symlink",
-       [TOMOYO_TYPE_REWRITE_ACL]    = "rewrite",
+static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+       [TOMOYO_TYPE_READ_WRITE] = "read/write",
+       [TOMOYO_TYPE_EXECUTE]    = "execute",
+       [TOMOYO_TYPE_READ]       = "read",
+       [TOMOYO_TYPE_WRITE]      = "write",
+       [TOMOYO_TYPE_CREATE]     = "create",
+       [TOMOYO_TYPE_UNLINK]     = "unlink",
+       [TOMOYO_TYPE_MKDIR]      = "mkdir",
+       [TOMOYO_TYPE_RMDIR]      = "rmdir",
+       [TOMOYO_TYPE_MKFIFO]     = "mkfifo",
+       [TOMOYO_TYPE_MKSOCK]     = "mksock",
+       [TOMOYO_TYPE_MKBLOCK]    = "mkblock",
+       [TOMOYO_TYPE_MKCHAR]     = "mkchar",
+       [TOMOYO_TYPE_TRUNCATE]   = "truncate",
+       [TOMOYO_TYPE_SYMLINK]    = "symlink",
+       [TOMOYO_TYPE_REWRITE]    = "rewrite",
+       [TOMOYO_TYPE_IOCTL]      = "ioctl",
+       [TOMOYO_TYPE_CHMOD]      = "chmod",
+       [TOMOYO_TYPE_CHOWN]      = "chown",
+       [TOMOYO_TYPE_CHGRP]      = "chgrp",
+       [TOMOYO_TYPE_CHROOT]     = "chroot",
+       [TOMOYO_TYPE_MOUNT]      = "mount",
+       [TOMOYO_TYPE_UMOUNT]     = "unmount",
 };
 
 /* Keyword array for double path operations. */
-static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = {
-       [TOMOYO_TYPE_LINK_ACL]    = "link",
-       [TOMOYO_TYPE_RENAME_ACL]  = "rename",
+static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
+       [TOMOYO_TYPE_LINK]    = "link",
+       [TOMOYO_TYPE_RENAME]  = "rename",
+       [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
 };
 
 /**
- * tomoyo_sp2keyword - Get the name of single path operation.
+ * tomoyo_path2keyword - Get the name of single path operation.
  *
  * @operation: Type of operation.
  *
  * Returns the name of single path operation.
  */
-const char *tomoyo_sp2keyword(const u8 operation)
+const char *tomoyo_path2keyword(const u8 operation)
 {
-       return (operation < TOMOYO_MAX_SINGLE_PATH_OPERATION)
-               ? tomoyo_sp_keyword[operation] : NULL;
+       return (operation < TOMOYO_MAX_PATH_OPERATION)
+               ? tomoyo_path_keyword[operation] : NULL;
 }
 
 /**
- * tomoyo_dp2keyword - Get the name of double path operation.
+ * tomoyo_path22keyword - Get the name of double path operation.
  *
  * @operation: Type of operation.
  *
  * Returns the name of double path operation.
  */
-const char *tomoyo_dp2keyword(const u8 operation)
+const char *tomoyo_path22keyword(const u8 operation)
 {
-       return (operation < TOMOYO_MAX_DOUBLE_PATH_OPERATION)
-               ? tomoyo_dp_keyword[operation] : NULL;
+       return (operation < TOMOYO_MAX_PATH2_OPERATION)
+               ? tomoyo_path2_keyword[operation] : NULL;
 }
 
 /**
@@ -142,7 +98,8 @@ static bool tomoyo_strendswith(const char *name, const char *tail)
 static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
 {
        int error;
-       struct tomoyo_path_info_with_data *buf = tomoyo_alloc(sizeof(*buf));
+       struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf),
+                                                        GFP_KERNEL);
 
        if (!buf)
                return NULL;
@@ -154,20 +111,17 @@ static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
                tomoyo_fill_path_info(&buf->head);
                return &buf->head;
        }
-       tomoyo_free(buf);
+       kfree(buf);
        return NULL;
 }
 
-/* Lock for domain->acl_info_list. */
-DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
-
-static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
-                                        const char *filename2,
-                                        struct tomoyo_domain_info *
-                                        const domain, const bool is_delete);
-static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
-                                        struct tomoyo_domain_info *
-                                        const domain, const bool is_delete);
+static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
+                                  const char *filename2,
+                                  struct tomoyo_domain_info *const domain,
+                                  const bool is_delete);
+static int tomoyo_update_path_acl(const u8 type, const char *filename,
+                                 struct tomoyo_domain_info *const domain,
+                                 const bool is_delete);
 
 /*
  * tomoyo_globally_readable_list is used for holding list of pathnames which
@@ -194,8 +148,7 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
  * given "allow_read /lib/libc-2.5.so" to the domain which current process
  * belongs to.
  */
-static LIST_HEAD(tomoyo_globally_readable_list);
-static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
+LIST_HEAD(tomoyo_globally_readable_list);
 
 /**
  * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
@@ -204,40 +157,42 @@ static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_update_globally_readable_entry(const char *filename,
                                                 const bool is_delete)
 {
-       struct tomoyo_globally_readable_file_entry *new_entry;
+       struct tomoyo_globally_readable_file_entry *entry = NULL;
        struct tomoyo_globally_readable_file_entry *ptr;
        const struct tomoyo_path_info *saved_filename;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
 
-       if (!tomoyo_is_correct_path(filename, 1, 0, -1, __func__))
+       if (!tomoyo_is_correct_path(filename, 1, 0, -1))
                return -EINVAL;
-       saved_filename = tomoyo_save_name(filename);
+       saved_filename = tomoyo_get_name(filename);
        if (!saved_filename)
                return -ENOMEM;
-       down_write(&tomoyo_globally_readable_list_lock);
-       list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
                if (ptr->filename != saved_filename)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->filename = saved_filename;
+               saved_filename = NULL;
+               list_add_tail_rcu(&entry->list, &tomoyo_globally_readable_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->filename = saved_filename;
-       list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
-       error = 0;
- out:
-       up_write(&tomoyo_globally_readable_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       tomoyo_put_name(saved_filename);
+       kfree(entry);
        return error;
 }
 
@@ -247,21 +202,22 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
  * @filename: The filename to check.
  *
  * Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
                                             filename)
 {
        struct tomoyo_globally_readable_file_entry *ptr;
        bool found = false;
-       down_read(&tomoyo_globally_readable_list_lock);
-       list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+
+       list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
                if (!ptr->is_deleted &&
                    tomoyo_path_matches_pattern(filename, ptr->filename)) {
                        found = true;
                        break;
                }
        }
-       up_read(&tomoyo_globally_readable_list_lock);
        return found;
 }
 
@@ -272,6 +228,8 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
 {
@@ -284,13 +242,14 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_globally_readable_list_lock);
        list_for_each_cookie(pos, head->read_var2,
                             &tomoyo_globally_readable_list) {
                struct tomoyo_globally_readable_file_entry *ptr;
@@ -304,7 +263,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_globally_readable_list_lock);
        return done;
 }
 
@@ -337,8 +295,7 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
  * which pretends as if /proc/self/ is not a symlink; so that we can forbid
  * current process from accessing other process's information.
  */
-static LIST_HEAD(tomoyo_pattern_list);
-static DECLARE_RWSEM(tomoyo_pattern_list_lock);
+LIST_HEAD(tomoyo_pattern_list);
 
 /**
  * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
@@ -347,40 +304,43 @@ static DECLARE_RWSEM(tomoyo_pattern_list_lock);
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_update_file_pattern_entry(const char *pattern,
                                            const bool is_delete)
 {
-       struct tomoyo_pattern_entry *new_entry;
+       struct tomoyo_pattern_entry *entry = NULL;
        struct tomoyo_pattern_entry *ptr;
        const struct tomoyo_path_info *saved_pattern;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
 
-       if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __func__))
-               return -EINVAL;
-       saved_pattern = tomoyo_save_name(pattern);
+       saved_pattern = tomoyo_get_name(pattern);
        if (!saved_pattern)
-               return -ENOMEM;
-       down_write(&tomoyo_pattern_list_lock);
-       list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+               return error;
+       if (!saved_pattern->is_patterned)
+               goto out;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
                if (saved_pattern != ptr->pattern)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->pattern = saved_pattern;
+               saved_pattern = NULL;
+               list_add_tail_rcu(&entry->list, &tomoyo_pattern_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->pattern = saved_pattern;
-       list_add_tail(&new_entry->list, &tomoyo_pattern_list);
-       error = 0;
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_pattern_list_lock);
+       kfree(entry);
+       tomoyo_put_name(saved_pattern);
        return error;
 }
 
@@ -390,6 +350,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
  * @filename: The filename to find patterned pathname.
  *
  * Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static const struct tomoyo_path_info *
 tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
@@ -397,8 +359,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
        struct tomoyo_pattern_entry *ptr;
        const struct tomoyo_path_info *pattern = NULL;
 
-       down_read(&tomoyo_pattern_list_lock);
-       list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
                if (ptr->is_deleted)
                        continue;
                if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -411,7 +372,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
                        break;
                }
        }
-       up_read(&tomoyo_pattern_list_lock);
        if (pattern)
                filename = pattern;
        return filename;
@@ -424,6 +384,8 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_pattern_policy(char *data, const bool is_delete)
 {
@@ -436,13 +398,14 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete)
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_pattern_list_lock);
        list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
                struct tomoyo_pattern_entry *ptr;
                ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -453,7 +416,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_pattern_list_lock);
        return done;
 }
 
@@ -486,8 +448,7 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
  * " (deleted)" suffix if the file is already unlink()ed; so that we don't
  * need to worry whether the file is already unlink()ed or not.
  */
-static LIST_HEAD(tomoyo_no_rewrite_list);
-static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
+LIST_HEAD(tomoyo_no_rewrite_list);
 
 /**
  * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
@@ -496,39 +457,42 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_update_no_rewrite_entry(const char *pattern,
                                          const bool is_delete)
 {
-       struct tomoyo_no_rewrite_entry *new_entry, *ptr;
+       struct tomoyo_no_rewrite_entry *entry = NULL;
+       struct tomoyo_no_rewrite_entry *ptr;
        const struct tomoyo_path_info *saved_pattern;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
 
-       if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __func__))
+       if (!tomoyo_is_correct_path(pattern, 0, 0, 0))
                return -EINVAL;
-       saved_pattern = tomoyo_save_name(pattern);
+       saved_pattern = tomoyo_get_name(pattern);
        if (!saved_pattern)
-               return -ENOMEM;
-       down_write(&tomoyo_no_rewrite_list_lock);
-       list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+               return error;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
                if (ptr->pattern != saved_pattern)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->pattern = saved_pattern;
+               saved_pattern = NULL;
+               list_add_tail_rcu(&entry->list, &tomoyo_no_rewrite_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->pattern = saved_pattern;
-       list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
-       error = 0;
- out:
-       up_write(&tomoyo_no_rewrite_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       tomoyo_put_name(saved_pattern);
+       kfree(entry);
        return error;
 }
 
@@ -539,14 +503,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
  *
  * Returns true if @filename is specified by "deny_rewrite" directive,
  * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
 {
        struct tomoyo_no_rewrite_entry *ptr;
        bool found = false;
 
-       down_read(&tomoyo_no_rewrite_list_lock);
-       list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
                if (ptr->is_deleted)
                        continue;
                if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -554,7 +519,6 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
                found = true;
                break;
        }
-       up_read(&tomoyo_no_rewrite_list_lock);
        return found;
 }
 
@@ -565,6 +529,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
 {
@@ -577,13 +543,14 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_no_rewrite_list_lock);
        list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
                struct tomoyo_no_rewrite_entry *ptr;
                ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -594,7 +561,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_no_rewrite_list_lock);
        return done;
 }
 
@@ -612,6 +578,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
  * Current policy syntax uses "allow_read/write" instead of "6",
  * "allow_read" instead of "4", "allow_write" instead of "2",
  * "allow_execute" instead of "1".
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_update_file_acl(const char *filename, u8 perm,
                                  struct tomoyo_domain_info * const domain,
@@ -629,19 +597,19 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
                 */
                return 0;
        if (perm & 4)
-               tomoyo_update_single_path_acl(TOMOYO_TYPE_READ_ACL, filename,
-                                             domain, is_delete);
+               tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain,
+                                      is_delete);
        if (perm & 2)
-               tomoyo_update_single_path_acl(TOMOYO_TYPE_WRITE_ACL, filename,
-                                             domain, is_delete);
+               tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain,
+                                      is_delete);
        if (perm & 1)
-               tomoyo_update_single_path_acl(TOMOYO_TYPE_EXECUTE_ACL,
-                                             filename, domain, is_delete);
+               tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain,
+                                      is_delete);
        return 0;
 }
 
 /**
- * tomoyo_check_single_path_acl2 - Check permission for single path operation.
+ * tomoyo_path_acl2 - Check permission for single path operation.
  *
  * @domain:          Pointer to "struct tomoyo_domain_info".
  * @filename:        Filename to check.
@@ -649,26 +617,28 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
  * @may_use_pattern: True if patterned ACL is permitted.
  *
  * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
-                                        domain,
-                                        const struct tomoyo_path_info *
-                                        filename,
-                                        const u16 perm,
-                                        const bool may_use_pattern)
+static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain,
+                           const struct tomoyo_path_info *filename,
+                           const u32 perm, const bool may_use_pattern)
 {
        struct tomoyo_acl_info *ptr;
        int error = -EPERM;
 
-       down_read(&tomoyo_domain_acl_info_list_lock);
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
-               struct tomoyo_single_path_acl_record *acl;
-               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
-                       continue;
-               acl = container_of(ptr, struct tomoyo_single_path_acl_record,
-                                  head);
-               if (!(acl->perm & perm))
+       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+               struct tomoyo_path_acl *acl;
+               if (ptr->type != TOMOYO_TYPE_PATH_ACL)
                        continue;
+               acl = container_of(ptr, struct tomoyo_path_acl, head);
+               if (perm <= 0xFFFF) {
+                       if (!(acl->perm & perm))
+                               continue;
+               } else {
+                       if (!(acl->perm_high & (perm >> 16)))
+                               continue;
+               }
                if (may_use_pattern || !acl->filename->is_patterned) {
                        if (!tomoyo_path_matches_pattern(filename,
                                                         acl->filename))
@@ -679,7 +649,6 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
                error = 0;
                break;
        }
-       up_read(&tomoyo_domain_acl_info_list_lock);
        return error;
 }
 
@@ -691,27 +660,28 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
  * @operation: Mode ("read" or "write" or "read/write" or "execute").
  *
  * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
                                 const struct tomoyo_path_info *filename,
                                 const u8 operation)
 {
-       u16 perm = 0;
+       u32 perm = 0;
 
        if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
                return 0;
        if (operation == 6)
-               perm = 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+               perm = 1 << TOMOYO_TYPE_READ_WRITE;
        else if (operation == 4)
-               perm = 1 << TOMOYO_TYPE_READ_ACL;
+               perm = 1 << TOMOYO_TYPE_READ;
        else if (operation == 2)
-               perm = 1 << TOMOYO_TYPE_WRITE_ACL;
+               perm = 1 << TOMOYO_TYPE_WRITE;
        else if (operation == 1)
-               perm = 1 << TOMOYO_TYPE_EXECUTE_ACL;
+               perm = 1 << TOMOYO_TYPE_EXECUTE;
        else
                BUG();
-       return tomoyo_check_single_path_acl2(domain, filename, perm,
-                                            operation != 1);
+       return tomoyo_path_acl2(domain, filename, perm, operation != 1);
 }
 
 /**
@@ -724,6 +694,8 @@ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
  * @mode:      Access control mode.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
                                   const struct tomoyo_path_info *filename,
@@ -737,18 +709,17 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
        if (!filename)
                return 0;
        error = tomoyo_check_file_acl(domain, filename, perm);
-       if (error && perm == 4 &&
-           (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0
+       if (error && perm == 4 && !domain->ignore_global_allow_read
            && tomoyo_is_globally_readable_file(filename))
                error = 0;
        if (perm == 6)
-               msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_WRITE_ACL);
+               msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
        else if (perm == 4)
-               msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_ACL);
+               msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
        else if (perm == 2)
-               msg = tomoyo_sp2keyword(TOMOYO_TYPE_WRITE_ACL);
+               msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
        else if (perm == 1)
-               msg = tomoyo_sp2keyword(TOMOYO_TYPE_EXECUTE_ACL);
+               msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
        else
                BUG();
        if (!error)
@@ -777,6 +748,8 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
                             const bool is_delete)
@@ -795,28 +768,28 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
        if (strncmp(data, "allow_", 6))
                goto out;
        data += 6;
-       for (type = 0; type < TOMOYO_MAX_SINGLE_PATH_OPERATION; type++) {
-               if (strcmp(data, tomoyo_sp_keyword[type]))
+       for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
+               if (strcmp(data, tomoyo_path_keyword[type]))
                        continue;
-               return tomoyo_update_single_path_acl(type, filename,
-                                                    domain, is_delete);
+               return tomoyo_update_path_acl(type, filename, domain,
+                                             is_delete);
        }
        filename2 = strchr(filename, ' ');
        if (!filename2)
                goto out;
        *filename2++ = '\0';
-       for (type = 0; type < TOMOYO_MAX_DOUBLE_PATH_OPERATION; type++) {
-               if (strcmp(data, tomoyo_dp_keyword[type]))
+       for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
+               if (strcmp(data, tomoyo_path2_keyword[type]))
                        continue;
-               return tomoyo_update_double_path_acl(type, filename, filename2,
-                                                    domain, is_delete);
+               return tomoyo_update_path2_acl(type, filename, filename2,
+                                              domain, is_delete);
        }
  out:
        return -EINVAL;
 }
 
 /**
- * tomoyo_update_single_path_acl - Update "struct tomoyo_single_path_acl_record" list.
+ * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
  *
  * @type:      Type of operation.
  * @filename:  Filename.
@@ -824,85 +797,82 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
  * @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_single_path_acl(const u8 type, const char *filename,
-                                        struct tomoyo_domain_info *
-                                        const domain, const bool is_delete)
+static int tomoyo_update_path_acl(const u8 type, const char *filename,
+                                 struct tomoyo_domain_info *const domain,
+                                 const bool is_delete)
 {
-       static const u16 rw_mask =
-               (1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
+       static const u32 rw_mask =
+               (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE);
        const struct tomoyo_path_info *saved_filename;
        struct tomoyo_acl_info *ptr;
-       struct tomoyo_single_path_acl_record *acl;
-       int error = -ENOMEM;
-       const u16 perm = 1 << type;
+       struct tomoyo_path_acl *entry = NULL;
+       int error = is_delete ? -ENOENT : -ENOMEM;
+       const u32 perm = 1 << type;
 
        if (!domain)
                return -EINVAL;
-       if (!tomoyo_is_correct_path(filename, 0, 0, 0, __func__))
+       if (!tomoyo_is_correct_path(filename, 0, 0, 0))
                return -EINVAL;
-       saved_filename = tomoyo_save_name(filename);
+       saved_filename = tomoyo_get_name(filename);
        if (!saved_filename)
                return -ENOMEM;
-       down_write(&tomoyo_domain_acl_info_list_lock);
-       if (is_delete)
-               goto delete;
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
-               if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+               struct tomoyo_path_acl *acl =
+                       container_of(ptr, struct tomoyo_path_acl, head);
+               if (ptr->type != TOMOYO_TYPE_PATH_ACL)
                        continue;
-               acl = container_of(ptr, struct tomoyo_single_path_acl_record,
-                                  head);
                if (acl->filename != saved_filename)
                        continue;
-               /* Special case. Clear all bits if marked as deleted. */
-               if (ptr->type & TOMOYO_ACL_DELETED)
-                       acl->perm = 0;
-               acl->perm |= perm;
-               if ((acl->perm & rw_mask) == rw_mask)
-                       acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL;
-               else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
-                       acl->perm |= rw_mask;
-               ptr->type &= ~TOMOYO_ACL_DELETED;
+               if (is_delete) {
+                       if (perm <= 0xFFFF)
+                               acl->perm &= ~perm;
+                       else
+                               acl->perm_high &= ~(perm >> 16);
+                       if ((acl->perm & rw_mask) != rw_mask)
+                               acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
+                       else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)))
+                               acl->perm &= ~rw_mask;
+               } else {
+                       if (perm <= 0xFFFF)
+                               acl->perm |= perm;
+                       else
+                               acl->perm_high |= (perm >> 16);
+                       if ((acl->perm & rw_mask) == rw_mask)
+                               acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE;
+                       else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))
+                               acl->perm |= rw_mask;
+               }
                error = 0;
-               goto out;
+               break;
        }
-       /* Not found. Append it to the tail. */
-       acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
-       if (!acl)
-               goto out;
-       acl->perm = perm;
-       if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
-               acl->perm |= rw_mask;
-       acl->filename = saved_filename;
-       list_add_tail(&acl->head.list, &domain->acl_info_list);
-       error = 0;
-       goto out;
- delete:
-       error = -ENOENT;
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
-               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
-                       continue;
-               acl = container_of(ptr, struct tomoyo_single_path_acl_record,
-                                  head);
-               if (acl->filename != saved_filename)
-                       continue;
-               acl->perm &= ~perm;
-               if ((acl->perm & rw_mask) != rw_mask)
-                       acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL);
-               else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
-                       acl->perm &= ~rw_mask;
-               if (!acl->perm)
-                       ptr->type |= TOMOYO_ACL_DELETED;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->head.type = TOMOYO_TYPE_PATH_ACL;
+               if (perm <= 0xFFFF)
+                       entry->perm = perm;
+               else
+                       entry->perm_high = (perm >> 16);
+               if (perm == (1 << TOMOYO_TYPE_READ_WRITE))
+                       entry->perm |= rw_mask;
+               entry->filename = saved_filename;
+               saved_filename = NULL;
+               list_add_tail_rcu(&entry->head.list, &domain->acl_info_list);
+               entry = NULL;
                error = 0;
-               break;
        }
- out:
-       up_write(&tomoyo_domain_acl_info_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(entry);
+       tomoyo_put_name(saved_filename);
        return error;
 }
 
 /**
- * tomoyo_update_double_path_acl - Update "struct tomoyo_double_path_acl_record" list.
+ * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
  *
  * @type:      Type of operation.
  * @filename1: First filename.
@@ -911,98 +881,88 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
  * @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_double_path_acl(const u8 type, const char *filename1,
-                                        const char *filename2,
-                                        struct tomoyo_domain_info *
-                                        const domain, const bool is_delete)
+static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
+                                  const char *filename2,
+                                  struct tomoyo_domain_info *const domain,
+                                  const bool is_delete)
 {
        const struct tomoyo_path_info *saved_filename1;
        const struct tomoyo_path_info *saved_filename2;
        struct tomoyo_acl_info *ptr;
-       struct tomoyo_double_path_acl_record *acl;
-       int error = -ENOMEM;
+       struct tomoyo_path2_acl *entry = NULL;
+       int error = is_delete ? -ENOENT : -ENOMEM;
        const u8 perm = 1 << type;
 
        if (!domain)
                return -EINVAL;
-       if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __func__) ||
-           !tomoyo_is_correct_path(filename2, 0, 0, 0, __func__))
+       if (!tomoyo_is_correct_path(filename1, 0, 0, 0) ||
+           !tomoyo_is_correct_path(filename2, 0, 0, 0))
                return -EINVAL;
-       saved_filename1 = tomoyo_save_name(filename1);
-       saved_filename2 = tomoyo_save_name(filename2);
+       saved_filename1 = tomoyo_get_name(filename1);
+       saved_filename2 = tomoyo_get_name(filename2);
        if (!saved_filename1 || !saved_filename2)
-               return -ENOMEM;
-       down_write(&tomoyo_domain_acl_info_list_lock);
-       if (is_delete)
-               goto delete;
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
-               if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
-                       continue;
-               acl = container_of(ptr, struct tomoyo_double_path_acl_record,
-                                  head);
-               if (acl->filename1 != saved_filename1 ||
-                   acl->filename2 != saved_filename2)
-                       continue;
-               /* Special case. Clear all bits if marked as deleted. */
-               if (ptr->type & TOMOYO_ACL_DELETED)
-                       acl->perm = 0;
-               acl->perm |= perm;
-               ptr->type &= ~TOMOYO_ACL_DELETED;
-               error = 0;
                goto out;
-       }
-       /* Not found. Append it to the tail. */
-       acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
-       if (!acl)
-               goto out;
-       acl->perm = perm;
-       acl->filename1 = saved_filename1;
-       acl->filename2 = saved_filename2;
-       list_add_tail(&acl->head.list, &domain->acl_info_list);
-       error = 0;
-       goto out;
- delete:
-       error = -ENOENT;
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
-               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+               struct tomoyo_path2_acl *acl =
+                       container_of(ptr, struct tomoyo_path2_acl, head);
+               if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
                        continue;
-               acl = container_of(ptr, struct tomoyo_double_path_acl_record,
-                                  head);
                if (acl->filename1 != saved_filename1 ||
                    acl->filename2 != saved_filename2)
                        continue;
-               acl->perm &= ~perm;
-               if (!acl->perm)
-                       ptr->type |= TOMOYO_ACL_DELETED;
+               if (is_delete)
+                       acl->perm &= ~perm;
+               else
+                       acl->perm |= perm;
                error = 0;
                break;
        }
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->head.type = TOMOYO_TYPE_PATH2_ACL;
+               entry->perm = perm;
+               entry->filename1 = saved_filename1;
+               saved_filename1 = NULL;
+               entry->filename2 = saved_filename2;
+               saved_filename2 = NULL;
+               list_add_tail_rcu(&entry->head.list, &domain->acl_info_list);
+               entry = NULL;
+               error = 0;
+       }
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_domain_acl_info_list_lock);
+       tomoyo_put_name(saved_filename1);
+       tomoyo_put_name(saved_filename2);
+       kfree(entry);
        return error;
 }
 
 /**
- * tomoyo_check_single_path_acl - Check permission for single path operation.
+ * tomoyo_path_acl - Check permission for single path operation.
  *
  * @domain:   Pointer to "struct tomoyo_domain_info".
  * @type:     Type of operation.
  * @filename: Filename to check.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
-                                       const u8 type,
-                                       const struct tomoyo_path_info *filename)
+static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type,
+                          const struct tomoyo_path_info *filename)
 {
        if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
                return 0;
-       return tomoyo_check_single_path_acl2(domain, filename, 1 << type, 1);
+       return tomoyo_path_acl2(domain, filename, 1 << type, 1);
 }
 
 /**
- * tomoyo_check_double_path_acl - Check permission for double path operation.
+ * tomoyo_path2_acl - Check permission for double path operation.
  *
  * @domain:    Pointer to "struct tomoyo_domain_info".
  * @type:      Type of operation.
@@ -1010,13 +970,13 @@ static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
  * @filename2: Second filename to check.
  *
  * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
-                                       const u8 type,
-                                       const struct tomoyo_path_info *
-                                       filename1,
-                                       const struct tomoyo_path_info *
-                                       filename2)
+static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain,
+                           const u8 type,
+                           const struct tomoyo_path_info *filename1,
+                           const struct tomoyo_path_info *filename2)
 {
        struct tomoyo_acl_info *ptr;
        const u8 perm = 1 << type;
@@ -1024,13 +984,11 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
 
        if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
                return 0;
-       down_read(&tomoyo_domain_acl_info_list_lock);
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
-               struct tomoyo_double_path_acl_record *acl;
-               if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+               struct tomoyo_path2_acl *acl;
+               if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
                        continue;
-               acl = container_of(ptr, struct tomoyo_double_path_acl_record,
-                                  head);
+               acl = container_of(ptr, struct tomoyo_path2_acl, head);
                if (!(acl->perm & perm))
                        continue;
                if (!tomoyo_path_matches_pattern(filename1, acl->filename1))
@@ -1040,12 +998,11 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
                error = 0;
                break;
        }
-       up_read(&tomoyo_domain_acl_info_list_lock);
        return error;
 }
 
 /**
- * tomoyo_check_single_path_permission2 - Check permission for single path operation.
+ * tomoyo_path_permission2 - Check permission for single path operation.
  *
  * @domain:    Pointer to "struct tomoyo_domain_info".
  * @operation: Type of operation.
@@ -1053,11 +1010,13 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
  * @mode:      Access control mode.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
-                                               const domain, u8 operation,
-                                               const struct tomoyo_path_info *
-                                               filename, const u8 mode)
+static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain,
+                                  u8 operation,
+                                  const struct tomoyo_path_info *filename,
+                                  const u8 mode)
 {
        const char *msg;
        int error;
@@ -1066,8 +1025,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
        if (!mode)
                return 0;
  next:
-       error = tomoyo_check_single_path_acl(domain, operation, filename);
-       msg = tomoyo_sp2keyword(operation);
+       error = tomoyo_path_acl(domain, operation, filename);
+       msg = tomoyo_path2keyword(operation);
        if (!error)
                goto ok;
        if (tomoyo_verbose_mode(domain))
@@ -1076,7 +1035,7 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
                       tomoyo_get_last_name(domain));
        if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
                const char *name = tomoyo_get_file_pattern(filename)->name;
-               tomoyo_update_single_path_acl(operation, name, domain, false);
+               tomoyo_update_path_acl(operation, name, domain, false);
        }
        if (!is_enforce)
                error = 0;
@@ -1086,9 +1045,9 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
         * we need to check "allow_rewrite" permission if the filename is
         * specified by "deny_rewrite" keyword.
         */
-       if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL &&
+       if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
            tomoyo_is_no_rewrite_file(filename)) {
-               operation = TOMOYO_TYPE_REWRITE_ACL;
+               operation = TOMOYO_TYPE_REWRITE;
                goto next;
        }
        return error;
@@ -1101,6 +1060,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
  * @filename: Check permission for "execute".
  *
  * Returns 0 on success, negativevalue otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
                           const struct tomoyo_path_info *filename)
@@ -1129,6 +1090,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
        struct tomoyo_path_info *buf;
        const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
        const bool is_enforce = (mode == 3);
+       int idx;
 
        if (!mode || !path->mnt)
                return 0;
@@ -1140,6 +1102,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
                 * don't call me.
                 */
                return 0;
+       idx = tomoyo_read_lock();
        buf = tomoyo_get_path(path);
        if (!buf)
                goto out;
@@ -1152,49 +1115,50 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
        if ((acc_mode & MAY_WRITE) &&
            ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
            (tomoyo_is_no_rewrite_file(buf))) {
-               error = tomoyo_check_single_path_permission2(domain,
-                                                    TOMOYO_TYPE_REWRITE_ACL,
-                                                            buf, mode);
+               error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE,
+                                               buf, mode);
        }
        if (!error)
                error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open",
                                                mode);
        if (!error && (flag & O_TRUNC))
-               error = tomoyo_check_single_path_permission2(domain,
-                                                    TOMOYO_TYPE_TRUNCATE_ACL,
-                                                            buf, mode);
+               error = tomoyo_path_permission2(domain, TOMOYO_TYPE_TRUNCATE,
+                                               buf, mode);
  out:
-       tomoyo_free(buf);
+       kfree(buf);
+       tomoyo_read_unlock(idx);
        if (!is_enforce)
                error = 0;
        return error;
 }
 
 /**
- * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink".
+ * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount".
  *
- * @domain:    Pointer to "struct tomoyo_domain_info".
  * @operation: Type of operation.
  * @path:      Pointer to "struct path".
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
-                           const u8 operation, struct path *path)
+int tomoyo_path_perm(const u8 operation, struct path *path)
 {
        int error = -ENOMEM;
        struct tomoyo_path_info *buf;
+       struct tomoyo_domain_info *domain = tomoyo_domain();
        const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
        const bool is_enforce = (mode == 3);
+       int idx;
 
        if (!mode || !path->mnt)
                return 0;
+       idx = tomoyo_read_lock();
        buf = tomoyo_get_path(path);
        if (!buf)
                goto out;
        switch (operation) {
-       case TOMOYO_TYPE_MKDIR_ACL:
-       case TOMOYO_TYPE_RMDIR_ACL:
+       case TOMOYO_TYPE_MKDIR:
+       case TOMOYO_TYPE_RMDIR:
+       case TOMOYO_TYPE_CHROOT:
                if (!buf->is_dir) {
                        /*
                         * tomoyo_get_path() reserves space for appending "/."
@@ -1203,10 +1167,10 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
                        tomoyo_fill_path_info(buf);
                }
        }
-       error = tomoyo_check_single_path_permission2(domain, operation, buf,
-                                                    mode);
+       error = tomoyo_path_permission2(domain, operation, buf, mode);
  out:
-       tomoyo_free(buf);
+       kfree(buf);
+       tomoyo_read_unlock(idx);
        if (!is_enforce)
                error = 0;
        return error;
@@ -1215,21 +1179,23 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
 /**
  * tomoyo_check_rewrite_permission - Check permission for "rewrite".
  *
- * @domain: Pointer to "struct tomoyo_domain_info".
  * @filp: Pointer to "struct file".
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
-                                   struct file *filp)
+int tomoyo_check_rewrite_permission(struct file *filp)
 {
        int error = -ENOMEM;
+       struct tomoyo_domain_info *domain = tomoyo_domain();
        const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
        const bool is_enforce = (mode == 3);
        struct tomoyo_path_info *buf;
+       int idx;
 
        if (!mode || !filp->f_path.mnt)
                return 0;
+
+       idx = tomoyo_read_lock();
        buf = tomoyo_get_path(&filp->f_path);
        if (!buf)
                goto out;
@@ -1237,38 +1203,38 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
                error = 0;
                goto out;
        }
-       error = tomoyo_check_single_path_permission2(domain,
-                                                    TOMOYO_TYPE_REWRITE_ACL,
-                                                    buf, mode);
+       error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, buf, mode);
  out:
-       tomoyo_free(buf);
+       kfree(buf);
+       tomoyo_read_unlock(idx);
        if (!is_enforce)
                error = 0;
        return error;
 }
 
 /**
- * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
  *
- * @domain:    Pointer to "struct tomoyo_domain_info".
  * @operation: Type of operation.
  * @path1:      Pointer to "struct path".
  * @path2:      Pointer to "struct path".
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
-                           const u8 operation, struct path *path1,
-                           struct path *path2)
+int tomoyo_path2_perm(const u8 operation, struct path *path1,
+                     struct path *path2)
 {
        int error = -ENOMEM;
        struct tomoyo_path_info *buf1, *buf2;
+       struct tomoyo_domain_info *domain = tomoyo_domain();
        const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
        const bool is_enforce = (mode == 3);
        const char *msg;
+       int idx;
 
        if (!mode || !path1->mnt || !path2->mnt)
                return 0;
+       idx = tomoyo_read_lock();
        buf1 = tomoyo_get_path(path1);
        buf2 = tomoyo_get_path(path2);
        if (!buf1 || !buf2)
@@ -1289,8 +1255,8 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
                        }
                }
        }
-       error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2);
-       msg = tomoyo_dp2keyword(operation);
+       error = tomoyo_path2_acl(domain, operation, buf1, buf2);
+       msg = tomoyo_path22keyword(operation);
        if (!error)
                goto out;
        if (tomoyo_verbose_mode(domain))
@@ -1301,12 +1267,13 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
        if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
                const char *name1 = tomoyo_get_file_pattern(buf1)->name;
                const char *name2 = tomoyo_get_file_pattern(buf2)->name;
-               tomoyo_update_double_path_acl(operation, name1, name2, domain,
-                                             false);
+               tomoyo_update_path2_acl(operation, name1, name2, domain,
+                                       false);
        }
  out:
-       tomoyo_free(buf1);
-       tomoyo_free(buf2);
+       kfree(buf1);
+       kfree(buf2);
+       tomoyo_read_unlock(idx);
        if (!is_enforce)
                error = 0;
        return error;
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
new file mode 100644 (file)
index 0000000..9645525
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * security/tomoyo/gc.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ *
+ */
+
+#include "common.h"
+#include <linux/kthread.h>
+
+enum tomoyo_gc_id {
+       TOMOYO_ID_DOMAIN_INITIALIZER,
+       TOMOYO_ID_DOMAIN_KEEPER,
+       TOMOYO_ID_ALIAS,
+       TOMOYO_ID_GLOBALLY_READABLE,
+       TOMOYO_ID_PATTERN,
+       TOMOYO_ID_NO_REWRITE,
+       TOMOYO_ID_MANAGER,
+       TOMOYO_ID_NAME,
+       TOMOYO_ID_ACL,
+       TOMOYO_ID_DOMAIN
+};
+
+struct tomoyo_gc_entry {
+       struct list_head list;
+       int type;
+       void *element;
+};
+static LIST_HEAD(tomoyo_gc_queue);
+static DEFINE_MUTEX(tomoyo_gc_mutex);
+
+/* Caller holds tomoyo_policy_lock mutex. */
+static bool tomoyo_add_to_gc(const int type, void *element)
+{
+       struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+       if (!entry)
+               return false;
+       entry->type = type;
+       entry->element = element;
+       list_add(&entry->list, &tomoyo_gc_queue);
+       return true;
+}
+
+static void tomoyo_del_allow_read
+(struct tomoyo_globally_readable_file_entry *ptr)
+{
+       tomoyo_put_name(ptr->filename);
+}
+
+static void tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr)
+{
+       tomoyo_put_name(ptr->pattern);
+}
+
+static void tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr)
+{
+       tomoyo_put_name(ptr->pattern);
+}
+
+static void tomoyo_del_domain_initializer
+(struct tomoyo_domain_initializer_entry *ptr)
+{
+       tomoyo_put_name(ptr->domainname);
+       tomoyo_put_name(ptr->program);
+}
+
+static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr)
+{
+       tomoyo_put_name(ptr->domainname);
+       tomoyo_put_name(ptr->program);
+}
+
+static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr)
+{
+       tomoyo_put_name(ptr->original_name);
+       tomoyo_put_name(ptr->aliased_name);
+}
+
+static void tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr)
+{
+       tomoyo_put_name(ptr->manager);
+}
+
+static void tomoyo_del_acl(struct tomoyo_acl_info *acl)
+{
+       switch (acl->type) {
+       case TOMOYO_TYPE_PATH_ACL:
+               {
+                       struct tomoyo_path_acl *entry
+                               = container_of(acl, typeof(*entry), head);
+                       tomoyo_put_name(entry->filename);
+               }
+               break;
+       case TOMOYO_TYPE_PATH2_ACL:
+               {
+                       struct tomoyo_path2_acl *entry
+                               = container_of(acl, typeof(*entry), head);
+                       tomoyo_put_name(entry->filename1);
+                       tomoyo_put_name(entry->filename2);
+               }
+               break;
+       default:
+               printk(KERN_WARNING "Unknown type\n");
+               break;
+       }
+}
+
+static bool tomoyo_del_domain(struct tomoyo_domain_info *domain)
+{
+       struct tomoyo_acl_info *acl;
+       struct tomoyo_acl_info *tmp;
+       /*
+        * Since we don't protect whole execve() operation using SRCU,
+        * we need to recheck domain->users at this point.
+        *
+        * (1) Reader starts SRCU section upon execve().
+        * (2) Reader traverses tomoyo_domain_list and finds this domain.
+        * (3) Writer marks this domain as deleted.
+        * (4) Garbage collector removes this domain from tomoyo_domain_list
+        *     because this domain is marked as deleted and used by nobody.
+        * (5) Reader saves reference to this domain into
+        *     "struct linux_binprm"->cred->security .
+        * (6) Reader finishes SRCU section, although execve() operation has
+        *     not finished yet.
+        * (7) Garbage collector waits for SRCU synchronization.
+        * (8) Garbage collector kfree() this domain because this domain is
+        *     used by nobody.
+        * (9) Reader finishes execve() operation and restores this domain from
+        *     "struct linux_binprm"->cred->security.
+        *
+        * By updating domain->users at (5), we can solve this race problem
+        * by rechecking domain->users at (8).
+        */
+       if (atomic_read(&domain->users))
+               return false;
+       list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
+               tomoyo_del_acl(acl);
+               tomoyo_memory_free(acl);
+       }
+       tomoyo_put_name(domain->domainname);
+       return true;
+}
+
+
+static void tomoyo_del_name(const struct tomoyo_name_entry *ptr)
+{
+}
+
+static void tomoyo_collect_entry(void)
+{
+       mutex_lock(&tomoyo_policy_lock);
+       {
+               struct tomoyo_globally_readable_file_entry *ptr;
+               list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
+                                       list) {
+                       if (!ptr->is_deleted)
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr))
+                               list_del_rcu(&ptr->list);
+                       else
+                               break;
+               }
+       }
+       {
+               struct tomoyo_pattern_entry *ptr;
+               list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
+                       if (!ptr->is_deleted)
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr))
+                               list_del_rcu(&ptr->list);
+                       else
+                               break;
+               }
+       }
+       {
+               struct tomoyo_no_rewrite_entry *ptr;
+               list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
+                       if (!ptr->is_deleted)
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr))
+                               list_del_rcu(&ptr->list);
+                       else
+                               break;
+               }
+       }
+       {
+               struct tomoyo_domain_initializer_entry *ptr;
+               list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list,
+                                       list) {
+                       if (!ptr->is_deleted)
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr))
+                               list_del_rcu(&ptr->list);
+                       else
+                               break;
+               }
+       }
+       {
+               struct tomoyo_domain_keeper_entry *ptr;
+               list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
+                       if (!ptr->is_deleted)
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr))
+                               list_del_rcu(&ptr->list);
+                       else
+                               break;
+               }
+       }
+       {
+               struct tomoyo_alias_entry *ptr;
+               list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
+                       if (!ptr->is_deleted)
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr))
+                               list_del_rcu(&ptr->list);
+                       else
+                               break;
+               }
+       }
+       {
+               struct tomoyo_policy_manager_entry *ptr;
+               list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list,
+                                       list) {
+                       if (!ptr->is_deleted)
+                               continue;
+                       if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr))
+                               list_del_rcu(&ptr->list);
+                       else
+                               break;
+               }
+       }
+       {
+               struct tomoyo_domain_info *domain;
+               list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+                       struct tomoyo_acl_info *acl;
+                       list_for_each_entry_rcu(acl, &domain->acl_info_list,
+                                               list) {
+                               switch (acl->type) {
+                               case TOMOYO_TYPE_PATH_ACL:
+                                       if (container_of(acl,
+                                        struct tomoyo_path_acl,
+                                                        head)->perm ||
+                                           container_of(acl,
+                                        struct tomoyo_path_acl,
+                                                        head)->perm_high)
+                                               continue;
+                                       break;
+                               case TOMOYO_TYPE_PATH2_ACL:
+                                       if (container_of(acl,
+                                        struct tomoyo_path2_acl,
+                                                        head)->perm)
+                                               continue;
+                                       break;
+                               default:
+                                       continue;
+                               }
+                               if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl))
+                                       list_del_rcu(&acl->list);
+                               else
+                                       break;
+                       }
+                       if (!domain->is_deleted || atomic_read(&domain->users))
+                               continue;
+                       /*
+                        * Nobody is referring this domain. But somebody may
+                        * refer this domain after successful execve().
+                        * We recheck domain->users after SRCU synchronization.
+                        */
+                       if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain))
+                               list_del_rcu(&domain->list);
+                       else
+                               break;
+               }
+       }
+       mutex_unlock(&tomoyo_policy_lock);
+       mutex_lock(&tomoyo_name_list_lock);
+       {
+               int i;
+               for (i = 0; i < TOMOYO_MAX_HASH; i++) {
+                       struct tomoyo_name_entry *ptr;
+                       list_for_each_entry_rcu(ptr, &tomoyo_name_list[i],
+                                               list) {
+                               if (atomic_read(&ptr->users))
+                                       continue;
+                               if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr))
+                                       list_del_rcu(&ptr->list);
+                               else {
+                                       i = TOMOYO_MAX_HASH;
+                                       break;
+                               }
+                       }
+               }
+       }
+       mutex_unlock(&tomoyo_name_list_lock);
+}
+
+static void tomoyo_kfree_entry(void)
+{
+       struct tomoyo_gc_entry *p;
+       struct tomoyo_gc_entry *tmp;
+
+       list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) {
+               switch (p->type) {
+               case TOMOYO_ID_DOMAIN_INITIALIZER:
+                       tomoyo_del_domain_initializer(p->element);
+                       break;
+               case TOMOYO_ID_DOMAIN_KEEPER:
+                       tomoyo_del_domain_keeper(p->element);
+                       break;
+               case TOMOYO_ID_ALIAS:
+                       tomoyo_del_alias(p->element);
+                       break;
+               case TOMOYO_ID_GLOBALLY_READABLE:
+                       tomoyo_del_allow_read(p->element);
+                       break;
+               case TOMOYO_ID_PATTERN:
+                       tomoyo_del_file_pattern(p->element);
+                       break;
+               case TOMOYO_ID_NO_REWRITE:
+                       tomoyo_del_no_rewrite(p->element);
+                       break;
+               case TOMOYO_ID_MANAGER:
+                       tomoyo_del_manager(p->element);
+                       break;
+               case TOMOYO_ID_NAME:
+                       tomoyo_del_name(p->element);
+                       break;
+               case TOMOYO_ID_ACL:
+                       tomoyo_del_acl(p->element);
+                       break;
+               case TOMOYO_ID_DOMAIN:
+                       if (!tomoyo_del_domain(p->element))
+                               continue;
+                       break;
+               default:
+                       printk(KERN_WARNING "Unknown type\n");
+                       break;
+               }
+               tomoyo_memory_free(p->element);
+               list_del(&p->list);
+               kfree(p);
+       }
+}
+
+static int tomoyo_gc_thread(void *unused)
+{
+       daemonize("GC for TOMOYO");
+       if (mutex_trylock(&tomoyo_gc_mutex)) {
+               int i;
+               for (i = 0; i < 10; i++) {
+                       tomoyo_collect_entry();
+                       if (list_empty(&tomoyo_gc_queue))
+                               break;
+                       synchronize_srcu(&tomoyo_ss);
+                       tomoyo_kfree_entry();
+               }
+               mutex_unlock(&tomoyo_gc_mutex);
+       }
+       do_exit(0);
+}
+
+void tomoyo_run_gc(void)
+{
+       struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL,
+                                                 "GC for TOMOYO");
+       if (!IS_ERR(task))
+               wake_up_process(task);
+}
index 18369d497eb80116da1d146ddc49268ae0b07229..c00df45c7ede899681344086e509c52f4b180bc0 100644 (file)
@@ -14,9 +14,8 @@
 #include <linux/mnt_namespace.h>
 #include <linux/fs_struct.h>
 #include <linux/hash.h>
-
+#include <linux/magic.h>
 #include "common.h"
-#include "realpath.h"
 
 /**
  * tomoyo_encode: Convert binary string to ascii string.
@@ -112,7 +111,7 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname,
                path_put(&ns_root);
                /* Prepend "/proc" prefix if using internal proc vfs mount. */
                if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) &&
-                   (strcmp(path->mnt->mnt_sb->s_type->name, "proc") == 0)) {
+                   (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
                        sp -= 5;
                        if (sp >= newname)
                                memcpy(sp, "/proc", 5);
@@ -149,12 +148,12 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname,
  *
  * Returns the realpath of the given @path on success, NULL otherwise.
  *
- * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * These functions use kzalloc(), so the caller must call kfree()
  * if these functions didn't return NULL.
  */
 char *tomoyo_realpath_from_path(struct path *path)
 {
-       char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer));
+       char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_KERNEL);
 
        BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
                     <= TOMOYO_MAX_PATHNAME_LEN - 1);
@@ -163,7 +162,7 @@ char *tomoyo_realpath_from_path(struct path *path)
        if (tomoyo_realpath_from_path2(path, buf,
                                       TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
                return buf;
-       tomoyo_free(buf);
+       kfree(buf);
        return NULL;
 }
 
@@ -206,98 +205,47 @@ char *tomoyo_realpath_nofollow(const char *pathname)
 }
 
 /* Memory allocated for non-string data. */
-static unsigned int tomoyo_allocated_memory_for_elements;
-/* Quota for holding non-string data. */
-static unsigned int tomoyo_quota_for_elements;
+static atomic_t tomoyo_policy_memory_size;
+/* Quota for holding policy. */
+static unsigned int tomoyo_quota_for_policy;
 
 /**
- * tomoyo_alloc_element - Allocate permanent memory for structures.
+ * tomoyo_memory_ok - Check memory quota.
  *
- * @size: Size in bytes.
+ * @ptr: Pointer to allocated memory.
  *
- * Returns pointer to allocated memory on success, NULL otherwise.
+ * Returns true on success, false otherwise.
  *
- * Memory has to be zeroed.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ * Caller holds tomoyo_policy_lock.
+ * Memory pointed by @ptr will be zeroed on success.
  */
-void *tomoyo_alloc_element(const unsigned int size)
+bool tomoyo_memory_ok(void *ptr)
 {
-       static char *buf;
-       static DEFINE_MUTEX(lock);
-       static unsigned int buf_used_len = PATH_MAX;
-       char *ptr = NULL;
-       /*Assumes sizeof(void *) >= sizeof(long) is true. */
-       const unsigned int word_aligned_size
-               = roundup(size, max(sizeof(void *), sizeof(long)));
-       if (word_aligned_size > PATH_MAX)
-               return NULL;
-       mutex_lock(&lock);
-       if (buf_used_len + word_aligned_size > PATH_MAX) {
-               if (!tomoyo_quota_for_elements ||
-                   tomoyo_allocated_memory_for_elements
-                   + PATH_MAX <= tomoyo_quota_for_elements)
-                       ptr = kzalloc(PATH_MAX, GFP_KERNEL);
-               if (!ptr) {
-                       printk(KERN_WARNING "ERROR: Out of memory "
-                              "for tomoyo_alloc_element().\n");
-                       if (!tomoyo_policy_loaded)
-                               panic("MAC Initialization failed.\n");
-               } else {
-                       buf = ptr;
-                       tomoyo_allocated_memory_for_elements += PATH_MAX;
-                       buf_used_len = word_aligned_size;
-                       ptr = buf;
-               }
-       } else if (word_aligned_size) {
-               int i;
-               ptr = buf + buf_used_len;
-               buf_used_len += word_aligned_size;
-               for (i = 0; i < word_aligned_size; i++) {
-                       if (!ptr[i])
-                               continue;
-                       printk(KERN_ERR "WARNING: Reserved memory was tainted! "
-                              "The system might go wrong.\n");
-                       ptr[i] = '\0';
-               }
+       int allocated_len = ptr ? ksize(ptr) : 0;
+       atomic_add(allocated_len, &tomoyo_policy_memory_size);
+       if (ptr && (!tomoyo_quota_for_policy ||
+                   atomic_read(&tomoyo_policy_memory_size)
+                   <= tomoyo_quota_for_policy)) {
+               memset(ptr, 0, allocated_len);
+               return true;
        }
-       mutex_unlock(&lock);
-       return ptr;
+       printk(KERN_WARNING "ERROR: Out of memory "
+              "for tomoyo_alloc_element().\n");
+       if (!tomoyo_policy_loaded)
+               panic("MAC Initialization failed.\n");
+       return false;
 }
 
-/* Memory allocated for string data in bytes. */
-static unsigned int tomoyo_allocated_memory_for_savename;
-/* Quota for holding string data in bytes. */
-static unsigned int tomoyo_quota_for_savename;
-
-/*
- * TOMOYO uses this hash only when appending a string into the string
- * table. Frequency of appending strings is very low. So we don't need
- * large (e.g. 64k) hash size. 256 will be sufficient.
- */
-#define TOMOYO_HASH_BITS  8
-#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
-
-/*
- * tomoyo_name_entry is a structure which is used for linking
- * "struct tomoyo_path_info" into tomoyo_name_list .
+/**
+ * tomoyo_memory_free - Free memory for elements.
  *
- * Since tomoyo_name_list manages a list of strings which are shared by
- * multiple processes (whereas "struct tomoyo_path_info" inside
- * "struct tomoyo_path_info_with_data" is not shared), a reference counter will
- * be added to "struct tomoyo_name_entry" rather than "struct tomoyo_path_info"
- * when TOMOYO starts supporting garbage collector.
+ * @ptr:  Pointer to allocated memory.
  */
-struct tomoyo_name_entry {
-       struct list_head list;
-       struct tomoyo_path_info entry;
-};
-
-/* Structure for available memory region. */
-struct tomoyo_free_memory_block_list {
-       struct list_head list;
-       char *ptr;             /* Pointer to a free area. */
-       int len;               /* Length of the area.     */
-};
+void tomoyo_memory_free(void *ptr)
+{
+       atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);
+       kfree(ptr);
+}
 
 /*
  * tomoyo_name_list is used for holding string data used by TOMOYO.
@@ -305,87 +253,58 @@ struct tomoyo_free_memory_block_list {
  * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of
  * "const struct tomoyo_path_info *".
  */
-static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+/* Lock for protecting tomoyo_name_list . */
+DEFINE_MUTEX(tomoyo_name_list_lock);
 
 /**
- * tomoyo_save_name - Allocate permanent memory for string data.
+ * tomoyo_get_name - Allocate permanent memory for string data.
  *
  * @name: The string to store into the permernent memory.
  *
  * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
- *
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
  */
-const struct tomoyo_path_info *tomoyo_save_name(const char *name)
+const struct tomoyo_path_info *tomoyo_get_name(const char *name)
 {
-       static LIST_HEAD(fmb_list);
-       static DEFINE_MUTEX(lock);
        struct tomoyo_name_entry *ptr;
        unsigned int hash;
-       /* fmb contains available size in bytes.
-          fmb is removed from the fmb_list when fmb->len becomes 0. */
-       struct tomoyo_free_memory_block_list *fmb;
        int len;
-       char *cp;
+       int allocated_len;
        struct list_head *head;
 
        if (!name)
                return NULL;
        len = strlen(name) + 1;
-       if (len > TOMOYO_MAX_PATHNAME_LEN) {
-               printk(KERN_WARNING "ERROR: Name too long "
-                      "for tomoyo_save_name().\n");
-               return NULL;
-       }
        hash = full_name_hash((const unsigned char *) name, len - 1);
        head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
-
-       mutex_lock(&lock);
+       mutex_lock(&tomoyo_name_list_lock);
        list_for_each_entry(ptr, head, list) {
-               if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
-                       goto out;
-       }
-       list_for_each_entry(fmb, &fmb_list, list) {
-               if (len <= fmb->len)
-                       goto ready;
+               if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+                       continue;
+               atomic_inc(&ptr->users);
+               goto out;
        }
-       if (!tomoyo_quota_for_savename ||
-           tomoyo_allocated_memory_for_savename + PATH_MAX
-           <= tomoyo_quota_for_savename)
-               cp = kzalloc(PATH_MAX, GFP_KERNEL);
-       else
-               cp = NULL;
-       fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
-       if (!cp || !fmb) {
-               kfree(cp);
-               kfree(fmb);
+       ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL);
+       allocated_len = ptr ? ksize(ptr) : 0;
+       if (!ptr || (tomoyo_quota_for_policy &&
+                    atomic_read(&tomoyo_policy_memory_size) + allocated_len
+                    > tomoyo_quota_for_policy)) {
+               kfree(ptr);
                printk(KERN_WARNING "ERROR: Out of memory "
-                      "for tomoyo_save_name().\n");
+                      "for tomoyo_get_name().\n");
                if (!tomoyo_policy_loaded)
                        panic("MAC Initialization failed.\n");
                ptr = NULL;
                goto out;
        }
-       tomoyo_allocated_memory_for_savename += PATH_MAX;
-       list_add(&fmb->list, &fmb_list);
-       fmb->ptr = cp;
-       fmb->len = PATH_MAX;
- ready:
-       ptr = tomoyo_alloc_element(sizeof(*ptr));
-       if (!ptr)
-               goto out;
-       ptr->entry.name = fmb->ptr;
-       memmove(fmb->ptr, name, len);
+       atomic_add(allocated_len, &tomoyo_policy_memory_size);
+       ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+       memmove((char *) ptr->entry.name, name, len);
+       atomic_set(&ptr->users, 1);
        tomoyo_fill_path_info(&ptr->entry);
-       fmb->ptr += len;
-       fmb->len -= len;
        list_add_tail(&ptr->list, head);
-       if (fmb->len == 0) {
-               list_del(&fmb->list);
-               kfree(fmb);
-       }
  out:
-       mutex_unlock(&lock);
+       mutex_unlock(&tomoyo_name_list_lock);
        return ptr ? &ptr->entry : NULL;
 }
 
@@ -400,45 +319,14 @@ void __init tomoyo_realpath_init(void)
        for (i = 0; i < TOMOYO_MAX_HASH; i++)
                INIT_LIST_HEAD(&tomoyo_name_list[i]);
        INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
-       tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
-       list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
-       down_read(&tomoyo_domain_list_lock);
+       tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
+       /*
+        * tomoyo_read_lock() is not needed because this function is
+        * called before the first "delete" request.
+        */
+       list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
        if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
                panic("Can't register tomoyo_kernel_domain");
-       up_read(&tomoyo_domain_list_lock);
-}
-
-/* Memory allocated for temporary purpose. */
-static atomic_t tomoyo_dynamic_memory_size;
-
-/**
- * tomoyo_alloc - Allocate memory for temporary purpose.
- *
- * @size: Size in bytes.
- *
- * Returns pointer to allocated memory on success, NULL otherwise.
- */
-void *tomoyo_alloc(const size_t size)
-{
-       void *p = kzalloc(size, GFP_KERNEL);
-       if (p)
-               atomic_add(ksize(p), &tomoyo_dynamic_memory_size);
-       return p;
-}
-
-/**
- * tomoyo_free - Release memory allocated by tomoyo_alloc().
- *
- * @p: Pointer returned by tomoyo_alloc(). May be NULL.
- *
- * Returns nothing.
- */
-void tomoyo_free(const void *p)
-{
-       if (p) {
-               atomic_sub(ksize(p), &tomoyo_dynamic_memory_size);
-               kfree(p);
-       }
 }
 
 /**
@@ -451,32 +339,19 @@ void tomoyo_free(const void *p)
 int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
 {
        if (!head->read_eof) {
-               const unsigned int shared
-                       = tomoyo_allocated_memory_for_savename;
-               const unsigned int private
-                       = tomoyo_allocated_memory_for_elements;
-               const unsigned int dynamic
-                       = atomic_read(&tomoyo_dynamic_memory_size);
+               const unsigned int policy
+                       = atomic_read(&tomoyo_policy_memory_size);
                char buffer[64];
 
                memset(buffer, 0, sizeof(buffer));
-               if (tomoyo_quota_for_savename)
-                       snprintf(buffer, sizeof(buffer) - 1,
-                                "   (Quota: %10u)",
-                                tomoyo_quota_for_savename);
-               else
-                       buffer[0] = '\0';
-               tomoyo_io_printf(head, "Shared:  %10u%s\n", shared, buffer);
-               if (tomoyo_quota_for_elements)
+               if (tomoyo_quota_for_policy)
                        snprintf(buffer, sizeof(buffer) - 1,
                                 "   (Quota: %10u)",
-                                tomoyo_quota_for_elements);
+                                tomoyo_quota_for_policy);
                else
                        buffer[0] = '\0';
-               tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer);
-               tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic);
-               tomoyo_io_printf(head, "Total:   %10u\n",
-                                shared + private + dynamic);
+               tomoyo_io_printf(head, "Policy:  %10u%s\n", policy, buffer);
+               tomoyo_io_printf(head, "Total:   %10u\n", policy);
                head->read_eof = true;
        }
        return 0;
@@ -494,9 +369,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
        char *data = head->write_buf;
        unsigned int size;
 
-       if (sscanf(data, "Shared: %u", &size) == 1)
-               tomoyo_quota_for_savename = size;
-       else if (sscanf(data, "Private: %u", &size) == 1)
-               tomoyo_quota_for_elements = size;
+       if (sscanf(data, "Policy: %u", &size) == 1)
+               tomoyo_quota_for_policy = size;
        return 0;
 }
diff --git a/security/tomoyo/realpath.h b/security/tomoyo/realpath.h
deleted file mode 100644 (file)
index 78217a3..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * security/tomoyo/realpath.h
- *
- * Get the canonicalized absolute pathnames. The basis for TOMOYO.
- *
- * Copyright (C) 2005-2009  NTT DATA CORPORATION
- *
- * Version: 2.2.0   2009/04/01
- *
- */
-
-#ifndef _SECURITY_TOMOYO_REALPATH_H
-#define _SECURITY_TOMOYO_REALPATH_H
-
-struct path;
-struct tomoyo_path_info;
-struct tomoyo_io_buffer;
-
-/* Convert binary string to ascii string. */
-int tomoyo_encode(char *buffer, int buflen, const char *str);
-
-/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
-int tomoyo_realpath_from_path2(struct path *path, char *newname,
-                              int newname_len);
-
-/*
- * Returns realpath(3) of the given pathname but ignores chroot'ed root.
- * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
- * if these functions didn't return NULL.
- */
-char *tomoyo_realpath(const char *pathname);
-/*
- * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
- */
-char *tomoyo_realpath_nofollow(const char *pathname);
-/* Same with tomoyo_realpath() except that the pathname is already solved. */
-char *tomoyo_realpath_from_path(struct path *path);
-
-/*
- * Allocate memory for ACL entry.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
- */
-void *tomoyo_alloc_element(const unsigned int size);
-
-/*
- * Keep the given name on the RAM.
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
- */
-const struct tomoyo_path_info *tomoyo_save_name(const char *name);
-
-/* Allocate memory for temporary use (e.g. permission checks). */
-void *tomoyo_alloc(const size_t size);
-
-/* Free memory allocated by tomoyo_alloc(). */
-void tomoyo_free(const void *p);
-
-/* Check for memory usage. */
-int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
-
-/* Set memory quota. */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
-
-/* Initialize realpath related code. */
-void __init tomoyo_realpath_init(void);
-
-#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */
index 2aceebf5f3546f1dffa2594d04fa12cc0702923b..dedd97d0c163675a116ec48c95acd119b8547400 100644 (file)
@@ -11,8 +11,6 @@
 
 #include <linux/security.h>
 #include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
 
 static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
 {
@@ -23,21 +21,23 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
 static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
                               gfp_t gfp)
 {
-       /*
-        * Since "struct tomoyo_domain_info *" is a sharable pointer,
-        * we don't need to duplicate.
-        */
-       new->security = old->security;
+       struct tomoyo_domain_info *domain = old->security;
+       new->security = domain;
+       if (domain)
+               atomic_inc(&domain->users);
        return 0;
 }
 
 static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
 {
-       /*
-        * Since "struct tomoyo_domain_info *" is a sharable pointer,
-        * we don't need to duplicate.
-        */
-       new->security = old->security;
+       tomoyo_cred_prepare(new, old, 0);
+}
+
+static void tomoyo_cred_free(struct cred *cred)
+{
+       struct tomoyo_domain_info *domain = cred->security;
+       if (domain)
+               atomic_dec(&domain->users);
 }
 
 static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
@@ -60,6 +60,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
         */
        if (!tomoyo_policy_loaded)
                tomoyo_load_policy(bprm->filename);
+       /*
+        * Release reference to "struct tomoyo_domain_info" stored inside
+        * "bprm->cred->security". New reference to "struct tomoyo_domain_info"
+        * stored inside "bprm->cred->security" will be acquired later inside
+        * tomoyo_find_next_domain().
+        */
+       atomic_dec(&((struct tomoyo_domain_info *)
+                    bprm->cred->security)->users);
        /*
         * Tell tomoyo_bprm_check_security() is called for the first time of an
         * execve operation.
@@ -76,8 +84,12 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
         * Execute permission is checked against pathname passed to do_execve()
         * using current domain.
         */
-       if (!domain)
-               return tomoyo_find_next_domain(bprm);
+       if (!domain) {
+               const int idx = tomoyo_read_lock();
+               const int err = tomoyo_find_next_domain(bprm);
+               tomoyo_read_unlock(idx);
+               return err;
+       }
        /*
         * Read permission is checked against interpreters using next domain.
         */
@@ -87,67 +99,56 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
 static int tomoyo_path_truncate(struct path *path, loff_t length,
                                unsigned int time_attrs)
 {
-       return tomoyo_check_1path_perm(tomoyo_domain(),
-                                      TOMOYO_TYPE_TRUNCATE_ACL,
-                                      path);
+       return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
 }
 
 static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
 {
        struct path path = { parent->mnt, dentry };
-       return tomoyo_check_1path_perm(tomoyo_domain(),
-                                      TOMOYO_TYPE_UNLINK_ACL,
-                                      &path);
+       return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path);
 }
 
 static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
                             int mode)
 {
        struct path path = { parent->mnt, dentry };
-       return tomoyo_check_1path_perm(tomoyo_domain(),
-                                      TOMOYO_TYPE_MKDIR_ACL,
-                                      &path);
+       return tomoyo_path_perm(TOMOYO_TYPE_MKDIR, &path);
 }
 
 static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
 {
        struct path path = { parent->mnt, dentry };
-       return tomoyo_check_1path_perm(tomoyo_domain(),
-                                      TOMOYO_TYPE_RMDIR_ACL,
-                                      &path);
+       return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path);
 }
 
 static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
                               const char *old_name)
 {
        struct path path = { parent->mnt, dentry };
-       return tomoyo_check_1path_perm(tomoyo_domain(),
-                                      TOMOYO_TYPE_SYMLINK_ACL,
-                                      &path);
+       return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path);
 }
 
 static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
                             int mode, unsigned int dev)
 {
        struct path path = { parent->mnt, dentry };
-       int type = TOMOYO_TYPE_CREATE_ACL;
+       int type = TOMOYO_TYPE_CREATE;
 
        switch (mode & S_IFMT) {
        case S_IFCHR:
-               type = TOMOYO_TYPE_MKCHAR_ACL;
+               type = TOMOYO_TYPE_MKCHAR;
                break;
        case S_IFBLK:
-               type = TOMOYO_TYPE_MKBLOCK_ACL;
+               type = TOMOYO_TYPE_MKBLOCK;
                break;
        case S_IFIFO:
-               type = TOMOYO_TYPE_MKFIFO_ACL;
+               type = TOMOYO_TYPE_MKFIFO;
                break;
        case S_IFSOCK:
-               type = TOMOYO_TYPE_MKSOCK_ACL;
+               type = TOMOYO_TYPE_MKSOCK;
                break;
        }
-       return tomoyo_check_1path_perm(tomoyo_domain(),
-                                      type, &path);
+       return tomoyo_path_perm(type, &path);
 }
 
 static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
@@ -155,9 +156,7 @@ static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
 {
        struct path path1 = { new_dir->mnt, old_dentry };
        struct path path2 = { new_dir->mnt, new_dentry };
-       return tomoyo_check_2path_perm(tomoyo_domain(),
-                                      TOMOYO_TYPE_LINK_ACL,
-                                      &path1, &path2);
+       return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
 }
 
 static int tomoyo_path_rename(struct path *old_parent,
@@ -167,16 +166,14 @@ static int tomoyo_path_rename(struct path *old_parent,
 {
        struct path path1 = { old_parent->mnt, old_dentry };
        struct path path2 = { new_parent->mnt, new_dentry };
-       return tomoyo_check_2path_perm(tomoyo_domain(),
-                                      TOMOYO_TYPE_RENAME_ACL,
-                                      &path1, &path2);
+       return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
 }
 
 static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
                             unsigned long arg)
 {
        if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
-               return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
+               return tomoyo_check_rewrite_permission(file);
        return 0;
 }
 
@@ -189,6 +186,51 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
        return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
 }
 
+static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
+                            unsigned long arg)
+{
+       return tomoyo_path_perm(TOMOYO_TYPE_IOCTL, &file->f_path);
+}
+
+static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+                            mode_t mode)
+{
+       struct path path = { mnt, dentry };
+       return tomoyo_path_perm(TOMOYO_TYPE_CHMOD, &path);
+}
+
+static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+       int error = 0;
+       if (uid != (uid_t) -1)
+               error = tomoyo_path_perm(TOMOYO_TYPE_CHOWN, path);
+       if (!error && gid != (gid_t) -1)
+               error = tomoyo_path_perm(TOMOYO_TYPE_CHGRP, path);
+       return error;
+}
+
+static int tomoyo_path_chroot(struct path *path)
+{
+       return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path);
+}
+
+static int tomoyo_sb_mount(char *dev_name, struct path *path,
+                          char *type, unsigned long flags, void *data)
+{
+       return tomoyo_path_perm(TOMOYO_TYPE_MOUNT, path);
+}
+
+static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
+{
+       struct path path = { mnt, mnt->mnt_root };
+       return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path);
+}
+
+static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+       return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
+}
+
 /*
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
@@ -198,6 +240,7 @@ static struct security_operations tomoyo_security_ops = {
        .cred_alloc_blank    = tomoyo_cred_alloc_blank,
        .cred_prepare        = tomoyo_cred_prepare,
        .cred_transfer       = tomoyo_cred_transfer,
+       .cred_free           = tomoyo_cred_free,
        .bprm_set_creds      = tomoyo_bprm_set_creds,
        .bprm_check_security = tomoyo_bprm_check_security,
        .file_fcntl          = tomoyo_file_fcntl,
@@ -210,8 +253,18 @@ static struct security_operations tomoyo_security_ops = {
        .path_mknod          = tomoyo_path_mknod,
        .path_link           = tomoyo_path_link,
        .path_rename         = tomoyo_path_rename,
+       .file_ioctl          = tomoyo_file_ioctl,
+       .path_chmod          = tomoyo_path_chmod,
+       .path_chown          = tomoyo_path_chown,
+       .path_chroot         = tomoyo_path_chroot,
+       .sb_mount            = tomoyo_sb_mount,
+       .sb_umount           = tomoyo_sb_umount,
+       .sb_pivotroot        = tomoyo_sb_pivotroot,
 };
 
+/* Lock for GC. */
+struct srcu_struct tomoyo_ss;
+
 static int __init tomoyo_init(void)
 {
        struct cred *cred = (struct cred *) current_cred();
@@ -219,7 +272,8 @@ static int __init tomoyo_init(void)
        if (!security_module_enable(&tomoyo_security_ops))
                return 0;
        /* register ourselves with the security framework */
-       if (register_security(&tomoyo_security_ops))
+       if (register_security(&tomoyo_security_ops) ||
+           init_srcu_struct(&tomoyo_ss))
                panic("Failure registering TOMOYO Linux");
        printk(KERN_INFO "TOMOYO Linux initialized\n");
        cred->security = &tomoyo_kernel_domain;
diff --git a/security/tomoyo/tomoyo.h b/security/tomoyo/tomoyo.h
deleted file mode 100644 (file)
index ed75832..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * security/tomoyo/tomoyo.h
- *
- * Implementation of the Domain-Based Mandatory Access Control.
- *
- * Copyright (C) 2005-2009  NTT DATA CORPORATION
- *
- * Version: 2.2.0   2009/04/01
- *
- */
-
-#ifndef _SECURITY_TOMOYO_TOMOYO_H
-#define _SECURITY_TOMOYO_TOMOYO_H
-
-struct tomoyo_path_info;
-struct path;
-struct inode;
-struct linux_binprm;
-struct pt_regs;
-
-int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
-                          const struct tomoyo_path_info *filename);
-int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
-                                struct path *path, const int flag);
-int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
-                           const u8 operation, struct path *path);
-int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
-                           const u8 operation, struct path *path1,
-                           struct path *path2);
-int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
-                                   struct file *filp);
-int tomoyo_find_next_domain(struct linux_binprm *bprm);
-
-/* Index numbers for Access Controls. */
-
-#define TOMOYO_TYPE_SINGLE_PATH_ACL                 0
-#define TOMOYO_TYPE_DOUBLE_PATH_ACL                 1
-
-/* Index numbers for File Controls. */
-
-/*
- * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
- * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
- * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
- * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
- * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
- * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
- */
-
-#define TOMOYO_TYPE_READ_WRITE_ACL    0
-#define TOMOYO_TYPE_EXECUTE_ACL       1
-#define TOMOYO_TYPE_READ_ACL          2
-#define TOMOYO_TYPE_WRITE_ACL         3
-#define TOMOYO_TYPE_CREATE_ACL        4
-#define TOMOYO_TYPE_UNLINK_ACL        5
-#define TOMOYO_TYPE_MKDIR_ACL         6
-#define TOMOYO_TYPE_RMDIR_ACL         7
-#define TOMOYO_TYPE_MKFIFO_ACL        8
-#define TOMOYO_TYPE_MKSOCK_ACL        9
-#define TOMOYO_TYPE_MKBLOCK_ACL      10
-#define TOMOYO_TYPE_MKCHAR_ACL       11
-#define TOMOYO_TYPE_TRUNCATE_ACL     12
-#define TOMOYO_TYPE_SYMLINK_ACL      13
-#define TOMOYO_TYPE_REWRITE_ACL      14
-#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
-
-#define TOMOYO_TYPE_LINK_ACL         0
-#define TOMOYO_TYPE_RENAME_ACL       1
-#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
-
-#define TOMOYO_DOMAINPOLICY          0
-#define TOMOYO_EXCEPTIONPOLICY       1
-#define TOMOYO_DOMAIN_STATUS         2
-#define TOMOYO_PROCESS_STATUS        3
-#define TOMOYO_MEMINFO               4
-#define TOMOYO_SELFDOMAIN            5
-#define TOMOYO_VERSION               6
-#define TOMOYO_PROFILE               7
-#define TOMOYO_MANAGER               8
-
-extern struct tomoyo_domain_info tomoyo_kernel_domain;
-
-static inline struct tomoyo_domain_info *tomoyo_domain(void)
-{
-       return current_cred()->security;
-}
-
-static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
-                                                           *task)
-{
-       return task_cred_xxx(task, security);
-}
-
-#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */