Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
Linus Torvalds [Thu, 28 Jul 2011 02:26:38 +0000 (19:26 -0700)]
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (54 commits)
  tpm_nsc: Fix bug when loading multiple TPM drivers
  tpm: Move tpm_tis_reenable_interrupts out of CONFIG_PNP block
  tpm: Fix compilation warning when CONFIG_PNP is not defined
  TOMOYO: Update kernel-doc.
  tpm: Fix a typo
  tpm_tis: Probing function for Intel iTPM bug
  tpm_tis: Fix the probing for interrupts
  tpm_tis: Delay ACPI S3 suspend while the TPM is busy
  tpm_tis: Re-enable interrupts upon (S3) resume
  tpm: Fix display of data in pubek sysfs entry
  tpm_tis: Add timeouts sysfs entry
  tpm: Adjust interface timeouts if they are too small
  tpm: Use interface timeouts returned from the TPM
  tpm_tis: Introduce durations sysfs entry
  tpm: Adjust the durations if they are too small
  tpm: Use durations returned from TPM
  TOMOYO: Enable conditional ACL.
  TOMOYO: Allow using argv[]/envp[] of execve() as conditions.
  TOMOYO: Allow using executable's realpath and symlink's target as conditions.
  TOMOYO: Allow using owner/group etc. of file objects as conditions.
  ...

Fix up trivial conflict in security/tomoyo/realpath.c

36 files changed:
Documentation/security/keys-ecryptfs.txt [new file with mode: 0644]
Documentation/security/keys-trusted-encrypted.txt
MAINTAINERS
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_nsc.c
drivers/char/tpm/tpm_tis.c
fs/ecryptfs/ecryptfs_kernel.h
fs/ecryptfs/keystore.c
include/keys/encrypted-type.h
include/linux/ecryptfs.h [new file with mode: 0644]
kernel/cgroup.c
security/apparmor/domain.c
security/apparmor/lsm.c
security/keys/Makefile
security/keys/ecryptfs_format.c [new file with mode: 0644]
security/keys/ecryptfs_format.h [new file with mode: 0644]
security/keys/encrypted.c
security/keys/request_key_auth.c
security/tomoyo/Kconfig
security/tomoyo/Makefile
security/tomoyo/audit.c [new file with mode: 0644]
security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/condition.c [new file with mode: 0644]
security/tomoyo/domain.c
security/tomoyo/file.c
security/tomoyo/gc.c
security/tomoyo/group.c
security/tomoyo/load_policy.c
security/tomoyo/memory.c
security/tomoyo/mount.c
security/tomoyo/realpath.c
security/tomoyo/securityfs_if.c
security/tomoyo/tomoyo.c
security/tomoyo/util.c

diff --git a/Documentation/security/keys-ecryptfs.txt b/Documentation/security/keys-ecryptfs.txt
new file mode 100644 (file)
index 0000000..c3bbeba
--- /dev/null
@@ -0,0 +1,68 @@
+               Encrypted keys for the eCryptfs filesystem
+
+ECryptfs is a stacked filesystem which transparently encrypts and decrypts each
+file using a randomly generated File Encryption Key (FEK).
+
+Each FEK is in turn encrypted with a File Encryption Key Encryption Key (FEFEK)
+either in kernel space or in user space with a daemon called 'ecryptfsd'.  In
+the former case the operation is performed directly by the kernel CryptoAPI
+using a key, the FEFEK, derived from a user prompted passphrase;  in the latter
+the FEK is encrypted by 'ecryptfsd' with the help of external libraries in order
+to support other mechanisms like public key cryptography, PKCS#11 and TPM based
+operations.
+
+The data structure defined by eCryptfs to contain information required for the
+FEK decryption is called authentication token and, currently, can be stored in a
+kernel key of the 'user' type, inserted in the user's session specific keyring
+by the userspace utility 'mount.ecryptfs' shipped with the package
+'ecryptfs-utils'.
+
+The 'encrypted' key type has been extended with the introduction of the new
+format 'ecryptfs' in order to be used in conjunction with the eCryptfs
+filesystem.  Encrypted keys of the newly introduced format store an
+authentication token in its payload with a FEFEK randomly generated by the
+kernel and protected by the parent master key.
+
+In order to avoid known-plaintext attacks, the datablob obtained through
+commands 'keyctl print' or 'keyctl pipe' does not contain the overall
+authentication token, which content is well known, but only the FEFEK in
+encrypted form.
+
+The eCryptfs filesystem may really benefit from using encrypted keys in that the
+required key can be securely generated by an Administrator and provided at boot
+time after the unsealing of a 'trusted' key in order to perform the mount in a
+controlled environment.  Another advantage is that the key is not exposed to
+threats of malicious software, because it is available in clear form only at
+kernel level.
+
+Usage:
+   keyctl add encrypted name "new ecryptfs key-type:master-key-name keylen" ring
+   keyctl add encrypted name "load hex_blob" ring
+   keyctl update keyid "update key-type:master-key-name"
+
+name:= '<16 hexadecimal characters>'
+key-type:= 'trusted' | 'user'
+keylen:= 64
+
+
+Example of encrypted key usage with the eCryptfs filesystem:
+
+Create an encrypted key "1000100010001000" of length 64 bytes with format
+'ecryptfs' and save it using a previously loaded user key "test":
+
+    $ keyctl add encrypted 1000100010001000 "new ecryptfs user:test 64" @u
+    19184530
+
+    $ keyctl print 19184530
+    ecryptfs user:test 64 490045d4bfe48c99f0d465fbbbb79e7500da954178e2de0697
+    dd85091f5450a0511219e9f7cd70dcd498038181466f78ac8d4c19504fcc72402bfc41c2
+    f253a41b7507ccaa4b2b03fff19a69d1cc0b16e71746473f023a95488b6edfd86f7fdd40
+    9d292e4bacded1258880122dd553a661
+
+    $ keyctl pipe 19184530 > ecryptfs.blob
+
+Mount an eCryptfs filesystem using the created encrypted key "1000100010001000"
+into the '/secret' directory:
+
+    $ mount -i -t ecryptfs -oecryptfs_sig=1000100010001000,\
+      ecryptfs_cipher=aes,ecryptfs_key_bytes=32 /secret /secret
index 8fb79bc..5f50cca 100644 (file)
@@ -53,12 +53,19 @@ they are only as secure as the user key encrypting them.  The master user key
 should therefore be loaded in as secure a way as possible, preferably early in
 boot.
 
+The decrypted portion of encrypted keys can contain either a simple symmetric
+key or a more complex structure. The format of the more complex structure is
+application specific, which is identified by 'format'.
+
 Usage:
-  keyctl add encrypted name "new key-type:master-key-name keylen" ring
-  keyctl add encrypted name "load hex_blob" ring
-  keyctl update keyid "update key-type:master-key-name"
+    keyctl add encrypted name "new [format] key-type:master-key-name keylen"
+        ring
+    keyctl add encrypted name "load hex_blob" ring
+    keyctl update keyid "update key-type:master-key-name"
+
+format:= 'default | ecryptfs'
+key-type:= 'trusted' | 'user'
 
-where 'key-type' is either 'trusted' or 'user'.
 
 Examples of trusted and encrypted key usage:
 
@@ -114,15 +121,25 @@ Reseal a trusted key under new pcr values:
     7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
     df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8
 
-Create and save an encrypted key "evm" using the above trusted key "kmk":
+The initial consumer of trusted keys is EVM, which at boot time needs a high
+quality symmetric key for HMAC protection of file metadata.  The use of a
+trusted key provides strong guarantees that the EVM key has not been
+compromised by a user level problem, and when sealed to specific boot PCR
+values, protects against boot and offline attacks.  Create and save an
+encrypted key "evm" using the above trusted key "kmk":
 
+option 1: omitting 'format'
     $ keyctl add encrypted evm "new trusted:kmk 32" @u
     159771175
 
+option 2: explicitly defining 'format' as 'default'
+    $ keyctl add encrypted evm "new default trusted:kmk 32" @u
+    159771175
+
     $ keyctl print 159771175
-    trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55
-    be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64
-    5972dcb82ab2dde83376d82b2e3c09ffc
+    default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
+    82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
+    24717c64 5972dcb82ab2dde83376d82b2e3c09ffc
 
     $ keyctl pipe 159771175 > evm.blob
 
@@ -132,14 +149,11 @@ Load an encrypted key "evm" from saved blob:
     831684262
 
     $ keyctl print 831684262
-    trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55
-    be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64
-    5972dcb82ab2dde83376d82b2e3c09ffc
-
-
-The initial consumer of trusted keys is EVM, which at boot time needs a high
-quality symmetric key for HMAC protection of file metadata.  The use of a
-trusted key provides strong guarantees that the EVM key has not been
-compromised by a user level problem, and when sealed to specific boot PCR
-values, protects against boot and offline attacks.  Other uses for trusted and
-encrypted keys, such as for disk and file encryption are anticipated.
+    default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
+    82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
+    24717c64 5972dcb82ab2dde83376d82b2e3c09ffc
+
+Other uses for trusted and encrypted keys, such as for disk and file encryption
+are anticipated.  In particular the new format 'ecryptfs' has been defined in
+in order to use encrypted keys to mount an eCryptfs filesystem.  More details
+about the usage can be found in the file 'Documentation/keys-ecryptfs.txt'.
index 7b2e9e8..1d2e79d 100644 (file)
@@ -6408,7 +6408,7 @@ L:        tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English)
 L:     tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
 L:     tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
 W:     http://tomoyo.sourceforge.jp/
-T:     quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.3.x/tomoyo-lsm/patches/
+T:     quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.4.x/tomoyo-lsm/patches/
 S:     Maintained
 F:     security/tomoyo/
 
index 7beb0e2..caf8012 100644 (file)
@@ -534,6 +534,7 @@ void tpm_get_timeouts(struct tpm_chip *chip)
        struct duration_t *duration_cap;
        ssize_t rc;
        u32 timeout;
+       unsigned int scale = 1;
 
        tpm_cmd.header.in = tpm_getcap_header;
        tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
@@ -545,24 +546,30 @@ void tpm_get_timeouts(struct tpm_chip *chip)
        if (rc)
                goto duration;
 
-       if (be32_to_cpu(tpm_cmd.header.out.length)
-           != 4 * sizeof(u32))
-               goto duration;
+       if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+           be32_to_cpu(tpm_cmd.header.out.length)
+           != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
+               return;
 
        timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
        /* Don't overwrite default if value is 0 */
        timeout = be32_to_cpu(timeout_cap->a);
+       if (timeout && timeout < 1000) {
+               /* timeouts in msec rather usec */
+               scale = 1000;
+               chip->vendor.timeout_adjusted = true;
+       }
        if (timeout)
-               chip->vendor.timeout_a = usecs_to_jiffies(timeout);
+               chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
        timeout = be32_to_cpu(timeout_cap->b);
        if (timeout)
-               chip->vendor.timeout_b = usecs_to_jiffies(timeout);
+               chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
        timeout = be32_to_cpu(timeout_cap->c);
        if (timeout)
-               chip->vendor.timeout_c = usecs_to_jiffies(timeout);
+               chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
        timeout = be32_to_cpu(timeout_cap->d);
        if (timeout)
-               chip->vendor.timeout_d = usecs_to_jiffies(timeout);
+               chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
 
 duration:
        tpm_cmd.header.in = tpm_getcap_header;
@@ -575,23 +582,31 @@ duration:
        if (rc)
                return;
 
-       if (be32_to_cpu(tpm_cmd.header.out.return_code)
-           != 3 * sizeof(u32))
+       if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+           be32_to_cpu(tpm_cmd.header.out.length)
+           != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
                return;
+
        duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
        chip->vendor.duration[TPM_SHORT] =
            usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
+       chip->vendor.duration[TPM_MEDIUM] =
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
+       chip->vendor.duration[TPM_LONG] =
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+
        /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
         * value wrong and apparently reports msecs rather than usecs. So we
         * fix up the resulting too-small TPM_SHORT value to make things work.
+        * We also scale the TPM_MEDIUM and -_LONG values by 1000.
         */
-       if (chip->vendor.duration[TPM_SHORT] < (HZ/100))
+       if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
                chip->vendor.duration[TPM_SHORT] = HZ;
-
-       chip->vendor.duration[TPM_MEDIUM] =
-           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
-       chip->vendor.duration[TPM_LONG] =
-           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+               chip->vendor.duration[TPM_MEDIUM] *= 1000;
+               chip->vendor.duration[TPM_LONG] *= 1000;
+               chip->vendor.duration_adjusted = true;
+               dev_info(chip->dev, "Adjusting TPM timeout parameters.");
+       }
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 
@@ -600,7 +615,7 @@ void tpm_continue_selftest(struct tpm_chip *chip)
        u8 data[] = {
                0, 193,                 /* TPM_TAG_RQU_COMMAND */
                0, 0, 0, 10,            /* length */
-               0, 0, 0, 83,            /* TPM_ORD_GetCapability */
+               0, 0, 0, 83,            /* TPM_ORD_ContinueSelfTest */
        };
 
        tpm_transmit(chip, data, sizeof(data));
@@ -863,18 +878,24 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
        data = tpm_cmd.params.readpubek_out_buffer;
        str +=
            sprintf(str,
-                   "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
-                   "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X"
-                   " %02X %02X %02X %02X %02X %02X %02X %02X\n"
-                   "Modulus length: %d\nModulus: \n",
-                   data[10], data[11], data[12], data[13], data[14],
-                   data[15], data[16], data[17], data[22], data[23],
-                   data[24], data[25], data[26], data[27], data[28],
-                   data[29], data[30], data[31], data[32], data[33],
-                   be32_to_cpu(*((__be32 *) (data + 34))));
+                   "Algorithm: %02X %02X %02X %02X\n"
+                   "Encscheme: %02X %02X\n"
+                   "Sigscheme: %02X %02X\n"
+                   "Parameters: %02X %02X %02X %02X "
+                   "%02X %02X %02X %02X "
+                   "%02X %02X %02X %02X\n"
+                   "Modulus length: %d\n"
+                   "Modulus:\n",
+                   data[0], data[1], data[2], data[3],
+                   data[4], data[5],
+                   data[6], data[7],
+                   data[12], data[13], data[14], data[15],
+                   data[16], data[17], data[18], data[19],
+                   data[20], data[21], data[22], data[23],
+                   be32_to_cpu(*((__be32 *) (data + 24))));
 
        for (i = 0; i < 256; i++) {
-               str += sprintf(str, "%02X ", data[i + 38]);
+               str += sprintf(str, "%02X ", data[i + 28]);
                if ((i + 1) % 16 == 0)
                        str += sprintf(str, "\n");
        }
@@ -937,6 +958,35 @@ ssize_t tpm_show_caps_1_2(struct device * dev,
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
 
+ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d %d %d [%s]\n",
+                      jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+                      jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+                      jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
+                      chip->vendor.duration_adjusted
+                      ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_durations);
+
+ssize_t tpm_show_timeouts(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d %d %d %d [%s]\n",
+                      jiffies_to_usecs(chip->vendor.timeout_a),
+                      jiffies_to_usecs(chip->vendor.timeout_b),
+                      jiffies_to_usecs(chip->vendor.timeout_c),
+                      jiffies_to_usecs(chip->vendor.timeout_d),
+                      chip->vendor.timeout_adjusted
+                      ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_timeouts);
+
 ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
                        const char *buf, size_t count)
 {
index 72ddb03..9c4163c 100644 (file)
@@ -56,6 +56,10 @@ extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr,
                                char *);
 extern ssize_t tpm_show_temp_deactivated(struct device *,
                                         struct device_attribute *attr, char *);
+extern ssize_t tpm_show_durations(struct device *,
+                                 struct device_attribute *attr, char *);
+extern ssize_t tpm_show_timeouts(struct device *,
+                                struct device_attribute *attr, char *);
 
 struct tpm_chip;
 
@@ -67,6 +71,7 @@ struct tpm_vendor_specific {
        unsigned long base;             /* TPM base address */
 
        int irq;
+       int probed_irq;
 
        int region_size;
        int have_region;
@@ -81,7 +86,9 @@ struct tpm_vendor_specific {
        struct list_head list;
        int locality;
        unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
+       bool timeout_adjusted;
        unsigned long duration[3]; /* jiffies */
+       bool duration_adjusted;
 
        wait_queue_head_t read_queue;
        wait_queue_head_t int_queue;
index a605cb7..82facc9 100644 (file)
@@ -330,12 +330,12 @@ static int __init init_nsc(void)
        pdev->dev.driver = &nsc_drv.driver;
        pdev->dev.release = tpm_nsc_remove;
 
-       if ((rc = platform_device_register(pdev)) < 0)
-               goto err_free_dev;
+       if ((rc = platform_device_add(pdev)) < 0)
+               goto err_put_dev;
 
        if (request_region(base, 2, "tpm_nsc0") == NULL ) {
                rc = -EBUSY;
-               goto err_unreg_dev;
+               goto err_del_dev;
        }
 
        if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) {
@@ -382,10 +382,10 @@ static int __init init_nsc(void)
 
 err_rel_reg:
        release_region(base, 2);
-err_unreg_dev:
-       platform_device_unregister(pdev);
-err_free_dev:
-       kfree(pdev);
+err_del_dev:
+       platform_device_del(pdev);
+err_put_dev:
+       platform_device_put(pdev);
 err_unreg_drv:
        platform_driver_unregister(&nsc_drv);
        return rc;
index dd21df5..7fc2f10 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/wait.h>
 #include <linux/acpi.h>
+#include <linux/freezer.h>
 #include "tpm.h"
 
 #define TPM_HEADER_SIZE 10
@@ -79,7 +80,7 @@ enum tis_defaults {
 static LIST_HEAD(tis_chips);
 static DEFINE_SPINLOCK(tis_lock);
 
-#ifdef CONFIG_ACPI
+#ifdef CONFIG_PNP
 static int is_itpm(struct pnp_dev *dev)
 {
        struct acpi_device *acpi = pnp_acpi_device(dev);
@@ -92,11 +93,6 @@ static int is_itpm(struct pnp_dev *dev)
 
        return 0;
 }
-#else
-static int is_itpm(struct pnp_dev *dev)
-{
-       return 0;
-}
 #endif
 
 static int check_locality(struct tpm_chip *chip, int l)
@@ -120,7 +116,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force)
 
 static int request_locality(struct tpm_chip *chip, int l)
 {
-       unsigned long stop;
+       unsigned long stop, timeout;
        long rc;
 
        if (check_locality(chip, l) >= 0)
@@ -129,17 +125,25 @@ static int request_locality(struct tpm_chip *chip, int l)
        iowrite8(TPM_ACCESS_REQUEST_USE,
                 chip->vendor.iobase + TPM_ACCESS(l));
 
+       stop = jiffies + chip->vendor.timeout_a;
+
        if (chip->vendor.irq) {
+again:
+               timeout = stop - jiffies;
+               if ((long)timeout <= 0)
+                       return -1;
                rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
                                                      (check_locality
                                                       (chip, l) >= 0),
-                                                     chip->vendor.timeout_a);
+                                                     timeout);
                if (rc > 0)
                        return l;
-
+               if (rc == -ERESTARTSYS && freezing(current)) {
+                       clear_thread_flag(TIF_SIGPENDING);
+                       goto again;
+               }
        } else {
                /* wait for burstcount */
-               stop = jiffies + chip->vendor.timeout_a;
                do {
                        if (check_locality(chip, l) >= 0)
                                return l;
@@ -196,15 +200,24 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
        if ((status & mask) == mask)
                return 0;
 
+       stop = jiffies + timeout;
+
        if (chip->vendor.irq) {
+again:
+               timeout = stop - jiffies;
+               if ((long)timeout <= 0)
+                       return -ETIME;
                rc = wait_event_interruptible_timeout(*queue,
                                                      ((tpm_tis_status
                                                        (chip) & mask) ==
                                                       mask), timeout);
                if (rc > 0)
                        return 0;
+               if (rc == -ERESTARTSYS && freezing(current)) {
+                       clear_thread_flag(TIF_SIGPENDING);
+                       goto again;
+               }
        } else {
-               stop = jiffies + timeout;
                do {
                        msleep(TPM_TIMEOUT);
                        status = tpm_tis_status(chip);
@@ -288,11 +301,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
  * tpm.c can skip polling for the data to be available as the interrupt is
  * waited for here
  */
-static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 {
        int rc, status, burstcnt;
        size_t count = 0;
-       u32 ordinal;
 
        if (request_locality(chip, 0) < 0)
                return -EBUSY;
@@ -327,8 +339,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 
        /* write last byte */
        iowrite8(buf[count],
-                chip->vendor.iobase +
-                TPM_DATA_FIFO(chip->vendor.locality));
+                chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
        wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
                      &chip->vendor.int_queue);
        status = tpm_tis_status(chip);
@@ -337,6 +348,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
                goto out_err;
        }
 
+       return 0;
+
+out_err:
+       tpm_tis_ready(chip);
+       release_locality(chip, chip->vendor.locality, 0);
+       return rc;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       int rc;
+       u32 ordinal;
+
+       rc = tpm_tis_send_data(chip, buf, len);
+       if (rc < 0)
+               return rc;
+
        /* go and do it */
        iowrite8(TPM_STS_GO,
                 chip->vendor.iobase + TPM_STS(chip->vendor.locality));
@@ -358,6 +391,47 @@ out_err:
        return rc;
 }
 
+/*
+ * Early probing for iTPM with STS_DATA_EXPECT flaw.
+ * Try sending command without itpm flag set and if that
+ * fails, repeat with itpm flag set.
+ */
+static int probe_itpm(struct tpm_chip *chip)
+{
+       int rc = 0;
+       u8 cmd_getticks[] = {
+               0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
+               0x00, 0x00, 0x00, 0xf1
+       };
+       size_t len = sizeof(cmd_getticks);
+       int rem_itpm = itpm;
+
+       itpm = 0;
+
+       rc = tpm_tis_send_data(chip, cmd_getticks, len);
+       if (rc == 0)
+               goto out;
+
+       tpm_tis_ready(chip);
+       release_locality(chip, chip->vendor.locality, 0);
+
+       itpm = 1;
+
+       rc = tpm_tis_send_data(chip, cmd_getticks, len);
+       if (rc == 0) {
+               dev_info(chip->dev, "Detected an iTPM.\n");
+               rc = 1;
+       } else
+               rc = -EFAULT;
+
+out:
+       itpm = rem_itpm;
+       tpm_tis_ready(chip);
+       release_locality(chip, chip->vendor.locality, 0);
+
+       return rc;
+}
+
 static const struct file_operations tis_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
@@ -376,6 +450,8 @@ static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
                   NULL);
 static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
 static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
 
 static struct attribute *tis_attrs[] = {
        &dev_attr_pubek.attr,
@@ -385,7 +461,9 @@ static struct attribute *tis_attrs[] = {
        &dev_attr_owned.attr,
        &dev_attr_temp_deactivated.attr,
        &dev_attr_caps.attr,
-       &dev_attr_cancel.attr, NULL,
+       &dev_attr_cancel.attr,
+       &dev_attr_durations.attr,
+       &dev_attr_timeouts.attr, NULL,
 };
 
 static struct attribute_group tis_attr_grp = {
@@ -416,7 +494,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id)
        if (interrupt == 0)
                return IRQ_NONE;
 
-       chip->vendor.irq = irq;
+       chip->vendor.probed_irq = irq;
 
        /* Clear interrupts handled with TPM_EOI */
        iowrite32(interrupt,
@@ -464,7 +542,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
                        resource_size_t len, unsigned int irq)
 {
        u32 vendor, intfcaps, intmask;
-       int rc, i;
+       int rc, i, irq_s, irq_e;
        struct tpm_chip *chip;
 
        if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
@@ -493,6 +571,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
                 "1.2 TPM (device-id 0x%X, rev-id %d)\n",
                 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
+       if (!itpm) {
+               itpm = probe_itpm(chip);
+               if (itpm < 0) {
+                       rc = -ENODEV;
+                       goto out_err;
+               }
+       }
+
        if (itpm)
                dev_info(dev, "Intel iTPM workaround enabled\n");
 
@@ -522,6 +608,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
        if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
                dev_dbg(dev, "\tData Avail Int Support\n");
 
+       /* get the timeouts before testing for irqs */
+       tpm_get_timeouts(chip);
+
        /* INTERRUPT Setup */
        init_waitqueue_head(&chip->vendor.read_queue);
        init_waitqueue_head(&chip->vendor.int_queue);
@@ -540,13 +629,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
        if (interrupts)
                chip->vendor.irq = irq;
        if (interrupts && !chip->vendor.irq) {
-               chip->vendor.irq =
+               irq_s =
                    ioread8(chip->vendor.iobase +
                            TPM_INT_VECTOR(chip->vendor.locality));
+               if (irq_s) {
+                       irq_e = irq_s;
+               } else {
+                       irq_s = 3;
+                       irq_e = 15;
+               }
 
-               for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
+               for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
                        iowrite8(i, chip->vendor.iobase +
-                                   TPM_INT_VECTOR(chip->vendor.locality));
+                                TPM_INT_VECTOR(chip->vendor.locality));
                        if (request_irq
                            (i, tis_int_probe, IRQF_SHARED,
                             chip->vendor.miscdev.name, chip) != 0) {
@@ -568,9 +663,22 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
                                  chip->vendor.iobase +
                                  TPM_INT_ENABLE(chip->vendor.locality));
 
+                       chip->vendor.probed_irq = 0;
+
                        /* Generate Interrupts */
                        tpm_gen_interrupt(chip);
 
+                       chip->vendor.irq = chip->vendor.probed_irq;
+
+                       /* free_irq will call into tis_int_probe;
+                          clear all irqs we haven't seen while doing
+                          tpm_gen_interrupt */
+                       iowrite32(ioread32
+                                 (chip->vendor.iobase +
+                                  TPM_INT_STATUS(chip->vendor.locality)),
+                                 chip->vendor.iobase +
+                                 TPM_INT_STATUS(chip->vendor.locality));
+
                        /* Turn off */
                        iowrite32(intmask,
                                  chip->vendor.iobase +
@@ -609,7 +717,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
        list_add(&chip->vendor.list, &tis_chips);
        spin_unlock(&tis_lock);
 
-       tpm_get_timeouts(chip);
        tpm_continue_selftest(chip);
 
        return 0;
@@ -619,6 +726,29 @@ out_err:
        tpm_remove_hardware(chip->dev);
        return rc;
 }
+
+static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
+{
+       u32 intmask;
+
+       /* reenable interrupts that device may have lost or
+          BIOS/firmware may have disabled */
+       iowrite8(chip->vendor.irq, chip->vendor.iobase +
+                TPM_INT_VECTOR(chip->vendor.locality));
+
+       intmask =
+           ioread32(chip->vendor.iobase +
+                    TPM_INT_ENABLE(chip->vendor.locality));
+
+       intmask |= TPM_INTF_CMD_READY_INT
+           | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+           | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
+
+       iowrite32(intmask,
+                 chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
+}
+
+
 #ifdef CONFIG_PNP
 static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
                                      const struct pnp_device_id *pnp_id)
@@ -650,6 +780,9 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)
        struct tpm_chip *chip = pnp_get_drvdata(dev);
        int ret;
 
+       if (chip->vendor.irq)
+               tpm_tis_reenable_interrupts(chip);
+
        ret = tpm_pm_resume(&dev->dev);
        if (!ret)
                tpm_continue_selftest(chip);
@@ -702,6 +835,11 @@ static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg)
 
 static int tpm_tis_resume(struct platform_device *dev)
 {
+       struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
+
+       if (chip->vendor.irq)
+               tpm_tis_reenable_interrupts(chip);
+
        return tpm_pm_resume(&dev->dev);
 }
 static struct platform_driver tis_drv = {
index 43c7c43..b36c557 100644 (file)
@@ -29,6 +29,7 @@
 #define ECRYPTFS_KERNEL_H
 
 #include <keys/user-type.h>
+#include <keys/encrypted-type.h>
 #include <linux/fs.h>
 #include <linux/fs_stack.h>
 #include <linux/namei.h>
 #include <linux/hash.h>
 #include <linux/nsproxy.h>
 #include <linux/backing-dev.h>
+#include <linux/ecryptfs.h>
 
-/* Version verification for shared data structures w/ userspace */
-#define ECRYPTFS_VERSION_MAJOR 0x00
-#define ECRYPTFS_VERSION_MINOR 0x04
-#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
-/* These flags indicate which features are supported by the kernel
- * module; userspace tools such as the mount helper read
- * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
- * how to behave. */
-#define ECRYPTFS_VERSIONING_PASSPHRASE            0x00000001
-#define ECRYPTFS_VERSIONING_PUBKEY                0x00000002
-#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
-#define ECRYPTFS_VERSIONING_POLICY                0x00000008
-#define ECRYPTFS_VERSIONING_XATTR                 0x00000010
-#define ECRYPTFS_VERSIONING_MULTKEY               0x00000020
-#define ECRYPTFS_VERSIONING_DEVMISC               0x00000040
-#define ECRYPTFS_VERSIONING_HMAC                  0x00000080
-#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION   0x00000100
-#define ECRYPTFS_VERSIONING_GCM                   0x00000200
-#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
-                                 | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
-                                 | ECRYPTFS_VERSIONING_PUBKEY \
-                                 | ECRYPTFS_VERSIONING_XATTR \
-                                 | ECRYPTFS_VERSIONING_MULTKEY \
-                                 | ECRYPTFS_VERSIONING_DEVMISC \
-                                 | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
-#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
-#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
-#define ECRYPTFS_SALT_SIZE 8
-#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
-/* The original signature size is only for what is stored on disk; all
- * in-memory representations are expanded hex, so it better adapted to
- * be passed around or referenced on the command line */
-#define ECRYPTFS_SIG_SIZE 8
-#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
-#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
-#define ECRYPTFS_MAX_KEY_BYTES 64
-#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
 #define ECRYPTFS_DEFAULT_IV_BYTES 16
-#define ECRYPTFS_FILE_VERSION 0x03
 #define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
 #define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
 #define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32
 #define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ
 #define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3)
-#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
 #define ECRYPTFS_DEFAULT_NUM_USERS 4
 #define ECRYPTFS_MAX_NUM_USERS 32768
 #define ECRYPTFS_XATTR_NAME "user.ecryptfs"
 
-#define RFC2440_CIPHER_DES3_EDE 0x02
-#define RFC2440_CIPHER_CAST_5 0x03
-#define RFC2440_CIPHER_BLOWFISH 0x04
-#define RFC2440_CIPHER_AES_128 0x07
-#define RFC2440_CIPHER_AES_192 0x08
-#define RFC2440_CIPHER_AES_256 0x09
-#define RFC2440_CIPHER_TWOFISH 0x0a
-#define RFC2440_CIPHER_CAST_6 0x0b
-
-#define RFC2440_CIPHER_RSA 0x01
-
-/**
- * For convenience, we may need to pass around the encrypted session
- * key between kernel and userspace because the authentication token
- * may not be extractable.  For example, the TPM may not release the
- * private key, instead requiring the encrypted data and returning the
- * decrypted data.
- */
-struct ecryptfs_session_key {
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
-#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
-#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
-       u32 flags;
-       u32 encrypted_key_size;
-       u32 decrypted_key_size;
-       u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
-       u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
-};
-
-struct ecryptfs_password {
-       u32 password_bytes;
-       s32 hash_algo;
-       u32 hash_iterations;
-       u32 session_key_encryption_key_bytes;
-#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
-#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
-       u32 flags;
-       /* Iterated-hash concatenation of salt and passphrase */
-       u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
-       u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
-       /* Always in expanded hex */
-       u8 salt[ECRYPTFS_SALT_SIZE];
-};
-
-enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
-
-struct ecryptfs_private_key {
-       u32 key_size;
-       u32 data_len;
-       u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
-       char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
-       u8 data[];
-};
-
-/* May be a password or a private key */
-struct ecryptfs_auth_tok {
-       u16 version; /* 8-bit major and 8-bit minor */
-       u16 token_type;
-#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
-       u32 flags;
-       struct ecryptfs_session_key session_key;
-       u8 reserved[32];
-       union {
-               struct ecryptfs_password password;
-               struct ecryptfs_private_key private_key;
-       } token;
-} __attribute__ ((packed));
-
 void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
 extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size);
 extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
@@ -185,11 +79,47 @@ struct ecryptfs_page_crypt_context {
        } param;
 };
 
+#if defined(CONFIG_ENCRYPTED_KEYS) || defined(CONFIG_ENCRYPTED_KEYS_MODULE)
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+       if (key->type == &key_type_encrypted)
+               return (struct ecryptfs_auth_tok *)
+                       (&((struct encrypted_key_payload *)key->payload.data)->payload_data);
+       else
+               return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+       return request_key(&key_type_encrypted, sig, NULL);
+}
+
+#else
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+       return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+       return ERR_PTR(-ENOKEY);
+}
+
+#endif /* CONFIG_ENCRYPTED_KEYS */
+
 static inline struct ecryptfs_auth_tok *
 ecryptfs_get_key_payload_data(struct key *key)
 {
-       return (struct ecryptfs_auth_tok *)
-               (((struct user_key_payload*)key->payload.data)->data);
+       struct ecryptfs_auth_tok *auth_tok;
+
+       auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
+       if (!auth_tok)
+               return (struct ecryptfs_auth_tok *)
+                       (((struct user_key_payload *)key->payload.data)->data);
+       else
+               return auth_tok;
 }
 
 #define ECRYPTFS_MAX_KEYSET_SIZE 1024
index fa8049e..c472533 100644 (file)
@@ -1635,11 +1635,14 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key,
 
        (*auth_tok_key) = request_key(&key_type_user, sig, NULL);
        if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
-               printk(KERN_ERR "Could not find key with description: [%s]\n",
-                      sig);
-               rc = process_request_key_err(PTR_ERR(*auth_tok_key));
-               (*auth_tok_key) = NULL;
-               goto out;
+               (*auth_tok_key) = ecryptfs_get_encrypted_key(sig);
+               if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
+                       printk(KERN_ERR "Could not find key with description: [%s]\n",
+                             sig);
+                       rc = process_request_key_err(PTR_ERR(*auth_tok_key));
+                       (*auth_tok_key) = NULL;
+                       goto out;
+               }
        }
        down_write(&(*auth_tok_key)->sem);
        rc = ecryptfs_verify_auth_tok_from_key(*auth_tok_key, auth_tok);
index 9585501..1d45413 100644 (file)
@@ -1,6 +1,11 @@
 /*
  * Copyright (C) 2010 IBM Corporation
- * Author: Mimi Zohar <zohar@us.ibm.com>
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
  *
  * 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
 
 struct encrypted_key_payload {
        struct rcu_head rcu;
+       char *format;           /* datablob: format */
        char *master_desc;      /* datablob: master key name */
        char *datalen;          /* datablob: decrypted key length */
        u8 *iv;                 /* datablob: iv */
        u8 *encrypted_data;     /* datablob: encrypted data */
        unsigned short datablob_len;    /* length of datablob */
        unsigned short decrypted_datalen;       /* decrypted data length */
-       u8 decrypted_data[0];   /* decrypted data +  datablob + hmac */
+       unsigned short payload_datalen;         /* payload data length */
+       unsigned short encrypted_key_format;    /* encrypted key format */
+       u8 *decrypted_data;     /* decrypted data */
+       u8 payload_data[0];     /* payload data + datablob + hmac */
 };
 
 extern struct key_type key_type_encrypted;
diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h
new file mode 100644 (file)
index 0000000..2224a8c
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef _LINUX_ECRYPTFS_H
+#define _LINUX_ECRYPTFS_H
+
+/* Version verification for shared data structures w/ userspace */
+#define ECRYPTFS_VERSION_MAJOR 0x00
+#define ECRYPTFS_VERSION_MINOR 0x04
+#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
+/* These flags indicate which features are supported by the kernel
+ * module; userspace tools such as the mount helper read
+ * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
+ * how to behave. */
+#define ECRYPTFS_VERSIONING_PASSPHRASE            0x00000001
+#define ECRYPTFS_VERSIONING_PUBKEY                0x00000002
+#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
+#define ECRYPTFS_VERSIONING_POLICY                0x00000008
+#define ECRYPTFS_VERSIONING_XATTR                 0x00000010
+#define ECRYPTFS_VERSIONING_MULTKEY               0x00000020
+#define ECRYPTFS_VERSIONING_DEVMISC               0x00000040
+#define ECRYPTFS_VERSIONING_HMAC                  0x00000080
+#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION   0x00000100
+#define ECRYPTFS_VERSIONING_GCM                   0x00000200
+#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
+                                 | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
+                                 | ECRYPTFS_VERSIONING_PUBKEY \
+                                 | ECRYPTFS_VERSIONING_XATTR \
+                                 | ECRYPTFS_VERSIONING_MULTKEY \
+                                 | ECRYPTFS_VERSIONING_DEVMISC \
+                                 | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
+#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
+#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
+#define ECRYPTFS_SALT_SIZE 8
+#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
+/* The original signature size is only for what is stored on disk; all
+ * in-memory representations are expanded hex, so it better adapted to
+ * be passed around or referenced on the command line */
+#define ECRYPTFS_SIG_SIZE 8
+#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
+#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
+#define ECRYPTFS_MAX_KEY_BYTES 64
+#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
+#define ECRYPTFS_FILE_VERSION 0x03
+#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
+
+#define RFC2440_CIPHER_DES3_EDE 0x02
+#define RFC2440_CIPHER_CAST_5 0x03
+#define RFC2440_CIPHER_BLOWFISH 0x04
+#define RFC2440_CIPHER_AES_128 0x07
+#define RFC2440_CIPHER_AES_192 0x08
+#define RFC2440_CIPHER_AES_256 0x09
+#define RFC2440_CIPHER_TWOFISH 0x0a
+#define RFC2440_CIPHER_CAST_6 0x0b
+
+#define RFC2440_CIPHER_RSA 0x01
+
+/**
+ * For convenience, we may need to pass around the encrypted session
+ * key between kernel and userspace because the authentication token
+ * may not be extractable.  For example, the TPM may not release the
+ * private key, instead requiring the encrypted data and returning the
+ * decrypted data.
+ */
+struct ecryptfs_session_key {
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
+       u32 flags;
+       u32 encrypted_key_size;
+       u32 decrypted_key_size;
+       u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+       u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+struct ecryptfs_password {
+       u32 password_bytes;
+       s32 hash_algo;
+       u32 hash_iterations;
+       u32 session_key_encryption_key_bytes;
+#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
+#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
+       u32 flags;
+       /* Iterated-hash concatenation of salt and passphrase */
+       u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+       u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+       /* Always in expanded hex */
+       u8 salt[ECRYPTFS_SALT_SIZE];
+};
+
+enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
+
+struct ecryptfs_private_key {
+       u32 key_size;
+       u32 data_len;
+       u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+       char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
+       u8 data[];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+       u16 version; /* 8-bit major and 8-bit minor */
+       u16 token_type;
+#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
+       u32 flags;
+       struct ecryptfs_session_key session_key;
+       u8 reserved[32];
+       union {
+               struct ecryptfs_password password;
+               struct ecryptfs_private_key private_key;
+       } token;
+} __attribute__ ((packed));
+
+#endif /* _LINUX_ECRYPTFS_H */
index 9844580..1d2b6ce 100644 (file)
  */
 
 #include <linux/cgroup.h>
+#include <linux/cred.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/init_task.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/mm.h>
@@ -1514,6 +1516,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
                struct cgroup *root_cgrp = &root->top_cgroup;
                struct inode *inode;
                struct cgroupfs_root *existing_root;
+               const struct cred *cred;
                int i;
 
                BUG_ON(sb->s_root != NULL);
@@ -1593,7 +1596,9 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
                BUG_ON(!list_empty(&root_cgrp->children));
                BUG_ON(root->number_of_cgroups != 1);
 
+               cred = override_creds(&init_cred);
                cgroup_populate_dir(root_cgrp);
+               revert_creds(cred);
                mutex_unlock(&cgroup_mutex);
                mutex_unlock(&inode->i_mutex);
        } else {
index 7312bf9..c1e18ba 100644 (file)
@@ -73,7 +73,6 @@ static int may_change_ptraced_domain(struct task_struct *task,
                cred = get_task_cred(tracer);
                tracerp = aa_cred_profile(cred);
        }
-       rcu_read_unlock();
 
        /* not ptraced */
        if (!tracer || unconfined(tracerp))
@@ -82,6 +81,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
        error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
 
 out:
+       rcu_read_unlock();
        if (cred)
                put_cred(cred);
 
index 3d2fd14..3783202 100644 (file)
@@ -127,7 +127,7 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
        *inheritable = cred->cap_inheritable;
        *permitted = cred->cap_permitted;
 
-       if (!unconfined(profile)) {
+       if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
                *effective = cap_intersect(*effective, profile->caps.allow);
                *permitted = cap_intersect(*permitted, profile->caps.allow);
        }
index 1bf090a..b34cc6e 100644 (file)
@@ -14,7 +14,7 @@ obj-y := \
        user_defined.o
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/ecryptfs_format.c b/security/keys/ecryptfs_format.c
new file mode 100644 (file)
index 0000000..6daa3b6
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * ecryptfs_format.c: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * 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, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include "ecryptfs_format.h"
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
+{
+       return auth_tok->token.password.session_key_encryption_key;
+}
+EXPORT_SYMBOL(ecryptfs_get_auth_tok_key);
+
+/*
+ * ecryptfs_get_versions()
+ *
+ * Source code taken from the software 'ecryptfs-utils' version 83.
+ *
+ */
+void ecryptfs_get_versions(int *major, int *minor, int *file_version)
+{
+       *major = ECRYPTFS_VERSION_MAJOR;
+       *minor = ECRYPTFS_VERSION_MINOR;
+       if (file_version)
+               *file_version = ECRYPTFS_SUPPORTED_FILE_VERSION;
+}
+EXPORT_SYMBOL(ecryptfs_get_versions);
+
+/*
+ * ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure
+ *
+ * Fill the ecryptfs_auth_tok structure with required ecryptfs data.
+ * The source code is inspired to the original function generate_payload()
+ * shipped with the software 'ecryptfs-utils' version 83.
+ *
+ */
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+                          const char *key_desc)
+{
+       int major, minor;
+
+       ecryptfs_get_versions(&major, &minor, NULL);
+       auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
+                            | ((uint16_t)minor & 0x00FF));
+       auth_tok->token_type = ECRYPTFS_PASSWORD;
+       strncpy((char *)auth_tok->token.password.signature, key_desc,
+               ECRYPTFS_PASSWORD_SIG_SIZE);
+       auth_tok->token.password.session_key_encryption_key_bytes =
+               ECRYPTFS_MAX_KEY_BYTES;
+       /*
+        * Removed auth_tok->token.password.salt and
+        * auth_tok->token.password.session_key_encryption_key
+        * initialization from the original code
+        */
+       /* TODO: Make the hash parameterizable via policy */
+       auth_tok->token.password.flags |=
+               ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
+       /* The kernel code will encrypt the session key. */
+       auth_tok->session_key.encrypted_key[0] = 0;
+       auth_tok->session_key.encrypted_key_size = 0;
+       /* Default; subject to change by kernel eCryptfs */
+       auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512;
+       auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD);
+       return 0;
+}
+EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/ecryptfs_format.h b/security/keys/ecryptfs_format.h
new file mode 100644 (file)
index 0000000..40294de
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * ecryptfs_format.h: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * 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, version 2 of the License.
+ */
+
+#ifndef __KEYS_ECRYPTFS_H
+#define __KEYS_ECRYPTFS_H
+
+#include <linux/ecryptfs.h>
+
+#define PGP_DIGEST_ALGO_SHA512   10
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok);
+void ecryptfs_get_versions(int *major, int *minor, int *file_version);
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+                          const char *key_desc);
+
+#endif /* __KEYS_ECRYPTFS_H */
index b1cba5b..e7eca9e 100644 (file)
@@ -1,8 +1,11 @@
 /*
  * Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
  *
- * Author:
+ * Authors:
  * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
  *
  * 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
 #include <linux/rcupdate.h>
 #include <linux/scatterlist.h>
 #include <linux/crypto.h>
+#include <linux/ctype.h>
 #include <crypto/hash.h>
 #include <crypto/sha.h>
 #include <crypto/aes.h>
 
 #include "encrypted.h"
+#include "ecryptfs_format.h"
 
 static const char KEY_TRUSTED_PREFIX[] = "trusted:";
 static const char KEY_USER_PREFIX[] = "user:";
 static const char hash_alg[] = "sha256";
 static const char hmac_alg[] = "hmac(sha256)";
 static const char blkcipher_alg[] = "cbc(aes)";
+static const char key_format_default[] = "default";
+static const char key_format_ecryptfs[] = "ecryptfs";
 static unsigned int ivsize;
 static int blksize;
 
 #define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
 #define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
+#define KEY_ECRYPTFS_DESC_LEN 16
 #define HASH_SIZE SHA256_DIGEST_SIZE
 #define MAX_DATA_SIZE 4096
 #define MIN_DATA_SIZE  20
@@ -58,6 +66,16 @@ enum {
        Opt_err = -1, Opt_new, Opt_load, Opt_update
 };
 
+enum {
+       Opt_error = -1, Opt_default, Opt_ecryptfs
+};
+
+static const match_table_t key_format_tokens = {
+       {Opt_default, "default"},
+       {Opt_ecryptfs, "ecryptfs"},
+       {Opt_error, NULL}
+};
+
 static const match_table_t key_tokens = {
        {Opt_new, "new"},
        {Opt_load, "load"},
@@ -82,9 +100,37 @@ static int aes_get_sizes(void)
 }
 
 /*
+ * valid_ecryptfs_desc - verify the description of a new/loaded encrypted key
+ *
+ * The description of a encrypted key with format 'ecryptfs' must contain
+ * exactly 16 hexadecimal characters.
+ *
+ */
+static int valid_ecryptfs_desc(const char *ecryptfs_desc)
+{
+       int i;
+
+       if (strlen(ecryptfs_desc) != KEY_ECRYPTFS_DESC_LEN) {
+               pr_err("encrypted_key: key description must be %d hexadecimal "
+                      "characters long\n", KEY_ECRYPTFS_DESC_LEN);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < KEY_ECRYPTFS_DESC_LEN; i++) {
+               if (!isxdigit(ecryptfs_desc[i])) {
+                       pr_err("encrypted_key: key description must contain "
+                              "only hexadecimal characters\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/*
  * valid_master_desc - verify the 'key-type:desc' of a new/updated master-key
  *
- * key-type:= "trusted:" | "encrypted:"
+ * key-type:= "trusted:" | "user:"
  * desc:= master-key description
  *
  * Verify that 'key-type' is valid and that 'desc' exists. On key update,
@@ -118,8 +164,9 @@ out:
  * datablob_parse - parse the keyctl data
  *
  * datablob format:
- * new <master-key name> <decrypted data length>
- * load <master-key name> <decrypted data length> <encrypted iv + data>
+ * new [<format>] <master-key name> <decrypted data length>
+ * load [<format>] <master-key name> <decrypted data length>
+ *     <encrypted iv + data>
  * update <new-master-key name>
  *
  * Tokenizes a copy of the keyctl data, returning a pointer to each token,
@@ -127,52 +174,95 @@ out:
  *
  * On success returns 0, otherwise -EINVAL.
  */
-static int datablob_parse(char *datablob, char **master_desc,
-                         char **decrypted_datalen, char **hex_encoded_iv)
+static int datablob_parse(char *datablob, const char **format,
+                         char **master_desc, char **decrypted_datalen,
+                         char **hex_encoded_iv)
 {
        substring_t args[MAX_OPT_ARGS];
        int ret = -EINVAL;
        int key_cmd;
-       char *p;
+       int key_format;
+       char *p, *keyword;
+
+       keyword = strsep(&datablob, " \t");
+       if (!keyword) {
+               pr_info("encrypted_key: insufficient parameters specified\n");
+               return ret;
+       }
+       key_cmd = match_token(keyword, key_tokens, args);
 
+       /* Get optional format: default | ecryptfs */
        p = strsep(&datablob, " \t");
-       if (!p)
+       if (!p) {
+               pr_err("encrypted_key: insufficient parameters specified\n");
                return ret;
-       key_cmd = match_token(p, key_tokens, args);
+       }
 
-       *master_desc = strsep(&datablob, " \t");
-       if (!*master_desc)
+       key_format = match_token(p, key_format_tokens, args);
+       switch (key_format) {
+       case Opt_ecryptfs:
+       case Opt_default:
+               *format = p;
+               *master_desc = strsep(&datablob, " \t");
+               break;
+       case Opt_error:
+               *master_desc = p;
+               break;
+       }
+
+       if (!*master_desc) {
+               pr_info("encrypted_key: master key parameter is missing\n");
                goto out;
+       }
 
-       if (valid_master_desc(*master_desc, NULL) < 0)
+       if (valid_master_desc(*master_desc, NULL) < 0) {
+               pr_info("encrypted_key: master key parameter \'%s\' "
+                       "is invalid\n", *master_desc);
                goto out;
+       }
 
        if (decrypted_datalen) {
                *decrypted_datalen = strsep(&datablob, " \t");
-               if (!*decrypted_datalen)
+               if (!*decrypted_datalen) {
+                       pr_info("encrypted_key: keylen parameter is missing\n");
                        goto out;
+               }
        }
 
        switch (key_cmd) {
        case Opt_new:
-               if (!decrypted_datalen)
+               if (!decrypted_datalen) {
+                       pr_info("encrypted_key: keyword \'%s\' not allowed "
+                               "when called from .update method\n", keyword);
                        break;
+               }
                ret = 0;
                break;
        case Opt_load:
-               if (!decrypted_datalen)
+               if (!decrypted_datalen) {
+                       pr_info("encrypted_key: keyword \'%s\' not allowed "
+                               "when called from .update method\n", keyword);
                        break;
+               }
                *hex_encoded_iv = strsep(&datablob, " \t");
-               if (!*hex_encoded_iv)
+               if (!*hex_encoded_iv) {
+                       pr_info("encrypted_key: hex blob is missing\n");
                        break;
+               }
                ret = 0;
                break;
        case Opt_update:
-               if (decrypted_datalen)
+               if (decrypted_datalen) {
+                       pr_info("encrypted_key: keyword \'%s\' not allowed "
+                               "when called from .instantiate method\n",
+                               keyword);
                        break;
+               }
                ret = 0;
                break;
        case Opt_err:
+               pr_info("encrypted_key: keyword \'%s\' not recognized\n",
+                       keyword);
                break;
        }
 out:
@@ -197,8 +287,8 @@ static char *datablob_format(struct encrypted_key_payload *epayload,
        ascii_buf[asciiblob_len] = '\0';
 
        /* copy datablob master_desc and datalen strings */
-       len = sprintf(ascii_buf, "%s %s ", epayload->master_desc,
-                     epayload->datalen);
+       len = sprintf(ascii_buf, "%s %s %s ", epayload->format,
+                     epayload->master_desc, epayload->datalen);
 
        /* convert the hex encoded iv, encrypted-data and HMAC to ascii */
        bufp = &ascii_buf[len];
@@ -378,11 +468,13 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload,
        } else
                goto out;
 
-       if (IS_ERR(mkey))
+       if (IS_ERR(mkey)) {
                pr_info("encrypted_key: key %s not found",
                        epayload->master_desc);
-       if (mkey)
-               dump_master_key(*master_key, *master_keylen);
+               goto out;
+       }
+
+       dump_master_key(*master_key, *master_keylen);
 out:
        return mkey;
 }
@@ -439,9 +531,9 @@ static int datablob_hmac_append(struct encrypted_key_payload *epayload,
        if (ret < 0)
                goto out;
 
-       digest = epayload->master_desc + epayload->datablob_len;
+       digest = epayload->format + epayload->datablob_len;
        ret = calc_hmac(digest, derived_key, sizeof derived_key,
-                       epayload->master_desc, epayload->datablob_len);
+                       epayload->format, epayload->datablob_len);
        if (!ret)
                dump_hmac(NULL, digest, HASH_SIZE);
 out:
@@ -450,26 +542,35 @@ out:
 
 /* verify HMAC before decrypting encrypted key */
 static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
-                               const u8 *master_key, size_t master_keylen)
+                               const u8 *format, const u8 *master_key,
+                               size_t master_keylen)
 {
        u8 derived_key[HASH_SIZE];
        u8 digest[HASH_SIZE];
        int ret;
+       char *p;
+       unsigned short len;
 
        ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
        if (ret < 0)
                goto out;
 
-       ret = calc_hmac(digest, derived_key, sizeof derived_key,
-                       epayload->master_desc, epayload->datablob_len);
+       len = epayload->datablob_len;
+       if (!format) {
+               p = epayload->master_desc;
+               len -= strlen(epayload->format) + 1;
+       } else
+               p = epayload->format;
+
+       ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len);
        if (ret < 0)
                goto out;
-       ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
+       ret = memcmp(digest, epayload->format + epayload->datablob_len,
                     sizeof digest);
        if (ret) {
                ret = -EINVAL;
                dump_hmac("datablob",
-                         epayload->master_desc + epayload->datablob_len,
+                         epayload->format + epayload->datablob_len,
                          HASH_SIZE);
                dump_hmac("calc", digest, HASH_SIZE);
        }
@@ -514,13 +615,16 @@ out:
 
 /* Allocate memory for decrypted key and datablob. */
 static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
+                                                        const char *format,
                                                         const char *master_desc,
                                                         const char *datalen)
 {
        struct encrypted_key_payload *epayload = NULL;
        unsigned short datablob_len;
        unsigned short decrypted_datalen;
+       unsigned short payload_datalen;
        unsigned int encrypted_datalen;
+       unsigned int format_len;
        long dlen;
        int ret;
 
@@ -528,29 +632,43 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
        if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
                return ERR_PTR(-EINVAL);
 
+       format_len = (!format) ? strlen(key_format_default) : strlen(format);
        decrypted_datalen = dlen;
+       payload_datalen = decrypted_datalen;
+       if (format && !strcmp(format, key_format_ecryptfs)) {
+               if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
+                       pr_err("encrypted_key: keylen for the ecryptfs format "
+                              "must be equal to %d bytes\n",
+                              ECRYPTFS_MAX_KEY_BYTES);
+                       return ERR_PTR(-EINVAL);
+               }
+               decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
+               payload_datalen = sizeof(struct ecryptfs_auth_tok);
+       }
+
        encrypted_datalen = roundup(decrypted_datalen, blksize);
 
-       datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
-           + ivsize + 1 + encrypted_datalen;
+       datablob_len = format_len + 1 + strlen(master_desc) + 1
+           + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
 
-       ret = key_payload_reserve(key, decrypted_datalen + datablob_len
+       ret = key_payload_reserve(key, payload_datalen + datablob_len
                                  + HASH_SIZE + 1);
        if (ret < 0)
                return ERR_PTR(ret);
 
-       epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
+       epayload = kzalloc(sizeof(*epayload) + payload_datalen +
                           datablob_len + HASH_SIZE + 1, GFP_KERNEL);
        if (!epayload)
                return ERR_PTR(-ENOMEM);
 
+       epayload->payload_datalen = payload_datalen;
        epayload->decrypted_datalen = decrypted_datalen;
        epayload->datablob_len = datablob_len;
        return epayload;
 }
 
 static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
-                                const char *hex_encoded_iv)
+                                const char *format, const char *hex_encoded_iv)
 {
        struct key *mkey;
        u8 derived_key[HASH_SIZE];
@@ -571,14 +689,14 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
        hex2bin(epayload->iv, hex_encoded_iv, ivsize);
        hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
 
-       hmac = epayload->master_desc + epayload->datablob_len;
+       hmac = epayload->format + epayload->datablob_len;
        hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
 
        mkey = request_master_key(epayload, &master_key, &master_keylen);
        if (IS_ERR(mkey))
                return PTR_ERR(mkey);
 
-       ret = datablob_hmac_verify(epayload, master_key, master_keylen);
+       ret = datablob_hmac_verify(epayload, format, master_key, master_keylen);
        if (ret < 0) {
                pr_err("encrypted_key: bad hmac (%d)\n", ret);
                goto out;
@@ -598,13 +716,28 @@ out:
 }
 
 static void __ekey_init(struct encrypted_key_payload *epayload,
-                       const char *master_desc, const char *datalen)
+                       const char *format, const char *master_desc,
+                       const char *datalen)
 {
-       epayload->master_desc = epayload->decrypted_data
-           + epayload->decrypted_datalen;
+       unsigned int format_len;
+
+       format_len = (!format) ? strlen(key_format_default) : strlen(format);
+       epayload->format = epayload->payload_data + epayload->payload_datalen;
+       epayload->master_desc = epayload->format + format_len + 1;
        epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
        epayload->iv = epayload->datalen + strlen(datalen) + 1;
        epayload->encrypted_data = epayload->iv + ivsize + 1;
+       epayload->decrypted_data = epayload->payload_data;
+
+       if (!format)
+               memcpy(epayload->format, key_format_default, format_len);
+       else {
+               if (!strcmp(format, key_format_ecryptfs))
+                       epayload->decrypted_data =
+                               ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data);
+
+               memcpy(epayload->format, format, format_len);
+       }
 
        memcpy(epayload->master_desc, master_desc, strlen(master_desc));
        memcpy(epayload->datalen, datalen, strlen(datalen));
@@ -617,19 +750,29 @@ static void __ekey_init(struct encrypted_key_payload *epayload,
  * itself.  For an old key, decrypt the hex encoded data.
  */
 static int encrypted_init(struct encrypted_key_payload *epayload,
+                         const char *key_desc, const char *format,
                          const char *master_desc, const char *datalen,
                          const char *hex_encoded_iv)
 {
        int ret = 0;
 
-       __ekey_init(epayload, master_desc, datalen);
+       if (format && !strcmp(format, key_format_ecryptfs)) {
+               ret = valid_ecryptfs_desc(key_desc);
+               if (ret < 0)
+                       return ret;
+
+               ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data,
+                                      key_desc);
+       }
+
+       __ekey_init(epayload, format, master_desc, datalen);
        if (!hex_encoded_iv) {
                get_random_bytes(epayload->iv, ivsize);
 
                get_random_bytes(epayload->decrypted_data,
                                 epayload->decrypted_datalen);
        } else
-               ret = encrypted_key_decrypt(epayload, hex_encoded_iv);
+               ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv);
        return ret;
 }
 
@@ -646,6 +789,7 @@ static int encrypted_instantiate(struct key *key, const void *data,
 {
        struct encrypted_key_payload *epayload = NULL;
        char *datablob = NULL;
+       const char *format = NULL;
        char *master_desc = NULL;
        char *decrypted_datalen = NULL;
        char *hex_encoded_iv = NULL;
@@ -659,18 +803,19 @@ static int encrypted_instantiate(struct key *key, const void *data,
                return -ENOMEM;
        datablob[datalen] = 0;
        memcpy(datablob, data, datalen);
-       ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
-                            &hex_encoded_iv);
+       ret = datablob_parse(datablob, &format, &master_desc,
+                            &decrypted_datalen, &hex_encoded_iv);
        if (ret < 0)
                goto out;
 
-       epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
+       epayload = encrypted_key_alloc(key, format, master_desc,
+                                      decrypted_datalen);
        if (IS_ERR(epayload)) {
                ret = PTR_ERR(epayload);
                goto out;
        }
-       ret = encrypted_init(epayload, master_desc, decrypted_datalen,
-                            hex_encoded_iv);
+       ret = encrypted_init(epayload, key->description, format, master_desc,
+                            decrypted_datalen, hex_encoded_iv);
        if (ret < 0) {
                kfree(epayload);
                goto out;
@@ -706,6 +851,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
        struct encrypted_key_payload *new_epayload;
        char *buf;
        char *new_master_desc = NULL;
+       const char *format = NULL;
        int ret = 0;
 
        if (datalen <= 0 || datalen > 32767 || !data)
@@ -717,7 +863,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
 
        buf[datalen] = 0;
        memcpy(buf, data, datalen);
-       ret = datablob_parse(buf, &new_master_desc, NULL, NULL);
+       ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
        if (ret < 0)
                goto out;
 
@@ -725,18 +871,19 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
        if (ret < 0)
                goto out;
 
-       new_epayload = encrypted_key_alloc(key, new_master_desc,
-                                          epayload->datalen);
+       new_epayload = encrypted_key_alloc(key, epayload->format,
+                                          new_master_desc, epayload->datalen);
        if (IS_ERR(new_epayload)) {
                ret = PTR_ERR(new_epayload);
                goto out;
        }
 
-       __ekey_init(new_epayload, new_master_desc, epayload->datalen);
+       __ekey_init(new_epayload, epayload->format, new_master_desc,
+                   epayload->datalen);
 
        memcpy(new_epayload->iv, epayload->iv, ivsize);
-       memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
-              epayload->decrypted_datalen);
+       memcpy(new_epayload->payload_data, epayload->payload_data,
+              epayload->payload_datalen);
 
        rcu_assign_pointer(key->payload.data, new_epayload);
        call_rcu(&epayload->rcu, encrypted_rcu_free);
index 6cff375..60d4e3f 100644 (file)
@@ -251,6 +251,8 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
 
        if (IS_ERR(authkey_ref)) {
                authkey = ERR_CAST(authkey_ref);
+               if (authkey == ERR_PTR(-EAGAIN))
+                       authkey = ERR_PTR(-ENOKEY);
                goto error;
        }
 
index c8f3857..7c7f8c1 100644 (file)
@@ -9,3 +9,64 @@ config SECURITY_TOMOYO
          Required userspace tools and further information may be
          found at <http://tomoyo.sourceforge.jp/>.
          If you are unsure how to answer this question, answer N.
+
+config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
+       int "Default maximal count for learning mode"
+       default 2048
+       range 0 2147483647
+       depends on SECURITY_TOMOYO
+       help
+         This is the default value for maximal ACL entries
+         that are automatically appended into policy at "learning mode".
+         Some programs access thousands of objects, so running
+         such programs in "learning mode" dulls the system response
+         and consumes much memory.
+         This is the safeguard for such programs.
+
+config SECURITY_TOMOYO_MAX_AUDIT_LOG
+       int "Default maximal count for audit log"
+       default 1024
+       range 0 2147483647
+       depends on SECURITY_TOMOYO
+       help
+         This is the default value for maximal entries for
+         audit logs that the kernel can hold on memory.
+         You can read the log via /sys/kernel/security/tomoyo/audit.
+         If you don't need audit logs, you may set this value to 0.
+
+config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+       bool "Activate without calling userspace policy loader."
+       default n
+       depends on SECURITY_TOMOYO
+       ---help---
+         Say Y here if you want to activate access control as soon as built-in
+         policy was loaded. This option will be useful for systems where
+         operations which can lead to the hijacking of the boot sequence are
+         needed before loading the policy. For example, you can activate
+         immediately after loading the fixed part of policy which will allow
+         only operations needed for mounting a partition which contains the
+         variant part of policy and verifying (e.g. running GPG check) and
+         loading the variant part of policy. Since you can start using
+         enforcing mode from the beginning, you can reduce the possibility of
+         hijacking the boot sequence.
+
+config SECURITY_TOMOYO_POLICY_LOADER
+       string "Location of userspace policy loader"
+       default "/sbin/tomoyo-init"
+       depends on SECURITY_TOMOYO
+       depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+       ---help---
+         This is the default pathname of policy loader which is called before
+         activation. You can override this setting via TOMOYO_loader= kernel
+         command line option.
+
+config SECURITY_TOMOYO_ACTIVATION_TRIGGER
+       string "Trigger for calling userspace policy loader"
+       default "/sbin/init"
+       depends on SECURITY_TOMOYO
+       depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+       ---help---
+         This is the default pathname of activation trigger.
+         You can override this setting via TOMOYO_trigger= kernel command line
+         option. For example, if you pass init=/bin/systemd option, you may
+         want to also pass TOMOYO_trigger=/bin/systemd option.
index 91640e9..95278b7 100644 (file)
@@ -1 +1,48 @@
-obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+
+$(obj)/policy/profile.conf:
+       @mkdir -p $(obj)/policy/
+       @echo Creating an empty policy/profile.conf
+       @touch $@
+
+$(obj)/policy/exception_policy.conf:
+       @mkdir -p $(obj)/policy/
+       @echo Creating a default policy/exception_policy.conf
+       @echo initialize_domain /sbin/modprobe from any >> $@
+       @echo initialize_domain /sbin/hotplug from any >> $@
+
+$(obj)/policy/domain_policy.conf:
+       @mkdir -p $(obj)/policy/
+       @echo Creating an empty policy/domain_policy.conf
+       @touch $@
+
+$(obj)/policy/manager.conf:
+       @mkdir -p $(obj)/policy/
+       @echo Creating an empty policy/manager.conf
+       @touch $@
+
+$(obj)/policy/stat.conf:
+       @mkdir -p $(obj)/policy/
+       @echo Creating an empty policy/stat.conf
+       @touch $@
+
+$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
+       @echo Generating built-in policy for TOMOYO 2.4.x.
+       @echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
+       @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
+       @echo "\"\";" >> $@.tmp
+       @echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp
+       @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp
+       @echo "\"\";" >> $@.tmp
+       @echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp
+       @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp
+       @echo "\"\";" >> $@.tmp
+       @echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp
+       @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp
+       @echo "\"\";" >> $@.tmp
+       @echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp
+       @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp
+       @echo "\"\";" >> $@.tmp
+       @mv $@.tmp $@
+
+$(obj)/common.o: $(obj)/builtin-policy.h
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c
new file mode 100644 (file)
index 0000000..5dbb1f7
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * security/tomoyo/audit.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/**
+ * tomoyo_print_bprm - Print "struct linux_binprm" for auditing.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @dump: Pointer to "struct tomoyo_page_dump".
+ *
+ * Returns the contents of @bprm on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_print_bprm(struct linux_binprm *bprm,
+                              struct tomoyo_page_dump *dump)
+{
+       static const int tomoyo_buffer_len = 4096 * 2;
+       char *buffer = kzalloc(tomoyo_buffer_len, GFP_NOFS);
+       char *cp;
+       char *last_start;
+       int len;
+       unsigned long pos = bprm->p;
+       int offset = pos % PAGE_SIZE;
+       int argv_count = bprm->argc;
+       int envp_count = bprm->envc;
+       bool truncated = false;
+       if (!buffer)
+               return NULL;
+       len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
+       cp = buffer + len;
+       if (!argv_count) {
+               memmove(cp, "} envp[]={ ", 11);
+               cp += 11;
+       }
+       last_start = cp;
+       while (argv_count || envp_count) {
+               if (!tomoyo_dump_page(bprm, pos, dump))
+                       goto out;
+               pos += PAGE_SIZE - offset;
+               /* Read. */
+               while (offset < PAGE_SIZE) {
+                       const char *kaddr = dump->data;
+                       const unsigned char c = kaddr[offset++];
+                       if (cp == last_start)
+                               *cp++ = '"';
+                       if (cp >= buffer + tomoyo_buffer_len - 32) {
+                               /* Reserve some room for "..." string. */
+                               truncated = true;
+                       } else if (c == '\\') {
+                               *cp++ = '\\';
+                               *cp++ = '\\';
+                       } else if (c > ' ' && c < 127) {
+                               *cp++ = c;
+                       } else if (!c) {
+                               *cp++ = '"';
+                               *cp++ = ' ';
+                               last_start = cp;
+                       } else {
+                               *cp++ = '\\';
+                               *cp++ = (c >> 6) + '0';
+                               *cp++ = ((c >> 3) & 7) + '0';
+                               *cp++ = (c & 7) + '0';
+                       }
+                       if (c)
+                               continue;
+                       if (argv_count) {
+                               if (--argv_count == 0) {
+                                       if (truncated) {
+                                               cp = last_start;
+                                               memmove(cp, "... ", 4);
+                                               cp += 4;
+                                       }
+                                       memmove(cp, "} envp[]={ ", 11);
+                                       cp += 11;
+                                       last_start = cp;
+                                       truncated = false;
+                               }
+                       } else if (envp_count) {
+                               if (--envp_count == 0) {
+                                       if (truncated) {
+                                               cp = last_start;
+                                               memmove(cp, "... ", 4);
+                                               cp += 4;
+                                       }
+                               }
+                       }
+                       if (!argv_count && !envp_count)
+                               break;
+               }
+               offset = 0;
+       }
+       *cp++ = '}';
+       *cp = '\0';
+       return buffer;
+out:
+       snprintf(buffer, tomoyo_buffer_len - 1,
+                "argv[]={ ... } envp[]= { ... }");
+       return buffer;
+}
+
+/**
+ * tomoyo_filetype - Get string representation of file type.
+ *
+ * @mode: Mode value for stat().
+ *
+ * Returns file type string.
+ */
+static inline const char *tomoyo_filetype(const mode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFREG:
+       case 0:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FILE];
+       case S_IFDIR:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_DIRECTORY];
+       case S_IFLNK:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SYMLINK];
+       case S_IFIFO:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FIFO];
+       case S_IFSOCK:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SOCKET];
+       case S_IFBLK:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_BLOCK_DEV];
+       case S_IFCHR:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_CHAR_DEV];
+       }
+       return "unknown"; /* This should not happen. */
+}
+
+/**
+ * tomoyo_print_header - Get header line of audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns string representation.
+ *
+ * This function uses kmalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_print_header(struct tomoyo_request_info *r)
+{
+       struct tomoyo_time stamp;
+       const pid_t gpid = task_pid_nr(current);
+       struct tomoyo_obj_info *obj = r->obj;
+       static const int tomoyo_buffer_len = 4096;
+       char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
+       int pos;
+       u8 i;
+       if (!buffer)
+               return NULL;
+       {
+               struct timeval tv;
+               do_gettimeofday(&tv);
+               tomoyo_convert_time(tv.tv_sec, &stamp);
+       }
+       pos = snprintf(buffer, tomoyo_buffer_len - 1,
+                      "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
+                      "granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
+                      "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
+                      "fsuid=%u fsgid=%u }", stamp.year, stamp.month,
+                      stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
+                      tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
+                      tomoyo_sys_getpid(), tomoyo_sys_getppid(),
+                      current_uid(), current_gid(), current_euid(),
+                      current_egid(), current_suid(), current_sgid(),
+                      current_fsuid(), current_fsgid());
+       if (!obj)
+               goto no_obj_info;
+       if (!obj->validate_done) {
+               tomoyo_get_attributes(obj);
+               obj->validate_done = true;
+       }
+       for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
+               struct tomoyo_mini_stat *stat;
+               unsigned int dev;
+               mode_t mode;
+               if (!obj->stat_valid[i])
+                       continue;
+               stat = &obj->stat[i];
+               dev = stat->dev;
+               mode = stat->mode;
+               if (i & 1) {
+                       pos += snprintf(buffer + pos,
+                                       tomoyo_buffer_len - 1 - pos,
+                                       " path%u.parent={ uid=%u gid=%u "
+                                       "ino=%lu perm=0%o }", (i >> 1) + 1,
+                                       stat->uid, stat->gid, (unsigned long)
+                                       stat->ino, stat->mode & S_IALLUGO);
+                       continue;
+               }
+               pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+                               " path%u={ uid=%u gid=%u ino=%lu major=%u"
+                               " minor=%u perm=0%o type=%s", (i >> 1) + 1,
+                               stat->uid, stat->gid, (unsigned long)
+                               stat->ino, MAJOR(dev), MINOR(dev),
+                               mode & S_IALLUGO, tomoyo_filetype(mode));
+               if (S_ISCHR(mode) || S_ISBLK(mode)) {
+                       dev = stat->rdev;
+                       pos += snprintf(buffer + pos,
+                                       tomoyo_buffer_len - 1 - pos,
+                                       " dev_major=%u dev_minor=%u",
+                                       MAJOR(dev), MINOR(dev));
+               }
+               pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+                               " }");
+       }
+no_obj_info:
+       if (pos < tomoyo_buffer_len - 1)
+               return buffer;
+       kfree(buffer);
+       return NULL;
+}
+
+/**
+ * tomoyo_init_log - Allocate buffer for audit logs.
+ *
+ * @r:    Pointer to "struct tomoyo_request_info".
+ * @len:  Buffer size needed for @fmt and @args.
+ * @fmt:  The printf()'s format string.
+ * @args: va_list structure for @fmt.
+ *
+ * Returns pointer to allocated memory.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
+                     va_list args)
+{
+       char *buf = NULL;
+       char *bprm_info = NULL;
+       const char *header = NULL;
+       char *realpath = NULL;
+       const char *symlink = NULL;
+       int pos;
+       const char *domainname = r->domain->domainname->name;
+       header = tomoyo_print_header(r);
+       if (!header)
+               return NULL;
+       /* +10 is for '\n' etc. and '\0'. */
+       len += strlen(domainname) + strlen(header) + 10;
+       if (r->ee) {
+               struct file *file = r->ee->bprm->file;
+               realpath = tomoyo_realpath_from_path(&file->f_path);
+               bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
+               if (!realpath || !bprm_info)
+                       goto out;
+               /* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */
+               len += strlen(realpath) + 80 + strlen(bprm_info);
+       } else if (r->obj && r->obj->symlink_target) {
+               symlink = r->obj->symlink_target->name;
+               /* +18 is for " symlink.target=\"%s\"" */
+               len += 18 + strlen(symlink);
+       }
+       len = tomoyo_round2(len);
+       buf = kzalloc(len, GFP_NOFS);
+       if (!buf)
+               goto out;
+       len--;
+       pos = snprintf(buf, len, "%s", header);
+       if (realpath) {
+               struct linux_binprm *bprm = r->ee->bprm;
+               pos += snprintf(buf + pos, len - pos,
+                               " exec={ realpath=\"%s\" argc=%d envc=%d %s }",
+                               realpath, bprm->argc, bprm->envc, bprm_info);
+       } else if (symlink)
+               pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
+                               symlink);
+       pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
+       vsnprintf(buf + pos, len - pos, fmt, args);
+out:
+       kfree(realpath);
+       kfree(bprm_info);
+       kfree(header);
+       return buf;
+}
+
+/* Wait queue for /sys/kernel/security/tomoyo/audit. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait);
+
+/* Structure for audit log. */
+struct tomoyo_log {
+       struct list_head list;
+       char *log;
+       int size;
+};
+
+/* The list for "struct tomoyo_log". */
+static LIST_HEAD(tomoyo_log);
+
+/* Lock for "struct list_head tomoyo_log". */
+static DEFINE_SPINLOCK(tomoyo_log_lock);
+
+/* Length of "stuct list_head tomoyo_log". */
+static unsigned int tomoyo_log_count;
+
+/**
+ * tomoyo_get_audit - Get audit mode.
+ *
+ * @ns:          Pointer to "struct tomoyo_policy_namespace".
+ * @profile:     Profile number.
+ * @index:       Index number of functionality.
+ * @is_granted:  True if granted log, false otherwise.
+ *
+ * Returns true if this request should be audited, false otherwise.
+ */
+static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
+                            const u8 profile, const u8 index,
+                            const bool is_granted)
+{
+       u8 mode;
+       const u8 category = tomoyo_index2category[index] +
+               TOMOYO_MAX_MAC_INDEX;
+       struct tomoyo_profile *p;
+       if (!tomoyo_policy_loaded)
+               return false;
+       p = tomoyo_profile(ns, profile);
+       if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
+               return false;
+       mode = p->config[index];
+       if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+               mode = p->config[category];
+       if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+               mode = p->default_config;
+       if (is_granted)
+               return mode & TOMOYO_CONFIG_WANT_GRANT_LOG;
+       return mode & TOMOYO_CONFIG_WANT_REJECT_LOG;
+}
+
+/**
+ * tomoyo_write_log2 - Write an audit log.
+ *
+ * @r:    Pointer to "struct tomoyo_request_info".
+ * @len:  Buffer size needed for @fmt and @args.
+ * @fmt:  The printf()'s format string.
+ * @args: va_list structure for @fmt.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+                      va_list args)
+{
+       char *buf;
+       struct tomoyo_log *entry;
+       bool quota_exceeded = false;
+       if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
+               goto out;
+       buf = tomoyo_init_log(r, len, fmt, args);
+       if (!buf)
+               goto out;
+       entry = kzalloc(sizeof(*entry), GFP_NOFS);
+       if (!entry) {
+               kfree(buf);
+               goto out;
+       }
+       entry->log = buf;
+       len = tomoyo_round2(strlen(buf) + 1);
+       /*
+        * The entry->size is used for memory quota checks.
+        * Don't go beyond strlen(entry->log).
+        */
+       entry->size = len + tomoyo_round2(sizeof(*entry));
+       spin_lock(&tomoyo_log_lock);
+       if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] &&
+           tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >=
+           tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) {
+               quota_exceeded = true;
+       } else {
+               tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size;
+               list_add_tail(&entry->list, &tomoyo_log);
+               tomoyo_log_count++;
+       }
+       spin_unlock(&tomoyo_log_lock);
+       if (quota_exceeded) {
+               kfree(buf);
+               kfree(entry);
+               goto out;
+       }
+       wake_up(&tomoyo_log_wait);
+out:
+       return;
+}
+
+/**
+ * tomoyo_write_log - Write an audit log.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+       va_list args;
+       int len;
+       va_start(args, fmt);
+       len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+       va_end(args);
+       va_start(args, fmt);
+       tomoyo_write_log2(r, len, fmt, args);
+       va_end(args);
+}
+
+/**
+ * tomoyo_read_log - Read an audit log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+void tomoyo_read_log(struct tomoyo_io_buffer *head)
+{
+       struct tomoyo_log *ptr = NULL;
+       if (head->r.w_pos)
+               return;
+       kfree(head->read_buf);
+       head->read_buf = NULL;
+       spin_lock(&tomoyo_log_lock);
+       if (!list_empty(&tomoyo_log)) {
+               ptr = list_entry(tomoyo_log.next, typeof(*ptr), list);
+               list_del(&ptr->list);
+               tomoyo_log_count--;
+               tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size;
+       }
+       spin_unlock(&tomoyo_log_lock);
+       if (ptr) {
+               head->read_buf = ptr->log;
+               head->r.w[head->r.w_pos++] = head->read_buf;
+               kfree(ptr);
+       }
+}
+
+/**
+ * tomoyo_poll_log - Wait for an audit log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read an audit log.
+ */
+int tomoyo_poll_log(struct file *file, poll_table *wait)
+{
+       if (tomoyo_log_count)
+               return POLLIN | POLLRDNORM;
+       poll_wait(file, &tomoyo_log_wait, wait);
+       if (tomoyo_log_count)
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
index a0d09e5..c8439cf 100644 (file)
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/common.c
  *
- * Common functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/uaccess.h>
 #include <linux/security.h>
 #include "common.h"
 
-static struct tomoyo_profile tomoyo_default_profile = {
-       .learning = &tomoyo_default_profile.preference,
-       .permissive = &tomoyo_default_profile.preference,
-       .enforcing = &tomoyo_default_profile.preference,
-       .preference.enforcing_verbose = true,
-       .preference.learning_max_entry = 2048,
-       .preference.learning_verbose = false,
-       .preference.permissive_verbose = true
+/* String table for operation mode. */
+const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
+       [TOMOYO_CONFIG_DISABLED]   = "disabled",
+       [TOMOYO_CONFIG_LEARNING]   = "learning",
+       [TOMOYO_CONFIG_PERMISSIVE] = "permissive",
+       [TOMOYO_CONFIG_ENFORCING]  = "enforcing"
 };
 
-/* Profile version. Currently only 20090903 is defined. */
-static unsigned int tomoyo_profile_version;
+/* String table for /sys/kernel/security/tomoyo/profile */
+const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+                                      + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+       [TOMOYO_MAC_FILE_EXECUTE]    = "execute",
+       [TOMOYO_MAC_FILE_OPEN]       = "open",
+       [TOMOYO_MAC_FILE_CREATE]     = "create",
+       [TOMOYO_MAC_FILE_UNLINK]     = "unlink",
+       [TOMOYO_MAC_FILE_GETATTR]    = "getattr",
+       [TOMOYO_MAC_FILE_MKDIR]      = "mkdir",
+       [TOMOYO_MAC_FILE_RMDIR]      = "rmdir",
+       [TOMOYO_MAC_FILE_MKFIFO]     = "mkfifo",
+       [TOMOYO_MAC_FILE_MKSOCK]     = "mksock",
+       [TOMOYO_MAC_FILE_TRUNCATE]   = "truncate",
+       [TOMOYO_MAC_FILE_SYMLINK]    = "symlink",
+       [TOMOYO_MAC_FILE_MKBLOCK]    = "mkblock",
+       [TOMOYO_MAC_FILE_MKCHAR]     = "mkchar",
+       [TOMOYO_MAC_FILE_LINK]       = "link",
+       [TOMOYO_MAC_FILE_RENAME]     = "rename",
+       [TOMOYO_MAC_FILE_CHMOD]      = "chmod",
+       [TOMOYO_MAC_FILE_CHOWN]      = "chown",
+       [TOMOYO_MAC_FILE_CHGRP]      = "chgrp",
+       [TOMOYO_MAC_FILE_IOCTL]      = "ioctl",
+       [TOMOYO_MAC_FILE_CHROOT]     = "chroot",
+       [TOMOYO_MAC_FILE_MOUNT]      = "mount",
+       [TOMOYO_MAC_FILE_UMOUNT]     = "unmount",
+       [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
+       [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+};
 
-/* Profile table. Memory is allocated as needed. */
-static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+/* String table for conditions. */
+const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
+       [TOMOYO_TASK_UID]             = "task.uid",
+       [TOMOYO_TASK_EUID]            = "task.euid",
+       [TOMOYO_TASK_SUID]            = "task.suid",
+       [TOMOYO_TASK_FSUID]           = "task.fsuid",
+       [TOMOYO_TASK_GID]             = "task.gid",
+       [TOMOYO_TASK_EGID]            = "task.egid",
+       [TOMOYO_TASK_SGID]            = "task.sgid",
+       [TOMOYO_TASK_FSGID]           = "task.fsgid",
+       [TOMOYO_TASK_PID]             = "task.pid",
+       [TOMOYO_TASK_PPID]            = "task.ppid",
+       [TOMOYO_EXEC_ARGC]            = "exec.argc",
+       [TOMOYO_EXEC_ENVC]            = "exec.envc",
+       [TOMOYO_TYPE_IS_SOCKET]       = "socket",
+       [TOMOYO_TYPE_IS_SYMLINK]      = "symlink",
+       [TOMOYO_TYPE_IS_FILE]         = "file",
+       [TOMOYO_TYPE_IS_BLOCK_DEV]    = "block",
+       [TOMOYO_TYPE_IS_DIRECTORY]    = "directory",
+       [TOMOYO_TYPE_IS_CHAR_DEV]     = "char",
+       [TOMOYO_TYPE_IS_FIFO]         = "fifo",
+       [TOMOYO_MODE_SETUID]          = "setuid",
+       [TOMOYO_MODE_SETGID]          = "setgid",
+       [TOMOYO_MODE_STICKY]          = "sticky",
+       [TOMOYO_MODE_OWNER_READ]      = "owner_read",
+       [TOMOYO_MODE_OWNER_WRITE]     = "owner_write",
+       [TOMOYO_MODE_OWNER_EXECUTE]   = "owner_execute",
+       [TOMOYO_MODE_GROUP_READ]      = "group_read",
+       [TOMOYO_MODE_GROUP_WRITE]     = "group_write",
+       [TOMOYO_MODE_GROUP_EXECUTE]   = "group_execute",
+       [TOMOYO_MODE_OTHERS_READ]     = "others_read",
+       [TOMOYO_MODE_OTHERS_WRITE]    = "others_write",
+       [TOMOYO_MODE_OTHERS_EXECUTE]  = "others_execute",
+       [TOMOYO_EXEC_REALPATH]        = "exec.realpath",
+       [TOMOYO_SYMLINK_TARGET]       = "symlink.target",
+       [TOMOYO_PATH1_UID]            = "path1.uid",
+       [TOMOYO_PATH1_GID]            = "path1.gid",
+       [TOMOYO_PATH1_INO]            = "path1.ino",
+       [TOMOYO_PATH1_MAJOR]          = "path1.major",
+       [TOMOYO_PATH1_MINOR]          = "path1.minor",
+       [TOMOYO_PATH1_PERM]           = "path1.perm",
+       [TOMOYO_PATH1_TYPE]           = "path1.type",
+       [TOMOYO_PATH1_DEV_MAJOR]      = "path1.dev_major",
+       [TOMOYO_PATH1_DEV_MINOR]      = "path1.dev_minor",
+       [TOMOYO_PATH2_UID]            = "path2.uid",
+       [TOMOYO_PATH2_GID]            = "path2.gid",
+       [TOMOYO_PATH2_INO]            = "path2.ino",
+       [TOMOYO_PATH2_MAJOR]          = "path2.major",
+       [TOMOYO_PATH2_MINOR]          = "path2.minor",
+       [TOMOYO_PATH2_PERM]           = "path2.perm",
+       [TOMOYO_PATH2_TYPE]           = "path2.type",
+       [TOMOYO_PATH2_DEV_MAJOR]      = "path2.dev_major",
+       [TOMOYO_PATH2_DEV_MINOR]      = "path2.dev_minor",
+       [TOMOYO_PATH1_PARENT_UID]     = "path1.parent.uid",
+       [TOMOYO_PATH1_PARENT_GID]     = "path1.parent.gid",
+       [TOMOYO_PATH1_PARENT_INO]     = "path1.parent.ino",
+       [TOMOYO_PATH1_PARENT_PERM]    = "path1.parent.perm",
+       [TOMOYO_PATH2_PARENT_UID]     = "path2.parent.uid",
+       [TOMOYO_PATH2_PARENT_GID]     = "path2.parent.gid",
+       [TOMOYO_PATH2_PARENT_INO]     = "path2.parent.ino",
+       [TOMOYO_PATH2_PARENT_PERM]    = "path2.parent.perm",
+};
 
-/* String table for functionality that takes 4 modes. */
-static const char *tomoyo_mode[4] = {
-       "disabled", "learning", "permissive", "enforcing"
+/* String table for PREFERENCE keyword. */
+static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = {
+       [TOMOYO_PREF_MAX_AUDIT_LOG]      = "max_audit_log",
+       [TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",
 };
 
-/* String table for /sys/kernel/security/tomoyo/profile */
-static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
-                                      + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
-       [TOMOYO_MAC_FILE_EXECUTE]    = "file::execute",
-       [TOMOYO_MAC_FILE_OPEN]       = "file::open",
-       [TOMOYO_MAC_FILE_CREATE]     = "file::create",
-       [TOMOYO_MAC_FILE_UNLINK]     = "file::unlink",
-       [TOMOYO_MAC_FILE_MKDIR]      = "file::mkdir",
-       [TOMOYO_MAC_FILE_RMDIR]      = "file::rmdir",
-       [TOMOYO_MAC_FILE_MKFIFO]     = "file::mkfifo",
-       [TOMOYO_MAC_FILE_MKSOCK]     = "file::mksock",
-       [TOMOYO_MAC_FILE_TRUNCATE]   = "file::truncate",
-       [TOMOYO_MAC_FILE_SYMLINK]    = "file::symlink",
-       [TOMOYO_MAC_FILE_REWRITE]    = "file::rewrite",
-       [TOMOYO_MAC_FILE_MKBLOCK]    = "file::mkblock",
-       [TOMOYO_MAC_FILE_MKCHAR]     = "file::mkchar",
-       [TOMOYO_MAC_FILE_LINK]       = "file::link",
-       [TOMOYO_MAC_FILE_RENAME]     = "file::rename",
-       [TOMOYO_MAC_FILE_CHMOD]      = "file::chmod",
-       [TOMOYO_MAC_FILE_CHOWN]      = "file::chown",
-       [TOMOYO_MAC_FILE_CHGRP]      = "file::chgrp",
-       [TOMOYO_MAC_FILE_IOCTL]      = "file::ioctl",
-       [TOMOYO_MAC_FILE_CHROOT]     = "file::chroot",
-       [TOMOYO_MAC_FILE_MOUNT]      = "file::mount",
-       [TOMOYO_MAC_FILE_UMOUNT]     = "file::umount",
-       [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root",
-       [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+/* String table for path operation. */
+const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+       [TOMOYO_TYPE_EXECUTE]    = "execute",
+       [TOMOYO_TYPE_READ]       = "read",
+       [TOMOYO_TYPE_WRITE]      = "write",
+       [TOMOYO_TYPE_APPEND]     = "append",
+       [TOMOYO_TYPE_UNLINK]     = "unlink",
+       [TOMOYO_TYPE_GETATTR]    = "getattr",
+       [TOMOYO_TYPE_RMDIR]      = "rmdir",
+       [TOMOYO_TYPE_TRUNCATE]   = "truncate",
+       [TOMOYO_TYPE_SYMLINK]    = "symlink",
+       [TOMOYO_TYPE_CHROOT]     = "chroot",
+       [TOMOYO_TYPE_UMOUNT]     = "unmount",
+};
+
+/* String table for categories. */
+static const char * const tomoyo_category_keywords
+[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+       [TOMOYO_MAC_CATEGORY_FILE]       = "file",
 };
 
 /* Permit policy management by non-root user? */
@@ -71,11 +146,20 @@ static bool tomoyo_manage_by_non_root;
  *
  * @value: Bool value.
  */
-static const char *tomoyo_yesno(const unsigned int value)
+const char *tomoyo_yesno(const unsigned int value)
 {
        return value ? "yes" : "no";
 }
 
+/**
+ * tomoyo_addprintf - strncat()-like-snprintf().
+ *
+ * @buffer: Buffer to write to. Must be '\0'-terminated.
+ * @len:    Size of @buffer.
+ * @fmt:    The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
 static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
 {
        va_list args;
@@ -96,7 +180,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
 {
        while (head->r.w_pos) {
                const char *w = head->r.w[0];
-               int len = strlen(w);
+               size_t len = strlen(w);
                if (len) {
                        if (len > head->read_user_buf_avail)
                                len = head->read_user_buf_avail;
@@ -111,7 +195,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
                head->r.w[0] = w;
                if (*w)
                        return false;
-               /* Add '\0' for query. */
+               /* Add '\0' for audit logs and query. */
                if (head->poll) {
                        if (!head->read_user_buf_avail ||
                            copy_to_user(head->read_user_buf, "", 1))
@@ -155,8 +239,8 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
 void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
 {
        va_list args;
-       int len;
-       int pos = head->r.avail;
+       size_t len;
+       size_t pos = head->r.avail;
        int size = head->readbuf_size - pos;
        if (size <= 0)
                return;
@@ -171,11 +255,25 @@ void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
        tomoyo_set_string(head, head->read_buf + pos);
 }
 
+/**
+ * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_set_space(struct tomoyo_io_buffer *head)
 {
        tomoyo_set_string(head, " ");
 }
 
+/**
+ * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
 static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
 {
        tomoyo_set_string(head, "\n");
@@ -183,6 +281,62 @@ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
 }
 
 /**
+ * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_slash(struct tomoyo_io_buffer *head)
+{
+       tomoyo_set_string(head, "/");
+}
+
+/* List of namespaces. */
+LIST_HEAD(tomoyo_namespace_list);
+/* True if namespace other than tomoyo_kernel_namespace is defined. */
+static bool tomoyo_namespace_enabled;
+
+/**
+ * tomoyo_init_policy_namespace - Initialize namespace.
+ *
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ *
+ * Returns nothing.
+ */
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
+{
+       unsigned int idx;
+       for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
+               INIT_LIST_HEAD(&ns->acl_group[idx]);
+       for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
+               INIT_LIST_HEAD(&ns->group_list[idx]);
+       for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
+               INIT_LIST_HEAD(&ns->policy_list[idx]);
+       ns->profile_version = 20100903;
+       tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
+       list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
+}
+
+/**
+ * tomoyo_print_namespace - Print namespace header.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_namespace(struct tomoyo_io_buffer *head)
+{
+       if (!tomoyo_namespace_enabled)
+               return;
+       tomoyo_set_string(head,
+                         container_of(head->r.ns,
+                                      struct tomoyo_policy_namespace,
+                                      namespace_list)->name);
+       tomoyo_set_space(head);
+}
+
+/**
  * tomoyo_print_name_union - Print a tomoyo_name_union.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -192,7 +346,7 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
                                    const struct tomoyo_name_union *ptr)
 {
        tomoyo_set_space(head);
-       if (ptr->is_group) {
+       if (ptr->group) {
                tomoyo_set_string(head, "@");
                tomoyo_set_string(head, ptr->group->group_name->name);
        } else {
@@ -201,24 +355,46 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
 }
 
 /**
- * tomoyo_print_number_union - Print a tomoyo_number_union.
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
  *
- * @head:       Pointer to "struct tomoyo_io_buffer".
- * @ptr:        Pointer to "struct tomoyo_number_union".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
  */
-static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
-                                     const struct tomoyo_number_union *ptr)
+static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+                                          const struct tomoyo_name_union *ptr)
 {
-       tomoyo_set_space(head);
-       if (ptr->is_group) {
+       if (ptr->group) {
+               tomoyo_set_string(head, "@");
+               tomoyo_set_string(head, ptr->group->group_name->name);
+       } else {
+               tomoyo_set_string(head, "\"");
+               tomoyo_set_string(head, ptr->filename->name);
+               tomoyo_set_string(head, "\"");
+       }
+}
+
+/**
+ * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union_nospace
+(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr)
+{
+       if (ptr->group) {
                tomoyo_set_string(head, "@");
                tomoyo_set_string(head, ptr->group->group_name->name);
        } else {
                int i;
                unsigned long min = ptr->values[0];
                const unsigned long max = ptr->values[1];
-               u8 min_type = ptr->min_type;
-               const u8 max_type = ptr->max_type;
+               u8 min_type = ptr->value_type[0];
+               const u8 max_type = ptr->value_type[1];
                char buffer[128];
                buffer[0] = '\0';
                for (i = 0; i < 2; i++) {
@@ -232,8 +408,8 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
                                                 "0%lo", min);
                                break;
                        default:
-                               tomoyo_addprintf(buffer, sizeof(buffer),
-                                                "%lu", min);
+                               tomoyo_addprintf(buffer, sizeof(buffer), "%lu",
+                                                min);
                                break;
                        }
                        if (min == max && min_type == max_type)
@@ -247,35 +423,53 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
 }
 
 /**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+                                     const struct tomoyo_number_union *ptr)
+{
+       tomoyo_set_space(head);
+       tomoyo_print_number_union_nospace(head, ptr);
+}
+
+/**
  * tomoyo_assign_profile - Create a new profile.
  *
+ * @ns:      Pointer to "struct tomoyo_policy_namespace".
  * @profile: Profile number to create.
  *
  * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
  */
-static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
+static struct tomoyo_profile *tomoyo_assign_profile
+(struct tomoyo_policy_namespace *ns, const unsigned int profile)
 {
        struct tomoyo_profile *ptr;
        struct tomoyo_profile *entry;
        if (profile >= TOMOYO_MAX_PROFILES)
                return NULL;
-       ptr = tomoyo_profile_ptr[profile];
+       ptr = ns->profile_ptr[profile];
        if (ptr)
                return ptr;
        entry = kzalloc(sizeof(*entry), GFP_NOFS);
        if (mutex_lock_interruptible(&tomoyo_policy_lock))
                goto out;
-       ptr = tomoyo_profile_ptr[profile];
+       ptr = ns->profile_ptr[profile];
        if (!ptr && tomoyo_memory_ok(entry)) {
                ptr = entry;
-               ptr->learning = &tomoyo_default_profile.preference;
-               ptr->permissive = &tomoyo_default_profile.preference;
-               ptr->enforcing = &tomoyo_default_profile.preference;
-               ptr->default_config = TOMOYO_CONFIG_DISABLED;
+               ptr->default_config = TOMOYO_CONFIG_DISABLED |
+                       TOMOYO_CONFIG_WANT_GRANT_LOG |
+                       TOMOYO_CONFIG_WANT_REJECT_LOG;
                memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
                       sizeof(ptr->config));
+               ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024;
+               ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;
                mb(); /* Avoid out-of-order execution. */
-               tomoyo_profile_ptr[profile] = ptr;
+               ns->profile_ptr[profile] = ptr;
                entry = NULL;
        }
        mutex_unlock(&tomoyo_policy_lock);
@@ -287,19 +481,29 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
 /**
  * tomoyo_profile - Find a profile.
  *
+ * @ns:      Pointer to "struct tomoyo_policy_namespace".
  * @profile: Profile number to find.
  *
  * Returns pointer to "struct tomoyo_profile".
  */
-struct tomoyo_profile *tomoyo_profile(const u8 profile)
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+                                     const u8 profile)
 {
-       struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
-       if (!tomoyo_policy_loaded)
-               return &tomoyo_default_profile;
-       BUG_ON(!ptr);
+       static struct tomoyo_profile tomoyo_null_profile;
+       struct tomoyo_profile *ptr = ns->profile_ptr[profile];
+       if (!ptr)
+               ptr = &tomoyo_null_profile;
        return ptr;
 }
 
+/**
+ * tomoyo_find_yesno - Find values for specified keyword.
+ *
+ * @string: String to check.
+ * @find:   Name of keyword.
+ *
+ * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise.
+ */
 static s8 tomoyo_find_yesno(const char *string, const char *find)
 {
        const char *cp = strstr(string, find);
@@ -313,18 +517,15 @@ static s8 tomoyo_find_yesno(const char *string, const char *find)
        return -1;
 }
 
-static void tomoyo_set_bool(bool *b, const char *string, const char *find)
-{
-       switch (tomoyo_find_yesno(string, find)) {
-       case 1:
-               *b = true;
-               break;
-       case 0:
-               *b = false;
-               break;
-       }
-}
-
+/**
+ * tomoyo_set_uint - Set value for specified preference.
+ *
+ * @i:      Pointer to "unsigned int".
+ * @string: String to check.
+ * @find:   Name of keyword.
+ *
+ * Returns nothing.
+ */
 static void tomoyo_set_uint(unsigned int *i, const char *string,
                            const char *find)
 {
@@ -333,51 +534,16 @@ static void tomoyo_set_uint(unsigned int *i, const char *string,
                sscanf(cp + strlen(find), "=%u", i);
 }
 
-static void tomoyo_set_pref(const char *name, const char *value,
-                           const bool use_default,
-                           struct tomoyo_profile *profile)
-{
-       struct tomoyo_preference **pref;
-       bool *verbose;
-       if (!strcmp(name, "enforcing")) {
-               if (use_default) {
-                       pref = &profile->enforcing;
-                       goto set_default;
-               }
-               profile->enforcing = &profile->preference;
-               verbose = &profile->preference.enforcing_verbose;
-               goto set_verbose;
-       }
-       if (!strcmp(name, "permissive")) {
-               if (use_default) {
-                       pref = &profile->permissive;
-                       goto set_default;
-               }
-               profile->permissive = &profile->preference;
-               verbose = &profile->preference.permissive_verbose;
-               goto set_verbose;
-       }
-       if (!strcmp(name, "learning")) {
-               if (use_default) {
-                       pref = &profile->learning;
-                       goto set_default;
-               }
-               profile->learning = &profile->preference;
-               tomoyo_set_uint(&profile->preference.learning_max_entry, value,
-                            "max_entry");
-               verbose = &profile->preference.learning_verbose;
-               goto set_verbose;
-       }
-       return;
- set_default:
-       *pref = &tomoyo_default_profile.preference;
-       return;
- set_verbose:
-       tomoyo_set_bool(verbose, value, "verbose");
-}
-
+/**
+ * tomoyo_set_mode - Set mode for specified profile.
+ *
+ * @name:    Name of functionality.
+ * @value:   Mode for @name.
+ * @profile: Pointer to "struct tomoyo_profile".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_set_mode(char *name, const char *value,
-                          const bool use_default,
                           struct tomoyo_profile *profile)
 {
        u8 i;
@@ -389,7 +555,17 @@ static int tomoyo_set_mode(char *name, const char *value,
                config = 0;
                for (i = 0; i < TOMOYO_MAX_MAC_INDEX
                             + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
-                       if (strcmp(name, tomoyo_mac_keywords[i]))
+                       int len = 0;
+                       if (i < TOMOYO_MAX_MAC_INDEX) {
+                               const u8 c = tomoyo_index2category[i];
+                               const char *category =
+                                       tomoyo_category_keywords[c];
+                               len = strlen(category);
+                               if (strncmp(name, category, len) ||
+                                   name[len++] != ':' || name[len++] != ':')
+                                       continue;
+                       }
+                       if (strcmp(name + len, tomoyo_mac_keywords[i]))
                                continue;
                        config = profile->config[i];
                        break;
@@ -399,7 +575,7 @@ static int tomoyo_set_mode(char *name, const char *value,
        } else {
                return -EINVAL;
        }
-       if (use_default) {
+       if (strstr(value, "use_default")) {
                config = TOMOYO_CONFIG_USE_DEFAULT;
        } else {
                u8 mode;
@@ -410,6 +586,24 @@ static int tomoyo_set_mode(char *name, const char *value,
                                 * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
                                 */
                                config = (config & ~7) | mode;
+               if (config != TOMOYO_CONFIG_USE_DEFAULT) {
+                       switch (tomoyo_find_yesno(value, "grant_log")) {
+                       case 1:
+                               config |= TOMOYO_CONFIG_WANT_GRANT_LOG;
+                               break;
+                       case 0:
+                               config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG;
+                               break;
+                       }
+                       switch (tomoyo_find_yesno(value, "reject_log")) {
+                       case 1:
+                               config |= TOMOYO_CONFIG_WANT_REJECT_LOG;
+                               break;
+                       case 0:
+                               config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG;
+                               break;
+                       }
+               }
        }
        if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
                profile->config[i] = config;
@@ -429,34 +623,22 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
 {
        char *data = head->write_buf;
        unsigned int i;
-       bool use_default = false;
        char *cp;
        struct tomoyo_profile *profile;
-       if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1)
+       if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
+           == 1)
                return 0;
        i = simple_strtoul(data, &cp, 10);
-       if (data == cp) {
-               profile = &tomoyo_default_profile;
-       } else {
-               if (*cp != '-')
-                       return -EINVAL;
-               data = cp + 1;
-               profile = tomoyo_assign_profile(i);
-               if (!profile)
-                       return -EINVAL;
-       }
+       if (*cp != '-')
+               return -EINVAL;
+       data = cp + 1;
+       profile = tomoyo_assign_profile(head->w.ns, i);
+       if (!profile)
+               return -EINVAL;
        cp = strchr(data, '=');
        if (!cp)
                return -EINVAL;
        *cp++ = '\0';
-       if (profile != &tomoyo_default_profile)
-               use_default = strstr(cp, "use_default") != NULL;
-       if (tomoyo_str_starts(&data, "PREFERENCE::")) {
-               tomoyo_set_pref(data, cp, use_default, profile);
-               return 0;
-       }
-       if (profile == &tomoyo_default_profile)
-               return -EINVAL;
        if (!strcmp(data, "COMMENT")) {
                static DEFINE_SPINLOCK(lock);
                const struct tomoyo_path_info *new_comment
@@ -471,77 +653,62 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
                tomoyo_put_name(old_comment);
                return 0;
        }
-       return tomoyo_set_mode(data, cp, use_default, profile);
-}
-
-static void tomoyo_print_preference(struct tomoyo_io_buffer *head,
-                                   const int idx)
-{
-       struct tomoyo_preference *pref = &tomoyo_default_profile.preference;
-       const struct tomoyo_profile *profile = idx >= 0 ?
-               tomoyo_profile_ptr[idx] : NULL;
-       char buffer[16] = "";
-       if (profile) {
-               buffer[sizeof(buffer) - 1] = '\0';
-               snprintf(buffer, sizeof(buffer) - 1, "%u-", idx);
-       }
-       if (profile) {
-               pref = profile->learning;
-               if (pref == &tomoyo_default_profile.preference)
-                       goto skip1;
-       }
-       tomoyo_io_printf(head, "%sPREFERENCE::%s={ "
-                        "verbose=%s max_entry=%u }\n",
-                        buffer, "learning",
-                        tomoyo_yesno(pref->learning_verbose),
-                        pref->learning_max_entry);
- skip1:
-       if (profile) {
-               pref = profile->permissive;
-               if (pref == &tomoyo_default_profile.preference)
-                       goto skip2;
-       }
-       tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
-                        buffer, "permissive",
-                        tomoyo_yesno(pref->permissive_verbose));
- skip2:
-       if (profile) {
-               pref = profile->enforcing;
-               if (pref == &tomoyo_default_profile.preference)
-                       return;
+       if (!strcmp(data, "PREFERENCE")) {
+               for (i = 0; i < TOMOYO_MAX_PREF; i++)
+                       tomoyo_set_uint(&profile->pref[i], cp,
+                                       tomoyo_pref_keywords[i]);
+               return 0;
        }
-       tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
-                        buffer, "enforcing",
-                        tomoyo_yesno(pref->enforcing_verbose));
+       return tomoyo_set_mode(data, cp, profile);
 }
 
+/**
+ * tomoyo_print_config - Print mode for specified functionality.
+ *
+ * @head:   Pointer to "struct tomoyo_io_buffer".
+ * @config: Mode for that functionality.
+ *
+ * Returns nothing.
+ *
+ * Caller prints functionality's name.
+ */
 static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config)
 {
-       tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]);
+       tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n",
+                        tomoyo_mode[config & 3],
+                        tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG),
+                        tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG));
 }
 
 /**
  * tomoyo_read_profile - Read profile table.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
  */
 static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
 {
        u8 index;
+       struct tomoyo_policy_namespace *ns =
+               container_of(head->r.ns, typeof(*ns), namespace_list);
        const struct tomoyo_profile *profile;
+       if (head->r.eof)
+               return;
  next:
        index = head->r.index;
-       profile = tomoyo_profile_ptr[index];
+       profile = ns->profile_ptr[index];
        switch (head->r.step) {
        case 0:
-               tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
-               tomoyo_print_preference(head, -1);
+               tomoyo_print_namespace(head);
+               tomoyo_io_printf(head, "PROFILE_VERSION=%u\n",
+                                ns->profile_version);
                head->r.step++;
                break;
        case 1:
                for ( ; head->r.index < TOMOYO_MAX_PROFILES;
                      head->r.index++)
-                       if (tomoyo_profile_ptr[head->r.index])
+                       if (ns->profile_ptr[head->r.index])
                                break;
                if (head->r.index == TOMOYO_MAX_PROFILES)
                        return;
@@ -549,16 +716,25 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
                break;
        case 2:
                {
+                       u8 i;
                        const struct tomoyo_path_info *comment =
                                profile->comment;
+                       tomoyo_print_namespace(head);
                        tomoyo_io_printf(head, "%u-COMMENT=", index);
                        tomoyo_set_string(head, comment ? comment->name : "");
                        tomoyo_set_lf(head);
+                       tomoyo_io_printf(head, "%u-PREFERENCE={ ", index);
+                       for (i = 0; i < TOMOYO_MAX_PREF; i++)
+                               tomoyo_io_printf(head, "%s=%u ",
+                                                tomoyo_pref_keywords[i],
+                                                profile->pref[i]);
+                       tomoyo_set_string(head, "}\n");
                        head->r.step++;
                }
                break;
        case 3:
                {
+                       tomoyo_print_namespace(head);
                        tomoyo_io_printf(head, "%u-%s", index, "CONFIG");
                        tomoyo_print_config(head, profile->default_config);
                        head->r.bit = 0;
@@ -572,15 +748,22 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
                        const u8 config = profile->config[i];
                        if (config == TOMOYO_CONFIG_USE_DEFAULT)
                                continue;
-                       tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::",
-                                        tomoyo_mac_keywords[i]);
+                       tomoyo_print_namespace(head);
+                       if (i < TOMOYO_MAX_MAC_INDEX)
+                               tomoyo_io_printf(head, "%u-CONFIG::%s::%s",
+                                                index,
+                                                tomoyo_category_keywords
+                                                [tomoyo_index2category[i]],
+                                                tomoyo_mac_keywords[i]);
+                       else
+                               tomoyo_io_printf(head, "%u-CONFIG::%s", index,
+                                                tomoyo_mac_keywords[i]);
                        tomoyo_print_config(head, config);
                        head->r.bit++;
                        break;
                }
                if (head->r.bit == TOMOYO_MAX_MAC_INDEX
                    + TOMOYO_MAX_MAC_CATEGORY_INDEX) {
-                       tomoyo_print_preference(head, index);
                        head->r.index++;
                        head->r.step = 1;
                }
@@ -590,6 +773,14 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
                goto next;
 }
 
+/**
+ * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static bool tomoyo_same_manager(const struct tomoyo_acl_head *a,
                                const struct tomoyo_acl_head *b)
 {
@@ -611,8 +802,13 @@ static int tomoyo_update_manager_entry(const char *manager,
                                       const bool is_delete)
 {
        struct tomoyo_manager e = { };
-       int error;
-
+       struct tomoyo_acl_param param = {
+               /* .ns = &tomoyo_kernel_namespace, */
+               .is_delete = is_delete,
+               .list = &tomoyo_kernel_namespace.
+               policy_list[TOMOYO_ID_MANAGER],
+       };
+       int error = is_delete ? -ENOENT : -ENOMEM;
        if (tomoyo_domain_def(manager)) {
                if (!tomoyo_correct_domain(manager))
                        return -EINVAL;
@@ -622,12 +818,11 @@ static int tomoyo_update_manager_entry(const char *manager,
                        return -EINVAL;
        }
        e.manager = tomoyo_get_name(manager);
-       if (!e.manager)
-               return -ENOMEM;
-       error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-                                    &tomoyo_policy_list[TOMOYO_ID_MANAGER],
-                                    tomoyo_same_manager);
-       tomoyo_put_name(e.manager);
+       if (e.manager) {
+               error = tomoyo_update_policy(&e.head, sizeof(e), &param,
+                                            tomoyo_same_manager);
+               tomoyo_put_name(e.manager);
+       }
        return error;
 }
 
@@ -643,13 +838,12 @@ static int tomoyo_update_manager_entry(const char *manager,
 static int tomoyo_write_manager(struct tomoyo_io_buffer *head)
 {
        char *data = head->write_buf;
-       bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
 
        if (!strcmp(data, "manage_by_non_root")) {
-               tomoyo_manage_by_non_root = !is_delete;
+               tomoyo_manage_by_non_root = !head->w.is_delete;
                return 0;
        }
-       return tomoyo_update_manager_entry(data, is_delete);
+       return tomoyo_update_manager_entry(data, head->w.is_delete);
 }
 
 /**
@@ -663,8 +857,8 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head)
 {
        if (head->r.eof)
                return;
-       list_for_each_cookie(head->r.acl,
-                            &tomoyo_policy_list[TOMOYO_ID_MANAGER]) {
+       list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.
+                            policy_list[TOMOYO_ID_MANAGER]) {
                struct tomoyo_manager *ptr =
                        list_entry(head->r.acl, typeof(*ptr), head.list);
                if (ptr->head.is_deleted)
@@ -697,8 +891,8 @@ static bool tomoyo_manager(void)
                return true;
        if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
                return false;
-       list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
-                               head.list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+                               policy_list[TOMOYO_ID_MANAGER], head.list) {
                if (!ptr->head.is_deleted && ptr->is_domain
                    && !tomoyo_pathcmp(domainname, ptr->manager)) {
                        found = true;
@@ -710,8 +904,8 @@ static bool tomoyo_manager(void)
        exe = tomoyo_get_exe();
        if (!exe)
                return false;
-       list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
-                               head.list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+                               policy_list[TOMOYO_ID_MANAGER], head.list) {
                if (!ptr->head.is_deleted && !ptr->is_domain
                    && !strcmp(exe, ptr->manager->name)) {
                        found = true;
@@ -732,7 +926,7 @@ static bool tomoyo_manager(void)
 }
 
 /**
- * tomoyo_select_one - Parse select command.
+ * tomoyo_select_domain - Parse select command.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @data: String to parse.
@@ -741,16 +935,15 @@ static bool tomoyo_manager(void)
  *
  * Caller holds tomoyo_read_lock().
  */
-static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
+static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
+                                const char *data)
 {
        unsigned int pid;
        struct tomoyo_domain_info *domain = NULL;
        bool global_pid = false;
-
-       if (!strcmp(data, "allow_execute")) {
-               head->r.print_execute_only = true;
-               return true;
-       }
+       if (strncmp(data, "select ", 7))
+               return false;
+       data += 7;
        if (sscanf(data, "pid=%u", &pid) == 1 ||
            (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
                struct task_struct *p;
@@ -769,7 +962,7 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
                        domain = tomoyo_find_domain(data + 7);
        } else
                return false;
-       head->write_var1 = domain;
+       head->w.domain = domain;
        /* Accessing read_buf is safe because head->io_sem is held. */
        if (!head->read_buf)
                return true; /* Do nothing if open(O_WRONLY). */
@@ -821,20 +1014,47 @@ static int tomoyo_delete_domain(char *domainname)
 /**
  * tomoyo_write_domain2 - Write domain policy.
  *
- * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ns:        Pointer to "struct tomoyo_policy_namespace".
+ * @list:      Pointer to "struct list_head".
+ * @data:      Policy to be interpreted.
+ * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
+static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
+                               struct list_head *list, char *data,
                                const bool is_delete)
 {
-       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
-               return tomoyo_write_mount(data, domain, is_delete);
-       return tomoyo_write_file(data, domain, is_delete);
+       struct tomoyo_acl_param param = {
+               .ns = ns,
+               .list = list,
+               .data = data,
+               .is_delete = is_delete,
+       };
+       static const struct {
+               const char *keyword;
+               int (*write) (struct tomoyo_acl_param *);
+       } tomoyo_callback[1] = {
+               { "file ", tomoyo_write_file },
+       };
+       u8 i;
+       for (i = 0; i < 1; i++) {
+               if (!tomoyo_str_starts(&param.data,
+                                      tomoyo_callback[i].keyword))
+                       continue;
+               return tomoyo_callback[i].write(&param);
+       }
+       return -EINVAL;
 }
 
+/* String table for domain flags. */
+const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = {
+       [TOMOYO_DIF_QUOTA_WARNED]      = "quota_exceeded\n",
+       [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n",
+};
+
 /**
  * tomoyo_write_domain - Write domain policy.
  *
@@ -847,69 +1067,198 @@ static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
 static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
 {
        char *data = head->write_buf;
-       struct tomoyo_domain_info *domain = head->write_var1;
-       bool is_delete = false;
-       bool is_select = false;
+       struct tomoyo_policy_namespace *ns;
+       struct tomoyo_domain_info *domain = head->w.domain;
+       const bool is_delete = head->w.is_delete;
+       bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
        unsigned int profile;
-
-       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
-               is_delete = true;
-       else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
-               is_select = true;
-       if (is_select && tomoyo_select_one(head, data))
-               return 0;
-       /* Don't allow updating policies by non manager programs. */
-       if (!tomoyo_manager())
-               return -EPERM;
-       if (tomoyo_domain_def(data)) {
+       if (*data == '<') {
                domain = NULL;
                if (is_delete)
                        tomoyo_delete_domain(data);
                else if (is_select)
                        domain = tomoyo_find_domain(data);
                else
-                       domain = tomoyo_assign_domain(data, 0);
-               head->write_var1 = domain;
+                       domain = tomoyo_assign_domain(data, false);
+               head->w.domain = domain;
                return 0;
        }
        if (!domain)
                return -EINVAL;
-
-       if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+       ns = domain->ns;
+       if (sscanf(data, "use_profile %u", &profile) == 1
            && profile < TOMOYO_MAX_PROFILES) {
-               if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+               if (!tomoyo_policy_loaded || ns->profile_ptr[profile])
                        domain->profile = (u8) profile;
                return 0;
        }
-       if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
-               domain->ignore_global_allow_read = !is_delete;
+       if (sscanf(data, "use_group %u\n", &profile) == 1
+           && profile < TOMOYO_MAX_ACL_GROUPS) {
+               if (!is_delete)
+                       domain->group = (u8) profile;
                return 0;
        }
-       if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) {
-               domain->quota_warned = !is_delete;
-               return 0;
-       }
-       if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) {
-               domain->transition_failed = !is_delete;
+       for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
+               const char *cp = tomoyo_dif[profile];
+               if (strncmp(data, cp, strlen(cp) - 1))
+                       continue;
+               domain->flags[profile] = !is_delete;
                return 0;
        }
-       return tomoyo_write_domain2(data, domain, is_delete);
+       return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
+                                   is_delete);
 }
 
 /**
- * tomoyo_fns - Find next set bit.
+ * tomoyo_print_condition - Print condition part.
  *
- * @perm: 8 bits value.
- * @bit:  First bit to find.
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @cond: Pointer to "struct tomoyo_condition".
  *
- * Returns next on-bit on success, 8 otherwise.
+ * Returns true on success, false otherwise.
  */
-static u8 tomoyo_fns(const u8 perm, u8 bit)
+static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
+                                  const struct tomoyo_condition *cond)
 {
-       for ( ; bit < 8; bit++)
-               if (perm & (1 << bit))
+       switch (head->r.cond_step) {
+       case 0:
+               head->r.cond_index = 0;
+               head->r.cond_step++;
+               /* fall through */
+       case 1:
+               {
+                       const u16 condc = cond->condc;
+                       const struct tomoyo_condition_element *condp =
+                               (typeof(condp)) (cond + 1);
+                       const struct tomoyo_number_union *numbers_p =
+                               (typeof(numbers_p)) (condp + condc);
+                       const struct tomoyo_name_union *names_p =
+                               (typeof(names_p))
+                               (numbers_p + cond->numbers_count);
+                       const struct tomoyo_argv *argv =
+                               (typeof(argv)) (names_p + cond->names_count);
+                       const struct tomoyo_envp *envp =
+                               (typeof(envp)) (argv + cond->argc);
+                       u16 skip;
+                       for (skip = 0; skip < head->r.cond_index; skip++) {
+                               const u8 left = condp->left;
+                               const u8 right = condp->right;
+                               condp++;
+                               switch (left) {
+                               case TOMOYO_ARGV_ENTRY:
+                                       argv++;
+                                       continue;
+                               case TOMOYO_ENVP_ENTRY:
+                                       envp++;
+                                       continue;
+                               case TOMOYO_NUMBER_UNION:
+                                       numbers_p++;
+                                       break;
+                               }
+                               switch (right) {
+                               case TOMOYO_NAME_UNION:
+                                       names_p++;
+                                       break;
+                               case TOMOYO_NUMBER_UNION:
+                                       numbers_p++;
+                                       break;
+                               }
+                       }
+                       while (head->r.cond_index < condc) {
+                               const u8 match = condp->equals;
+                               const u8 left = condp->left;
+                               const u8 right = condp->right;
+                               if (!tomoyo_flush(head))
+                                       return false;
+                               condp++;
+                               head->r.cond_index++;
+                               tomoyo_set_space(head);
+                               switch (left) {
+                               case TOMOYO_ARGV_ENTRY:
+                                       tomoyo_io_printf(head,
+                                                        "exec.argv[%lu]%s=\"",
+                                                        argv->index, argv->
+                                                        is_not ? "!" : "");
+                                       tomoyo_set_string(head,
+                                                         argv->value->name);
+                                       tomoyo_set_string(head, "\"");
+                                       argv++;
+                                       continue;
+                               case TOMOYO_ENVP_ENTRY:
+                                       tomoyo_set_string(head,
+                                                         "exec.envp[\"");
+                                       tomoyo_set_string(head,
+                                                         envp->name->name);
+                                       tomoyo_io_printf(head, "\"]%s=", envp->
+                                                        is_not ? "!" : "");
+                                       if (envp->value) {
+                                               tomoyo_set_string(head, "\"");
+                                               tomoyo_set_string(head, envp->
+                                                                 value->name);
+                                               tomoyo_set_string(head, "\"");
+                                       } else {
+                                               tomoyo_set_string(head,
+                                                                 "NULL");
+                                       }
+                                       envp++;
+                                       continue;
+                               case TOMOYO_NUMBER_UNION:
+                                       tomoyo_print_number_union_nospace
+                                               (head, numbers_p++);
+                                       break;
+                               default:
+                                       tomoyo_set_string(head,
+                                              tomoyo_condition_keyword[left]);
+                                       break;
+                               }
+                               tomoyo_set_string(head, match ? "=" : "!=");
+                               switch (right) {
+                               case TOMOYO_NAME_UNION:
+                                       tomoyo_print_name_union_quoted
+                                               (head, names_p++);
+                                       break;
+                               case TOMOYO_NUMBER_UNION:
+                                       tomoyo_print_number_union_nospace
+                                               (head, numbers_p++);
+                                       break;
+                               default:
+                                       tomoyo_set_string(head,
+                                         tomoyo_condition_keyword[right]);
+                                       break;
+                               }
+                       }
+               }
+               head->r.cond_step++;
+               /* fall through */
+       case 2:
+               if (!tomoyo_flush(head))
                        break;
-       return bit;
+               head->r.cond_step++;
+               /* fall through */
+       case 3:
+               tomoyo_set_lf(head);
+               return true;
+       }
+       return false;
+}
+
+/**
+ * tomoyo_set_group - Print "acl_group " header keyword and category name.
+ *
+ * @head:     Pointer to "struct tomoyo_io_buffer".
+ * @category: Category name.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_group(struct tomoyo_io_buffer *head,
+                            const char *category)
+{
+       if (head->type == TOMOYO_EXCEPTIONPOLICY) {
+               tomoyo_print_namespace(head);
+               tomoyo_io_printf(head, "acl_group %u ",
+                                head->r.acl_group_index);
+       }
+       tomoyo_set_string(head, category);
 }
 
 /**
@@ -924,63 +1273,96 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
                               struct tomoyo_acl_info *acl)
 {
        const u8 acl_type = acl->type;
+       bool first = true;
        u8 bit;
 
+       if (head->r.print_cond_part)
+               goto print_cond_part;
        if (acl->is_deleted)
                return true;
- next:
-       bit = head->r.bit;
        if (!tomoyo_flush(head))
                return false;
        else if (acl_type == TOMOYO_TYPE_PATH_ACL) {
                struct tomoyo_path_acl *ptr =
                        container_of(acl, typeof(*ptr), head);
                const u16 perm = ptr->perm;
-               for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
+               for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
                        if (!(perm & (1 << bit)))
                                continue;
-                       if (head->r.print_execute_only &&
+                       if (head->r.print_transition_related_only &&
                            bit != TOMOYO_TYPE_EXECUTE)
                                continue;
-                       /* Print "read/write" instead of "read" and "write". */
-                       if ((bit == TOMOYO_TYPE_READ ||
-                            bit == TOMOYO_TYPE_WRITE)
-                           && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
-                               continue;
-                       break;
+                       if (first) {
+                               tomoyo_set_group(head, "file ");
+                               first = false;
+                       } else {
+                               tomoyo_set_slash(head);
+                       }
+                       tomoyo_set_string(head, tomoyo_path_keyword[bit]);
                }
-               if (bit >= TOMOYO_MAX_PATH_OPERATION)
-                       goto done;
-               tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]);
+               if (first)
+                       return true;
                tomoyo_print_name_union(head, &ptr->name);
-       } else if (head->r.print_execute_only) {
+       } else if (head->r.print_transition_related_only) {
                return true;
        } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
                struct tomoyo_path2_acl *ptr =
                        container_of(acl, typeof(*ptr), head);
-               bit = tomoyo_fns(ptr->perm, bit);
-               if (bit >= TOMOYO_MAX_PATH2_OPERATION)
-                       goto done;
-               tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]);
+               const u8 perm = ptr->perm;
+               for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
+                       if (!(perm & (1 << bit)))
+                               continue;
+                       if (first) {
+                               tomoyo_set_group(head, "file ");
+                               first = false;
+                       } else {
+                               tomoyo_set_slash(head);
+                       }
+                       tomoyo_set_string(head, tomoyo_mac_keywords
+                                         [tomoyo_pp2mac[bit]]);
+               }
+               if (first)
+                       return true;
                tomoyo_print_name_union(head, &ptr->name1);
                tomoyo_print_name_union(head, &ptr->name2);
        } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
                struct tomoyo_path_number_acl *ptr =
                        container_of(acl, typeof(*ptr), head);
-               bit = tomoyo_fns(ptr->perm, bit);
-               if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION)
-                       goto done;
-               tomoyo_io_printf(head, "allow_%s",
-                                tomoyo_path_number_keyword[bit]);
+               const u8 perm = ptr->perm;
+               for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
+                       if (!(perm & (1 << bit)))
+                               continue;
+                       if (first) {
+                               tomoyo_set_group(head, "file ");
+                               first = false;
+                       } else {
+                               tomoyo_set_slash(head);
+                       }
+                       tomoyo_set_string(head, tomoyo_mac_keywords
+                                         [tomoyo_pn2mac[bit]]);
+               }
+               if (first)
+                       return true;
                tomoyo_print_name_union(head, &ptr->name);
                tomoyo_print_number_union(head, &ptr->number);
        } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) {
                struct tomoyo_mkdev_acl *ptr =
                        container_of(acl, typeof(*ptr), head);
-               bit = tomoyo_fns(ptr->perm, bit);
-               if (bit >= TOMOYO_MAX_MKDEV_OPERATION)
-                       goto done;
-               tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]);
+               const u8 perm = ptr->perm;
+               for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
+                       if (!(perm & (1 << bit)))
+                               continue;
+                       if (first) {
+                               tomoyo_set_group(head, "file ");
+                               first = false;
+                       } else {
+                               tomoyo_set_slash(head);
+                       }
+                       tomoyo_set_string(head, tomoyo_mac_keywords
+                                         [tomoyo_pnnn2mac[bit]]);
+               }
+               if (first)
+                       return true;
                tomoyo_print_name_union(head, &ptr->name);
                tomoyo_print_number_union(head, &ptr->mode);
                tomoyo_print_number_union(head, &ptr->major);
@@ -988,35 +1370,41 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
        } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
                struct tomoyo_mount_acl *ptr =
                        container_of(acl, typeof(*ptr), head);
-               tomoyo_io_printf(head, "allow_mount");
+               tomoyo_set_group(head, "file mount");
                tomoyo_print_name_union(head, &ptr->dev_name);
                tomoyo_print_name_union(head, &ptr->dir_name);
                tomoyo_print_name_union(head, &ptr->fs_type);
                tomoyo_print_number_union(head, &ptr->flags);
        }
-       head->r.bit = bit + 1;
-       tomoyo_io_printf(head, "\n");
-       if (acl_type != TOMOYO_TYPE_MOUNT_ACL)
-               goto next;
- done:
-       head->r.bit = 0;
+       if (acl->cond) {
+               head->r.print_cond_part = true;
+               head->r.cond_step = 0;
+               if (!tomoyo_flush(head))
+                       return false;
+print_cond_part:
+               if (!tomoyo_print_condition(head, acl->cond))
+                       return false;
+               head->r.print_cond_part = false;
+       } else {
+               tomoyo_set_lf(head);
+       }
        return true;
 }
 
 /**
  * tomoyo_read_domain2 - Read domain policy.
  *
- * @head:   Pointer to "struct tomoyo_io_buffer".
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @list: Pointer to "struct list_head".
  *
  * Caller holds tomoyo_read_lock().
  *
  * Returns true on success, false otherwise.
  */
 static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
-                               struct tomoyo_domain_info *domain)
+                               struct list_head *list)
 {
-       list_for_each_cookie(head->r.acl, &domain->acl_info_list) {
+       list_for_each_cookie(head->r.acl, list) {
                struct tomoyo_acl_info *ptr =
                        list_entry(head->r.acl, typeof(*ptr), list);
                if (!tomoyo_print_entry(head, ptr))
@@ -1041,6 +1429,7 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
                struct tomoyo_domain_info *domain =
                        list_entry(head->r.domain, typeof(*domain), list);
                switch (head->r.step) {
+                       u8 i;
                case 0:
                        if (domain->is_deleted &&
                            !head->r.print_this_domain_only)
@@ -1048,22 +1437,18 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
                        /* Print domainname and flags. */
                        tomoyo_set_string(head, domain->domainname->name);
                        tomoyo_set_lf(head);
-                       tomoyo_io_printf(head,
-                                        TOMOYO_KEYWORD_USE_PROFILE "%u\n",
+                       tomoyo_io_printf(head, "use_profile %u\n",
                                         domain->profile);
-                       if (domain->quota_warned)
-                               tomoyo_set_string(head, "quota_exceeded\n");
-                       if (domain->transition_failed)
-                               tomoyo_set_string(head, "transition_failed\n");
-                       if (domain->ignore_global_allow_read)
-                               tomoyo_set_string(head,
-                                      TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ
-                                                 "\n");
+                       tomoyo_io_printf(head, "use_group %u\n",
+                                        domain->group);
+                       for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
+                               if (domain->flags[i])
+                                       tomoyo_set_string(head, tomoyo_dif[i]);
                        head->r.step++;
                        tomoyo_set_lf(head);
                        /* fall through */
                case 1:
-                       if (!tomoyo_read_domain2(head, domain))
+                       if (!tomoyo_read_domain2(head, &domain->acl_info_list))
                                return;
                        head->r.step++;
                        if (!tomoyo_set_lf(head))
@@ -1080,73 +1465,6 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
 }
 
 /**
- * tomoyo_write_domain_profile - Assign profile for specified domain.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns 0 on success, -EINVAL otherwise.
- *
- * This is equivalent to doing
- *
- *     ( echo "select " $domainname; echo "use_profile " $profile ) |
- *     /usr/sbin/tomoyo-loadpolicy -d
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
-{
-       char *data = head->write_buf;
-       char *cp = strchr(data, ' ');
-       struct tomoyo_domain_info *domain;
-       unsigned long profile;
-
-       if (!cp)
-               return -EINVAL;
-       *cp = '\0';
-       domain = tomoyo_find_domain(cp + 1);
-       if (strict_strtoul(data, 10, &profile))
-               return -EINVAL;
-       if (domain && profile < TOMOYO_MAX_PROFILES
-           && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
-               domain->profile = (u8) profile;
-       return 0;
-}
-
-/**
- * tomoyo_read_domain_profile - Read only domainname and profile.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns list of profile number and domainname pairs.
- *
- * This is equivalent to doing
- *
- *     grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
- *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
- *     domainname = $0; } else if ( $1 == "use_profile" ) {
- *     print $2 " " domainname; domainname = ""; } } ; '
- *
- * Caller holds tomoyo_read_lock().
- */
-static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
-{
-       if (head->r.eof)
-               return;
-       list_for_each_cookie(head->r.domain, &tomoyo_domain_list) {
-               struct tomoyo_domain_info *domain =
-                       list_entry(head->r.domain, typeof(*domain), list);
-               if (domain->is_deleted)
-                       continue;
-               if (!tomoyo_flush(head))
-                       return;
-               tomoyo_io_printf(head, "%u ", domain->profile);
-               tomoyo_set_string(head, domain->domainname->name);
-               tomoyo_set_lf(head);
-       }
-       head->r.eof = true;
-}
-
-/**
  * tomoyo_write_pid: Specify PID to obtain domainname.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -1204,18 +1522,20 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
        tomoyo_set_string(head, domain->domainname->name);
 }
 
+/* String table for domain transition control keywords. */
 static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
-       [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE]
-       = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN,
-       [TOMOYO_TRANSITION_CONTROL_INITIALIZE]
-       = TOMOYO_KEYWORD_INITIALIZE_DOMAIN,
-       [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN,
-       [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN
+       [TOMOYO_TRANSITION_CONTROL_NO_RESET]      = "no_reset_domain ",
+       [TOMOYO_TRANSITION_CONTROL_RESET]         = "reset_domain ",
+       [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
+       [TOMOYO_TRANSITION_CONTROL_INITIALIZE]    = "initialize_domain ",
+       [TOMOYO_TRANSITION_CONTROL_NO_KEEP]       = "no_keep_domain ",
+       [TOMOYO_TRANSITION_CONTROL_KEEP]          = "keep_domain ",
 };
 
+/* String table for grouping keywords. */
 static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
-       [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP,
-       [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP
+       [TOMOYO_PATH_GROUP]   = "path_group ",
+       [TOMOYO_NUMBER_GROUP] = "number_group ",
 };
 
 /**
@@ -1229,29 +1549,30 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
  */
 static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
 {
-       char *data = head->write_buf;
-       bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
-       u8 i;
-       static const struct {
-               const char *keyword;
-               int (*write) (char *, const bool);
-       } tomoyo_callback[4] = {
-               { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator },
-               { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern },
-               { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite },
-               { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable },
+       const bool is_delete = head->w.is_delete;
+       struct tomoyo_acl_param param = {
+               .ns = head->w.ns,
+               .is_delete = is_delete,
+               .data = head->write_buf,
        };
-
+       u8 i;
+       if (tomoyo_str_starts(&param.data, "aggregator "))
+               return tomoyo_write_aggregator(&param);
        for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
-               if (tomoyo_str_starts(&data, tomoyo_transition_type[i]))
-                       return tomoyo_write_transition_control(data, is_delete,
-                                                              i);
-       for (i = 0; i < 4; i++)
-               if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword))
-                       return tomoyo_callback[i].write(data, is_delete);
+               if (tomoyo_str_starts(&param.data, tomoyo_transition_type[i]))
+                       return tomoyo_write_transition_control(&param, i);
        for (i = 0; i < TOMOYO_MAX_GROUP; i++)
-               if (tomoyo_str_starts(&data, tomoyo_group_name[i]))
-                       return tomoyo_write_group(data, is_delete, i);
+               if (tomoyo_str_starts(&param.data, tomoyo_group_name[i]))
+                       return tomoyo_write_group(&param, i);
+       if (tomoyo_str_starts(&param.data, "acl_group ")) {
+               unsigned int group;
+               char *data;
+               group = simple_strtoul(param.data, &data, 10);
+               if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
+                       return tomoyo_write_domain2
+                               (head->w.ns, &head->w.ns->acl_group[group],
+                                data, is_delete);
+       }
        return -EINVAL;
 }
 
@@ -1267,9 +1588,12 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
  */
 static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
 {
-       list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) {
+       struct tomoyo_policy_namespace *ns =
+               container_of(head->r.ns, typeof(*ns), namespace_list);
+       struct list_head *list = &ns->group_list[idx];
+       list_for_each_cookie(head->r.group, list) {
                struct tomoyo_group *group =
-                       list_entry(head->r.group, typeof(*group), list);
+                       list_entry(head->r.group, typeof(*group), head.list);
                list_for_each_cookie(head->r.acl, &group->member_list) {
                        struct tomoyo_acl_head *ptr =
                                list_entry(head->r.acl, typeof(*ptr), list);
@@ -1277,6 +1601,7 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
                                continue;
                        if (!tomoyo_flush(head))
                                return false;
+                       tomoyo_print_namespace(head);
                        tomoyo_set_string(head, tomoyo_group_name[idx]);
                        tomoyo_set_string(head, group->group_name->name);
                        if (idx == TOMOYO_PATH_GROUP) {
@@ -1310,7 +1635,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
  */
 static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
 {
-       list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) {
+       struct tomoyo_policy_namespace *ns =
+               container_of(head->r.ns, typeof(*ns), namespace_list);
+       struct list_head *list = &ns->policy_list[idx];
+       list_for_each_cookie(head->r.acl, list) {
                struct tomoyo_acl_head *acl =
                        container_of(head->r.acl, typeof(*acl), list);
                if (acl->is_deleted)
@@ -1322,35 +1650,23 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
                        {
                                struct tomoyo_transition_control *ptr =
                                        container_of(acl, typeof(*ptr), head);
-                               tomoyo_set_string(head,
-                                                 tomoyo_transition_type
+                               tomoyo_print_namespace(head);
+                               tomoyo_set_string(head, tomoyo_transition_type
                                                  [ptr->type]);
-                               if (ptr->program)
-                                       tomoyo_set_string(head,
-                                                         ptr->program->name);
-                               if (ptr->program && ptr->domainname)
-                                       tomoyo_set_string(head, " from ");
-                               if (ptr->domainname)
-                                       tomoyo_set_string(head,
-                                                         ptr->domainname->
-                                                         name);
-                       }
-                       break;
-               case TOMOYO_ID_GLOBALLY_READABLE:
-                       {
-                               struct tomoyo_readable_file *ptr =
-                                       container_of(acl, typeof(*ptr), head);
-                               tomoyo_set_string(head,
-                                                 TOMOYO_KEYWORD_ALLOW_READ);
-                               tomoyo_set_string(head, ptr->filename->name);
+                               tomoyo_set_string(head, ptr->program ?
+                                                 ptr->program->name : "any");
+                               tomoyo_set_string(head, " from ");
+                               tomoyo_set_string(head, ptr->domainname ?
+                                                 ptr->domainname->name :
+                                                 "any");
                        }
                        break;
                case TOMOYO_ID_AGGREGATOR:
                        {
                                struct tomoyo_aggregator *ptr =
                                        container_of(acl, typeof(*ptr), head);
-                               tomoyo_set_string(head,
-                                                 TOMOYO_KEYWORD_AGGREGATOR);
+                               tomoyo_print_namespace(head);
+                               tomoyo_set_string(head, "aggregator ");
                                tomoyo_set_string(head,
                                                  ptr->original_name->name);
                                tomoyo_set_space(head);
@@ -1358,24 +1674,6 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
                                               ptr->aggregated_name->name);
                        }
                        break;
-               case TOMOYO_ID_PATTERN:
-                       {
-                               struct tomoyo_no_pattern *ptr =
-                                       container_of(acl, typeof(*ptr), head);
-                               tomoyo_set_string(head,
-                                                 TOMOYO_KEYWORD_FILE_PATTERN);
-                               tomoyo_set_string(head, ptr->pattern->name);
-                       }
-                       break;
-               case TOMOYO_ID_NO_REWRITE:
-                       {
-                               struct tomoyo_no_rewrite *ptr =
-                                       container_of(acl, typeof(*ptr), head);
-                               tomoyo_set_string(head,
-                                                 TOMOYO_KEYWORD_DENY_REWRITE);
-                               tomoyo_set_string(head, ptr->pattern->name);
-                       }
-                       break;
                default:
                        continue;
                }
@@ -1394,6 +1692,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
  */
 static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
 {
+       struct tomoyo_policy_namespace *ns =
+               container_of(head->r.ns, typeof(*ns), namespace_list);
        if (head->r.eof)
                return;
        while (head->r.step < TOMOYO_MAX_POLICY &&
@@ -1406,95 +1706,40 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
                head->r.step++;
        if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)
                return;
+       while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP
+              + TOMOYO_MAX_ACL_GROUPS) {
+               head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY
+                       - TOMOYO_MAX_GROUP;
+               if (!tomoyo_read_domain2(head, &ns->acl_group
+                                        [head->r.acl_group_index]))
+                       return;
+               head->r.step++;
+       }
        head->r.eof = true;
 }
 
-/**
- * tomoyo_print_header - Get header line of audit log.
- *
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns string representation.
- *
- * This function uses kmalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_print_header(struct tomoyo_request_info *r)
-{
-       struct timeval tv;
-       const pid_t gpid = task_pid_nr(current);
-       static const int tomoyo_buffer_len = 4096;
-       char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
-       pid_t ppid;
-       if (!buffer)
-               return NULL;
-       do_gettimeofday(&tv);
-       rcu_read_lock();
-       ppid = task_tgid_vnr(current->real_parent);
-       rcu_read_unlock();
-       snprintf(buffer, tomoyo_buffer_len - 1,
-                "#timestamp=%lu profile=%u mode=%s (global-pid=%u)"
-                " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
-                " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
-                tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid,
-                task_tgid_vnr(current), ppid,
-                current_uid(), current_gid(), current_euid(),
-                current_egid(), current_suid(), current_sgid(),
-                current_fsuid(), current_fsgid());
-       return buffer;
-}
-
-/**
- * tomoyo_init_audit_log - Allocate buffer for audit logs.
- *
- * @len: Required size.
- * @r:   Pointer to "struct tomoyo_request_info".
- *
- * Returns pointer to allocated memory.
- *
- * The @len is updated to add the header lines' size on success.
- *
- * This function uses kzalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
-{
-       char *buf = NULL;
-       const char *header;
-       const char *domainname;
-       if (!r->domain)
-               r->domain = tomoyo_domain();
-       domainname = r->domain->domainname->name;
-       header = tomoyo_print_header(r);
-       if (!header)
-               return NULL;
-       *len += strlen(domainname) + strlen(header) + 10;
-       buf = kzalloc(*len, GFP_NOFS);
-       if (buf)
-               snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname);
-       kfree(header);
-       return buf;
-}
-
-/* Wait queue for tomoyo_query_list. */
+/* Wait queue for kernel -> userspace notification. */
 static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
-
-/* Lock for manipulating tomoyo_query_list. */
-static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+/* Wait queue for userspace -> kernel notification. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait);
 
 /* Structure for query. */
 struct tomoyo_query {
        struct list_head list;
        char *query;
-       int query_len;
+       size_t query_len;
        unsigned int serial;
-       int timer;
-       int answer;
+       u8 timer;
+       u8 answer;
+       u8 retry;
 };
 
 /* The list for "struct tomoyo_query". */
 static LIST_HEAD(tomoyo_query_list);
 
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
 /*
  * Number of "struct file" referring /sys/kernel/security/tomoyo/query
  * interface.
@@ -1502,10 +1747,82 @@ static LIST_HEAD(tomoyo_query_list);
 static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 
 /**
+ * tomoyo_truncate - Truncate a line.
+ *
+ * @str: String to truncate.
+ *
+ * Returns length of truncated @str.
+ */
+static int tomoyo_truncate(char *str)
+{
+       char *start = str;
+       while (*(unsigned char *) str > (unsigned char) ' ')
+               str++;
+       *str = '\0';
+       return strlen(start) + 1;
+}
+
+/**
+ * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @header: Lines containing ACL.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
+{
+       char *buffer;
+       char *realpath = NULL;
+       char *argv0 = NULL;
+       char *symlink = NULL;
+       char *cp = strchr(header, '\n');
+       int len;
+       if (!cp)
+               return;
+       cp = strchr(cp + 1, '\n');
+       if (!cp)
+               return;
+       *cp++ = '\0';
+       len = strlen(cp) + 1;
+       /* strstr() will return NULL if ordering is wrong. */
+       if (*cp == 'f') {
+               argv0 = strstr(header, " argv[]={ \"");
+               if (argv0) {
+                       argv0 += 10;
+                       len += tomoyo_truncate(argv0) + 14;
+               }
+               realpath = strstr(header, " exec={ realpath=\"");
+               if (realpath) {
+                       realpath += 8;
+                       len += tomoyo_truncate(realpath) + 6;
+               }
+               symlink = strstr(header, " symlink.target=\"");
+               if (symlink)
+                       len += tomoyo_truncate(symlink + 1) + 1;
+       }
+       buffer = kmalloc(len, GFP_NOFS);
+       if (!buffer)
+               return;
+       snprintf(buffer, len - 1, "%s", cp);
+       if (realpath)
+               tomoyo_addprintf(buffer, len, " exec.%s", realpath);
+       if (argv0)
+               tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0);
+       if (symlink)
+               tomoyo_addprintf(buffer, len, "%s", symlink);
+       tomoyo_normalize_line(buffer);
+       if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
+                                 false))
+               tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+       kfree(buffer);
+}
+
+/**
  * tomoyo_supervisor - Ask for the supervisor's decision.
  *
- * @r:       Pointer to "struct tomoyo_request_info".
- * @fmt:     The printf()'s format string, followed by parameters.
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
  *
  * Returns 0 if the supervisor decided to permit the access request which
  * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the
@@ -1515,88 +1832,79 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
 {
        va_list args;
-       int error = -EPERM;
-       int pos;
+       int error;
        int len;
        static unsigned int tomoyo_serial;
-       struct tomoyo_query *entry = NULL;
+       struct tomoyo_query entry = { };
        bool quota_exceeded = false;
-       char *header;
+       va_start(args, fmt);
+       len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+       va_end(args);
+       /* Write /sys/kernel/security/tomoyo/audit. */
+       va_start(args, fmt);
+       tomoyo_write_log2(r, len, fmt, args);
+       va_end(args);
+       /* Nothing more to do if granted. */
+       if (r->granted)
+               return 0;
+       if (r->mode)
+               tomoyo_update_stat(r->mode);
        switch (r->mode) {
-               char *buffer;
+       case TOMOYO_CONFIG_ENFORCING:
+               error = -EPERM;
+               if (atomic_read(&tomoyo_query_observers))
+                       break;
+               goto out;
        case TOMOYO_CONFIG_LEARNING:
-               if (!tomoyo_domain_quota_is_ok(r))
-                       return 0;
-               va_start(args, fmt);
-               len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4;
-               va_end(args);
-               buffer = kmalloc(len, GFP_NOFS);
-               if (!buffer)
-                       return 0;
-               va_start(args, fmt);
-               vsnprintf(buffer, len - 1, fmt, args);
-               va_end(args);
-               tomoyo_normalize_line(buffer);
-               tomoyo_write_domain2(buffer, r->domain, false);
-               kfree(buffer);
+               error = 0;
+               /* Check max_learning_entry parameter. */
+               if (tomoyo_domain_quota_is_ok(r))
+                       break;
                /* fall through */
-       case TOMOYO_CONFIG_PERMISSIVE:
+       default:
                return 0;
        }
-       if (!r->domain)
-               r->domain = tomoyo_domain();
-       if (!atomic_read(&tomoyo_query_observers))
-               return -EPERM;
+       /* Get message. */
        va_start(args, fmt);
-       len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+       entry.query = tomoyo_init_log(r, len, fmt, args);
        va_end(args);
-       header = tomoyo_init_audit_log(&len, r);
-       if (!header)
+       if (!entry.query)
                goto out;
-       entry = kzalloc(sizeof(*entry), GFP_NOFS);
-       if (!entry)
+       entry.query_len = strlen(entry.query) + 1;
+       if (!error) {
+               tomoyo_add_entry(r->domain, entry.query);
                goto out;
-       entry->query = kzalloc(len, GFP_NOFS);
-       if (!entry->query)
-               goto out;
-       len = ksize(entry->query);
+       }
+       len = tomoyo_round2(entry.query_len);
        spin_lock(&tomoyo_query_list_lock);
-       if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
-           sizeof(*entry) >= tomoyo_quota_for_query) {
+       if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] &&
+           tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len
+           >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {
                quota_exceeded = true;
        } else {
-               tomoyo_query_memory_size += len + sizeof(*entry);
-               entry->serial = tomoyo_serial++;
+               entry.serial = tomoyo_serial++;
+               entry.retry = r->retry;
+               tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len;
+               list_add_tail(&entry.list, &tomoyo_query_list);
        }
        spin_unlock(&tomoyo_query_list_lock);
        if (quota_exceeded)
                goto out;
-       pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s",
-                      entry->serial, r->retry, header);
-       kfree(header);
-       header = NULL;
-       va_start(args, fmt);
-       vsnprintf(entry->query + pos, len - 1 - pos, fmt, args);
-       entry->query_len = strlen(entry->query) + 1;
-       va_end(args);
-       spin_lock(&tomoyo_query_list_lock);
-       list_add_tail(&entry->list, &tomoyo_query_list);
-       spin_unlock(&tomoyo_query_list_lock);
        /* Give 10 seconds for supervisor's opinion. */
-       for (entry->timer = 0;
-            atomic_read(&tomoyo_query_observers) && entry->timer < 100;
-            entry->timer++) {
-               wake_up(&tomoyo_query_wait);
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ / 10);
-               if (entry->answer)
+       while (entry.timer < 10) {
+               wake_up_all(&tomoyo_query_wait);
+               if (wait_event_interruptible_timeout
+                   (tomoyo_answer_wait, entry.answer ||
+                    !atomic_read(&tomoyo_query_observers), HZ))
                        break;
+               else
+                       entry.timer++;
        }
        spin_lock(&tomoyo_query_list_lock);
-       list_del(&entry->list);
-       tomoyo_query_memory_size -= len + sizeof(*entry);
+       list_del(&entry.list);
+       tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;
        spin_unlock(&tomoyo_query_list_lock);
-       switch (entry->answer) {
+       switch (entry.answer) {
        case 3: /* Asked to retry by administrator. */
                error = TOMOYO_RETRY_REQUEST;
                r->retry++;
@@ -1605,18 +1913,12 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
                /* Granted by administrator. */
                error = 0;
                break;
-       case 0:
-               /* Timed out. */
-               break;
        default:
-               /* Rejected by administrator. */
+               /* Timed out or rejected by administrator. */
                break;
        }
- out:
-       if (entry)
-               kfree(entry->query);
-       kfree(entry);
-       kfree(header);
+out:
+       kfree(entry.query);
        return error;
 }
 
@@ -1663,8 +1965,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait)
 static void tomoyo_read_query(struct tomoyo_io_buffer *head)
 {
        struct list_head *tmp;
-       int pos = 0;
-       int len = 0;
+       unsigned int pos = 0;
+       size_t len = 0;
        char *buf;
        if (head->r.w_pos)
                return;
@@ -1687,7 +1989,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
                head->r.query_index = 0;
                return;
        }
-       buf = kzalloc(len, GFP_NOFS);
+       buf = kzalloc(len + 32, GFP_NOFS);
        if (!buf)
                return;
        pos = 0;
@@ -1703,7 +2005,8 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
                 * can change, but I don't care.
                 */
                if (len == ptr->query_len)
-                       memmove(buf, ptr->query, len);
+                       snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial,
+                                ptr->retry, ptr->query);
                break;
        }
        spin_unlock(&tomoyo_query_list_lock);
@@ -1760,7 +2063,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
 static void tomoyo_read_version(struct tomoyo_io_buffer *head)
 {
        if (!head->r.eof) {
-               tomoyo_io_printf(head, "2.3.0");
+               tomoyo_io_printf(head, "2.4.0");
                head->r.eof = true;
        }
 }
@@ -1785,15 +2088,111 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
        }
 }
 
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
+       [TOMOYO_STAT_POLICY_UPDATES]    = "update:",
+       [TOMOYO_STAT_POLICY_LEARNING]   = "violation in learning mode:",
+       [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:",
+       [TOMOYO_STAT_POLICY_ENFORCING]  = "violation in enforcing mode:",
+};
+
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = {
+       [TOMOYO_MEMORY_POLICY] = "policy:",
+       [TOMOYO_MEMORY_AUDIT]  = "audit log:",
+       [TOMOYO_MEMORY_QUERY]  = "query message:",
+};
+
+/* Timestamp counter for last updated. */
+static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT];
+/* Counter for number of updates. */
+static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT];
+
+/**
+ * tomoyo_update_stat - Update statistic counters.
+ *
+ * @index: Index for policy type.
+ *
+ * Returns nothing.
+ */
+void tomoyo_update_stat(const u8 index)
+{
+       struct timeval tv;
+       do_gettimeofday(&tv);
+       /*
+        * I don't use atomic operations because race condition is not fatal.
+        */
+       tomoyo_stat_updated[index]++;
+       tomoyo_stat_modified[index] = tv.tv_sec;
+}
+
+/**
+ * tomoyo_read_stat - Read statistic data.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
+{
+       u8 i;
+       unsigned int total = 0;
+       if (head->r.eof)
+               return;
+       for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
+               tomoyo_io_printf(head, "Policy %-30s %10u",
+                                tomoyo_policy_headers[i],
+                                tomoyo_stat_updated[i]);
+               if (tomoyo_stat_modified[i]) {
+                       struct tomoyo_time stamp;
+                       tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
+                       tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
+                                        "%02u:%02u:%02u)",
+                                        stamp.year, stamp.month, stamp.day,
+                                        stamp.hour, stamp.min, stamp.sec);
+               }
+               tomoyo_set_lf(head);
+       }
+       for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
+               unsigned int used = tomoyo_memory_used[i];
+               total += used;
+               tomoyo_io_printf(head, "Memory used by %-22s %10u",
+                                tomoyo_memory_headers[i], used);
+               used = tomoyo_memory_quota[i];
+               if (used)
+                       tomoyo_io_printf(head, " (Quota: %10u)", used);
+               tomoyo_set_lf(head);
+       }
+       tomoyo_io_printf(head, "Total memory used:                    %10u\n",
+                        total);
+       head->r.eof = true;
+}
+
+/**
+ * tomoyo_write_stat - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_stat(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       u8 i;
+       if (tomoyo_str_starts(&data, "Memory used by "))
+               for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
+                       if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
+                               sscanf(data, "%u", &tomoyo_memory_quota[i]);
+       return 0;
+}
+
 /**
  * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
  *
  * @type: Type of interface.
  * @file: Pointer to "struct file".
  *
- * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
- *
- * Caller acquires tomoyo_read_lock().
+ * Returns 0 on success, negative value otherwise.
  */
 int tomoyo_open_control(const u8 type, struct file *file)
 {
@@ -1814,15 +2213,15 @@ int tomoyo_open_control(const u8 type, struct file *file)
                head->write = tomoyo_write_exception;
                head->read = tomoyo_read_exception;
                break;
+       case TOMOYO_AUDIT:
+               /* /sys/kernel/security/tomoyo/audit */
+               head->poll = tomoyo_poll_log;
+               head->read = tomoyo_read_log;
+               break;
        case TOMOYO_SELFDOMAIN:
                /* /sys/kernel/security/tomoyo/self_domain */
                head->read = tomoyo_read_self_domain;
                break;
-       case TOMOYO_DOMAIN_STATUS:
-               /* /sys/kernel/security/tomoyo/.domain_status */
-               head->write = tomoyo_write_domain_profile;
-               head->read = tomoyo_read_domain_profile;
-               break;
        case TOMOYO_PROCESS_STATUS:
                /* /sys/kernel/security/tomoyo/.process_status */
                head->write = tomoyo_write_pid;
@@ -1833,11 +2232,11 @@ int tomoyo_open_control(const u8 type, struct file *file)
                head->read = tomoyo_read_version;
                head->readbuf_size = 128;
                break;
-       case TOMOYO_MEMINFO:
-               /* /sys/kernel/security/tomoyo/meminfo */
-               head->write = tomoyo_write_memory_quota;
-               head->read = tomoyo_read_memory_counter;
-               head->readbuf_size = 512;
+       case TOMOYO_STAT:
+               /* /sys/kernel/security/tomoyo/stat */
+               head->write = tomoyo_write_stat;
+               head->read = tomoyo_read_stat;
+               head->readbuf_size = 1024;
                break;
        case TOMOYO_PROFILE:
                /* /sys/kernel/security/tomoyo/profile */
@@ -1887,26 +2286,16 @@ int tomoyo_open_control(const u8 type, struct file *file)
                        return -ENOMEM;
                }
        }
-       if (type != TOMOYO_QUERY)
-               head->reader_idx = tomoyo_read_lock();
-       file->private_data = head;
-       /*
-        * Call the handler now if the file is
-        * /sys/kernel/security/tomoyo/self_domain
-        * so that the user can use
-        * cat < /sys/kernel/security/tomoyo/self_domain"
-        * to know the current process's domainname.
-        */
-       if (type == TOMOYO_SELFDOMAIN)
-               tomoyo_read_control(file, NULL, 0);
        /*
         * If the file is /sys/kernel/security/tomoyo/query , increment the
         * observer counter.
         * The obserber counter is used by tomoyo_supervisor() to see if
         * there is some process monitoring /sys/kernel/security/tomoyo/query.
         */
-       else if (type == TOMOYO_QUERY)
+       if (type == TOMOYO_QUERY)
                atomic_inc(&tomoyo_query_observers);
+       file->private_data = head;
+       tomoyo_notify_gc(head, true);
        return 0;
 }
 
@@ -1917,7 +2306,8 @@ int tomoyo_open_control(const u8 type, struct file *file)
  * @wait: Pointer to "poll_table".
  *
  * Waits for read readiness.
- * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd .
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and
+ * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd.
  */
 int tomoyo_poll_control(struct file *file, poll_table *wait)
 {
@@ -1928,21 +2318,58 @@ int tomoyo_poll_control(struct file *file, poll_table *wait)
 }
 
 /**
+ * tomoyo_set_namespace_cursor - Set namespace to read.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head)
+{
+       struct list_head *ns;
+       if (head->type != TOMOYO_EXCEPTIONPOLICY &&
+           head->type != TOMOYO_PROFILE)
+               return;
+       /*
+        * If this is the first read, or reading previous namespace finished
+        * and has more namespaces to read, update the namespace cursor.
+        */
+       ns = head->r.ns;
+       if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) {
+               /* Clearing is OK because tomoyo_flush() returned true. */
+               memset(&head->r, 0, sizeof(head->r));
+               head->r.ns = ns ? ns->next : tomoyo_namespace_list.next;
+       }
+}
+
+/**
+ * tomoyo_has_more_namespace - Check for unread namespaces.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true if we have more entries to print, false otherwise.
+ */
+static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head)
+{
+       return (head->type == TOMOYO_EXCEPTIONPOLICY ||
+               head->type == TOMOYO_PROFILE) && head->r.eof &&
+               head->r.ns->next != &tomoyo_namespace_list;
+}
+
+/**
  * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
  *
- * @file:       Pointer to "struct file".
+ * @head:       Pointer to "struct tomoyo_io_buffer".
  * @buffer:     Poiner to buffer to write to.
  * @buffer_len: Size of @buffer.
  *
  * Returns bytes read on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
  */
-int tomoyo_read_control(struct file *file, char __user *buffer,
-                       const int buffer_len)
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+                           const int buffer_len)
 {
        int len;
-       struct tomoyo_io_buffer *head = file->private_data;
+       int idx;
 
        if (!head->read)
                return -ENOSYS;
@@ -1950,64 +2377,156 @@ int tomoyo_read_control(struct file *file, char __user *buffer,
                return -EINTR;
        head->read_user_buf = buffer;
        head->read_user_buf_avail = buffer_len;
+       idx = tomoyo_read_lock();
        if (tomoyo_flush(head))
                /* Call the policy handler. */
-               head->read(head);
-       tomoyo_flush(head);
+               do {
+                       tomoyo_set_namespace_cursor(head);
+                       head->read(head);
+               } while (tomoyo_flush(head) &&
+                        tomoyo_has_more_namespace(head));
+       tomoyo_read_unlock(idx);
        len = head->read_user_buf - buffer;
        mutex_unlock(&head->io_sem);
        return len;
 }
 
 /**
+ * tomoyo_parse_policy - Parse a policy line.
+ *
+ * @head: Poiter to "struct tomoyo_io_buffer".
+ * @line: Line to parse.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line)
+{
+       /* Delete request? */
+       head->w.is_delete = !strncmp(line, "delete ", 7);
+       if (head->w.is_delete)
+               memmove(line, line + 7, strlen(line + 7) + 1);
+       /* Selecting namespace to update. */
+       if (head->type == TOMOYO_EXCEPTIONPOLICY ||
+           head->type == TOMOYO_PROFILE) {
+               if (*line == '<') {
+                       char *cp = strchr(line, ' ');
+                       if (cp) {
+                               *cp++ = '\0';
+                               head->w.ns = tomoyo_assign_namespace(line);
+                               memmove(line, cp, strlen(cp) + 1);
+                       } else
+                               head->w.ns = NULL;
+               } else
+                       head->w.ns = &tomoyo_kernel_namespace;
+               /* Don't allow updating if namespace is invalid. */
+               if (!head->w.ns)
+                       return -ENOENT;
+       }
+       /* Do the update. */
+       return head->write(head);
+}
+
+/**
  * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
  *
- * @file:       Pointer to "struct file".
+ * @head:       Pointer to "struct tomoyo_io_buffer".
  * @buffer:     Pointer to buffer to read from.
  * @buffer_len: Size of @buffer.
  *
  * Returns @buffer_len on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
  */
-int tomoyo_write_control(struct file *file, const char __user *buffer,
-                        const int buffer_len)
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+                            const char __user *buffer, const int buffer_len)
 {
-       struct tomoyo_io_buffer *head = file->private_data;
        int error = buffer_len;
-       int avail_len = buffer_len;
+       size_t avail_len = buffer_len;
        char *cp0 = head->write_buf;
-
+       int idx;
        if (!head->write)
                return -ENOSYS;
        if (!access_ok(VERIFY_READ, buffer, buffer_len))
                return -EFAULT;
-       /* Don't allow updating policies by non manager programs. */
-       if (head->write != tomoyo_write_pid &&
-           head->write != tomoyo_write_domain && !tomoyo_manager())
-               return -EPERM;
        if (mutex_lock_interruptible(&head->io_sem))
                return -EINTR;
+       idx = tomoyo_read_lock();
        /* Read a line and dispatch it to the policy handler. */
        while (avail_len > 0) {
                char c;
-               if (head->write_avail >= head->writebuf_size - 1) {
-                       error = -ENOMEM;
-                       break;
-               } else if (get_user(c, buffer)) {
+               if (head->w.avail >= head->writebuf_size - 1) {
+                       const int len = head->writebuf_size * 2;
+                       char *cp = kzalloc(len, GFP_NOFS);
+                       if (!cp) {
+                               error = -ENOMEM;
+                               break;
+                       }
+                       memmove(cp, cp0, head->w.avail);
+                       kfree(cp0);
+                       head->write_buf = cp;
+                       cp0 = cp;
+                       head->writebuf_size = len;
+               }
+               if (get_user(c, buffer)) {
                        error = -EFAULT;
                        break;
                }
                buffer++;
                avail_len--;
-               cp0[head->write_avail++] = c;
+               cp0[head->w.avail++] = c;
                if (c != '\n')
                        continue;
-               cp0[head->write_avail - 1] = '\0';
-               head->write_avail = 0;
+               cp0[head->w.avail - 1] = '\0';
+               head->w.avail = 0;
                tomoyo_normalize_line(cp0);
-               head->write(head);
+               if (!strcmp(cp0, "reset")) {
+                       head->w.ns = &tomoyo_kernel_namespace;
+                       head->w.domain = NULL;
+                       memset(&head->r, 0, sizeof(head->r));
+                       continue;
+               }
+               /* Don't allow updating policies by non manager programs. */
+               switch (head->type) {
+               case TOMOYO_PROCESS_STATUS:
+                       /* This does not write anything. */
+                       break;
+               case TOMOYO_DOMAINPOLICY:
+                       if (tomoyo_select_domain(head, cp0))
+                               continue;
+                       /* fall through */
+               case TOMOYO_EXCEPTIONPOLICY:
+                       if (!strcmp(cp0, "select transition_only")) {
+                               head->r.print_transition_related_only = true;
+                               continue;
+                       }
+                       /* fall through */
+               default:
+                       if (!tomoyo_manager()) {
+                               error = -EPERM;
+                               goto out;
+                       }
+               }
+               switch (tomoyo_parse_policy(head, cp0)) {
+               case -EPERM:
+                       error = -EPERM;
+                       goto out;
+               case 0:
+                       switch (head->type) {
+                       case TOMOYO_DOMAINPOLICY:
+                       case TOMOYO_EXCEPTIONPOLICY:
+                       case TOMOYO_STAT:
+                       case TOMOYO_PROFILE:
+                       case TOMOYO_MANAGER:
+                               tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               }
        }
+out:
+       tomoyo_read_unlock(idx);
        mutex_unlock(&head->io_sem);
        return error;
 }
@@ -2015,35 +2534,20 @@ int tomoyo_write_control(struct file *file, const char __user *buffer,
 /**
  * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
  *
- * @file: Pointer to "struct file".
- *
- * Releases memory and returns 0.
+ * @head: Pointer to "struct tomoyo_io_buffer".
  *
- * Caller looses tomoyo_read_lock().
+ * Returns 0.
  */
-int tomoyo_close_control(struct file *file)
+int tomoyo_close_control(struct tomoyo_io_buffer *head)
 {
-       struct tomoyo_io_buffer *head = file->private_data;
-       const bool is_write = !!head->write_buf;
-
        /*
         * If the file is /sys/kernel/security/tomoyo/query , decrement the
         * observer counter.
         */
-       if (head->type == TOMOYO_QUERY)
-               atomic_dec(&tomoyo_query_observers);
-       else
-               tomoyo_read_unlock(head->reader_idx);
-       /* Release memory used for policy I/O. */
-       kfree(head->read_buf);
-       head->read_buf = NULL;
-       kfree(head->write_buf);
-       head->write_buf = NULL;
-       kfree(head);
-       head = NULL;
-       file->private_data = NULL;
-       if (is_write)
-               tomoyo_run_gc();
+       if (head->type == TOMOYO_QUERY &&
+           atomic_dec_and_test(&tomoyo_query_observers))
+               wake_up_all(&tomoyo_answer_wait);
+       tomoyo_notify_gc(head, false);
        return 0;
 }
 
@@ -2055,27 +2559,90 @@ void tomoyo_check_profile(void)
        struct tomoyo_domain_info *domain;
        const int idx = tomoyo_read_lock();
        tomoyo_policy_loaded = true;
-       /* Check all profiles currently assigned to domains are defined. */
+       printk(KERN_INFO "TOMOYO: 2.4.0\n");
        list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
                const u8 profile = domain->profile;
-               if (tomoyo_profile_ptr[profile])
+               const struct tomoyo_policy_namespace *ns = domain->ns;
+               if (ns->profile_version != 20100903)
+                       printk(KERN_ERR
+                              "Profile version %u is not supported.\n",
+                              ns->profile_version);
+               else if (!ns->profile_ptr[profile])
+                       printk(KERN_ERR
+                              "Profile %u (used by '%s') is not defined.\n",
+                              profile, domain->domainname->name);
+               else
                        continue;
-               printk(KERN_ERR "You need to define profile %u before using it.\n",
-                      profile);
-               printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
+               printk(KERN_ERR
+                      "Userland tools for TOMOYO 2.4 must be installed and "
+                      "policy must be initialized.\n");
+               printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
                       "for more information.\n");
-               panic("Profile %u (used by '%s') not defined.\n",
-                     profile, domain->domainname->name);
+               panic("STOP!");
        }
        tomoyo_read_unlock(idx);
-       if (tomoyo_profile_version != 20090903) {
-               printk(KERN_ERR "You need to install userland programs for "
-                      "TOMOYO 2.3 and initialize policy configuration.\n");
-               printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
-                      "for more information.\n");
-               panic("Profile version %u is not supported.\n",
-                     tomoyo_profile_version);
-       }
-       printk(KERN_INFO "TOMOYO: 2.3.0\n");
        printk(KERN_INFO "Mandatory Access Control activated.\n");
 }
+
+/**
+ * tomoyo_load_builtin_policy - Load built-in policy.
+ *
+ * Returns nothing.
+ */
+void __init tomoyo_load_builtin_policy(void)
+{
+       /*
+        * This include file is manually created and contains built-in policy
+        * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy",
+        * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager",
+        * "tomoyo_builtin_stat" in the form of "static char [] __initdata".
+        */
+#include "builtin-policy.h"
+       u8 i;
+       const int idx = tomoyo_read_lock();
+       for (i = 0; i < 5; i++) {
+               struct tomoyo_io_buffer head = { };
+               char *start = "";
+               switch (i) {
+               case 0:
+                       start = tomoyo_builtin_profile;
+                       head.type = TOMOYO_PROFILE;
+                       head.write = tomoyo_write_profile;
+                       break;
+               case 1:
+                       start = tomoyo_builtin_exception_policy;
+                       head.type = TOMOYO_EXCEPTIONPOLICY;
+                       head.write = tomoyo_write_exception;
+                       break;
+               case 2:
+                       start = tomoyo_builtin_domain_policy;
+                       head.type = TOMOYO_DOMAINPOLICY;
+                       head.write = tomoyo_write_domain;
+                       break;
+               case 3:
+                       start = tomoyo_builtin_manager;
+                       head.type = TOMOYO_MANAGER;
+                       head.write = tomoyo_write_manager;
+                       break;
+               case 4:
+                       start = tomoyo_builtin_stat;
+                       head.type = TOMOYO_STAT;
+                       head.write = tomoyo_write_stat;
+                       break;
+               }
+               while (1) {
+                       char *end = strchr(start, '\n');
+                       if (!end)
+                               break;
+                       *end = '\0';
+                       tomoyo_normalize_line(start);
+                       head.write_buf = start;
+                       tomoyo_parse_policy(&head, start);
+                       start = end + 1;
+               }
+       }
+       tomoyo_read_unlock(idx);
+#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+       tomoyo_check_profile();
+#endif
+}
index 7c66bd8..f7fbaa6 100644 (file)
@@ -21,7 +21,8 @@
 #include <linux/list.h>
 #include <linux/cred.h>
 #include <linux/poll.h>
-struct linux_binprm;
+#include <linux/binfmts.h>
+#include <linux/highmem.h>
 
 /********** Constants definitions. **********/
 
@@ -38,66 +39,149 @@ struct linux_binprm;
 /* Profile number is an integer between 0 and 255. */
 #define TOMOYO_MAX_PROFILES 256
 
+/* Group number is an integer between 0 and 255. */
+#define TOMOYO_MAX_ACL_GROUPS 256
+
+/* Index numbers for "struct tomoyo_condition". */
+enum tomoyo_conditions_index {
+       TOMOYO_TASK_UID,             /* current_uid()   */
+       TOMOYO_TASK_EUID,            /* current_euid()  */
+       TOMOYO_TASK_SUID,            /* current_suid()  */
+       TOMOYO_TASK_FSUID,           /* current_fsuid() */
+       TOMOYO_TASK_GID,             /* current_gid()   */
+       TOMOYO_TASK_EGID,            /* current_egid()  */
+       TOMOYO_TASK_SGID,            /* current_sgid()  */
+       TOMOYO_TASK_FSGID,           /* current_fsgid() */
+       TOMOYO_TASK_PID,             /* sys_getpid()   */
+       TOMOYO_TASK_PPID,            /* sys_getppid()  */
+       TOMOYO_EXEC_ARGC,            /* "struct linux_binprm *"->argc */
+       TOMOYO_EXEC_ENVC,            /* "struct linux_binprm *"->envc */
+       TOMOYO_TYPE_IS_SOCKET,       /* S_IFSOCK */
+       TOMOYO_TYPE_IS_SYMLINK,      /* S_IFLNK */
+       TOMOYO_TYPE_IS_FILE,         /* S_IFREG */
+       TOMOYO_TYPE_IS_BLOCK_DEV,    /* S_IFBLK */
+       TOMOYO_TYPE_IS_DIRECTORY,    /* S_IFDIR */
+       TOMOYO_TYPE_IS_CHAR_DEV,     /* S_IFCHR */
+       TOMOYO_TYPE_IS_FIFO,         /* S_IFIFO */
+       TOMOYO_MODE_SETUID,          /* S_ISUID */
+       TOMOYO_MODE_SETGID,          /* S_ISGID */
+       TOMOYO_MODE_STICKY,          /* S_ISVTX */
+       TOMOYO_MODE_OWNER_READ,      /* S_IRUSR */
+       TOMOYO_MODE_OWNER_WRITE,     /* S_IWUSR */
+       TOMOYO_MODE_OWNER_EXECUTE,   /* S_IXUSR */
+       TOMOYO_MODE_GROUP_READ,      /* S_IRGRP */
+       TOMOYO_MODE_GROUP_WRITE,     /* S_IWGRP */
+       TOMOYO_MODE_GROUP_EXECUTE,   /* S_IXGRP */
+       TOMOYO_MODE_OTHERS_READ,     /* S_IROTH */
+       TOMOYO_MODE_OTHERS_WRITE,    /* S_IWOTH */
+       TOMOYO_MODE_OTHERS_EXECUTE,  /* S_IXOTH */
+       TOMOYO_EXEC_REALPATH,
+       TOMOYO_SYMLINK_TARGET,
+       TOMOYO_PATH1_UID,
+       TOMOYO_PATH1_GID,
+       TOMOYO_PATH1_INO,
+       TOMOYO_PATH1_MAJOR,
+       TOMOYO_PATH1_MINOR,
+       TOMOYO_PATH1_PERM,
+       TOMOYO_PATH1_TYPE,
+       TOMOYO_PATH1_DEV_MAJOR,
+       TOMOYO_PATH1_DEV_MINOR,
+       TOMOYO_PATH2_UID,
+       TOMOYO_PATH2_GID,
+       TOMOYO_PATH2_INO,
+       TOMOYO_PATH2_MAJOR,
+       TOMOYO_PATH2_MINOR,
+       TOMOYO_PATH2_PERM,
+       TOMOYO_PATH2_TYPE,
+       TOMOYO_PATH2_DEV_MAJOR,
+       TOMOYO_PATH2_DEV_MINOR,
+       TOMOYO_PATH1_PARENT_UID,
+       TOMOYO_PATH1_PARENT_GID,
+       TOMOYO_PATH1_PARENT_INO,
+       TOMOYO_PATH1_PARENT_PERM,
+       TOMOYO_PATH2_PARENT_UID,
+       TOMOYO_PATH2_PARENT_GID,
+       TOMOYO_PATH2_PARENT_INO,
+       TOMOYO_PATH2_PARENT_PERM,
+       TOMOYO_MAX_CONDITION_KEYWORD,
+       TOMOYO_NUMBER_UNION,
+       TOMOYO_NAME_UNION,
+       TOMOYO_ARGV_ENTRY,
+       TOMOYO_ENVP_ENTRY,
+};
+
+
+/* Index numbers for stat(). */
+enum tomoyo_path_stat_index {
+       /* Do not change this order. */
+       TOMOYO_PATH1,
+       TOMOYO_PATH1_PARENT,
+       TOMOYO_PATH2,
+       TOMOYO_PATH2_PARENT,
+       TOMOYO_MAX_PATH_STAT
+};
+
+/* Index numbers for operation mode. */
 enum tomoyo_mode_index {
        TOMOYO_CONFIG_DISABLED,
        TOMOYO_CONFIG_LEARNING,
        TOMOYO_CONFIG_PERMISSIVE,
        TOMOYO_CONFIG_ENFORCING,
-       TOMOYO_CONFIG_USE_DEFAULT = 255
+       TOMOYO_CONFIG_MAX_MODE,
+       TOMOYO_CONFIG_WANT_REJECT_LOG =  64,
+       TOMOYO_CONFIG_WANT_GRANT_LOG  = 128,
+       TOMOYO_CONFIG_USE_DEFAULT     = 255,
 };
 
+/* Index numbers for entry type. */
 enum tomoyo_policy_id {
        TOMOYO_ID_GROUP,
        TOMOYO_ID_PATH_GROUP,
        TOMOYO_ID_NUMBER_GROUP,
        TOMOYO_ID_TRANSITION_CONTROL,
        TOMOYO_ID_AGGREGATOR,
-       TOMOYO_ID_GLOBALLY_READABLE,
-       TOMOYO_ID_PATTERN,
-       TOMOYO_ID_NO_REWRITE,
        TOMOYO_ID_MANAGER,
+       TOMOYO_ID_CONDITION,
        TOMOYO_ID_NAME,
        TOMOYO_ID_ACL,
        TOMOYO_ID_DOMAIN,
        TOMOYO_MAX_POLICY
 };
 
+/* Index numbers for domain's attributes. */
+enum tomoyo_domain_info_flags_index {
+       /* Quota warnning flag.   */
+       TOMOYO_DIF_QUOTA_WARNED,
+       /*
+        * 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.
+        */
+       TOMOYO_DIF_TRANSITION_FAILED,
+       TOMOYO_MAX_DOMAIN_INFO_FLAGS
+};
+
+/* Index numbers for group entries. */
 enum tomoyo_group_id {
        TOMOYO_PATH_GROUP,
        TOMOYO_NUMBER_GROUP,
        TOMOYO_MAX_GROUP
 };
 
-/* Keywords for ACLs. */
-#define TOMOYO_KEYWORD_AGGREGATOR                "aggregator "
-#define TOMOYO_KEYWORD_ALLOW_MOUNT               "allow_mount "
-#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read "
-#define TOMOYO_KEYWORD_DELETE                    "delete "
-#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite "
-#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern "
-#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain "
-#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
-#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
-#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
-#define TOMOYO_KEYWORD_PATH_GROUP                "path_group "
-#define TOMOYO_KEYWORD_NUMBER_GROUP              "number_group "
-#define TOMOYO_KEYWORD_SELECT                    "select "
-#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
-#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
-#define TOMOYO_KEYWORD_QUOTA_EXCEEDED            "quota_exceeded"
-#define TOMOYO_KEYWORD_TRANSITION_FAILED         "transition_failed"
-/* A domain definition starts with <kernel>. */
-#define TOMOYO_ROOT_NAME                         "<kernel>"
-#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
-
-/* Value type definition. */
-#define TOMOYO_VALUE_TYPE_INVALID     0
-#define TOMOYO_VALUE_TYPE_DECIMAL     1
-#define TOMOYO_VALUE_TYPE_OCTAL       2
-#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
+/* Index numbers for type of numeric values. */
+enum tomoyo_value_type {
+       TOMOYO_VALUE_TYPE_INVALID,
+       TOMOYO_VALUE_TYPE_DECIMAL,
+       TOMOYO_VALUE_TYPE_OCTAL,
+       TOMOYO_VALUE_TYPE_HEXADECIMAL,
+};
 
+/* Index numbers for domain transition control keywords. */
 enum tomoyo_transition_type {
        /* Do not change this order, */
+       TOMOYO_TRANSITION_CONTROL_NO_RESET,
+       TOMOYO_TRANSITION_CONTROL_RESET,
        TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE,
        TOMOYO_TRANSITION_CONTROL_INITIALIZE,
        TOMOYO_TRANSITION_CONTROL_NO_KEEP,
@@ -114,35 +198,29 @@ enum tomoyo_acl_entry_type_index {
        TOMOYO_TYPE_MOUNT_ACL,
 };
 
-/* Index numbers for File Controls. */
-
-/*
- * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically
- * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if
- * TOMOYO_TYPE_READ_WRITE is set.
- * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ
- * or TOMOYO_TYPE_WRITE is cleared.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if
- * TOMOYO_TYPE_READ_WRITE is cleared.
- */
-
+/* Index numbers for access controls with one pathname. */
 enum tomoyo_path_acl_index {
-       TOMOYO_TYPE_READ_WRITE,
        TOMOYO_TYPE_EXECUTE,
        TOMOYO_TYPE_READ,
        TOMOYO_TYPE_WRITE,
+       TOMOYO_TYPE_APPEND,
        TOMOYO_TYPE_UNLINK,
+       TOMOYO_TYPE_GETATTR,
        TOMOYO_TYPE_RMDIR,
        TOMOYO_TYPE_TRUNCATE,
        TOMOYO_TYPE_SYMLINK,
-       TOMOYO_TYPE_REWRITE,
        TOMOYO_TYPE_CHROOT,
        TOMOYO_TYPE_UMOUNT,
        TOMOYO_MAX_PATH_OPERATION
 };
 
-#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE))
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_memory_stat_type {
+       TOMOYO_MEMORY_POLICY,
+       TOMOYO_MEMORY_AUDIT,
+       TOMOYO_MEMORY_QUERY,
+       TOMOYO_MAX_MEMORY_STAT
+};
 
 enum tomoyo_mkdev_acl_index {
        TOMOYO_TYPE_MKBLOCK,
@@ -150,6 +228,7 @@ enum tomoyo_mkdev_acl_index {
        TOMOYO_MAX_MKDEV_OPERATION
 };
 
+/* Index numbers for access controls with two pathnames. */
 enum tomoyo_path2_acl_index {
        TOMOYO_TYPE_LINK,
        TOMOYO_TYPE_RENAME,
@@ -157,6 +236,7 @@ enum tomoyo_path2_acl_index {
        TOMOYO_MAX_PATH2_OPERATION
 };
 
+/* Index numbers for access controls with one pathname and one number. */
 enum tomoyo_path_number_acl_index {
        TOMOYO_TYPE_CREATE,
        TOMOYO_TYPE_MKDIR,
@@ -169,31 +249,45 @@ enum tomoyo_path_number_acl_index {
        TOMOYO_MAX_PATH_NUMBER_OPERATION
 };
 
+/* Index numbers for /sys/kernel/security/tomoyo/ interfaces. */
 enum tomoyo_securityfs_interface_index {
        TOMOYO_DOMAINPOLICY,
        TOMOYO_EXCEPTIONPOLICY,
-       TOMOYO_DOMAIN_STATUS,
        TOMOYO_PROCESS_STATUS,
-       TOMOYO_MEMINFO,
+       TOMOYO_STAT,
        TOMOYO_SELFDOMAIN,
+       TOMOYO_AUDIT,
        TOMOYO_VERSION,
        TOMOYO_PROFILE,
        TOMOYO_QUERY,
        TOMOYO_MANAGER
 };
 
+/* Index numbers for special mount operations. */
+enum tomoyo_special_mount {
+       TOMOYO_MOUNT_BIND,            /* mount --bind /source /dest   */
+       TOMOYO_MOUNT_MOVE,            /* mount --move /old /new       */
+       TOMOYO_MOUNT_REMOUNT,         /* mount -o remount /dir        */
+       TOMOYO_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */
+       TOMOYO_MOUNT_MAKE_PRIVATE,    /* mount --make-private /dir    */
+       TOMOYO_MOUNT_MAKE_SLAVE,      /* mount --make-slave /dir      */
+       TOMOYO_MOUNT_MAKE_SHARED,     /* mount --make-shared /dir     */
+       TOMOYO_MAX_SPECIAL_MOUNT
+};
+
+/* Index numbers for functionality. */
 enum tomoyo_mac_index {
        TOMOYO_MAC_FILE_EXECUTE,
        TOMOYO_MAC_FILE_OPEN,
        TOMOYO_MAC_FILE_CREATE,
        TOMOYO_MAC_FILE_UNLINK,
+       TOMOYO_MAC_FILE_GETATTR,
        TOMOYO_MAC_FILE_MKDIR,
        TOMOYO_MAC_FILE_RMDIR,
        TOMOYO_MAC_FILE_MKFIFO,
        TOMOYO_MAC_FILE_MKSOCK,
        TOMOYO_MAC_FILE_TRUNCATE,
        TOMOYO_MAC_FILE_SYMLINK,
-       TOMOYO_MAC_FILE_REWRITE,
        TOMOYO_MAC_FILE_MKBLOCK,
        TOMOYO_MAC_FILE_MKCHAR,
        TOMOYO_MAC_FILE_LINK,
@@ -209,38 +303,66 @@ enum tomoyo_mac_index {
        TOMOYO_MAX_MAC_INDEX
 };
 
+/* Index numbers for category of functionality. */
 enum tomoyo_mac_category_index {
        TOMOYO_MAC_CATEGORY_FILE,
        TOMOYO_MAX_MAC_CATEGORY_INDEX
 };
 
-#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */
-
-/********** Structure definitions. **********/
-
 /*
- * tomoyo_acl_head is a structure which is used for holding elements not in
- * domain policy.
- * It has following fields.
+ * Retry this request. Returned by tomoyo_supervisor() if policy violation has
+ * occurred in enforcing mode and the userspace daemon decided to retry.
  *
- *  (1) "list" which is linked to tomoyo_policy_list[] .
- *  (2) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
+ * We must choose a positive value in order to distinguish "granted" (which is
+ * 0) and "rejected" (which is a negative value) and "retry".
  */
+#define TOMOYO_RETRY_REQUEST 1
+
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_policy_stat_type {
+       /* Do not change this order. */
+       TOMOYO_STAT_POLICY_UPDATES,
+       TOMOYO_STAT_POLICY_LEARNING,   /* == TOMOYO_CONFIG_LEARNING */
+       TOMOYO_STAT_POLICY_PERMISSIVE, /* == TOMOYO_CONFIG_PERMISSIVE */
+       TOMOYO_STAT_POLICY_ENFORCING,  /* == TOMOYO_CONFIG_ENFORCING */
+       TOMOYO_MAX_POLICY_STAT
+};
+
+/* Index numbers for profile's PREFERENCE values. */
+enum tomoyo_pref_index {
+       TOMOYO_PREF_MAX_AUDIT_LOG,
+       TOMOYO_PREF_MAX_LEARNING_ENTRY,
+       TOMOYO_MAX_PREF
+};
+
+/********** Structure definitions. **********/
+
+/* Common header for holding ACL entries. */
 struct tomoyo_acl_head {
        struct list_head list;
        bool is_deleted;
 } __packed;
 
-/*
- * tomoyo_request_info is a structure which is used for holding
- *
- * (1) Domain information of current process.
- * (2) How many retries are made for this request.
- * (3) Profile number used for this request.
- * (4) Access control mode of the profile.
- */
+/* Common header for shared entries. */
+struct tomoyo_shared_acl_head {
+       struct list_head list;
+       atomic_t users;
+} __packed;
+
+struct tomoyo_policy_namespace;
+
+/* Structure for request info. */
 struct tomoyo_request_info {
+       /*
+        * For holding parameters specific to operations which deal files.
+        * NULL if not dealing files.
+        */
+       struct tomoyo_obj_info *obj;
+       /*
+        * For holding parameters specific to execve() request.
+        * NULL if not dealing do_execve().
+        */
+       struct tomoyo_execve *ee;
        struct tomoyo_domain_info *domain;
        /* For holding parameters. */
        union {
@@ -248,11 +370,13 @@ struct tomoyo_request_info {
                        const struct tomoyo_path_info *filename;
                        /* For using wildcards at tomoyo_find_next_domain(). */
                        const struct tomoyo_path_info *matched_path;
+                       /* One of values in "enum tomoyo_path_acl_index". */
                        u8 operation;
                } path;
                struct {
                        const struct tomoyo_path_info *filename1;
                        const struct tomoyo_path_info *filename2;
+                       /* One of values in "enum tomoyo_path2_acl_index". */
                        u8 operation;
                } path2;
                struct {
@@ -260,11 +384,16 @@ struct tomoyo_request_info {
                        unsigned int mode;
                        unsigned int major;
                        unsigned int minor;
+                       /* One of values in "enum tomoyo_mkdev_acl_index". */
                        u8 operation;
                } mkdev;
                struct {
                        const struct tomoyo_path_info *filename;
                        unsigned long number;
+                       /*
+                        * One of values in
+                        * "enum tomoyo_path_number_acl_index".
+                        */
                        u8 operation;
                } path_number;
                struct {
@@ -283,26 +412,7 @@ struct tomoyo_request_info {
        u8 type;
 };
 
-/*
- * tomoyo_path_info is a structure which is used for holding a string data
- * used by TOMOYO.
- * This structure has several fields for supporting pattern matching.
- *
- * (1) "name" is the '\0' terminated string data.
- * (2) "hash" is full_name_hash(name, strlen(name)).
- *     This allows tomoyo_pathcmp() to compare by hash before actually compare
- *     using strcmp().
- * (3) "const_len" is the length of the initial segment of "name" which
- *     consists entirely of non wildcard characters. In other words, the length
- *     which we can compare two strings using strncmp().
- * (4) "is_dir" is a bool which is true if "name" ends with "/",
- *     false otherwise.
- *     TOMOYO distinguishes directory and non-directory. A directory ends with
- *     "/" and non-directory does not end with "/".
- * (5) "is_patterned" is a bool which is true if "name" contains wildcard
- *     characters, false otherwise. This allows TOMOYO to use "hash" and
- *     strcmp() for string comparison if "is_patterned" is false.
- */
+/* Structure for holding a token. */
 struct tomoyo_path_info {
        const char *name;
        u32 hash;          /* = full_name_hash(name, strlen(name)) */
@@ -311,36 +421,32 @@ struct tomoyo_path_info {
        bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
 };
 
-/*
- * tomoyo_name is a structure which is used for linking
- * "struct tomoyo_path_info" into tomoyo_name_list .
- */
+/* Structure for holding string data. */
 struct tomoyo_name {
-       struct list_head list;
-       atomic_t users;
+       struct tomoyo_shared_acl_head head;
        struct tomoyo_path_info entry;
 };
 
+/* Structure for holding a word. */
 struct tomoyo_name_union {
+       /* Either @filename or @group is NULL. */
        const struct tomoyo_path_info *filename;
        struct tomoyo_group *group;
-       u8 is_group;
 };
 
+/* Structure for holding a number. */
 struct tomoyo_number_union {
        unsigned long values[2];
-       struct tomoyo_group *group;
-       u8 min_type;
-       u8 max_type;
-       u8 is_group;
+       struct tomoyo_group *group; /* Maybe NULL. */
+       /* One of values in "enum tomoyo_value_type". */
+       u8 value_type[2];
 };
 
 /* Structure for "path_group"/"number_group" directive. */
 struct tomoyo_group {
-       struct list_head list;
+       struct tomoyo_shared_acl_head head;
        const struct tomoyo_path_info *group_name;
        struct list_head member_list;
-       atomic_t users;
 };
 
 /* Structure for "path_group" directive. */
@@ -355,130 +461,158 @@ struct tomoyo_number_group {
        struct tomoyo_number_union number;
 };
 
-/*
- * tomoyo_acl_info is a structure which is used for holding
- *
- *  (1) "list" which is linked to the ->acl_info_list of
- *      "struct tomoyo_domain_info"
- *  (2) "is_deleted" is a bool which is true if this domain is marked as
- *      "deleted", false otherwise.
- *  (3) "type" which tells type of the entry.
- *
- * Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl"
- * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed
- * "u8" without enlarging their structure size.
- */
+/* Subset of "struct stat". Used by conditional ACL and audit logs. */
+struct tomoyo_mini_stat {
+       uid_t uid;
+       gid_t gid;
+       ino_t ino;
+       mode_t mode;
+       dev_t dev;
+       dev_t rdev;
+};
+
+/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */
+struct tomoyo_page_dump {
+       struct page *page;    /* Previously dumped page. */
+       char *data;           /* Contents of "page". Size is PAGE_SIZE. */
+};
+
+/* Structure for attribute checks in addition to pathname checks. */
+struct tomoyo_obj_info {
+       /*
+        * True if tomoyo_get_attributes() was already called, false otherwise.
+        */
+       bool validate_done;
+       /* True if @stat[] is valid. */
+       bool stat_valid[TOMOYO_MAX_PATH_STAT];
+       /* First pathname. Initialized with { NULL, NULL } if no path. */
+       struct path path1;
+       /* Second pathname. Initialized with { NULL, NULL } if no path. */
+       struct path path2;
+       /*
+        * Information on @path1, @path1's parent directory, @path2, @path2's
+        * parent directory.
+        */
+       struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT];
+       /*
+        * Content of symbolic link to be created. NULL for operations other
+        * than symlink().
+        */
+       struct tomoyo_path_info *symlink_target;
+};
+
+/* Structure for argv[]. */
+struct tomoyo_argv {
+       unsigned long index;
+       const struct tomoyo_path_info *value;
+       bool is_not;
+};
+
+/* Structure for envp[]. */
+struct tomoyo_envp {
+       const struct tomoyo_path_info *name;
+       const struct tomoyo_path_info *value;
+       bool is_not;
+};
+
+/* Structure for execve() operation. */
+struct tomoyo_execve {
+       struct tomoyo_request_info r;
+       struct tomoyo_obj_info obj;
+       struct linux_binprm *bprm;
+       /* For dumping argv[] and envp[]. */
+       struct tomoyo_page_dump dump;
+       /* For temporary use. */
+       char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
+};
+
+/* Structure for entries which follows "struct tomoyo_condition". */
+struct tomoyo_condition_element {
+       /*
+        * Left hand operand. A "struct tomoyo_argv" for TOMOYO_ARGV_ENTRY, a
+        * "struct tomoyo_envp" for TOMOYO_ENVP_ENTRY is attached to the tail
+        * of the array of this struct.
+        */
+       u8 left;
+       /*
+        * Right hand operand. A "struct tomoyo_number_union" for
+        * TOMOYO_NUMBER_UNION, a "struct tomoyo_name_union" for
+        * TOMOYO_NAME_UNION is attached to the tail of the array of this
+        * struct.
+        */
+       u8 right;
+       /* Equation operator. True if equals or overlaps, false otherwise. */
+       bool equals;
+};
+
+/* Structure for optional arguments. */
+struct tomoyo_condition {
+       struct tomoyo_shared_acl_head head;
+       u32 size; /* Memory size allocated for this entry. */
+       u16 condc; /* Number of conditions in this struct. */
+       u16 numbers_count; /* Number of "struct tomoyo_number_union values". */
+       u16 names_count; /* Number of "struct tomoyo_name_union names". */
+       u16 argc; /* Number of "struct tomoyo_argv". */
+       u16 envc; /* Number of "struct tomoyo_envp". */
+       /*
+        * struct tomoyo_condition_element condition[condc];
+        * struct tomoyo_number_union values[numbers_count];
+        * struct tomoyo_name_union names[names_count];
+        * struct tomoyo_argv argv[argc];
+        * struct tomoyo_envp envp[envc];
+        */
+};
+
+/* Common header for individual entries. */
 struct tomoyo_acl_info {
        struct list_head list;
+       struct tomoyo_condition *cond; /* Maybe NULL. */
        bool is_deleted;
-       u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */
+       u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
 } __packed;
 
-/*
- * tomoyo_domain_info is a structure which is used for holding permissions
- * (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_domain_list .
- *  (2) "acl_info_list" which is linked to "struct tomoyo_acl_info".
- *  (3) "domainname" which holds the name of the domain.
- *  (4) "profile" which remembers profile number assigned to this domain.
- *  (5) "is_deleted" is a bool which is true if this domain is marked as
- *      "deleted", false otherwise.
- *  (6) "quota_warned" is a bool which is used for suppressing warning message
- *      when learning mode learned too much entries.
- *  (7) "ignore_global_allow_read" is a bool which is true if this domain
- *      should ignore "allow_read" directive in exception policy.
- *  (8) "transition_failed" is a bool which is set to true when this domain was
- *      unable to create a new domain at tomoyo_find_next_domain() because the
- *      name of the domain to be created was too long or it could not allocate
- *      memory. If set to true, more than one process continued execve()
- *      without domain transition.
- *  (9) "users" is an atomic_t that holds how many "struct cred"->security
- *      are referring this "struct tomoyo_domain_info". If is_deleted == true
- *      and users == 0, this struct will be kfree()d upon next garbage
- *      collection.
- *
- * A domain's lifecycle is an analogy of files on / directory.
- * Multiple domains with the same domainname cannot be created (as with
- * creating files with the same filename fails with -EEXIST).
- * If a process reached a domain, that process can reside in that domain after
- * that domain is marked as "deleted" (as with a process can access an already
- * open()ed file after that file was unlink()ed).
- */
+/* Structure for domain information. */
 struct tomoyo_domain_info {
        struct list_head list;
        struct list_head acl_info_list;
        /* Name of this domain. Never NULL.          */
        const struct tomoyo_path_info *domainname;
+       /* Namespace for this domain. Never NULL. */
+       struct tomoyo_policy_namespace *ns;
        u8 profile;        /* Profile number to use. */
+       u8 group;          /* Group number to use.   */
        bool is_deleted;   /* Delete flag.           */
-       bool quota_warned; /* Quota warnning flag.   */
-       bool ignore_global_allow_read; /* Ignore "allow_read" flag. */
-       bool transition_failed; /* Domain transition failed flag. */
+       bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
        atomic_t users; /* Number of referring credentials. */
 };
 
 /*
- * tomoyo_path_acl is a structure which is used for holding an
- * entry with one pathname operation (e.g. open(), mkdir()).
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "name" is the pathname.
- *
- * Directives held by this structure are "allow_read/write", "allow_execute",
- * "allow_read", "allow_write", "allow_unlink", "allow_rmdir",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and
- * "allow_unmount".
+ * Structure for "file execute", "file read", "file write", "file append",
+ * "file unlink", "file getattr", "file rmdir", "file truncate",
+ * "file symlink", "file chroot" and "file unmount" directive.
  */
 struct tomoyo_path_acl {
        struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
-       u16 perm;
+       u16 perm; /* Bitmask of values in "enum tomoyo_path_acl_index". */
        struct tomoyo_name_union name;
 };
 
 /*
- * tomoyo_path_number_acl is a structure which is used for holding an
- * entry with one pathname and one number operation.
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "name" is the pathname.
- *  (4) "number" is the numeric value.
- *
- * Directives held by this structure are "allow_create", "allow_mkdir",
- * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown"
- * and "allow_chgrp".
- *
+ * Structure for "file create", "file mkdir", "file mkfifo", "file mksock",
+ * "file ioctl", "file chmod", "file chown" and "file chgrp" directive.
  */
 struct tomoyo_path_number_acl {
        struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */
+       /* Bitmask of values in "enum tomoyo_path_number_acl_index". */
        u8 perm;
        struct tomoyo_name_union name;
        struct tomoyo_number_union number;
 };
 
-/*
- * tomoyo_mkdev_acl is a structure which is used for holding an
- * entry with one pathname and three numbers operation.
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "mode" is the create mode.
- *  (4) "major" is the major number of device node.
- *  (5) "minor" is the minor number of device node.
- *
- * Directives held by this structure are "allow_mkchar", "allow_mkblock".
- *
- */
+/* Structure for "file mkblock" and "file mkchar" directive. */
 struct tomoyo_mkdev_acl {
        struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */
-       u8 perm;
+       u8 perm; /* Bitmask of values in "enum tomoyo_mkdev_acl_index". */
        struct tomoyo_name_union name;
        struct tomoyo_number_union mode;
        struct tomoyo_number_union major;
@@ -486,38 +620,16 @@ struct tomoyo_mkdev_acl {
 };
 
 /*
- * tomoyo_path2_acl is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "name1" is the source/old pathname.
- *  (4) "name2" is the destination/new pathname.
- *
- * Directives held by this structure are "allow_rename", "allow_link" and
- * "allow_pivot_root".
+ * Structure for "file rename", "file link" and "file pivot_root" directive.
  */
 struct tomoyo_path2_acl {
        struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
-       u8 perm;
+       u8 perm; /* Bitmask of values in "enum tomoyo_path2_acl_index". */
        struct tomoyo_name_union name1;
        struct tomoyo_name_union name2;
 };
 
-/*
- * tomoyo_mount_acl is a structure which is used for holding an
- * entry for mount operation.
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "dev_name" is the device name.
- *  (3) "dir_name" is the mount point.
- *  (4) "fs_type" is the filesystem type.
- *  (5) "flags" is the mount flags.
- *
- * Directive held by this structure is "allow_mount".
- */
+/* Structure for "file mount" directive. */
 struct tomoyo_mount_acl {
        struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */
        struct tomoyo_name_union dev_name;
@@ -526,7 +638,15 @@ struct tomoyo_mount_acl {
        struct tomoyo_number_union flags;
 };
 
-#define TOMOYO_MAX_IO_READ_QUEUE 32
+/* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
+struct tomoyo_acl_param {
+       char *data;
+       struct list_head *list;
+       struct tomoyo_policy_namespace *ns;
+       bool is_delete;
+};
+
+#define TOMOYO_MAX_IO_READ_QUEUE 64
 
 /*
  * Structure for reading/writing policy via /sys/kernel/security/tomoyo
@@ -538,95 +658,55 @@ struct tomoyo_io_buffer {
        int (*poll) (struct file *file, poll_table *wait);
        /* Exclusive lock for this structure.   */
        struct mutex io_sem;
-       /* Index returned by tomoyo_read_lock(). */
-       int reader_idx;
        char __user *read_user_buf;
-       int read_user_buf_avail;
+       size_t read_user_buf_avail;
        struct {
+               struct list_head *ns;
                struct list_head *domain;
                struct list_head *group;
                struct list_head *acl;
-               int avail;
-               int step;
-               int query_index;
+               size_t avail;
+               unsigned int step;
+               unsigned int query_index;
                u16 index;
+               u16 cond_index;
+               u8 acl_group_index;
+               u8 cond_step;
                u8 bit;
                u8 w_pos;
                bool eof;
                bool print_this_domain_only;
-               bool print_execute_only;
+               bool print_transition_related_only;
+               bool print_cond_part;
                const char *w[TOMOYO_MAX_IO_READ_QUEUE];
        } r;
-       /* The position currently writing to.   */
-       struct tomoyo_domain_info *write_var1;
+       struct {
+               struct tomoyo_policy_namespace *ns;
+               /* The position currently writing to.   */
+               struct tomoyo_domain_info *domain;
+               /* Bytes available for writing.         */
+               size_t avail;
+               bool is_delete;
+       } w;
        /* Buffer for reading.                  */
        char *read_buf;
        /* Size of read buffer.                 */
-       int readbuf_size;
+       size_t readbuf_size;
        /* Buffer for writing.                  */
        char *write_buf;
-       /* Bytes available for writing.         */
-       int write_avail;
        /* Size of write buffer.                */
-       int writebuf_size;
+       size_t writebuf_size;
        /* Type of this interface.              */
-       u8 type;
-};
-
-/*
- * tomoyo_readable_file is a structure which is used for holding
- * "allow_read" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "filename" is a pathname which is allowed to open(O_RDONLY).
- */
-struct tomoyo_readable_file {
-       struct tomoyo_acl_head head;
-       const struct tomoyo_path_info *filename;
-};
-
-/*
- * tomoyo_no_pattern is a structure which is used for holding
- * "file_pattern" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "pattern" is a pathname pattern which is used for converting pathnames
- *      to pathname patterns during learning mode.
- */
-struct tomoyo_no_pattern {
-       struct tomoyo_acl_head head;
-       const struct tomoyo_path_info *pattern;
-};
-
-/*
- * tomoyo_no_rewrite is a structure which is used for holding
- * "deny_rewrite" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "pattern" is a pathname which is by default not permitted to modify
- *      already existing content.
- */
-struct tomoyo_no_rewrite {
-       struct tomoyo_acl_head head;
-       const struct tomoyo_path_info *pattern;
+       enum tomoyo_securityfs_interface_index type;
+       /* Users counter protected by tomoyo_io_buffer_list_lock. */
+       u8 users;
+       /* List for telling GC not to kfree() elements. */
+       struct list_head list;
 };
 
 /*
- * tomoyo_transition_control is a structure which is used for holding
- * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain"
- * entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "type" is type of this entry.
- *  (3) "is_last_name" is a bool which is true if "domainname" is "the last
- *      component of a domainname", false otherwise.
- *  (4) "domainname" which is "a domainname" or "the last component of a
- *      domainname".
- *  (5) "program" which is a program's pathname.
+ * Structure for "initialize_domain"/"no_initialize_domain"/"keep_domain"/
+ * "no_keep_domain" keyword.
  */
 struct tomoyo_transition_control {
        struct tomoyo_acl_head head;
@@ -637,32 +717,14 @@ struct tomoyo_transition_control {
        const struct tomoyo_path_info *program;    /* Maybe NULL */
 };
 
-/*
- * tomoyo_aggregator is a structure which is used for holding
- * "aggregator" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "original_name" which is originally requested name.
- *  (3) "aggregated_name" which is name to rewrite.
- */
+/* Structure for "aggregator" keyword. */
 struct tomoyo_aggregator {
        struct tomoyo_acl_head head;
        const struct tomoyo_path_info *original_name;
        const struct tomoyo_path_info *aggregated_name;
 };
 
-/*
- * tomoyo_manager is a structure which is used for holding list of
- * domainnames or programs which are permitted to modify configuration via
- * /sys/kernel/security/tomoyo/ interface.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "is_domain" is a bool which is true if "manager" is a domainname, false
- *      otherwise.
- *  (3) "manager" is a domainname or a program's pathname.
- */
+/* Structure for policy manager. */
 struct tomoyo_manager {
        struct tomoyo_acl_head head;
        bool is_domain;  /* True if manager is a domainname. */
@@ -677,6 +739,7 @@ struct tomoyo_preference {
        bool permissive_verbose;
 };
 
+/* Structure for /sys/kernel/security/tomnoyo/profile interface. */
 struct tomoyo_profile {
        const struct tomoyo_path_info *comment;
        struct tomoyo_preference *learning;
@@ -685,323 +748,409 @@ struct tomoyo_profile {
        struct tomoyo_preference preference;
        u8 default_config;
        u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+       unsigned int pref[TOMOYO_MAX_PREF];
+};
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct tomoyo_time {
+       u16 year;
+       u8 month;
+       u8 day;
+       u8 hour;
+       u8 min;
+       u8 sec;
+};
+
+/* Structure for policy namespace. */
+struct tomoyo_policy_namespace {
+       /* Profile table. Memory is allocated as needed. */
+       struct tomoyo_profile *profile_ptr[TOMOYO_MAX_PROFILES];
+       /* List of "struct tomoyo_group". */
+       struct list_head group_list[TOMOYO_MAX_GROUP];
+       /* List of policy. */
+       struct list_head policy_list[TOMOYO_MAX_POLICY];
+       /* The global ACL referred by "use_group" keyword. */
+       struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
+       /* List for connecting to tomoyo_namespace_list list. */
+       struct list_head namespace_list;
+       /* Profile version. Currently only 20100903 is defined. */
+       unsigned int profile_version;
+       /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
+       const char *name;
 };
 
 /********** Function prototypes. **********/
 
-/* Check whether the given string starts with the given keyword. */
-bool tomoyo_str_starts(char **src, const char *find);
-/* Get tomoyo_realpath() of current process. */
-const char *tomoyo_get_exe(void);
-/* Format string. */
-void tomoyo_normalize_line(unsigned char *buffer);
-/* Print warning or error message on console. */
-void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
-     __attribute__ ((format(printf, 2, 3)));
-/* Check all profiles currently assigned to domains are defined. */
-void tomoyo_check_profile(void);
-/* Open operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_open_control(const u8 type, struct file *file);
-/* Close /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_close_control(struct file *file);
-/* Poll operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_poll_control(struct file *file, poll_table *wait);
-/* Read operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_read_control(struct file *file, char __user *buffer,
-                       const int buffer_len);
-/* Write operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_write_control(struct file *file, const char __user *buffer,
-                        const int buffer_len);
-/* Check whether the domain has too many ACL entries to hold. */
-bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
-/* Print out of memory warning message. */
-void tomoyo_warn_oom(const char *function);
-/* Check whether the given name matches the given name_union. */
-const struct tomoyo_path_info *
-tomoyo_compare_name_union(const struct tomoyo_path_info *name,
-                         const struct tomoyo_name_union *ptr);
-/* Check whether the given number matches the given number_union. */
 bool tomoyo_compare_number_union(const unsigned long value,
                                 const struct tomoyo_number_union *ptr);
-int tomoyo_get_mode(const u8 profile, const u8 index);
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
-       __attribute__ ((format(printf, 2, 3)));
-/* Check whether the domainname is correct. */
+bool tomoyo_condition(struct tomoyo_request_info *r,
+                     const struct tomoyo_condition *cond);
 bool tomoyo_correct_domain(const unsigned char *domainname);
-/* Check whether the token is correct. */
 bool tomoyo_correct_path(const char *filename);
 bool tomoyo_correct_word(const char *string);
-/* Check whether the token can be a domainname. */
 bool tomoyo_domain_def(const unsigned char *buffer);
-bool tomoyo_parse_name_union(const char *filename,
-                            struct tomoyo_name_union *ptr);
-/* Check whether the given filename matches the given path_group. */
-const struct tomoyo_path_info *
-tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
-                         const struct tomoyo_group *group);
-/* Check whether the given value matches the given number_group. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+                     struct tomoyo_page_dump *dump);
+bool tomoyo_memory_ok(void *ptr);
 bool tomoyo_number_matches_group(const unsigned long min,
                                 const unsigned long max,
                                 const struct tomoyo_group *group);
-/* Check whether the given filename matches the given pattern. */
+bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
+                            struct tomoyo_name_union *ptr);
+bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
+                              struct tomoyo_number_union *ptr);
 bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
                                 const struct tomoyo_path_info *pattern);
-
-bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num);
-/* Tokenize a line. */
-bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
-/* Write domain policy violation warning message to console? */
-bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
-/* Fill "struct tomoyo_request_info". */
-int tomoyo_init_request_info(struct tomoyo_request_info *r,
-                            struct tomoyo_domain_info *domain,
-                            const u8 index);
-/* Check permission for mount operation. */
-int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
-                           unsigned long flags, void *data_page);
-/* Create "aggregator" entry in exception policy. */
-int tomoyo_write_aggregator(char *data, const bool is_delete);
-int tomoyo_write_transition_control(char *data, const bool is_delete,
-                                   const u8 type);
-/*
- * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
- * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
- * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
- * "allow_link" entry in domain policy.
- */
-int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
-                     const bool is_delete);
-/* Create "allow_read" entry in exception policy. */
-int tomoyo_write_globally_readable(char *data, const bool is_delete);
-/* Create "allow_mount" entry in domain policy. */
-int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
-                      const bool is_delete);
-/* Create "deny_rewrite" entry in exception policy. */
-int tomoyo_write_no_rewrite(char *data, const bool is_delete);
-/* Create "file_pattern" entry in exception policy. */
-int tomoyo_write_pattern(char *data, const bool is_delete);
-/* Create "path_group"/"number_group" entry in exception policy. */
-int tomoyo_write_group(char *data, const bool is_delete, const u8 type);
-int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
-     __attribute__ ((format(printf, 2, 3)));
-/* Find a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
-/* Find or create a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
-                                               const u8 profile);
-struct tomoyo_profile *tomoyo_profile(const u8 profile);
-/*
- * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
- */
-struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type);
-
-/* Check mode for specified functionality. */
-unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
-                               const u8 index);
-/* Fill in "struct tomoyo_path_info" members. */
-void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
-/* Run policy loader when /sbin/init starts. */
-void tomoyo_load_policy(const char *filename);
-
-void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
-
-/* Convert binary string to ascii string. */
+bool tomoyo_permstr(const char *string, const char *keyword);
+bool tomoyo_str_starts(char **src, const char *find);
 char *tomoyo_encode(const char *str);
-
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and does not follow the final symlink.
- */
-char *tomoyo_realpath_nofollow(const char *pathname);
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and the pathname is already solved.
- */
+char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
+                     va_list args);
+char *tomoyo_read_token(struct tomoyo_acl_param *param);
 char *tomoyo_realpath_from_path(struct path *path);
-/* Get patterned pathname. */
-const char *tomoyo_pattern(const struct tomoyo_path_info *filename);
-
-/* Check memory quota. */
-bool tomoyo_memory_ok(void *ptr);
-void *tomoyo_commit_ok(void *data, const unsigned int size);
-
-/*
- * Keep the given name on the RAM.
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
- */
+char *tomoyo_realpath_nofollow(const char *pathname);
+const char *tomoyo_get_exe(void);
+const char *tomoyo_yesno(const unsigned int value);
+const struct tomoyo_path_info *tomoyo_compare_name_union
+(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
 const struct tomoyo_path_info *tomoyo_get_name(const char *name);
-
-/* Check for memory usage. */
-void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
-
-/* Set memory quota. */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
-
-/* Initialize mm related code. */
-void __init tomoyo_mm_init(void);
-int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
-                          const struct tomoyo_path_info *filename);
+const struct tomoyo_path_info *tomoyo_path_matches_group
+(const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
 int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
                                 struct path *path, const int flag);
-int tomoyo_path_number_perm(const u8 operation, struct path *path,
-                           unsigned long number);
+int tomoyo_close_control(struct tomoyo_io_buffer *head);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
+int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
+                   const u8 index);
+int tomoyo_init_request_info(struct tomoyo_request_info *r,
+                            struct tomoyo_domain_info *domain,
+                            const u8 index);
 int tomoyo_mkdev_perm(const u8 operation, struct path *path,
                      const unsigned int mode, unsigned int dev);
-int tomoyo_path_perm(const u8 operation, struct path *path);
+int tomoyo_mount_permission(char *dev_name, struct path *path,
+                           const char *type, unsigned long flags,
+                           void *data_page);
+int tomoyo_open_control(const u8 type, struct file *file);
 int tomoyo_path2_perm(const u8 operation, struct path *path1,
                      struct path *path2);
-int tomoyo_find_next_domain(struct linux_binprm *bprm);
-
-void tomoyo_print_ulong(char *buffer, const int buffer_len,
-                       const unsigned long value, const u8 type);
-
-/* Drop refcount on tomoyo_name_union. */
-void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
-
-/* Run garbage collector. */
-void tomoyo_run_gc(void);
-
-void tomoyo_memory_free(void *ptr);
-
+int tomoyo_path_number_perm(const u8 operation, struct path *path,
+                           unsigned long number);
+int tomoyo_path_perm(const u8 operation, struct path *path,
+                    const char *target);
+int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
+                          const struct tomoyo_path_info *filename);
+int tomoyo_poll_control(struct file *file, poll_table *wait);
+int tomoyo_poll_log(struct file *file, poll_table *wait);
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+       __printf(2, 3);
 int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
-                        bool is_delete, struct tomoyo_domain_info *domain,
-                        bool (*check_duplicate) (const struct tomoyo_acl_info
-                                                 *,
-                                                 const struct tomoyo_acl_info
-                                                 *),
-                        bool (*merge_duplicate) (struct tomoyo_acl_info *,
-                                                 struct tomoyo_acl_info *,
-                                                 const bool));
+                        struct tomoyo_acl_param *param,
+                        bool (*check_duplicate)
+                        (const struct tomoyo_acl_info *,
+                         const struct tomoyo_acl_info *),
+                        bool (*merge_duplicate)
+                        (struct tomoyo_acl_info *, struct tomoyo_acl_info *,
+                         const bool));
 int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
-                        bool is_delete, struct list_head *list,
-                        bool (*check_duplicate) (const struct tomoyo_acl_head
-                                                 *,
-                                                 const struct tomoyo_acl_head
-                                                 *));
+                        struct tomoyo_acl_param *param,
+                        bool (*check_duplicate)
+                        (const struct tomoyo_acl_head *,
+                         const struct tomoyo_acl_head *));
+int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
+int tomoyo_write_file(struct tomoyo_acl_param *param);
+int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
+int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
+                                   const u8 type);
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+                           const int buffer_len);
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+                            const char __user *buffer, const int buffer_len);
+struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param);
+struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
+                                               const bool transit);
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
+                                     const u8 idx);
+struct tomoyo_policy_namespace *tomoyo_assign_namespace
+(const char *domainname);
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+                                     const u8 profile);
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+                               const u8 index);
+u8 tomoyo_parse_ulong(unsigned long *result, char **str);
+void *tomoyo_commit_ok(void *data, const unsigned int size);
+void __init tomoyo_load_builtin_policy(void);
+void __init tomoyo_mm_init(void);
 void tomoyo_check_acl(struct tomoyo_request_info *r,
                      bool (*check_entry) (struct tomoyo_request_info *,
                                           const struct tomoyo_acl_info *));
+void tomoyo_check_profile(void);
+void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp);
+void tomoyo_del_condition(struct list_head *element);
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
+void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+        __printf(2, 3);
+void tomoyo_load_policy(const char *filename);
+void tomoyo_memory_free(void *ptr);
+void tomoyo_normalize_line(unsigned char *buffer);
+void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+                       const unsigned long value, const u8 type);
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
+void tomoyo_read_log(struct tomoyo_io_buffer *head);
+void tomoyo_update_stat(const u8 index);
+void tomoyo_warn_oom(const char *function);
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+       __printf(2, 3);
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+                      va_list args);
 
 /********** External variable definitions. **********/
 
-/* Lock for GC. */
-extern struct srcu_struct tomoyo_ss;
-
-/* The list for "struct tomoyo_domain_info". */
+extern bool tomoyo_policy_loaded;
+extern const char * const tomoyo_condition_keyword
+[TOMOYO_MAX_CONDITION_KEYWORD];
+extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
+extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+                                             + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
+extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
+extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
+extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
+extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
+extern const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION];
+extern struct list_head tomoyo_condition_list;
 extern struct list_head tomoyo_domain_list;
-
-extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
-extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
 extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
-
-/* Lock for protecting policy. */
+extern struct list_head tomoyo_namespace_list;
 extern struct mutex tomoyo_policy_lock;
-
-/* Has /sbin/init started? */
-extern bool tomoyo_policy_loaded;
-
-/* The kernel's domain. */
+extern struct srcu_struct tomoyo_ss;
 extern struct tomoyo_domain_info tomoyo_kernel_domain;
-
-extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
-extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION];
-extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION];
-extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION];
-
-extern unsigned int tomoyo_quota_for_query;
-extern unsigned int tomoyo_query_memory_size;
+extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
+extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
+extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
 
 /********** Inlined functions. **********/
 
+/**
+ * tomoyo_read_lock - Take lock for protecting policy.
+ *
+ * Returns index number for tomoyo_read_unlock().
+ */
 static inline int tomoyo_read_lock(void)
 {
        return srcu_read_lock(&tomoyo_ss);
 }
 
+/**
+ * tomoyo_read_unlock - Release lock for protecting policy.
+ *
+ * @idx: Index number returned by tomoyo_read_lock().
+ *
+ * Returns nothing.
+ */
 static inline void tomoyo_read_unlock(int idx)
 {
        srcu_read_unlock(&tomoyo_ss, idx);
 }
 
-/* strcmp() for "struct tomoyo_path_info" structure. */
-static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
-                                 const struct tomoyo_path_info *b)
+/**
+ * tomoyo_sys_getppid - Copy of getppid().
+ *
+ * Returns parent process's PID.
+ *
+ * Alpha does not have getppid() defined. To be able to build this module on
+ * Alpha, I have to copy getppid() from kernel/timer.c.
+ */
+static inline pid_t tomoyo_sys_getppid(void)
 {
-       return a->hash != b->hash || strcmp(a->name, b->name);
+       pid_t pid;
+       rcu_read_lock();
+       pid = task_tgid_vnr(current->real_parent);
+       rcu_read_unlock();
+       return pid;
 }
 
 /**
- * tomoyo_valid - Check whether the character is a valid char.
+ * tomoyo_sys_getpid - Copy of getpid().
  *
- * @c: The character to check.
+ * Returns current thread's PID.
  *
- * Returns true if @c is a valid character, false otherwise.
+ * Alpha does not have getpid() defined. To be able to build this module on
+ * Alpha, I have to copy getpid() from kernel/timer.c.
  */
-static inline bool tomoyo_valid(const unsigned char c)
+static inline pid_t tomoyo_sys_getpid(void)
 {
-       return c > ' ' && c < 127;
+       return task_tgid_vnr(current);
 }
 
 /**
- * tomoyo_invalid - Check whether the character is an invalid char.
+ * tomoyo_pathcmp - strcmp() for "struct tomoyo_path_info" structure.
  *
- * @c: The character to check.
+ * @a: Pointer to "struct tomoyo_path_info".
+ * @b: Pointer to "struct tomoyo_path_info".
  *
- * Returns true if @c is an invalid character, false otherwise.
+ * Returns true if @a == @b, false otherwise.
  */
-static inline bool tomoyo_invalid(const unsigned char c)
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+                                 const struct tomoyo_path_info *b)
 {
-       return c && (c <= ' ' || c >= 127);
+       return a->hash != b->hash || strcmp(a->name, b->name);
 }
 
+/**
+ * tomoyo_put_name - Drop reference on "struct tomoyo_name".
+ *
+ * @name: Pointer to "struct tomoyo_path_info". Maybe NULL.
+ *
+ * Returns nothing.
+ */
 static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
 {
        if (name) {
                struct tomoyo_name *ptr =
                        container_of(name, typeof(*ptr), entry);
-               atomic_dec(&ptr->users);
+               atomic_dec(&ptr->head.users);
        }
 }
 
+/**
+ * tomoyo_put_condition - Drop reference on "struct tomoyo_condition".
+ *
+ * @cond: Pointer to "struct tomoyo_condition". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_put_condition(struct tomoyo_condition *cond)
+{
+       if (cond)
+               atomic_dec(&cond->head.users);
+}
+
+/**
+ * tomoyo_put_group - Drop reference on "struct tomoyo_group".
+ *
+ * @group: Pointer to "struct tomoyo_group". Maybe NULL.
+ *
+ * Returns nothing.
+ */
 static inline void tomoyo_put_group(struct tomoyo_group *group)
 {
        if (group)
-               atomic_dec(&group->users);
+               atomic_dec(&group->head.users);
 }
 
+/**
+ * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" for current thread.
+ */
 static inline struct tomoyo_domain_info *tomoyo_domain(void)
 {
        return current_cred()->security;
 }
 
+/**
+ * tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread.
+ *
+ * @task: Pointer to "struct task_struct".
+ *
+ * Returns pointer to "struct tomoyo_security" for specified thread.
+ */
 static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
                                                            *task)
 {
        return task_cred_xxx(task, security);
 }
 
-static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1,
-                                          const struct tomoyo_acl_info *p2)
+/**
+ * tomoyo_same_name_union - Check for duplicated "struct tomoyo_name_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_name_union".
+ * @b: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_name_union
+(const struct tomoyo_name_union *a, const struct tomoyo_name_union *b)
 {
-       return p1->type == p2->type;
+       return a->filename == b->filename && a->group == b->group;
 }
 
-static inline bool tomoyo_same_name_union
-(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2)
+/**
+ * tomoyo_same_number_union - Check for duplicated "struct tomoyo_number_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_number_union".
+ * @b: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_number_union
+(const struct tomoyo_number_union *a, const struct tomoyo_number_union *b)
 {
-       return p1->filename == p2->filename && p1->group == p2->group &&
-               p1->is_group == p2->is_group;
+       return a->values[0] == b->values[0] && a->values[1] == b->values[1] &&
+               a->group == b->group && a->value_type[0] == b->value_type[0] &&
+               a->value_type[1] == b->value_type[1];
 }
 
-static inline bool tomoyo_same_number_union
-(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2)
+/**
+ * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_policy_namespace" for current thread.
+ */
+static inline struct tomoyo_policy_namespace *tomoyo_current_namespace(void)
+{
+       return tomoyo_domain()->ns;
+}
+
+#if defined(CONFIG_SLOB)
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns @size.
+ *
+ * Since SLOB does not round up, this function simply returns @size.
+ */
+static inline int tomoyo_round2(size_t size)
+{
+       return size;
+}
+
+#else
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns rounded size.
+ *
+ * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of
+ * (e.g.) 128 bytes.
+ */
+static inline int tomoyo_round2(size_t size)
 {
-       return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1]
-               && p1->group == p2->group && p1->min_type == p2->min_type &&
-               p1->max_type == p2->max_type && p1->is_group == p2->is_group;
+#if PAGE_SIZE == 4096
+       size_t bsize = 32;
+#else
+       size_t bsize = 64;
+#endif
+       if (!size)
+               return 0;
+       while (size > bsize)
+               bsize <<= 1;
+       return bsize;
 }
 
+#endif
+
 /**
  * list_for_each_cookie - iterate over a list with cookie.
  * @pos:        the &struct list_head to use as a loop cursor.
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c
new file mode 100644 (file)
index 0000000..8a05f71
--- /dev/null
@@ -0,0 +1,1035 @@
+/*
+ * security/tomoyo/condition.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/* List of "struct tomoyo_condition". */
+LIST_HEAD(tomoyo_condition_list);
+
+/**
+ * tomoyo_argv - Check argv[] in "struct linux_binbrm".
+ *
+ * @index:   Index number of @arg_ptr.
+ * @arg_ptr: Contents of argv[@index].
+ * @argc:    Length of @argv.
+ * @argv:    Pointer to "struct tomoyo_argv".
+ * @checked: Set to true if @argv[@index] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_argv(const unsigned int index, const char *arg_ptr,
+                       const int argc, const struct tomoyo_argv *argv,
+                       u8 *checked)
+{
+       int i;
+       struct tomoyo_path_info arg;
+       arg.name = arg_ptr;
+       for (i = 0; i < argc; argv++, checked++, i++) {
+               bool result;
+               if (index != argv->index)
+                       continue;
+               *checked = 1;
+               tomoyo_fill_path_info(&arg);
+               result = tomoyo_path_matches_pattern(&arg, argv->value);
+               if (argv->is_not)
+                       result = !result;
+               if (!result)
+                       return false;
+       }
+       return true;
+}
+
+/**
+ * tomoyo_envp - Check envp[] in "struct linux_binbrm".
+ *
+ * @env_name:  The name of environment variable.
+ * @env_value: The value of environment variable.
+ * @envc:      Length of @envp.
+ * @envp:      Pointer to "struct tomoyo_envp".
+ * @checked:   Set to true if @envp[@env_name] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_envp(const char *env_name, const char *env_value,
+                       const int envc, const struct tomoyo_envp *envp,
+                       u8 *checked)
+{
+       int i;
+       struct tomoyo_path_info name;
+       struct tomoyo_path_info value;
+       name.name = env_name;
+       tomoyo_fill_path_info(&name);
+       value.name = env_value;
+       tomoyo_fill_path_info(&value);
+       for (i = 0; i < envc; envp++, checked++, i++) {
+               bool result;
+               if (!tomoyo_path_matches_pattern(&name, envp->name))
+                       continue;
+               *checked = 1;
+               if (envp->value) {
+                       result = tomoyo_path_matches_pattern(&value,
+                                                            envp->value);
+                       if (envp->is_not)
+                               result = !result;
+               } else {
+                       result = true;
+                       if (!envp->is_not)
+                               result = !result;
+               }
+               if (!result)
+                       return false;
+       }
+       return true;
+}
+
+/**
+ * tomoyo_scan_bprm - Scan "struct linux_binprm".
+ *
+ * @ee:   Pointer to "struct tomoyo_execve".
+ * @argc: Length of @argc.
+ * @argv: Pointer to "struct tomoyo_argv".
+ * @envc: Length of @envp.
+ * @envp: Poiner to "struct tomoyo_envp".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_bprm(struct tomoyo_execve *ee,
+                            const u16 argc, const struct tomoyo_argv *argv,
+                            const u16 envc, const struct tomoyo_envp *envp)
+{
+       struct linux_binprm *bprm = ee->bprm;
+       struct tomoyo_page_dump *dump = &ee->dump;
+       char *arg_ptr = ee->tmp;
+       int arg_len = 0;
+       unsigned long pos = bprm->p;
+       int offset = pos % PAGE_SIZE;
+       int argv_count = bprm->argc;
+       int envp_count = bprm->envc;
+       bool result = true;
+       u8 local_checked[32];
+       u8 *checked;
+       if (argc + envc <= sizeof(local_checked)) {
+               checked = local_checked;
+               memset(local_checked, 0, sizeof(local_checked));
+       } else {
+               checked = kzalloc(argc + envc, GFP_NOFS);
+               if (!checked)
+                       return false;
+       }
+       while (argv_count || envp_count) {
+               if (!tomoyo_dump_page(bprm, pos, dump)) {
+                       result = false;
+                       goto out;
+               }
+               pos += PAGE_SIZE - offset;
+               while (offset < PAGE_SIZE) {
+                       /* Read. */
+                       const char *kaddr = dump->data;
+                       const unsigned char c = kaddr[offset++];
+                       if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+                               if (c == '\\') {
+                                       arg_ptr[arg_len++] = '\\';
+                                       arg_ptr[arg_len++] = '\\';
+                               } else if (c > ' ' && c < 127) {
+                                       arg_ptr[arg_len++] = c;
+                               } else {
+                                       arg_ptr[arg_len++] = '\\';
+                                       arg_ptr[arg_len++] = (c >> 6) + '0';
+                                       arg_ptr[arg_len++] =
+                                               ((c >> 3) & 7) + '0';
+                                       arg_ptr[arg_len++] = (c & 7) + '0';
+                               }
+                       } else {
+                               arg_ptr[arg_len] = '\0';
+                       }
+                       if (c)
+                               continue;
+                       /* Check. */
+                       if (argv_count) {
+                               if (!tomoyo_argv(bprm->argc - argv_count,
+                                                arg_ptr, argc, argv,
+                                                checked)) {
+                                       result = false;
+                                       break;
+                               }
+                               argv_count--;
+                       } else if (envp_count) {
+                               char *cp = strchr(arg_ptr, '=');
+                               if (cp) {
+                                       *cp = '\0';
+                                       if (!tomoyo_envp(arg_ptr, cp + 1,
+                                                        envc, envp,
+                                                        checked + argc)) {
+                                               result = false;
+                                               break;
+                                       }
+                               }
+                               envp_count--;
+                       } else {
+                               break;
+                       }
+                       arg_len = 0;
+               }
+               offset = 0;
+               if (!result)
+                       break;
+       }
+out:
+       if (result) {
+               int i;
+               /* Check not-yet-checked entries. */
+               for (i = 0; i < argc; i++) {
+                       if (checked[i])
+                               continue;
+                       /*
+                        * Return true only if all unchecked indexes in
+                        * bprm->argv[] are not matched.
+                        */
+                       if (argv[i].is_not)
+                               continue;
+                       result = false;
+                       break;
+               }
+               for (i = 0; i < envc; envp++, i++) {
+                       if (checked[argc + i])
+                               continue;
+                       /*
+                        * Return true only if all unchecked environ variables
+                        * in bprm->envp[] are either undefined or not matched.
+                        */
+                       if ((!envp->value && !envp->is_not) ||
+                           (envp->value && envp->is_not))
+                               continue;
+                       result = false;
+                       break;
+               }
+       }
+       if (checked != local_checked)
+               kfree(checked);
+       return result;
+}
+
+/**
+ * tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition".
+ *
+ * @file:  Pointer to "struct file".
+ * @ptr:   Pointer to "struct tomoyo_name_union".
+ * @match: True if "exec.realpath=", false if "exec.realpath!=".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_exec_realpath(struct file *file,
+                                     const struct tomoyo_name_union *ptr,
+                                     const bool match)
+{
+       bool result;
+       struct tomoyo_path_info exe;
+       if (!file)
+               return false;
+       exe.name = tomoyo_realpath_from_path(&file->f_path);
+       if (!exe.name)
+               return false;
+       tomoyo_fill_path_info(&exe);
+       result = tomoyo_compare_name_union(&exe, ptr);
+       kfree(exe.name);
+       return result == match;
+}
+
+/**
+ * tomoyo_get_dqword - tomoyo_get_name() for a quoted string.
+ *
+ * @start: String to save.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
+{
+       char *cp = start + strlen(start) - 1;
+       if (cp == start || *start++ != '"' || *cp != '"')
+               return NULL;
+       *cp = '\0';
+       if (*start && !tomoyo_correct_word(start))
+               return NULL;
+       return tomoyo_get_name(start);
+}
+
+/**
+ * tomoyo_parse_name_union_quoted - Parse a quoted word.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param,
+                                          struct tomoyo_name_union *ptr)
+{
+       char *filename = param->data;
+       if (*filename == '@')
+               return tomoyo_parse_name_union(param, ptr);
+       ptr->filename = tomoyo_get_dqword(filename);
+       return ptr->filename != NULL;
+}
+
+/**
+ * tomoyo_parse_argv - Parse an argv[] condition part.
+ *
+ * @left:  Lefthand value.
+ * @right: Righthand value.
+ * @argv:  Pointer to "struct tomoyo_argv".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_argv(char *left, char *right,
+                             struct tomoyo_argv *argv)
+{
+       if (tomoyo_parse_ulong(&argv->index, &left) !=
+           TOMOYO_VALUE_TYPE_DECIMAL || *left++ != ']' || *left)
+               return false;
+       argv->value = tomoyo_get_dqword(right);
+       return argv->value != NULL;
+}
+
+/**
+ * tomoyo_parse_envp - Parse an envp[] condition part.
+ *
+ * @left:  Lefthand value.
+ * @right: Righthand value.
+ * @envp:  Pointer to "struct tomoyo_envp".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_envp(char *left, char *right,
+                             struct tomoyo_envp *envp)
+{
+       const struct tomoyo_path_info *name;
+       const struct tomoyo_path_info *value;
+       char *cp = left + strlen(left) - 1;
+       if (*cp-- != ']' || *cp != '"')
+               goto out;
+       *cp = '\0';
+       if (!tomoyo_correct_word(left))
+               goto out;
+       name = tomoyo_get_name(left);
+       if (!name)
+               goto out;
+       if (!strcmp(right, "NULL")) {
+               value = NULL;
+       } else {
+               value = tomoyo_get_dqword(right);
+               if (!value) {
+                       tomoyo_put_name(name);
+                       goto out;
+               }
+       }
+       envp->name = name;
+       envp->value = value;
+       return true;
+out:
+       return false;
+}
+
+/**
+ * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry.
+ *
+ * @a: Pointer to "struct tomoyo_condition".
+ * @b: Pointer to "struct tomoyo_condition".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
+                                        const struct tomoyo_condition *b)
+{
+       return a->size == b->size && a->condc == b->condc &&
+               a->numbers_count == b->numbers_count &&
+               a->names_count == b->names_count &&
+               a->argc == b->argc && a->envc == b->envc &&
+               !memcmp(a + 1, b + 1, a->size - sizeof(*a));
+}
+
+/**
+ * tomoyo_condition_type - Get condition type.
+ *
+ * @word: Keyword string.
+ *
+ * Returns one of values in "enum tomoyo_conditions_index" on success,
+ * TOMOYO_MAX_CONDITION_KEYWORD otherwise.
+ */
+static u8 tomoyo_condition_type(const char *word)
+{
+       u8 i;
+       for (i = 0; i < TOMOYO_MAX_CONDITION_KEYWORD; i++) {
+               if (!strcmp(word, tomoyo_condition_keyword[i]))
+                       break;
+       }
+       return i;
+}
+
+/* Define this to enable debug mode. */
+/* #define DEBUG_CONDITION */
+
+#ifdef DEBUG_CONDITION
+#define dprintk printk
+#else
+#define dprintk(...) do { } while (0)
+#endif
+
+/**
+ * tomoyo_commit_condition - Commit "struct tomoyo_condition".
+ *
+ * @entry: Pointer to "struct tomoyo_condition".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ *
+ * This function merges duplicated entries. This function returns NULL if
+ * @entry is not duplicated but memory quota for policy has exceeded.
+ */
+static struct tomoyo_condition *tomoyo_commit_condition
+(struct tomoyo_condition *entry)
+{
+       struct tomoyo_condition *ptr;
+       bool found = false;
+       if (mutex_lock_interruptible(&tomoyo_policy_lock)) {
+               dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+               ptr = NULL;
+               found = true;
+               goto out;
+       }
+       list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) {
+               if (!tomoyo_same_condition(ptr, entry))
+                       continue;
+               /* Same entry found. Share this entry. */
+               atomic_inc(&ptr->head.users);
+               found = true;
+               break;
+       }
+       if (!found) {
+               if (tomoyo_memory_ok(entry)) {
+                       atomic_set(&entry->head.users, 1);
+                       list_add_rcu(&entry->head.list,
+                                    &tomoyo_condition_list);
+               } else {
+                       found = true;
+                       ptr = NULL;
+               }
+       }
+       mutex_unlock(&tomoyo_policy_lock);
+out:
+       if (found) {
+               tomoyo_del_condition(&entry->head.list);
+               kfree(entry);
+               entry = ptr;
+       }
+       return entry;
+}
+
+/**
+ * tomoyo_get_condition - Parse condition part.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
+{
+       struct tomoyo_condition *entry = NULL;
+       struct tomoyo_condition_element *condp = NULL;
+       struct tomoyo_number_union *numbers_p = NULL;
+       struct tomoyo_name_union *names_p = NULL;
+       struct tomoyo_argv *argv = NULL;
+       struct tomoyo_envp *envp = NULL;
+       struct tomoyo_condition e = { };
+       char * const start_of_string = param->data;
+       char * const end_of_string = start_of_string + strlen(start_of_string);
+       char *pos;
+rerun:
+       pos = start_of_string;
+       while (1) {
+               u8 left = -1;
+               u8 right = -1;
+               char *left_word = pos;
+               char *cp;
+               char *right_word;
+               bool is_not;
+               if (!*left_word)
+                       break;
+               /*
+                * Since left-hand condition does not allow use of "path_group"
+                * or "number_group" and environment variable's names do not
+                * accept '=', it is guaranteed that the original line consists
+                * of one or more repetition of $left$operator$right blocks
+                * where "$left is free from '=' and ' '" and "$operator is
+                * either '=' or '!='" and "$right is free from ' '".
+                * Therefore, we can reconstruct the original line at the end
+                * of dry run even if we overwrite $operator with '\0'.
+                */
+               cp = strchr(pos, ' ');
+               if (cp) {
+                       *cp = '\0'; /* Will restore later. */
+                       pos = cp + 1;
+               } else {
+                       pos = "";
+               }
+               right_word = strchr(left_word, '=');
+               if (!right_word || right_word == left_word)
+                       goto out;
+               is_not = *(right_word - 1) == '!';
+               if (is_not)
+                       *(right_word++ - 1) = '\0'; /* Will restore later. */
+               else if (*(right_word + 1) != '=')
+                       *right_word++ = '\0'; /* Will restore later. */
+               else
+                       goto out;
+               dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
+                       is_not ? "!" : "", right_word);
+               if (!strncmp(left_word, "exec.argv[", 10)) {
+                       if (!argv) {
+                               e.argc++;
+                               e.condc++;
+                       } else {
+                               e.argc--;
+                               e.condc--;
+                               left = TOMOYO_ARGV_ENTRY;
+                               argv->is_not = is_not;
+                               if (!tomoyo_parse_argv(left_word + 10,
+                                                      right_word, argv++))
+                                       goto out;
+                       }
+                       goto store_value;
+               }
+               if (!strncmp(left_word, "exec.envp[\"", 11)) {
+                       if (!envp) {
+                               e.envc++;
+                               e.condc++;
+                       } else {
+                               e.envc--;
+                               e.condc--;
+                               left = TOMOYO_ENVP_ENTRY;
+                               envp->is_not = is_not;
+                               if (!tomoyo_parse_envp(left_word + 11,
+                                                      right_word, envp++))
+                                       goto out;
+                       }
+                       goto store_value;
+               }
+               left = tomoyo_condition_type(left_word);
+               dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word,