]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'security/next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 22 Sep 2011 06:29:04 +0000 (16:29 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 22 Sep 2011 06:29:04 +0000 (16:29 +1000)
Conflicts:
fs/ocfs2/xattr.c

90 files changed:
Documentation/ABI/testing/evm [new file with mode: 0644]
Documentation/kernel-parameters.txt
MAINTAINERS
drivers/char/tpm/tpm.c
fs/attr.c
fs/btrfs/xattr.c
fs/cifs/xattr.c
fs/ext2/xattr_security.c
fs/ext3/xattr_security.c
fs/ext4/xattr_security.c
fs/gfs2/inode.c
fs/jffs2/security.c
fs/jfs/xattr.c
fs/ocfs2/xattr.c
fs/reiserfs/xattr_security.c
fs/xattr.c
fs/xfs/xfs_iops.c
include/linux/evm.h [new file with mode: 0644]
include/linux/ima.h
include/linux/integrity.h [new file with mode: 0644]
include/linux/security.h
include/linux/xattr.h
kernel/cred.c
mm/shmem.c
security/Kconfig
security/Makefile
security/apparmor/apparmorfs.c
security/apparmor/ipc.c
security/apparmor/lib.c
security/apparmor/policy_unpack.c
security/apparmor/procattr.c
security/commoncap.c
security/integrity/Kconfig [new file with mode: 0644]
security/integrity/Makefile [new file with mode: 0644]
security/integrity/evm/Kconfig [new file with mode: 0644]
security/integrity/evm/Makefile [new file with mode: 0644]
security/integrity/evm/evm.h [new file with mode: 0644]
security/integrity/evm/evm_crypto.c [new file with mode: 0644]
security/integrity/evm/evm_main.c [new file with mode: 0644]
security/integrity/evm/evm_posix_acl.c [new file with mode: 0644]
security/integrity/evm/evm_secfs.c [new file with mode: 0644]
security/integrity/iint.c [new file with mode: 0644]
security/integrity/ima/Kconfig
security/integrity/ima/Makefile
security/integrity/ima/ima.h
security/integrity/ima/ima_api.c
security/integrity/ima/ima_fs.c
security/integrity/ima/ima_iint.c [deleted file]
security/integrity/ima/ima_main.c
security/integrity/integrity.h [new file with mode: 0644]
security/keys/Makefile
security/keys/encrypted-keys/Makefile [new file with mode: 0644]
security/keys/encrypted-keys/ecryptfs_format.c [moved from security/keys/ecryptfs_format.c with 100% similarity]
security/keys/encrypted-keys/ecryptfs_format.h [moved from security/keys/ecryptfs_format.h with 100% similarity]
security/keys/encrypted-keys/encrypted.c [moved from security/keys/encrypted.c with 97% similarity]
security/keys/encrypted-keys/encrypted.h [moved from security/keys/encrypted.h with 81% similarity]
security/keys/encrypted-keys/masterkey_trusted.c [new file with mode: 0644]
security/keys/gc.c
security/keys/internal.h
security/keys/key.c
security/keys/keyring.c
security/keys/process_keys.c
security/security.c
security/selinux/exports.c
security/selinux/hooks.c
security/selinux/include/avc_ss.h
security/selinux/include/security.h
security/selinux/netlink.c
security/selinux/nlmsgtab.c
security/selinux/selinuxfs.c
security/selinux/ss/conditional.c
security/selinux/ss/conditional.h
security/selinux/ss/policydb.c
security/selinux/ss/services.c
security/tomoyo/Kconfig
security/tomoyo/Makefile
security/tomoyo/audit.c
security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/condition.c
security/tomoyo/domain.c
security/tomoyo/environ.c [new file with mode: 0644]
security/tomoyo/file.c
security/tomoyo/gc.c
security/tomoyo/group.c
security/tomoyo/network.c [new file with mode: 0644]
security/tomoyo/realpath.c
security/tomoyo/securityfs_if.c
security/tomoyo/tomoyo.c
security/tomoyo/util.c

diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm
new file mode 100644 (file)
index 0000000..8374d45
--- /dev/null
@@ -0,0 +1,23 @@
+What:          security/evm
+Date:          March 2011
+Contact:       Mimi Zohar <zohar@us.ibm.com>
+Description:
+               EVM protects a file's security extended attributes(xattrs)
+               against integrity attacks. The initial method maintains an
+               HMAC-sha1 value across the extended attributes, storing the
+               value as the extended attribute 'security.evm'.
+
+               EVM depends on the Kernel Key Retention System to provide it
+               with a trusted/encrypted key for the HMAC-sha1 operation.
+               The key is loaded onto the root's keyring using keyctl.  Until
+               EVM receives notification that the key has been successfully
+               loaded onto the keyring (echo 1 > <securityfs>/evm), EVM
+               can not create or validate the 'security.evm' xattr, but
+               returns INTEGRITY_UNKNOWN.  Loading the key and signaling EVM
+               should be done as early as possible.  Normally this is done
+               in the initramfs, which has already been measured as part
+               of the trusted boot.  For more information on creating and
+               loading existing trusted/encrypted keys, refer to:
+               Documentation/keys-trusted-encrypted.txt.  (A sample dracut
+               patch, which loads the trusted/encrypted key and enables
+               EVM, is available from http://linux-ima.sourceforge.net/#EVM.)
index 0c412ff3986238d6d0af4d85f302d15cdd2685b6..e54ec2d14aac86a97eab554e61262ad91a9cc3ce 100644 (file)
@@ -49,6 +49,7 @@ parameter is applicable:
        EDD     BIOS Enhanced Disk Drive Services (EDD) is enabled
        EFI     EFI Partitioning (GPT) is enabled
        EIDE    EIDE/ATAPI support is enabled.
+       EVM     Extended Verification Module
        FB      The frame buffer device is enabled.
        FTRACE  Function tracing enabled.
        GCOV    GCOV profiling is enabled.
@@ -760,6 +761,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        This option is obsoleted by the "netdev=" option, which
                        has equivalent usage. See its documentation for details.
 
+       evm=            [EVM]
+                       Format: { "fix" }
+                       Permit 'security.evm' to be updated regardless of
+                       current integrity status.
+
        failslab=
        fail_page_alloc=
        fail_make_request=[KNL]
index c09836a08f3f746ed280806089bb9cd850fade42..ca20d3a66be17271c455f55ce2a2f8db1317aff9 100644 (file)
@@ -2560,6 +2560,11 @@ S:       Maintained
 F:     Documentation/filesystems/ext4.txt
 F:     fs/ext4/
 
+Extended Verification Module (EVM)
+M:     Mimi Zohar <zohar@us.ibm.com>
+S:     Supported
+F:     security/integrity/evm/
+
 F71805F HARDWARE MONITORING DRIVER
 M:     Jean Delvare <khali@linux-fr.org>
 L:     lm-sensors@lm-sensors.org
@@ -6468,7 +6473,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.4.x/tomoyo-lsm/patches/
+T:     quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.5.x/tomoyo-lsm/patches/
 S:     Maintained
 F:     security/tomoyo/
 
index caf8012ef47ce1c69af6f07df4f62e7c5676a581..e53af7638186840d449a65de533667dbb164316c 100644 (file)
@@ -963,6 +963,9 @@ ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
 
+       if (chip->vendor.duration[TPM_LONG] == 0)
+               return 0;
+
        return sprintf(buf, "%d %d %d [%s]\n",
                       jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
                       jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
index 538e27959d3f7fec79a8a0647723830b2bb4a5b4..7ee7ba48831358f7bf4f738a13c20511dcdd1698 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -13,6 +13,7 @@
 #include <linux/fsnotify.h>
 #include <linux/fcntl.h>
 #include <linux/security.h>
+#include <linux/evm.h>
 
 /**
  * inode_change_ok - check if attribute changes to an inode are allowed
@@ -237,8 +238,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
        else
                error = simple_setattr(dentry, attr);
 
-       if (!error)
+       if (!error) {
                fsnotify_change(dentry, ia_valid);
+               evm_inode_post_setattr(dentry, ia_valid);
+       }
 
        return error;
 }
index 69565e5fc6a0f1031394f75e1fe73677ea168a83..426aa464f1afc45aa5501e35af6ebb4f8ab37aa7 100644 (file)
@@ -383,36 +383,36 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
                                XATTR_REPLACE);
 }
 
-int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
-                             struct inode *inode, struct inode *dir,
-                             const struct qstr *qstr)
+int btrfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                    void *fs_info)
 {
-       int err;
-       size_t len;
-       void *value;
-       char *suffix;
+       const struct xattr *xattr;
+       struct btrfs_trans_handle *trans = fs_info;
        char *name;
+       int err = 0;
 
-       err = security_inode_init_security(inode, dir, qstr, &suffix, &value,
-                                          &len);
-       if (err) {
-               if (err == -EOPNOTSUPP)
-                       return 0;
-               return err;
-       }
-
-       name = kmalloc(XATTR_SECURITY_PREFIX_LEN + strlen(suffix) + 1,
-                      GFP_NOFS);
-       if (!name) {
-               err = -ENOMEM;
-       } else {
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+                              strlen(xattr->name) + 1, GFP_NOFS);
+               if (!name) {
+                       err = -ENOMEM;
+                       break;
+               }
                strcpy(name, XATTR_SECURITY_PREFIX);
-               strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
-               err = __btrfs_setxattr(trans, inode, name, value, len, 0);
+               strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
+               err = __btrfs_setxattr(trans, inode, name,
+                                      xattr->value, xattr->value_len, 0);
                kfree(name);
+               if (err < 0)
+                       break;
        }
-
-       kfree(suffix);
-       kfree(value);
        return err;
 }
+
+int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
+                             struct inode *inode, struct inode *dir,
+                             const struct qstr *qstr)
+{
+       return security_inode_init_security(inode, dir, qstr,
+                                           &btrfs_initxattrs, trans);
+}
index 2a22fb2989e4780fd977c683b27ddf23e78e7d8e..c323088821485c226db0e0c0cb72f22f36370a7b 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/fs.h>
 #include <linux/posix_acl_xattr.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
 #include "cifsglob.h"
 #define MAX_EA_VALUE_SIZE 65535
 #define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
-#define CIFS_XATTR_USER_PREFIX "user."
-#define CIFS_XATTR_SYSTEM_PREFIX "system."
-#define CIFS_XATTR_OS2_PREFIX "os2."
-#define CIFS_XATTR_SECURITY_PREFIX "security."
-#define CIFS_XATTR_TRUSTED_PREFIX "trusted."
-#define XATTR_TRUSTED_PREFIX_LEN  8
-#define XATTR_SECURITY_PREFIX_LEN 9
-/* BB need to add server (Samba e.g) support for security and trusted prefix */
-
 
+/* BB need to add server (Samba e.g) support for security and trusted prefix */
 
 int cifs_removexattr(struct dentry *direntry, const char *ea_name)
 {
@@ -76,8 +69,8 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
        }
        if (ea_name == NULL) {
                cFYI(1, "Null xattr names not supported");
-       } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5)
-               && (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4))) {
+       } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+               && (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) {
                cFYI(1,
                     "illegal xattr request %s (only user namespace supported)",
                     ea_name);
@@ -88,7 +81,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto remove_ea_exit;
 
-               ea_name += 5; /* skip past user. prefix */
+               ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
                rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL,
                        (__u16)0, cifs_sb->local_nls,
                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -149,21 +142,23 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
 
        if (ea_name == NULL) {
                cFYI(1, "Null xattr names not supported");
-       } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+       } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+                  == 0) {
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto set_ea_exit;
                if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0)
                        cFYI(1, "attempt to set cifs inode metadata");
 
-               ea_name += 5; /* skip past user. prefix */
+               ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
                rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
                        (__u16)value_size, cifs_sb->local_nls,
                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
-       } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+       } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)
+                  == 0) {
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto set_ea_exit;
 
-               ea_name += 4; /* skip past os2. prefix */
+               ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
                rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
                        (__u16)value_size, cifs_sb->local_nls,
                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -269,7 +264,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
        /* return alt name if available as pseudo attr */
        if (ea_name == NULL) {
                cFYI(1, "Null xattr names not supported");
-       } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+       } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+                  == 0) {
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto get_ea_exit;
 
@@ -277,15 +273,15 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
                        cFYI(1, "attempt to query cifs inode metadata");
                        /* revalidate/getattr then populate from inode */
                } /* BB add else when above is implemented */
-               ea_name += 5; /* skip past user. prefix */
+               ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
                rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
                        buf_size, cifs_sb->local_nls,
                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
-       } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+       } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto get_ea_exit;
 
-               ea_name += 4; /* skip past os2. prefix */
+               ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
                rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
                        buf_size, cifs_sb->local_nls,
                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -339,10 +335,10 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
                cFYI(1, "Query CIFS ACL not supported yet");
 #endif /* CONFIG_CIFS_ACL */
        } else if (strncmp(ea_name,
-                 CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
+                 XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
                cFYI(1, "Trusted xattr namespace not supported yet");
        } else if (strncmp(ea_name,
-                 CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
+                 XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
                cFYI(1, "Security xattr namespace not supported yet");
        } else
                cFYI(1,
index 5d979b4347b03c3b3820f6cb14c8566cdeb9497a..c922adc8ef4175844c42a2dcb4b08d7499694d08 100644 (file)
@@ -46,28 +46,30 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name,
                              value, size, flags);
 }
 
-int
-ext2_init_security(struct inode *inode, struct inode *dir,
-                  const struct qstr *qstr)
+int ext2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                   void *fs_info)
 {
-       int err;
-       size_t len;
-       void *value;
-       char *name;
+       const struct xattr *xattr;
+       int err = 0;
 
-       err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-       if (err) {
-               if (err == -EOPNOTSUPP)
-                       return 0;
-               return err;
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
+                                    xattr->name, xattr->value,
+                                    xattr->value_len, 0);
+               if (err < 0)
+                       break;
        }
-       err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
-                            name, value, len, 0);
-       kfree(name);
-       kfree(value);
        return err;
 }
 
+int
+ext2_init_security(struct inode *inode, struct inode *dir,
+                  const struct qstr *qstr)
+{
+       return security_inode_init_security(inode, dir, qstr,
+                                           &ext2_initxattrs, NULL);
+}
+
 const struct xattr_handler ext2_xattr_security_handler = {
        .prefix = XATTR_SECURITY_PREFIX,
        .list   = ext2_xattr_security_list,
index b8d9f83aa5c545f23b48f14530b6ee0578204a66..3c218b8a51d4a4668e3edb4ea144f92cc71dce7d 100644 (file)
@@ -48,28 +48,32 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name,
                              name, value, size, flags);
 }
 
-int
-ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
-                  const struct qstr *qstr)
+int ext3_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                   void *fs_info)
 {
-       int err;
-       size_t len;
-       void *value;
-       char *name;
+       const struct xattr *xattr;
+       handle_t *handle = fs_info;
+       int err = 0;
 
-       err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-       if (err) {
-               if (err == -EOPNOTSUPP)
-                       return 0;
-               return err;
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               err = ext3_xattr_set_handle(handle, inode,
+                                           EXT3_XATTR_INDEX_SECURITY,
+                                           xattr->name, xattr->value,
+                                           xattr->value_len, 0);
+               if (err < 0)
+                       break;
        }
-       err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY,
-                                   name, value, len, 0);
-       kfree(name);
-       kfree(value);
        return err;
 }
 
+int
+ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
+                  const struct qstr *qstr)
+{
+       return security_inode_init_security(inode, dir, qstr,
+                                           &ext3_initxattrs, handle);
+}
+
 const struct xattr_handler ext3_xattr_security_handler = {
        .prefix = XATTR_SECURITY_PREFIX,
        .list   = ext3_xattr_security_list,
index 007c3bfbf094221988855d9eb754bfdf430d569e..34e4350dd4d92f2ee2273a909b3f1b85867cf580 100644 (file)
@@ -48,28 +48,32 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name,
                              name, value, size, flags);
 }
 
-int
-ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
-                  const struct qstr *qstr)
+int ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                   void *fs_info)
 {
-       int err;
-       size_t len;
-       void *value;
-       char *name;
+       const struct xattr *xattr;
+       handle_t *handle = fs_info;
+       int err = 0;
 
-       err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-       if (err) {
-               if (err == -EOPNOTSUPP)
-                       return 0;
-               return err;
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               err = ext4_xattr_set_handle(handle, inode,
+                                           EXT4_XATTR_INDEX_SECURITY,
+                                           xattr->name, xattr->value,
+                                           xattr->value_len, 0);
+               if (err < 0)
+                       break;
        }
-       err = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SECURITY,
-                                   name, value, len, 0);
-       kfree(name);
-       kfree(value);
        return err;
 }
 
+int
+ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
+                  const struct qstr *qstr)
+{
+       return security_inode_init_security(inode, dir, qstr,
+                                           &ext4_initxattrs, handle);
+}
+
 const struct xattr_handler ext4_xattr_security_handler = {
        .prefix = XATTR_SECURITY_PREFIX,
        .list   = ext4_xattr_security_list,
index 2af69056a9fd0729683525766a771d0900320445..5431de94085dc89152521e96e3c2b9dd18be59b7 100644 (file)
@@ -624,31 +624,29 @@ fail:
        return error;
 }
 
-static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
-                             const struct qstr *qstr)
+int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                   void *fs_info)
 {
-       int err;
-       size_t len;
-       void *value;
-       char *name;
-
-       err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
-                                          &name, &value, &len);
-
-       if (err) {
-               if (err == -EOPNOTSUPP)
-                       return 0;
-               return err;
+       const struct xattr *xattr;
+       int err = 0;
+
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               err = __gfs2_xattr_set(inode, xattr->name, xattr->value,
+                                      xattr->value_len, 0,
+                                      GFS2_EATYPE_SECURITY);
+               if (err < 0)
+                       break;
        }
-
-       err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0,
-                              GFS2_EATYPE_SECURITY);
-       kfree(value);
-       kfree(name);
-
        return err;
 }
 
+static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
+                             const struct qstr *qstr)
+{
+       return security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
+                                           &gfs2_initxattrs, NULL);
+}
+
 /**
  * gfs2_create_inode - Create a new inode
  * @dir: The parent directory
index cfeb7164b0853a0d163cf7552f89577743067575..0f20208df60278c9f63f6c14c7b626cfd632ebf2 100644 (file)
 #include <linux/security.h>
 #include "nodelist.h"
 
-/* ---- Initial Security Label Attachment -------------- */
-int jffs2_init_security(struct inode *inode, struct inode *dir,
-                       const struct qstr *qstr)
+/* ---- Initial Security Label(s) Attachment callback --- */
+int jffs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                    void *fs_info)
 {
-       int rc;
-       size_t len;
-       void *value;
-       char *name;
+       const struct xattr *xattr;
+       int err = 0;
 
-       rc = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-       if (rc) {
-               if (rc == -EOPNOTSUPP)
-                       return 0;
-               return rc;
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               err = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY,
+                                       xattr->name, xattr->value,
+                                       xattr->value_len, 0);
+               if (err < 0)
+                       break;
        }
-       rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
+       return err;
+}
 
-       kfree(name);
-       kfree(value);
-       return rc;
+/* ---- Initial Security Label(s) Attachment ----------- */
+int jffs2_init_security(struct inode *inode, struct inode *dir,
+                       const struct qstr *qstr)
+{
+       return security_inode_init_security(inode, dir, qstr,
+                                           &jffs2_initxattrs, NULL);
 }
 
 /* ---- XATTR Handler for "security.*" ----------------- */
index e87fedef23db7938aa7b38f64f385b0615e193b4..26683e15b3ac9f23991110687023e0f6e3bb0db0 100644 (file)
@@ -1089,38 +1089,37 @@ int jfs_removexattr(struct dentry *dentry, const char *name)
 }
 
 #ifdef CONFIG_JFS_SECURITY
-int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
-                     const struct qstr *qstr)
+int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                  void *fs_info)
 {
-       int rc;
-       size_t len;
-       void *value;
-       char *suffix;
+       const struct xattr *xattr;
+       tid_t *tid = fs_info;
        char *name;
-
-       rc = security_inode_init_security(inode, dir, qstr, &suffix, &value,
-                                         &len);
-       if (rc) {
-               if (rc == -EOPNOTSUPP)
-                       return 0;
-               return rc;
-       }
-       name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 1 + strlen(suffix),
-                      GFP_NOFS);
-       if (!name) {
-               rc = -ENOMEM;
-               goto kmalloc_failed;
+       int err = 0;
+
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+                              strlen(xattr->name) + 1, GFP_NOFS);
+               if (!name) {
+                       err = -ENOMEM;
+                       break;
+               }
+               strcpy(name, XATTR_SECURITY_PREFIX);
+               strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
+
+               err = __jfs_setxattr(*tid, inode, name,
+                                    xattr->value, xattr->value_len, 0);
+               kfree(name);
+               if (err < 0)
+                       break;
        }
-       strcpy(name, XATTR_SECURITY_PREFIX);
-       strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
-
-       rc = __jfs_setxattr(tid, inode, name, value, len, 0);
-
-       kfree(name);
-kmalloc_failed:
-       kfree(suffix);
-       kfree(value);
+       return err;
+}
 
-       return rc;
+int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
+                     const struct qstr *qstr)
+{
+       return security_inode_init_security(inode, dir, qstr,
+                                           &jfs_initxattrs, &tid);
 }
 #endif
index fcd77a03fc631a403aa01a4d4e31565e920bbbd0..194fb22ef79d590580f3245b522d0b095ef3794c 100644 (file)
@@ -7185,19 +7185,9 @@ int ocfs2_init_security_and_acl(struct inode *dir,
 {
        int ret = 0;
        struct buffer_head *dir_bh = NULL;
-       struct ocfs2_security_xattr_info si = {0, };
 
-       ret = ocfs2_init_security_get(inode, dir, qstr, &si);
+       ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
        if (!ret) {
-               si.enable = 1;
-               ret = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
-                                     si.name, si.value, si.value_len,
-                                     XATTR_CREATE);
-               if (ret) {
-                       mlog_errno(ret);
-                       goto leave;
-               }
-       } else if (ret != -EOPNOTSUPP) {
                mlog_errno(ret);
                goto leave;
        }
@@ -7215,10 +7205,6 @@ int ocfs2_init_security_and_acl(struct inode *dir,
        ocfs2_inode_unlock(dir, 0);
        brelse(dir_bh);
 leave:
-       if (si.enable) {
-               kfree(si.name);
-               kfree(si.value);
-       }
        return ret;
 }
 /*
@@ -7258,6 +7244,22 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name,
                               name, value, size, flags);
 }
 
+int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                    void *fs_info)
+{
+       const struct xattr *xattr;
+       int err = 0;
+
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               err = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
+                                     xattr->name, xattr->value,
+                                     xattr->value_len, XATTR_CREATE);
+               if (err)
+                       break;
+       }
+       return err;
+}
+
 int ocfs2_init_security_get(struct inode *inode,
                            struct inode *dir,
                            const struct qstr *qstr,
@@ -7266,8 +7268,13 @@ int ocfs2_init_security_get(struct inode *inode,
        /* check whether ocfs2 support feature xattr */
        if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
                return -EOPNOTSUPP;
-       return security_inode_init_security(inode, dir, qstr, &si->name,
-                                           &si->value, &si->value_len);
+       if (si)
+               return security_old_inode_init_security(inode, dir, qstr,
+                                                       &si->name, &si->value,
+                                                       &si->value_len);
+
+       return security_inode_init_security(inode, dir, qstr,
+                                           &ocfs2_initxattrs, NULL);
 }
 
 int ocfs2_init_security_set(handle_t *handle,
index ef66c18a933266e9c658fde311f7a1d0e2d6d023..534668fa41bee012a038ce48a94af04ad0d19327 100644 (file)
@@ -66,8 +66,8 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode,
        if (IS_PRIVATE(dir))
                return 0;
 
-       error = security_inode_init_security(inode, dir, qstr, &sec->name,
-                                            &sec->value, &sec->length);
+       error = security_old_inode_init_security(inode, dir, qstr, &sec->name,
+                                                &sec->value, &sec->length);
        if (error) {
                if (error == -EOPNOTSUPP)
                        error = 0;
index f060663ab70c2b2c602aa7a7f4f8a343f621f5e4..67583de8218cf54213379f586926824f30da5350 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/mount.h>
 #include <linux/namei.h>
 #include <linux/security.h>
+#include <linux/evm.h>
 #include <linux/syscalls.h>
 #include <linux/module.h>
 #include <linux/fsnotify.h>
@@ -166,6 +167,64 @@ out_noalloc:
 }
 EXPORT_SYMBOL_GPL(xattr_getsecurity);
 
+/*
+ * vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
+ *
+ * Allocate memory, if not already allocated, or re-allocate correct size,
+ * before retrieving the extended attribute.
+ *
+ * Returns the result of alloc, if failed, or the getxattr operation.
+ */
+ssize_t
+vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
+                  size_t xattr_size, gfp_t flags)
+{
+       struct inode *inode = dentry->d_inode;
+       char *value = *xattr_value;
+       int error;
+
+       error = xattr_permission(inode, name, MAY_READ);
+       if (error)
+               return error;
+
+       if (!inode->i_op->getxattr)
+               return -EOPNOTSUPP;
+
+       error = inode->i_op->getxattr(dentry, name, NULL, 0);
+       if (error < 0)
+               return error;
+
+       if (!value || (error > xattr_size)) {
+               value = krealloc(*xattr_value, error + 1, flags);
+               if (!value)
+                       return -ENOMEM;
+               memset(value, 0, error + 1);
+       }
+
+       error = inode->i_op->getxattr(dentry, name, value, error);
+       *xattr_value = value;
+       return error;
+}
+
+/* Compare an extended attribute value with the given value */
+int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
+                 const char *value, size_t size, gfp_t flags)
+{
+       char *xattr_value = NULL;
+       int rc;
+
+       rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags);
+       if (rc < 0)
+               return rc;
+
+       if ((rc != size) || (memcmp(xattr_value, value, rc) != 0))
+               rc = -EINVAL;
+       else
+               rc = 0;
+       kfree(xattr_value);
+       return rc;
+}
+
 ssize_t
 vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
 {
@@ -243,8 +302,10 @@ vfs_removexattr(struct dentry *dentry, const char *name)
        error = inode->i_op->removexattr(dentry, name);
        mutex_unlock(&inode->i_mutex);
 
-       if (!error)
+       if (!error) {
                fsnotify_xattr(dentry);
+               evm_inode_post_removexattr(dentry, name);
+       }
        return error;
 }
 EXPORT_SYMBOL_GPL(vfs_removexattr);
index e6b3e76208887d2a1737515bc8f116ad9976ac1a..8aafac871f0632520c1f629f37543a0c52cb4e70 100644 (file)
@@ -102,37 +102,38 @@ xfs_mark_inode_dirty(
 
 }
 
+
+int xfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+                  void *fs_info)
+{
+       const struct xattr *xattr;
+       struct xfs_inode *ip = XFS_I(inode);
+       int error = 0;
+
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               error = xfs_attr_set(ip, xattr->name, xattr->value,
+                                    xattr->value_len, ATTR_SECURE);
+               if (error < 0)
+                       break;
+       }
+       return error;
+}
+
 /*
  * Hook in SELinux.  This is not quite correct yet, what we really need
  * here (as we do for default ACLs) is a mechanism by which creation of
  * these attrs can be journalled at inode creation time (along with the
  * inode, of course, such that log replay can't cause these to be lost).
  */
+
 STATIC int
 xfs_init_security(
        struct inode    *inode,
        struct inode    *dir,
        const struct qstr *qstr)
 {
-       struct xfs_inode *ip = XFS_I(inode);
-       size_t          length;
-       void            *value;
-       unsigned char   *name;
-       int             error;
-
-       error = security_inode_init_security(inode, dir, qstr, (char **)&name,
-                                            &value, &length);
-       if (error) {
-               if (error == -EOPNOTSUPP)
-                       return 0;
-               return -error;
-       }
-
-       error = xfs_attr_set(ip, name, value, length, ATTR_SECURE);
-
-       kfree(name);
-       kfree(value);
-       return error;
+       return security_inode_init_security(inode, dir, qstr,
+                                           &xfs_initxattrs, NULL);
 }
 
 static void
diff --git a/include/linux/evm.h b/include/linux/evm.h
new file mode 100644 (file)
index 0000000..9fc13a7
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * evm.h
+ *
+ * Copyright (c) 2009 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ */
+
+#ifndef _LINUX_EVM_H
+#define _LINUX_EVM_H
+
+#include <linux/integrity.h>
+#include <linux/xattr.h>
+
+struct integrity_iint_cache;
+
+#ifdef CONFIG_EVM
+extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
+                                            const char *xattr_name,
+                                            void *xattr_value,
+                                            size_t xattr_value_len,
+                                            struct integrity_iint_cache *iint);
+extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr);
+extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
+extern int evm_inode_setxattr(struct dentry *dentry, const char *name,
+                             const void *value, size_t size);
+extern void evm_inode_post_setxattr(struct dentry *dentry,
+                                   const char *xattr_name,
+                                   const void *xattr_value,
+                                   size_t xattr_value_len);
+extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name);
+extern void evm_inode_post_removexattr(struct dentry *dentry,
+                                      const char *xattr_name);
+extern int evm_inode_init_security(struct inode *inode,
+                                  const struct xattr *xattr_array,
+                                  struct xattr *evm);
+#ifdef CONFIG_FS_POSIX_ACL
+extern int posix_xattr_acl(const char *xattrname);
+#else
+static inline int posix_xattr_acl(const char *xattrname)
+{
+       return 0;
+}
+#endif
+#else
+#ifdef CONFIG_INTEGRITY
+static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
+                                                   const char *xattr_name,
+                                                   void *xattr_value,
+                                                   size_t xattr_value_len,
+                                       struct integrity_iint_cache *iint)
+{
+       return INTEGRITY_UNKNOWN;
+}
+#endif
+
+static inline int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+       return 0;
+}
+
+static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+{
+       return;
+}
+
+static inline int evm_inode_setxattr(struct dentry *dentry, const char *name,
+                                    const void *value, size_t size)
+{
+       return 0;
+}
+
+static inline void evm_inode_post_setxattr(struct dentry *dentry,
+                                          const char *xattr_name,
+                                          const void *xattr_value,
+                                          size_t xattr_value_len)
+{
+       return;
+}
+
+static inline int evm_inode_removexattr(struct dentry *dentry,
+                                       const char *xattr_name)
+{
+       return 0;
+}
+
+static inline void evm_inode_post_removexattr(struct dentry *dentry,
+                                             const char *xattr_name)
+{
+       return;
+}
+
+static inline int evm_inode_init_security(struct inode *inode,
+                                         const struct xattr *xattr_array,
+                                         struct xattr *evm)
+{
+       return 0;
+}
+
+#endif /* CONFIG_EVM_H */
+#endif /* LINUX_EVM_H */
index 09e6e62f9953f445e4e92df8bfeb99c6ae4e8f1a..6ac8e50c6cf5453e338fec108f9dd1418046e3e9 100644 (file)
@@ -15,8 +15,6 @@ struct linux_binprm;
 
 #ifdef CONFIG_IMA
 extern int ima_bprm_check(struct linux_binprm *bprm);
-extern int ima_inode_alloc(struct inode *inode);
-extern void ima_inode_free(struct inode *inode);
 extern int ima_file_check(struct file *file, int mask);
 extern void ima_file_free(struct file *file);
 extern int ima_file_mmap(struct file *file, unsigned long prot);
@@ -27,16 +25,6 @@ static inline int ima_bprm_check(struct linux_binprm *bprm)
        return 0;
 }
 
-static inline int ima_inode_alloc(struct inode *inode)
-{
-       return 0;
-}
-
-static inline void ima_inode_free(struct inode *inode)
-{
-       return;
-}
-
 static inline int ima_file_check(struct file *file, int mask)
 {
        return 0;
@@ -51,6 +39,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
 {
        return 0;
 }
-
 #endif /* CONFIG_IMA_H */
 #endif /* _LINUX_IMA_H */
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
new file mode 100644 (file)
index 0000000..a0c4125
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#ifndef _LINUX_INTEGRITY_H
+#define _LINUX_INTEGRITY_H
+
+#include <linux/fs.h>
+
+enum integrity_status {
+       INTEGRITY_PASS = 0,
+       INTEGRITY_FAIL,
+       INTEGRITY_NOLABEL,
+       INTEGRITY_NOXATTRS,
+       INTEGRITY_UNKNOWN,
+};
+
+/* List of EVM protected security xattrs */
+#ifdef CONFIG_INTEGRITY
+extern int integrity_inode_alloc(struct inode *inode);
+extern void integrity_inode_free(struct inode *inode);
+
+#else
+static inline int integrity_inode_alloc(struct inode *inode)
+{
+       return 0;
+}
+
+static inline void integrity_inode_free(struct inode *inode)
+{
+       return;
+}
+#endif /* CONFIG_INTEGRITY_H */
+#endif /* _LINUX_INTEGRITY_H */
index ebd2a53a3d073cbf0489848c50725250a3f6872b..19d8e04e16884c2bfb860fe2f72715306753ea15 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/key.h>
 #include <linux/xfrm.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
 #include <net/flow.h>
 
 /* Maximum number of letters for an LSM name string */
@@ -147,6 +148,10 @@ extern int mmap_min_addr_handler(struct ctl_table *table, int write,
                                 void __user *buffer, size_t *lenp, loff_t *ppos);
 #endif
 
+/* security_inode_init_security callback function to write xattrs */
+typedef int (*initxattrs) (struct inode *inode,
+                          const struct xattr *xattr_array, void *fs_data);
+
 #ifdef CONFIG_SECURITY
 
 struct security_mnt_opts {
@@ -1367,7 +1372,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  * @inode_getsecctx:
  *     Returns a string containing all relavent security context information
  *
- *     @inode we wish to set the security context of.
+ *     @inode we wish to get the security context of.
  *     @ctx is a pointer in which to place the allocated security context.
  *     @ctxlen points to the place to put the length of @ctx.
  * This is the main security structure.
@@ -1655,6 +1660,8 @@ struct security_operations {
 extern int security_init(void);
 extern int security_module_enable(struct security_operations *ops);
 extern int register_security(struct security_operations *ops);
+extern void __init security_fixup_ops(struct security_operations *ops);
+
 
 /* Security operations */
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
@@ -1704,8 +1711,11 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
 int security_inode_alloc(struct inode *inode);
 void security_inode_free(struct inode *inode);
 int security_inode_init_security(struct inode *inode, struct inode *dir,
-                                const struct qstr *qstr, char **name,
-                                void **value, size_t *len);
+                                const struct qstr *qstr,
+                                initxattrs initxattrs, void *fs_data);
+int security_old_inode_init_security(struct inode *inode, struct inode *dir,
+                                    const struct qstr *qstr, char **name,
+                                    void **value, size_t *len);
 int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
                         struct dentry *new_dentry);
@@ -2034,11 +2044,19 @@ static inline void security_inode_free(struct inode *inode)
 static inline int security_inode_init_security(struct inode *inode,
                                                struct inode *dir,
                                                const struct qstr *qstr,
-                                               char **name,
-                                               void **value,
-                                               size_t *len)
+                                               initxattrs initxattrs,
+                                               void *fs_data)
 {
-       return -EOPNOTSUPP;
+       return 0;
+}
+
+static inline int security_old_inode_init_security(struct inode *inode,
+                                                  struct inode *dir,
+                                                  const struct qstr *qstr,
+                                                  char **name, void **value,
+                                                  size_t *len)
+{
+       return 0;
 }
 
 static inline int security_inode_create(struct inode *dir,
index aed54c50aa665b89e61bf007c6715e65a26cbefb..e5d122031542f5e3628d5a3bbbd044f0f36f51a3 100644 (file)
@@ -30,6 +30,9 @@
 #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
 
 /* Security namespace */
+#define XATTR_EVM_SUFFIX "evm"
+#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
+
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 
 #define XATTR_CAPS_SUFFIX "capability"
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
 
+#define XATTR_POSIX_ACL_ACCESS  "posix_acl_access"
+#define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS
+#define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
+#define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT
+
 #ifdef  __KERNEL__
 
 #include <linux/types.h>
@@ -67,6 +75,12 @@ struct xattr_handler {
                   size_t size, int flags, int handler_flags);
 };
 
+struct xattr {
+       char *name;
+       void *value;
+       size_t value_len;
+};
+
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
 ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
@@ -78,7 +92,10 @@ ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer,
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
 int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
 int generic_removexattr(struct dentry *dentry, const char *name);
-
+ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
+                          char **xattr_value, size_t size, gfp_t flags);
+int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
+                 const char *value, size_t size, gfp_t flags);
 #endif  /*  __KERNEL__  */
 
 #endif /* _LINUX_XATTR_H */
index 8ef31f53c44c9aa8e74d3b15649fdf7d246a2a36..bb55d052d858f6c2f356d32f36a77445dcda20fa 100644 (file)
@@ -644,6 +644,9 @@ void __init cred_init(void)
  */
 struct cred *prepare_kernel_cred(struct task_struct *daemon)
 {
+#ifdef CONFIG_KEYS
+       struct thread_group_cred *tgcred;
+#endif
        const struct cred *old;
        struct cred *new;
 
@@ -651,6 +654,14 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
        if (!new)
                return NULL;
 
+#ifdef CONFIG_KEYS
+       tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
+       if (!tgcred) {
+               kmem_cache_free(cred_jar, new);
+               return NULL;
+       }
+#endif
+
        kdebug("prepare_kernel_cred() alloc %p", new);
 
        if (daemon)
@@ -667,8 +678,11 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
        get_group_info(new->group_info);
 
 #ifdef CONFIG_KEYS
-       atomic_inc(&init_tgcred.usage);
-       new->tgcred = &init_tgcred;
+       atomic_set(&tgcred->usage, 1);
+       spin_lock_init(&tgcred->lock);
+       tgcred->process_keyring = NULL;
+       tgcred->session_keyring = NULL;
+       new->tgcred = tgcred;
        new->request_key_auth = NULL;
        new->thread_keyring = NULL;
        new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
index 32f6763f16fb82ad7068c4d73479a96583f4ef15..2d357729529880b29f18704edda807108f6cdc5b 100644 (file)
@@ -1458,7 +1458,7 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
        if (inode) {
                error = security_inode_init_security(inode, dir,
-                                                    &dentry->d_name, NULL,
+                                                    &dentry->d_name,
                                                     NULL, NULL);
                if (error) {
                        if (error != -EOPNOTSUPP) {
@@ -1598,7 +1598,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
        if (!inode)
                return -ENOSPC;
 
-       error = security_inode_init_security(inode, dir, &dentry->d_name, NULL,
+       error = security_inode_init_security(inode, dir, &dentry->d_name,
                                             NULL, NULL);
        if (error) {
                if (error != -EOPNOTSUPP) {
index e0f08b52e4ab440933c9f72120092b8bcd527b46..51bd5a0b69ae80440470b9aa9ed6eca0e38f66a6 100644 (file)
@@ -38,7 +38,9 @@ config TRUSTED_KEYS
 
 config ENCRYPTED_KEYS
        tristate "ENCRYPTED KEYS"
-       depends on KEYS && TRUSTED_KEYS
+       depends on KEYS
+       select CRYPTO
+       select CRYPTO_HMAC
        select CRYPTO_AES
        select CRYPTO_CBC
        select CRYPTO_SHA256
@@ -186,7 +188,7 @@ source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 
-source security/integrity/ima/Kconfig
+source security/integrity/Kconfig
 
 choice
        prompt "Default security module"
index 8bb0fe9e1ca94e68cac077196fad183975955d95..a5e502f8a05bcf9285a6924a37ad431bb55c6bb4 100644 (file)
@@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR)               += apparmor/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)            += device_cgroup.o
 
 # Object integrity file lists
-subdir-$(CONFIG_IMA)                   += integrity/ima
-obj-$(CONFIG_IMA)                      += integrity/ima/built-in.o
+subdir-$(CONFIG_INTEGRITY)             += integrity
+obj-$(CONFIG_INTEGRITY)                        += integrity/built-in.o
index 0848292982a2315f9e491728afe2714ad767bca0..69ddb47787b2801dc0fb2742ce20dcd1716eaa90 100644 (file)
@@ -200,7 +200,7 @@ void __init aa_destroy_aafs(void)
  *
  * Returns: error on failure
  */
-int __init aa_create_aafs(void)
+static int __init aa_create_aafs(void)
 {
        int error;
 
index 649fad88869b8a1dd4f2fe179006ddcdd1544923..7ee05c6f3c64ff313708a68f288479b2726ff7b6 100644 (file)
@@ -19,6 +19,7 @@
 #include "include/capability.h"
 #include "include/context.h"
 #include "include/policy.h"
+#include "include/ipc.h"
 
 /* call back to audit ptrace fields */
 static void audit_cb(struct audit_buffer *ab, void *va)
index b82e383beb77937b8b001bc5da67876cde0c4aba..9516948041adee7bc4c32c7a5e699346b6f5bc0f 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/vmalloc.h>
 
 #include "include/audit.h"
+#include "include/apparmor.h"
 
 
 /**
index d6d9a57b56525e5c7ca9df569f19d3da7d84feba..741dd13e089bacbbc14098d444faf89a82262bf2 100644 (file)
@@ -381,11 +381,11 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
                profile->file.trans.size = size;
                for (i = 0; i < size; i++) {
                        char *str;
-                       int c, j, size = unpack_strdup(e, &str, NULL);
+                       int c, j, size2 = unpack_strdup(e, &str, NULL);
                        /* unpack_strdup verifies that the last character is
                         * null termination byte.
                         */
-                       if (!size)
+                       if (!size2)
                                goto fail;
                        profile->file.trans.table[i] = str;
                        /* verify that name doesn't start with space */
@@ -393,7 +393,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
                                goto fail;
 
                        /* count internal #  of internal \0 */
-                       for (c = j = 0; j < size - 2; j++) {
+                       for (c = j = 0; j < size2 - 2; j++) {
                                if (!str[j])
                                        c++;
                        }
@@ -440,11 +440,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
                if (size > RLIM_NLIMITS)
                        goto fail;
                for (i = 0; i < size; i++) {
-                       u64 tmp = 0;
+                       u64 tmp2 = 0;
                        int a = aa_map_resource(i);
-                       if (!unpack_u64(e, &tmp, NULL))
+                       if (!unpack_u64(e, &tmp2, NULL))
                                goto fail;
-                       profile->rlimits.limits[a].rlim_max = tmp;
+                       profile->rlimits.limits[a].rlim_max = tmp2;
                }
                if (!unpack_nameX(e, AA_ARRAYEND, NULL))
                        goto fail;
index 04a2cf8d1b6596bcafd1ca6dde9dba013cd1abcb..1b41c542d376721646ba67dc4f17a3e21e1f241f 100644 (file)
@@ -16,6 +16,7 @@
 #include "include/context.h"
 #include "include/policy.h"
 #include "include/domain.h"
+#include "include/procattr.h"
 
 
 /**
index a93b3b73307991c69738bc0dab712e56330ba932..ee4f8486e5f563dff63c13278a1258e43e2b7859 100644 (file)
@@ -332,7 +332,8 @@ int cap_inode_killpriv(struct dentry *dentry)
  */
 static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
                                          struct linux_binprm *bprm,
-                                         bool *effective)
+                                         bool *effective,
+                                         bool *has_cap)
 {
        struct cred *new = bprm->cred;
        unsigned i;
@@ -341,6 +342,9 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
        if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
                *effective = true;
 
+       if (caps->magic_etc & VFS_CAP_REVISION_MASK)
+               *has_cap = true;
+
        CAP_FOR_EACH_U32(i) {
                __u32 permitted = caps->permitted.cap[i];
                __u32 inheritable = caps->inheritable.cap[i];
@@ -424,7 +428,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
  * its xattrs and, if present, apply them to the proposed credentials being
  * constructed by execve().
  */
-static int get_file_caps(struct linux_binprm *bprm, bool *effective)
+static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap)
 {
        struct dentry *dentry;
        int rc = 0;
@@ -450,7 +454,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective)
                goto out;
        }
 
-       rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective);
+       rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_cap);
        if (rc == -EINVAL)
                printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
                       __func__, rc, bprm->filename);
@@ -475,11 +479,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
 {
        const struct cred *old = current_cred();
        struct cred *new = bprm->cred;
-       bool effective;
+       bool effective, has_cap = false;
        int ret;
 
        effective = false;
-       ret = get_file_caps(bprm, &effective);
+       ret = get_file_caps(bprm, &effective, &has_cap);
        if (ret < 0)
                return ret;
 
@@ -489,7 +493,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
                 * for a setuid root binary run by a non-root user.  Do set it
                 * for a root user just to cause least surprise to an admin.
                 */
-               if (effective && new->uid != 0 && new->euid == 0) {
+               if (has_cap && new->uid != 0 && new->euid == 0) {
                        warn_setuid_and_fcaps_mixed(bprm->filename);
                        goto skip;
                }
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
new file mode 100644 (file)
index 0000000..4bf00ac
--- /dev/null
@@ -0,0 +1,7 @@
+#
+config INTEGRITY
+       def_bool y
+       depends on IMA || EVM
+
+source security/integrity/ima/Kconfig
+source security/integrity/evm/Kconfig
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
new file mode 100644 (file)
index 0000000..0ae44ae
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Makefile for caching inode integrity data (iint)
+#
+
+obj-$(CONFIG_INTEGRITY) += integrity.o
+
+integrity-y := iint.o
+
+subdir-$(CONFIG_IMA)                   += ima
+obj-$(CONFIG_IMA)                      += ima/built-in.o
+subdir-$(CONFIG_EVM)                   += evm
+obj-$(CONFIG_EVM)                      += evm/built-in.o
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
new file mode 100644 (file)
index 0000000..afbb59d
--- /dev/null
@@ -0,0 +1,13 @@
+config EVM
+       boolean "EVM support"
+       depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n)
+       select CRYPTO_HMAC
+       select CRYPTO_MD5
+       select CRYPTO_SHA1
+       select ENCRYPTED_KEYS
+       default n
+       help
+         EVM protects a file's security extended attributes against
+         integrity attacks.
+
+         If you are unsure how to answer this question, answer N.
diff --git a/security/integrity/evm/Makefile b/security/integrity/evm/Makefile
new file mode 100644 (file)
index 0000000..7393c41
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for building the Extended Verification Module(EVM)
+#
+obj-$(CONFIG_EVM) += evm.o
+
+evm-y := evm_main.o evm_crypto.o evm_secfs.o
+evm-$(CONFIG_FS_POSIX_ACL) += evm_posix_acl.o
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
new file mode 100644 (file)
index 0000000..d320f51
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm.h
+ *
+ */
+#include <linux/xattr.h>
+#include <linux/security.h>
+#include "../integrity.h"
+
+extern int evm_initialized;
+extern char *evm_hmac;
+
+extern struct crypto_shash *hmac_tfm;
+
+/* List of EVM protected security xattrs */
+extern char *evm_config_xattrnames[];
+
+extern int evm_init_key(void);
+extern int evm_update_evmxattr(struct dentry *dentry,
+                              const char *req_xattr_name,
+                              const char *req_xattr_value,
+                              size_t req_xattr_value_len);
+extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+                        const char *req_xattr_value,
+                        size_t req_xattr_value_len, char *digest);
+extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
+                        char *hmac_val);
+extern int evm_init_secfs(void);
+extern void evm_cleanup_secfs(void);
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
new file mode 100644 (file)
index 0000000..5dd5b14
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm_crypto.c
+ *      Using root's kernel master key (kmk), calculate the HMAC
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <keys/encrypted-type.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+#define EVMKEY "evm-key"
+#define MAX_KEY_SIZE 128
+static unsigned char evmkey[MAX_KEY_SIZE];
+static int evmkey_len = MAX_KEY_SIZE;
+
+struct crypto_shash *hmac_tfm;
+
+static struct shash_desc *init_desc(void)
+{
+       int rc;
+       struct shash_desc *desc;
+
+       if (hmac_tfm == NULL) {
+               hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC);
+               if (IS_ERR(hmac_tfm)) {
+                       pr_err("Can not allocate %s (reason: %ld)\n",
+                              evm_hmac, PTR_ERR(hmac_tfm));
+                       rc = PTR_ERR(hmac_tfm);
+                       hmac_tfm = NULL;
+                       return ERR_PTR(rc);
+               }
+       }
+
+       desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm),
+                       GFP_KERNEL);
+       if (!desc)
+               return ERR_PTR(-ENOMEM);
+
+       desc->tfm = hmac_tfm;
+       desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+       rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len);
+       if (rc)
+               goto out;
+       rc = crypto_shash_init(desc);
+out:
+       if (rc) {
+               kfree(desc);
+               return ERR_PTR(rc);
+       }
+       return desc;
+}
+
+/* Protect against 'cutting & pasting' security.evm xattr, include inode
+ * specific info.
+ *
+ * (Additional directory/file metadata needs to be added for more complete
+ * protection.)
+ */
+static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
+                         char *digest)
+{
+       struct h_misc {
+               unsigned long ino;
+               __u32 generation;
+               uid_t uid;
+               gid_t gid;
+               umode_t mode;
+       } hmac_misc;
+
+       memset(&hmac_misc, 0, sizeof hmac_misc);
+       hmac_misc.ino = inode->i_ino;
+       hmac_misc.generation = inode->i_generation;
+       hmac_misc.uid = inode->i_uid;
+       hmac_misc.gid = inode->i_gid;
+       hmac_misc.mode = inode->i_mode;
+       crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
+       crypto_shash_final(desc, digest);
+}
+
+/*
+ * Calculate the HMAC value across the set of protected security xattrs.
+ *
+ * Instead of retrieving the requested xattr, for performance, calculate
+ * the hmac using the requested xattr value. Don't alloc/free memory for
+ * each xattr, but attempt to re-use the previously allocated memory.
+ */
+int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+                 const char *req_xattr_value, size_t req_xattr_value_len,
+                 char *digest)
+{
+       struct inode *inode = dentry->d_inode;
+       struct shash_desc *desc;
+       char **xattrname;
+       size_t xattr_size = 0;
+       char *xattr_value = NULL;
+       int error;
+       int size;
+
+       if (!inode->i_op || !inode->i_op->getxattr)
+               return -EOPNOTSUPP;
+       desc = init_desc();
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       error = -ENODATA;
+       for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+               if ((req_xattr_name && req_xattr_value)
+                   && !strcmp(*xattrname, req_xattr_name)) {
+                       error = 0;
+                       crypto_shash_update(desc, (const u8 *)req_xattr_value,
+                                            req_xattr_value_len);
+                       continue;
+               }
+               size = vfs_getxattr_alloc(dentry, *xattrname,
+                                         &xattr_value, xattr_size, GFP_NOFS);
+               if (size == -ENOMEM) {
+                       error = -ENOMEM;
+                       goto out;
+               }
+               if (size < 0)
+                       continue;
+
+               error = 0;
+               xattr_size = size;
+               crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
+       }
+       hmac_add_misc(desc, inode, digest);
+
+out:
+       kfree(xattr_value);
+       kfree(desc);
+       return error;
+}
+
+/*
+ * Calculate the hmac and update security.evm xattr
+ *
+ * Expects to be called with i_mutex locked.
+ */
+int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
+                       const char *xattr_value, size_t xattr_value_len)
+{
+       struct inode *inode = dentry->d_inode;
+       struct evm_ima_xattr_data xattr_data;
+       int rc = 0;
+
+       rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+                          xattr_value_len, xattr_data.digest);
+       if (rc == 0) {
+               xattr_data.type = EVM_XATTR_HMAC;
+               rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
+                                          &xattr_data,
+                                          sizeof(xattr_data), 0);
+       }
+       else if (rc == -ENODATA)
+               rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
+       return rc;
+}
+
+int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
+                 char *hmac_val)
+{
+       struct shash_desc *desc;
+
+       desc = init_desc();
+       if (IS_ERR(desc)) {
+               printk(KERN_INFO "init_desc failed\n");
+               return PTR_ERR(desc);
+       }
+
+       crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
+       hmac_add_misc(desc, inode, hmac_val);
+       kfree(desc);
+       return 0;
+}
+
+/*
+ * Get the key from the TPM for the SHA1-HMAC
+ */
+int evm_init_key(void)
+{
+       struct key *evm_key;
+       struct encrypted_key_payload *ekp;
+       int rc = 0;
+
+       evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
+       if (IS_ERR(evm_key))
+               return -ENOENT;
+
+       down_read(&evm_key->sem);
+       ekp = evm_key->payload.data;
+       if (ekp->decrypted_datalen > MAX_KEY_SIZE) {
+               rc = -EINVAL;
+               goto out;
+       }
+       memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen);
+out:
+       /* burn the original key contents */
+       memset(ekp->decrypted_data, 0, ekp->decrypted_datalen);
+       up_read(&evm_key->sem);
+       key_put(evm_key);
+       return rc;
+}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
new file mode 100644 (file)
index 0000000..92d3d99
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm_main.c
+ *     implements evm_inode_setxattr, evm_inode_post_setxattr,
+ *     evm_inode_removexattr, and evm_verifyxattr
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <linux/integrity.h>
+#include <linux/evm.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+int evm_initialized;
+
+char *evm_hmac = "hmac(sha1)";
+
+char *evm_config_xattrnames[] = {
+#ifdef CONFIG_SECURITY_SELINUX
+       XATTR_NAME_SELINUX,
+#endif
+#ifdef CONFIG_SECURITY_SMACK
+       XATTR_NAME_SMACK,
+#endif
+       XATTR_NAME_CAPS,
+       NULL
+};
+
+static int evm_fixmode;
+static int __init evm_set_fixmode(char *str)
+{
+       if (strncmp(str, "fix", 3) == 0)
+               evm_fixmode = 1;
+       return 0;
+}
+__setup("evm=", evm_set_fixmode);
+
+/*
+ * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr
+ *
+ * Compute the HMAC on the dentry's protected set of extended attributes
+ * and compare it against the stored security.evm xattr.
+ *
+ * For performance:
+ * - use the previoulsy retrieved xattr value and length to calculate the
+ *   HMAC.)
+ * - cache the verification result in the iint, when available.
+ *
+ * Returns integrity status
+ */
+static enum integrity_status evm_verify_hmac(struct dentry *dentry,
+                                            const char *xattr_name,
+                                            char *xattr_value,
+                                            size_t xattr_value_len,
+                                            struct integrity_iint_cache *iint)
+{
+       struct evm_ima_xattr_data xattr_data;
+       enum integrity_status evm_status = INTEGRITY_PASS;
+       int rc;
+
+       if (iint && iint->evm_status == INTEGRITY_PASS)
+               return iint->evm_status;
+
+       /* if status is not PASS, try to check again - against -ENOMEM */
+
+       rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+                          xattr_value_len, xattr_data.digest);
+       if (rc < 0) {
+               evm_status = (rc == -ENODATA)
+                   ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
+               goto out;
+       }
+
+       xattr_data.type = EVM_XATTR_HMAC;
+       rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data,
+                          sizeof xattr_data, GFP_NOFS);
+       if (rc < 0)
+               evm_status = (rc == -ENODATA)
+                   ? INTEGRITY_NOLABEL : INTEGRITY_FAIL;
+out:
+       if (iint)
+               iint->evm_status = evm_status;
+       return evm_status;
+}
+
+static int evm_protected_xattr(const char *req_xattr_name)
+{
+       char **xattrname;
+       int namelen;
+       int found = 0;
+
+       namelen = strlen(req_xattr_name);
+       for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+               if ((strlen(*xattrname) == namelen)
+                   && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) {
+                       found = 1;
+                       break;
+               }
+               if (strncmp(req_xattr_name,
+                           *xattrname + XATTR_SECURITY_PREFIX_LEN,
+                           strlen(req_xattr_name)) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+       return found;
+}
+
+/**
+ * evm_verifyxattr - verify the integrity of the requested xattr
+ * @dentry: object of the verify xattr
+ * @xattr_name: requested xattr
+ * @xattr_value: requested xattr value
+ * @xattr_value_len: requested xattr value length
+ *
+ * Calculate the HMAC for the given dentry and verify it against the stored
+ * security.evm xattr. For performance, use the xattr value and length
+ * previously retrieved to calculate the HMAC.
+ *
+ * Returns the xattr integrity status.
+ *
+ * This function requires the caller to lock the inode's i_mutex before it
+ * is executed.
+ */
+enum integrity_status evm_verifyxattr(struct dentry *dentry,
+                                     const char *xattr_name,
+                                     void *xattr_value, size_t xattr_value_len,
+                                     struct integrity_iint_cache *iint)
+{
+       if (!evm_initialized || !evm_protected_xattr(xattr_name))
+               return INTEGRITY_UNKNOWN;
+
+       if (!iint) {
+               iint = integrity_iint_find(dentry->d_inode);
+               if (!iint)
+                       return INTEGRITY_UNKNOWN;
+       }
+       return evm_verify_hmac(dentry, xattr_name, xattr_value,
+                                xattr_value_len, iint);
+}
+EXPORT_SYMBOL_GPL(evm_verifyxattr);
+
+/*
+ * evm_verify_current_integrity - verify the dentry's metadata integrity
+ * @dentry: pointer to the affected dentry
+ *
+ * Verify and return the dentry's metadata integrity. The exceptions are
+ * before EVM is initialized or in 'fix' mode.
+ */
+static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode)
+               return 0;
+       return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
+}
+
+/*
+ * evm_protect_xattr - protect the EVM extended attribute
+ *
+ * Prevent security.evm from being modified or removed without the
+ * necessary permissions or when the existing value is invalid.
+ *
+ * The posix xattr acls are 'system' prefixed, which normally would not
+ * affect security.evm.  An interesting side affect of writing posix xattr
+ * acls is their modifying of the i_mode, which is included in security.evm.
+ * For posix xattr acls only, permit security.evm, even if it currently
+ * doesn't exist, to be updated.
+ */
+static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
+                            const void *xattr_value, size_t xattr_value_len)
+{
+       enum integrity_status evm_status;
+
+       if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+       } else if (!evm_protected_xattr(xattr_name)) {
+               if (!posix_xattr_acl(xattr_name))
+                       return 0;
+               evm_status = evm_verify_current_integrity(dentry);
+               if ((evm_status == INTEGRITY_PASS) ||
+                   (evm_status == INTEGRITY_NOXATTRS))
+                       return 0;
+               return -EPERM;
+       }
+       evm_status = evm_verify_current_integrity(dentry);
+       return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * evm_inode_setxattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+                      const void *xattr_value, size_t xattr_value_len)
+{
+       return evm_protect_xattr(dentry, xattr_name, xattr_value,
+                                xattr_value_len);
+}
+
+/**
+ * evm_inode_removexattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+       return evm_protect_xattr(dentry, xattr_name, NULL, 0);
+}
+
+/**
+ * evm_inode_post_setxattr - update 'security.evm' to reflect the changes
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Update the HMAC stored in 'security.evm' to reflect the change.
+ *
+ * No need to take the i_mutex lock here, as this function is called from
+ * __vfs_setxattr_noperm().  The caller of which has taken the inode's
+ * i_mutex lock.
+ */
+void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
+                            const void *xattr_value, size_t xattr_value_len)
+{
+       if (!evm_initialized || (!evm_protected_xattr(xattr_name)
+                                && !posix_xattr_acl(xattr_name)))
+               return;
+
+       evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
+       return;
+}
+
+/**
+ * evm_inode_post_removexattr - update 'security.evm' after removing the xattr
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Update the HMAC stored in 'security.evm' to reflect removal of the xattr.
+ */
+void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (!evm_initialized || !evm_protected_xattr(xattr_name))
+               return;
+
+       mutex_lock(&inode->i_mutex);
+       evm_update_evmxattr(dentry, xattr_name, NULL, 0);
+       mutex_unlock(&inode->i_mutex);
+       return;
+}
+
+/**
+ * evm_inode_setattr - prevent updating an invalid EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ */
+int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+       unsigned int ia_valid = attr->ia_valid;
+       enum integrity_status evm_status;
+
+       if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
+               return 0;
+       evm_status = evm_verify_current_integrity(dentry);
+       if ((evm_status == INTEGRITY_PASS) ||
+           (evm_status == INTEGRITY_NOXATTRS))
+               return 0;
+       return -EPERM;
+}
+
+/**
+ * evm_inode_post_setattr - update 'security.evm' after modifying metadata
+ * @dentry: pointer to the affected dentry
+ * @ia_valid: for the UID and GID status
+ *
+ * For now, update the HMAC stored in 'security.evm' to reflect UID/GID
+ * changes.
+ *
+ * This function is called from notify_change(), which expects the caller
+ * to lock the inode's i_mutex.
+ */
+void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+{
+       if (!evm_initialized)
+               return;
+
+       if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
+               evm_update_evmxattr(dentry, NULL, NULL, 0);
+       return;
+}
+
+/*
+ * evm_inode_init_security - initializes security.evm
+ */
+int evm_inode_init_security(struct inode *inode,
+                                const struct xattr *lsm_xattr,
+                                struct xattr *evm_xattr)
+{
+       struct evm_ima_xattr_data *xattr_data;
+       int rc;
+
+       if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name))
+               return 0;
+
+       xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
+       if (!xattr_data)
+               return -ENOMEM;
+
+       xattr_data->type = EVM_XATTR_HMAC;
+       rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
+       if (rc < 0)
+               goto out;
+
+       evm_xattr->value = xattr_data;
+       evm_xattr->value_len = sizeof(*xattr_data);
+       evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS);
+       return 0;
+out:
+       kfree(xattr_data);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(evm_inode_init_security);
+
+static int __init init_evm(void)
+{
+       int error;
+
+       error = evm_init_secfs();
+       if (error < 0) {
+               printk(KERN_INFO "EVM: Error registering secfs\n");
+               goto err;
+       }
+err:
+       return error;
+}
+
+static void __exit cleanup_evm(void)
+{
+       evm_cleanup_secfs();
+       if (hmac_tfm)
+               crypto_free_shash(hmac_tfm);
+}
+
+/*
+ * evm_display_config - list the EVM protected security extended attributes
+ */
+static int __init evm_display_config(void)
+{
+       char **xattrname;
+
+       for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++)
+               printk(KERN_INFO "EVM: %s\n", *xattrname);
+       return 0;
+}
+
+pure_initcall(evm_display_config);
+late_initcall(init_evm);
+
+MODULE_DESCRIPTION("Extended Verification Module");
+MODULE_LICENSE("GPL");
diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c
new file mode 100644 (file)
index 0000000..b1753e9
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/xattr.h>
+
+int posix_xattr_acl(char *xattr)
+{
+       int xattr_len = strlen(xattr);
+
+       if ((strlen(XATTR_NAME_POSIX_ACL_ACCESS) == xattr_len)
+            && (strncmp(XATTR_NAME_POSIX_ACL_ACCESS, xattr, xattr_len) == 0))
+               return 1;
+       if ((strlen(XATTR_NAME_POSIX_ACL_DEFAULT) == xattr_len)
+            && (strncmp(XATTR_NAME_POSIX_ACL_DEFAULT, xattr, xattr_len) == 0))
+               return 1;
+       return 0;
+}
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
new file mode 100644 (file)
index 0000000..ac76299
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm_secfs.c
+ *     - Used to signal when key is on keyring
+ *     - Get the key and enable EVM
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include "evm.h"
+
+static struct dentry *evm_init_tpm;
+
+/**
+ * evm_read_key - read() for <securityfs>/evm
+ *
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t evm_read_key(struct file *filp, char __user *buf,
+                           size_t count, loff_t *ppos)
+{
+       char temp[80];
+       ssize_t rc;
+
+       if (*ppos != 0)
+               return 0;
+
+       sprintf(temp, "%d", evm_initialized);
+       rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+       return rc;
+}
+
+/**
+ * evm_write_key - write() for <securityfs>/evm
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Used to signal that key is on the kernel key ring.
+ * - get the integrity hmac key from the kernel key ring
+ * - create list of hmac protected extended attributes
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t evm_write_key(struct file *file, const char __user *buf,
+                            size_t count, loff_t *ppos)
+{
+       char temp[80];
+       int i, error;
+
+       if (!capable(CAP_SYS_ADMIN) || evm_initialized)
+               return -EPERM;
+
+       if (count >= sizeof(temp) || count == 0)
+               return -EINVAL;
+
+       if (copy_from_user(temp, buf, count) != 0)
+               return -EFAULT;
+
+       temp[count] = '\0';
+
+       if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
+               return -EINVAL;
+
+       error = evm_init_key();
+       if (!error) {
+               evm_initialized = 1;
+               pr_info("EVM: initialized\n");
+       } else
+               pr_err("EVM: initialization failed\n");
+       return count;
+}
+
+static const struct file_operations evm_key_ops = {
+       .read           = evm_read_key,
+       .write          = evm_write_key,
+};
+
+int __init evm_init_secfs(void)
+{
+       int error = 0;
+
+       evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP,
+                                             NULL, NULL, &evm_key_ops);
+       if (!evm_init_tpm || IS_ERR(evm_init_tpm))
+               error = -EFAULT;
+       return error;
+}
+
+void __exit evm_cleanup_secfs(void)
+{
+       if (evm_init_tpm)
+               securityfs_remove(evm_init_tpm);
+}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
new file mode 100644 (file)
index 0000000..399641c
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: integrity_iint.c
+ *     - implements the integrity hooks: integrity_inode_alloc,
+ *       integrity_inode_free
+ *     - cache integrity information associated with an inode
+ *       using a rbtree tree.
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/rbtree.h>
+#include "integrity.h"
+
+static struct rb_root integrity_iint_tree = RB_ROOT;
+static DEFINE_SPINLOCK(integrity_iint_lock);
+static struct kmem_cache *iint_cache __read_mostly;
+
+int iint_initialized;
+
+/*
+ * __integrity_iint_find - return the iint associated with an inode
+ */
+static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
+{
+       struct integrity_iint_cache *iint;
+       struct rb_node *n = integrity_iint_tree.rb_node;
+
+       assert_spin_locked(&integrity_iint_lock);
+
+       while (n) {
+               iint = rb_entry(n, struct integrity_iint_cache, rb_node);
+
+               if (inode < iint->inode)
+                       n = n->rb_left;
+               else if (inode > iint->inode)
+                       n = n->rb_right;
+               else
+                       break;
+       }
+       if (!n)
+               return NULL;
+
+       return iint;
+}
+
+/*
+ * integrity_iint_find - return the iint associated with an inode
+ */
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+{
+       struct integrity_iint_cache *iint;
+
+       if (!IS_IMA(inode))
+               return NULL;
+
+       spin_lock(&integrity_iint_lock);
+       iint = __integrity_iint_find(inode);
+       spin_unlock(&integrity_iint_lock);
+
+       return iint;
+}
+
+static void iint_free(struct integrity_iint_cache *iint)
+{
+       iint->version = 0;
+       iint->flags = 0UL;
+       iint->evm_status = INTEGRITY_UNKNOWN;
+       kmem_cache_free(iint_cache, iint);
+}
+
+/**
+ * integrity_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ */
+int integrity_inode_alloc(struct inode *inode)
+{
+       struct rb_node **p;
+       struct rb_node *new_node, *parent = NULL;
+       struct integrity_iint_cache *new_iint, *test_iint;
+       int rc;
+
+       new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
+       if (!new_iint)
+               return -ENOMEM;
+
+       new_iint->inode = inode;
+       new_node = &new_iint->rb_node;
+
+       mutex_lock(&inode->i_mutex);    /* i_flags */
+       spin_lock(&integrity_iint_lock);
+
+       p = &integrity_iint_tree.rb_node;
+       while (*p) {
+               parent = *p;
+               test_iint = rb_entry(parent, struct integrity_iint_cache,
+                                    rb_node);
+               rc = -EEXIST;
+               if (inode < test_iint->inode)
+                       p = &(*p)->rb_left;
+               else if (inode > test_iint->inode)
+                       p = &(*p)->rb_right;
+               else
+                       goto out_err;
+       }
+
+       inode->i_flags |= S_IMA;
+       rb_link_node(new_node, parent, p);
+       rb_insert_color(new_node, &integrity_iint_tree);
+
+       spin_unlock(&integrity_iint_lock);
+       mutex_unlock(&inode->i_mutex);  /* i_flags */
+
+       return 0;
+out_err:
+       spin_unlock(&integrity_iint_lock);
+       mutex_unlock(&inode->i_mutex);  /* i_flags */
+       iint_free(new_iint);
+
+       return rc;
+}
+
+/**
+ * integrity_inode_free - called on security_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void integrity_inode_free(struct inode *inode)
+{
+       struct integrity_iint_cache *iint;
+
+       if (!IS_IMA(inode))
+               return;
+
+       spin_lock(&integrity_iint_lock);
+       iint = __integrity_iint_find(inode);
+       rb_erase(&iint->rb_node, &integrity_iint_tree);
+       spin_unlock(&integrity_iint_lock);
+
+       iint_free(iint);
+}
+
+static void init_once(void *foo)
+{
+       struct integrity_iint_cache *iint = foo;
+
+       memset(iint, 0, sizeof *iint);
+       iint->version = 0;
+       iint->flags = 0UL;
+       mutex_init(&iint->mutex);
+       iint->evm_status = INTEGRITY_UNKNOWN;
+}
+
+static int __init integrity_iintcache_init(void)
+{
+       iint_cache =
+           kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
+                             0, SLAB_PANIC, init_once);
+       iint_initialized = 1;
+       return 0;
+}
+security_initcall(integrity_iintcache_init);
index b6ecfd4d8d784ebad9c9f85ebed091b94690c3b3..19c053b823035a48bc2e47f735bdf125aad8384e 100644 (file)
@@ -3,6 +3,7 @@
 config IMA
        bool "Integrity Measurement Architecture(IMA)"
        depends on SECURITY
+       select INTEGRITY
        select SECURITYFS
        select CRYPTO
        select CRYPTO_HMAC
index 787c4cb916cd6bf5e82c851467a5445a39e4e5db..5690c021de8fbc2b069d622634a33841a0498bff 100644 (file)
@@ -6,4 +6,4 @@
 obj-$(CONFIG_IMA) += ima.o
 
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
-        ima_policy.o ima_iint.o ima_audit.o
+        ima_policy.o ima_audit.o
index 08408bd71462b2395f419647ae057950d879a231..3ccf7acac6df88c01e80a509d6a129f88953acd1 100644 (file)
 #include <linux/tpm.h>
 #include <linux/audit.h>
 
+#include "../integrity.h"
+
 enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 
 /* digest size for IMA, fits SHA1 or MD5 */
-#define IMA_DIGEST_SIZE                20
+#define IMA_DIGEST_SIZE                SHA1_DIGEST_SIZE
 #define IMA_EVENT_NAME_LEN_MAX 255
 
 #define IMA_HASH_BITS 9
 #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
 
 /* set during initialization */
-extern int iint_initialized;
 extern int ima_initialized;
 extern int ima_used_chip;
 extern char *ima_hash;
@@ -96,34 +97,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
        return hash_long(*digest, IMA_HASH_BITS);
 }
 
-/* iint cache flags */
-#define IMA_MEASURED           0x01
-
-/* integrity data associated with an inode */
-struct ima_iint_cache {
-       struct rb_node rb_node; /* rooted in ima_iint_tree */
-       struct inode *inode;    /* back pointer to inode in question */
-       u64 version;            /* track inode changes */
-       unsigned char flags;
-       u8 digest[IMA_DIGEST_SIZE];
-       struct mutex mutex;     /* protects: version, flags, digest */
-};
-
 /* LIM API function definitions */
 int ima_must_measure(struct inode *inode, int mask, int function);
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+                           struct file *file);
+void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
                           const unsigned char *filename);
 int ima_store_template(struct ima_template_entry *entry, int violation,
                       struct inode *inode);
-void ima_template_show(struct seq_file *m, void *e,
-                      enum ima_show_type show);
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
 
 /* rbtree tree calls to lookup, insert, delete
  * integrity data associated with an inode.
  */
-struct ima_iint_cache *ima_iint_insert(struct inode *inode);
-struct ima_iint_cache *ima_iint_find(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 
 /* IMA policy related functions */
 enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
index da36d2c085a4ff732967a273fe41423da0ae1b46..0d50df04ccc469f6cab0e68323b7de9d915ca09e 100644 (file)
@@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function)
  *
  * Return 0 on success, error code otherwise
  */
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+                           struct file *file)
 {
        int result = -EEXIST;
 
@@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
  *
  * Must be called with iint->mutex held.
  */
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
-                          const unsigned char *filename)
+void ima_store_measurement(struct integrity_iint_cache *iint,
+                          struct file *file, const unsigned char *filename)
 {
        const char *op = "add_template_measure";
        const char *audit_cause = "ENOMEM";
index ef21b96a0b4231638c4d932747174bbda2b73309..e1aa2b482dd2c5ae064a024656baa3f524c3a4aa 100644 (file)
@@ -287,7 +287,7 @@ static atomic_t policy_opencount = ATOMIC_INIT(1);
 /*
  * ima_open_policy: sequentialize access to the policy file
  */
-int ima_open_policy(struct inode * inode, struct file * filp)
+static int ima_open_policy(struct inode * inode, struct file * filp)
 {
        /* No point in being allowed to open it if you aren't going to write */
        if (!(filp->f_flags & O_WRONLY))
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
deleted file mode 100644 (file)
index 4ae7304..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2008 IBM Corporation
- *
- * Authors:
- * Mimi Zohar <zohar@us.ibm.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- *
- * File: ima_iint.c
- *     - implements the IMA hooks: ima_inode_alloc, ima_inode_free
- *     - cache integrity information associated with an inode
- *       using a rbtree tree.
- */
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/rbtree.h>
-#include "ima.h"
-
-static struct rb_root ima_iint_tree = RB_ROOT;
-static DEFINE_SPINLOCK(ima_iint_lock);
-static struct kmem_cache *iint_cache __read_mostly;
-
-int iint_initialized = 0;
-
-/*
- * __ima_iint_find - return the iint associated with an inode
- */
-static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
-{
-       struct ima_iint_cache *iint;
-       struct rb_node *n = ima_iint_tree.rb_node;
-
-       assert_spin_locked(&ima_iint_lock);
-
-       while (n) {
-               iint = rb_entry(n, struct ima_iint_cache, rb_node);
-
-               if (inode < iint->inode)
-                       n = n->rb_left;
-               else if (inode > iint->inode)
-                       n = n->rb_right;
-               else
-                       break;
-       }
-       if (!n)
-               return NULL;
-
-       return iint;
-}
-
-/*
- * ima_iint_find - return the iint associated with an inode
- */
-struct ima_iint_cache *ima_iint_find(struct inode *inode)
-{
-       struct ima_iint_cache *iint;
-
-       if (!IS_IMA(inode))
-               return NULL;
-
-       spin_lock(&ima_iint_lock);
-       iint = __ima_iint_find(inode);
-       spin_unlock(&ima_iint_lock);
-
-       return iint;
-}
-
-static void iint_free(struct ima_iint_cache *iint)
-{
-       iint->version = 0;
-       iint->flags = 0UL;
-       kmem_cache_free(iint_cache, iint);
-}
-
-/**
- * ima_inode_alloc - allocate an iint associated with an inode
- * @inode: pointer to the inode
- */
-int ima_inode_alloc(struct inode *inode)
-{
-       struct rb_node **p;
-       struct rb_node *new_node, *parent = NULL;
-       struct ima_iint_cache *new_iint, *test_iint;
-       int rc;
-
-       new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
-       if (!new_iint)
-               return -ENOMEM;
-
-       new_iint->inode = inode;
-       new_node = &new_iint->rb_node;
-
-       mutex_lock(&inode->i_mutex); /* i_flags */
-       spin_lock(&ima_iint_lock);
-
-       p = &ima_iint_tree.rb_node;
-       while (*p) {
-               parent = *p;
-               test_iint = rb_entry(parent, struct ima_iint_cache, rb_node);
-
-               rc = -EEXIST;
-               if (inode < test_iint->inode)
-                       p = &(*p)->rb_left;
-               else if (inode > test_iint->inode)
-                       p = &(*p)->rb_right;
-               else
-                       goto out_err;
-       }
-
-       inode->i_flags |= S_IMA;
-       rb_link_node(new_node, parent, p);
-       rb_insert_color(new_node, &ima_iint_tree);
-
-       spin_unlock(&ima_iint_lock);
-       mutex_unlock(&inode->i_mutex); /* i_flags */
-
-       return 0;
-out_err:
-       spin_unlock(&ima_iint_lock);
-       mutex_unlock(&inode->i_mutex); /* i_flags */
-       iint_free(new_iint);
-
-       return rc;
-}
-
-/**
- * ima_inode_free - called on security_inode_free
- * @inode: pointer to the inode
- *
- * Free the integrity information(iint) associated with an inode.
- */
-void ima_inode_free(struct inode *inode)
-{
-       struct ima_iint_cache *iint;
-
-       if (!IS_IMA(inode))
-               return;
-
-       spin_lock(&ima_iint_lock);
-       iint = __ima_iint_find(inode);
-       rb_erase(&iint->rb_node, &ima_iint_tree);
-       spin_unlock(&ima_iint_lock);
-
-       iint_free(iint);
-}
-
-static void init_once(void *foo)
-{
-       struct ima_iint_cache *iint = foo;
-
-       memset(iint, 0, sizeof *iint);
-       iint->version = 0;
-       iint->flags = 0UL;
-       mutex_init(&iint->mutex);
-}
-
-static int __init ima_iintcache_init(void)
-{
-       iint_cache =
-           kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
-                             SLAB_PANIC, init_once);
-       iint_initialized = 1;
-       return 0;
-}
-security_initcall(ima_iintcache_init);
index 26b46ff7466353bcf8fbe58545bd8ab6c2f0e384..1eff5cb001e53b511d7d9ec96adc320af73a6a66 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/mount.h>
 #include <linux/mman.h>
 #include <linux/slab.h>
+#include <linux/ima.h>
 
 #include "ima.h"
 
@@ -82,7 +83,7 @@ out:
                                  "open_writers");
 }
 
-static void ima_check_last_writer(struct ima_iint_cache *iint,
+static void ima_check_last_writer(struct integrity_iint_cache *iint,
                                  struct inode *inode,
                                  struct file *file)
 {
@@ -105,12 +106,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
 void ima_file_free(struct file *file)
 {
        struct inode *inode = file->f_dentry->d_inode;
-       struct ima_iint_cache *iint;
+       struct integrity_iint_cache *iint;
 
        if (!iint_initialized || !S_ISREG(inode->i_mode))
                return;
 
-       iint = ima_iint_find(inode);
+       iint = integrity_iint_find(inode);
        if (!iint)
                return;
 
@@ -121,7 +122,7 @@ static int process_measurement(struct file *file, const unsigned char *filename,
                               int mask, int function)
 {
        struct inode *inode = file->f_dentry->d_inode;
-       struct ima_iint_cache *iint;
+       struct integrity_iint_cache *iint;
        int rc = 0;
 
        if (!ima_initialized || !S_ISREG(inode->i_mode))
@@ -131,9 +132,9 @@ static int process_measurement(struct file *file, const unsigned char *filename,
        if (rc != 0)
                return rc;
 retry:
-       iint = ima_iint_find(inode);
+       iint = integrity_iint_find(inode);
        if (!iint) {
-               rc = ima_inode_alloc(inode);
+               rc = integrity_inode_alloc(inode);
                if (!rc || rc == -EEXIST)
                        goto retry;
                return rc;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
new file mode 100644 (file)
index 0000000..3143a3c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/integrity.h>
+#include <crypto/sha.h>
+
+/* iint cache flags */
+#define IMA_MEASURED           0x01
+
+enum evm_ima_xattr_type {
+       IMA_XATTR_DIGEST = 0x01,
+       EVM_XATTR_HMAC,
+       EVM_IMA_XATTR_DIGSIG,
+};
+
+struct evm_ima_xattr_data {
+       u8 type;
+       u8 digest[SHA1_DIGEST_SIZE];
+}  __attribute__((packed));
+
+/* integrity data associated with an inode */
+struct integrity_iint_cache {
+       struct rb_node rb_node; /* rooted in integrity_iint_tree */
+       struct inode *inode;    /* back pointer to inode in question */
+       u64 version;            /* track inode changes */
+       unsigned char flags;
+       u8 digest[SHA1_DIGEST_SIZE];
+       struct mutex mutex;     /* protects: version, flags, digest */
+       enum integrity_status evm_status;
+};
+
+/* rbtree tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
+
+/* set during initialization */
+extern int iint_initialized;
index b34cc6ee6900c3be1453e6aa205b5469f5461f03..a56f1ffdc64d0f8c3fcde694832a20dafe78f21d 100644 (file)
@@ -14,7 +14,7 @@ obj-y := \
        user_defined.o
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/encrypted-keys/Makefile b/security/keys/encrypted-keys/Makefile
new file mode 100644 (file)
index 0000000..6bc7a86
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for encrypted keys
+#
+
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o ecryptfs_format.o
+obj-$(CONFIG_TRUSTED_KEYS) += masterkey_trusted.o
similarity index 97%
rename from security/keys/encrypted.c
rename to security/keys/encrypted-keys/encrypted.c
index e7eca9ec4c65ffdda866f1a7d713506818fd3c9c..3f577954b85adf46615ff8b3740924d553007428 100644 (file)
@@ -298,31 +298,6 @@ out:
        return ascii_buf;
 }
 
-/*
- * request_trusted_key - request the trusted key
- *
- * Trusted keys are sealed to PCRs and other metadata. Although userspace
- * manages both trusted/encrypted key-types, like the encrypted key type
- * data, trusted key type data is not visible decrypted from userspace.
- */
-static struct key *request_trusted_key(const char *trusted_desc,
-                                      u8 **master_key, size_t *master_keylen)
-{
-       struct trusted_key_payload *tpayload;
-       struct key *tkey;
-
-       tkey = request_key(&key_type_trusted, trusted_desc, NULL);
-       if (IS_ERR(tkey))
-               goto error;
-
-       down_read(&tkey->sem);
-       tpayload = rcu_dereference(tkey->payload.data);
-       *master_key = tpayload->key;
-       *master_keylen = tpayload->key_len;
-error:
-       return tkey;
-}
-
 /*
  * request_user_key - request the user key
  *
@@ -469,8 +444,14 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload,
                goto out;
 
        if (IS_ERR(mkey)) {
-               pr_info("encrypted_key: key %s not found",
-                       epayload->master_desc);
+               int ret = PTR_ERR(epayload);
+
+               if (ret == -ENOTSUPP)
+                       pr_info("encrypted_key: key %s not supported",
+                               epayload->master_desc);
+               else
+                       pr_info("encrypted_key: key %s not found",
+                               epayload->master_desc);
                goto out;
        }
 
similarity index 81%
rename from security/keys/encrypted.h
rename to security/keys/encrypted-keys/encrypted.h
index cef5e2f2b7d1e977e66f975e58029cee8cea5859..b6ade8945250c0b3f13913c56ee5b9686a3f39d1 100644 (file)
@@ -2,6 +2,17 @@
 #define __ENCRYPTED_KEY_H
 
 #define ENCRYPTED_DEBUG 0
+#ifdef CONFIG_TRUSTED_KEYS
+extern struct key *request_trusted_key(const char *trusted_desc,
+                                      u8 **master_key, size_t *master_keylen);
+#else
+static inline struct key *request_trusted_key(const char *trusted_desc,
+                                             u8 **master_key,
+                                             size_t *master_keylen)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+#endif
 
 #if ENCRYPTED_DEBUG
 static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c
new file mode 100644 (file)
index 0000000..df87272
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ * 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
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * See Documentation/security/keys-trusted-encrypted.txt
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <keys/trusted-type.h>
+
+/*
+ * request_trusted_key - request the trusted key
+ *
+ * Trusted keys are sealed to PCRs and other metadata. Although userspace
+ * manages both trusted/encrypted key-types, like the encrypted key type
+ * data, trusted key type data is not visible decrypted from userspace.
+ */
+struct key *request_trusted_key(const char *trusted_desc,
+                               u8 **master_key, size_t *master_keylen)
+{
+       struct trusted_key_payload *tpayload;
+       struct key *tkey;
+
+       tkey = request_key(&key_type_trusted, trusted_desc, NULL);
+       if (IS_ERR(tkey))
+               goto error;
+
+       down_read(&tkey->sem);
+       tpayload = rcu_dereference(tkey->payload.data);
+       *master_key = tpayload->key;
+       *master_keylen = tpayload->key_len;
+error:
+       return tkey;
+}
index 89df6b5f203c534c64fffd3fc0547f611dedebea..bf4d8da5a79502f626a849473e4af18e229eb074 100644 (file)
@@ -1,6 +1,6 @@
 /* Key garbage collector
  *
- * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -10,6 +10,8 @@
  */
 
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/security.h>
 #include <keys/keyring-type.h>
 #include "internal.h"
 
 unsigned key_gc_delay = 5 * 60;
 
 /*
- * Reaper
+ * Reaper for unused keys.
+ */
+static void key_garbage_collector(struct work_struct *work);
+DECLARE_WORK(key_gc_work, key_garbage_collector);
+
+/*
+ * Reaper for links from keyrings to dead keys.
  */
 static void key_gc_timer_func(unsigned long);
-static void key_garbage_collector(struct work_struct *);
 static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
-static DECLARE_WORK(key_gc_work, key_garbage_collector);
-static key_serial_t key_gc_cursor; /* the last key the gc considered */
-static bool key_gc_again;
-static unsigned long key_gc_executing;
+
 static time_t key_gc_next_run = LONG_MAX;
-static time_t key_gc_new_timer;
+static struct key_type *key_gc_dead_keytype;
+
+static unsigned long key_gc_flags;
+#define KEY_GC_KEY_EXPIRED     0       /* A key expired and needs unlinking */
+#define KEY_GC_REAP_KEYTYPE    1       /* A keytype is being unregistered */
+#define KEY_GC_REAPING_KEYTYPE 2       /* Cleared when keytype reaped */
+
+
+/*
+ * Any key whose type gets unregistered will be re-typed to this if it can't be
+ * immediately unlinked.
+ */
+struct key_type key_type_dead = {
+       .name = "dead",
+};
 
 /*
  * Schedule a garbage collection run.
@@ -42,31 +60,75 @@ void key_schedule_gc(time_t gc_at)
 
        kenter("%ld", gc_at - now);
 
-       if (gc_at <= now) {
-               schedule_work(&key_gc_work);
+       if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) {
+               kdebug("IMMEDIATE");
+               queue_work(system_nrt_wq, &key_gc_work);
        } else if (gc_at < key_gc_next_run) {
+               kdebug("DEFERRED");
+               key_gc_next_run = gc_at;
                expires = jiffies + (gc_at - now) * HZ;
                mod_timer(&key_gc_timer, expires);
        }
 }
 
 /*
- * The garbage collector timer kicked off
+ * Some key's cleanup time was met after it expired, so we need to get the
+ * reaper to go through a cycle finding expired keys.
  */
 static void key_gc_timer_func(unsigned long data)
 {
        kenter("");
        key_gc_next_run = LONG_MAX;
-       schedule_work(&key_gc_work);
+       set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
+       queue_work(system_nrt_wq, &key_gc_work);
+}
+
+/*
+ * wait_on_bit() sleep function for uninterruptible waiting
+ */
+static int key_gc_wait_bit(void *flags)
+{
+       schedule();
+       return 0;
+}
+
+/*
+ * Reap keys of dead type.
+ *
+ * We use three flags to make sure we see three complete cycles of the garbage
+ * collector: the first to mark keys of that type as being dead, the second to
+ * collect dead links and the third to clean up the dead keys.  We have to be
+ * careful as there may already be a cycle in progress.
+ *
+ * The caller must be holding key_types_sem.
+ */
+void key_gc_keytype(struct key_type *ktype)
+{
+       kenter("%s", ktype->name);
+
+       key_gc_dead_keytype = ktype;
+       set_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
+       smp_mb();
+       set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags);
+
+       kdebug("schedule");
+       queue_work(system_nrt_wq, &key_gc_work);
+
+       kdebug("sleep");
+       wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit,
+                   TASK_UNINTERRUPTIBLE);
+
+       key_gc_dead_keytype = NULL;
+       kleave("");
 }
 
 /*
  * Garbage collect pointers from a keyring.
  *
- * Return true if we altered the keyring.
+ * Not called with any locks held.  The keyring's key struct will not be
+ * deallocated under us as only our caller may deallocate it.
  */
-static bool key_gc_keyring(struct key *keyring, time_t limit)
-       __releases(key_serial_lock)
+static void key_gc_keyring(struct key *keyring, time_t limit)
 {
        struct keyring_list *klist;
        struct key *key;
@@ -93,130 +155,234 @@ static bool key_gc_keyring(struct key *keyring, time_t limit)
 unlock_dont_gc:
        rcu_read_unlock();
 dont_gc:
-       kleave(" = false");
-       return false;
+       kleave(" [no gc]");
+       return;
 
 do_gc:
        rcu_read_unlock();
-       key_gc_cursor = keyring->serial;
-       key_get(keyring);
-       spin_unlock(&key_serial_lock);
+
        keyring_gc(keyring, limit);
-       key_put(keyring);
-       kleave(" = true");
-       return true;
+       kleave(" [gc]");
 }
 
 /*
- * Garbage collector for keys.  This involves scanning the keyrings for dead,
- * expired and revoked keys that have overstayed their welcome
+ * Garbage collect an unreferenced, detached key
  */
-static void key_garbage_collector(struct work_struct *work)
+static noinline void key_gc_unused_key(struct key *key)
 {
-       struct rb_node *rb;
-       key_serial_t cursor;
-       struct key *key, *xkey;
-       time_t new_timer = LONG_MAX, limit, now;
-
-       now = current_kernel_time().tv_sec;
-       kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
-
-       if (test_and_set_bit(0, &key_gc_executing)) {
-               key_schedule_gc(current_kernel_time().tv_sec + 1);
-               kleave(" [busy; deferring]");
-               return;
+       key_check(key);
+
+       security_key_free(key);
+
+       /* deal with the user's key tracking and quota */
+       if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+               spin_lock(&key->user->lock);
+               key->user->qnkeys--;
+               key->user->qnbytes -= key->quotalen;
+               spin_unlock(&key->user->lock);
        }
 
-       limit = now;
+       atomic_dec(&key->user->nkeys);
+       if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+               atomic_dec(&key->user->nikeys);
+
+       key_user_put(key->user);
+
+       /* now throw away the key memory */
+       if (key->type->destroy)
+               key->type->destroy(key);
+
+       kfree(key->description);
+
+#ifdef KEY_DEBUGGING
+       key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+       kmem_cache_free(key_jar, key);
+}
+
+/*
+ * Garbage collector for unused keys.
+ *
+ * This is done in process context so that we don't have to disable interrupts
+ * all over the place.  key_put() schedules this rather than trying to do the
+ * cleanup itself, which means key_put() doesn't have to sleep.
+ */
+static void key_garbage_collector(struct work_struct *work)
+{
+       static u8 gc_state;             /* Internal persistent state */
+#define KEY_GC_REAP_AGAIN      0x01    /* - Need another cycle */
+#define KEY_GC_REAPING_LINKS   0x02    /* - We need to reap links */
+#define KEY_GC_SET_TIMER       0x04    /* - We need to restart the timer */
+#define KEY_GC_REAPING_DEAD_1  0x10    /* - We need to mark dead keys */
+#define KEY_GC_REAPING_DEAD_2  0x20    /* - We need to reap dead key links */
+#define KEY_GC_REAPING_DEAD_3  0x40    /* - We need to reap dead keys */
+#define KEY_GC_FOUND_DEAD_KEY  0x80    /* - We found at least one dead key */
+
+       struct rb_node *cursor;
+       struct key *key;
+       time_t new_timer, limit;
+
+       kenter("[%lx,%x]", key_gc_flags, gc_state);
+
+       limit = current_kernel_time().tv_sec;
        if (limit > key_gc_delay)
                limit -= key_gc_delay;
        else
                limit = key_gc_delay;
 
+       /* Work out what we're going to be doing in this pass */
+       gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2;
+       gc_state <<= 1;
+       if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags))
+               gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER;
+
+       if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags))
+               gc_state |= KEY_GC_REAPING_DEAD_1;
+       kdebug("new pass %x", gc_state);
+
+       new_timer = LONG_MAX;
+
+       /* As only this function is permitted to remove things from the key
+        * serial tree, if cursor is non-NULL then it will always point to a
+        * valid node in the tree - even if lock got dropped.
+        */
        spin_lock(&key_serial_lock);
+       cursor = rb_first(&key_serial_tree);
 
-       if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) {
-               spin_unlock(&key_serial_lock);
-               clear_bit(0, &key_gc_executing);
-               return;
-       }
+continue_scanning:
+       while (cursor) {
+               key = rb_entry(cursor, struct key, serial_node);
+               cursor = rb_next(cursor);
 
-       cursor = key_gc_cursor;
-       if (cursor < 0)
-               cursor = 0;
-       if (cursor > 0)
-               new_timer = key_gc_new_timer;
-       else
-               key_gc_again = false;
-
-       /* find the first key above the cursor */
-       key = NULL;
-       rb = key_serial_tree.rb_node;
-       while (rb) {
-               xkey = rb_entry(rb, struct key, serial_node);
-               if (cursor < xkey->serial) {
-                       key = xkey;
-                       rb = rb->rb_left;
-               } else if (cursor > xkey->serial) {
-                       rb = rb->rb_right;
-               } else {
-                       rb = rb_next(rb);
-                       if (!rb)
-                               goto reached_the_end;
-                       key = rb_entry(rb, struct key, serial_node);
-                       break;
+               if (atomic_read(&key->usage) == 0)
+                       goto found_unreferenced_key;
+
+               if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
+                       if (key->type == key_gc_dead_keytype) {
+                               gc_state |= KEY_GC_FOUND_DEAD_KEY;
+                               set_bit(KEY_FLAG_DEAD, &key->flags);
+                               key->perm = 0;
+                               goto skip_dead_key;
+                       }
+               }
+
+               if (gc_state & KEY_GC_SET_TIMER) {
+                       if (key->expiry > limit && key->expiry < new_timer) {
+                               kdebug("will expire %x in %ld",
+                                      key_serial(key), key->expiry - limit);
+                               new_timer = key->expiry;
+                       }
                }
-       }
 
-       if (!key)
-               goto reached_the_end;
+               if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2))
+                       if (key->type == key_gc_dead_keytype)
+                               gc_state |= KEY_GC_FOUND_DEAD_KEY;
 
-       /* trawl through the keys looking for keyrings */
-       for (;;) {
-               if (key->expiry > limit && key->expiry < new_timer) {
-                       kdebug("will expire %x in %ld",
-                              key_serial(key), key->expiry - limit);
-                       new_timer = key->expiry;
+               if ((gc_state & KEY_GC_REAPING_LINKS) ||
+                   unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
+                       if (key->type == &key_type_keyring)
+                               goto found_keyring;
                }
 
-               if (key->type == &key_type_keyring &&
-                   key_gc_keyring(key, limit))
-                       /* the gc had to release our lock so that the keyring
-                        * could be modified, so we have to get it again */
-                       goto gc_released_our_lock;
+               if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3))
+                       if (key->type == key_gc_dead_keytype)
+                               goto destroy_dead_key;
 
-               rb = rb_next(&key->serial_node);
-               if (!rb)
-                       goto reached_the_end;
-               key = rb_entry(rb, struct key, serial_node);
+       skip_dead_key:
+               if (spin_is_contended(&key_serial_lock) || need_resched())
+                       goto contended;
        }
 
-gc_released_our_lock:
-       kdebug("gc_released_our_lock");
-       key_gc_new_timer = new_timer;
-       key_gc_again = true;
-       clear_bit(0, &key_gc_executing);
-       schedule_work(&key_gc_work);
-       kleave(" [continue]");
-       return;
-
-       /* when we reach the end of the run, we set the timer for the next one */
-reached_the_end:
-       kdebug("reached_the_end");
+contended:
        spin_unlock(&key_serial_lock);
-       key_gc_new_timer = new_timer;
-       key_gc_cursor = 0;
-       clear_bit(0, &key_gc_executing);
-
-       if (key_gc_again) {
-               /* there may have been a key that expired whilst we were
-                * scanning, so if we discarded any links we should do another
-                * scan */
-               new_timer = now + 1;
-               key_schedule_gc(new_timer);
-       } else if (new_timer < LONG_MAX) {
+
+maybe_resched:
+       if (cursor) {
+               cond_resched();
+               spin_lock(&key_serial_lock);
+               goto continue_scanning;
+       }
+
+       /* We've completed the pass.  Set the timer if we need to and queue a
+        * new cycle if necessary.  We keep executing cycles until we find one
+        * where we didn't reap any keys.
+        */
+       kdebug("pass complete");
+
+       if (gc_state & KEY_GC_SET_TIMER && new_timer != (time_t)LONG_MAX) {
                new_timer += key_gc_delay;
                key_schedule_gc(new_timer);
        }
-       kleave(" [end]");
+
+       if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
+               /* Make sure everyone revalidates their keys if we marked a
+                * bunch as being dead and make sure all keyring ex-payloads
+                * are destroyed.
+                */
+               kdebug("dead sync");
+               synchronize_rcu();
+       }
+
+       if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 |
+                                KEY_GC_REAPING_DEAD_2))) {
+               if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) {
+                       /* No remaining dead keys: short circuit the remaining
+                        * keytype reap cycles.
+                        */
+                       kdebug("dead short");
+                       gc_state &= ~(KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2);
+                       gc_state |= KEY_GC_REAPING_DEAD_3;
+               } else {
+                       gc_state |= KEY_GC_REAP_AGAIN;
+               }
+       }
+
+       if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) {
+               kdebug("dead wake");
+               smp_mb();
+               clear_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
+               wake_up_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE);
+       }
+
+       if (gc_state & KEY_GC_REAP_AGAIN)
+               queue_work(system_nrt_wq, &key_gc_work);
+       kleave(" [end %x]", gc_state);
+       return;
+
+       /* We found an unreferenced key - once we've removed it from the tree,
+        * we can safely drop the lock.
+        */
+found_unreferenced_key:
+       kdebug("unrefd key %d", key->serial);
+       rb_erase(&key->serial_node, &key_serial_tree);
+       spin_unlock(&key_serial_lock);
+
+       key_gc_unused_key(key);
+       gc_state |= KEY_GC_REAP_AGAIN;
+       goto maybe_resched;
+
+       /* We found a keyring and we need to check the payload for links to
+        * dead or expired keys.  We don't flag another reap immediately as we
+        * have to wait for the old payload to be destroyed by RCU before we
+        * can reap the keys to which it refers.
+        */
+found_keyring:
+       spin_unlock(&key_serial_lock);
+       kdebug("scan keyring %d", key->serial);
+       key_gc_keyring(key, limit);
+       goto maybe_resched;
+
+       /* We found a dead key that is still referenced.  Reset its type and
+        * destroy its payload with its semaphore held.
+        */
+destroy_dead_key:
+       spin_unlock(&key_serial_lock);
+       kdebug("destroy key %d", key->serial);
+       down_write(&key->sem);
+       key->type = &key_type_dead;
+       if (key_gc_dead_keytype->destroy)
+               key_gc_dead_keytype->destroy(key);
+       memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
+       up_write(&key->sem);
+       goto maybe_resched;
 }
index f375152a2500b1d747d7128517430d2401f3a4ce..c7a7caec4830b33e414ac7fa3b665633b853a080 100644 (file)
@@ -31,6 +31,7 @@
        no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
 #endif
 
+extern struct key_type key_type_dead;
 extern struct key_type key_type_user;
 
 /*****************************************************************************/
@@ -75,6 +76,7 @@ extern unsigned key_quota_maxbytes;
 #define KEYQUOTA_LINK_BYTES    4               /* a link in a keyring is worth 4 bytes */
 
 
+extern struct kmem_cache *key_jar;
 extern struct rb_root key_serial_tree;
 extern spinlock_t key_serial_lock;
 extern struct mutex key_construction_mutex;
@@ -146,9 +148,11 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
 
 extern long join_session_keyring(const char *name);
 
+extern struct work_struct key_gc_work;
 extern unsigned key_gc_delay;
 extern void keyring_gc(struct key *keyring, time_t limit);
 extern void key_schedule_gc(time_t expiry_at);
+extern void key_gc_keytype(struct key_type *ktype);
 
 extern int key_task_permission(const key_ref_t key_ref,
                               const struct cred *cred,
index f7f9d93f08d98df064104630c3f91ce4fb2aaab9..4414abddcb5bfd6951d168018990c6df2567b901 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/user_namespace.h>
 #include "internal.h"
 
-static struct kmem_cache       *key_jar;
+struct kmem_cache *key_jar;
 struct rb_root         key_serial_tree; /* tree of keys indexed by serial */
 DEFINE_SPINLOCK(key_serial_lock);
 
@@ -36,17 +36,9 @@ unsigned int key_quota_maxbytes = 20000;     /* general key space quota */
 static LIST_HEAD(key_types_list);
 static DECLARE_RWSEM(key_types_sem);
 
-static void key_cleanup(struct work_struct *work);
-static DECLARE_WORK(key_cleanup_task, key_cleanup);
-
 /* We serialise key instantiation and link */
 DEFINE_MUTEX(key_construction_mutex);
 
-/* Any key who's type gets unegistered will be re-typed to this */
-static struct key_type key_type_dead = {
-       .name           = "dead",
-};
-
 #ifdef KEY_DEBUGGING
 void __key_check(const struct key *key)
 {
@@ -591,71 +583,6 @@ int key_reject_and_link(struct key *key,
 }
 EXPORT_SYMBOL(key_reject_and_link);
 
-/*
- * Garbage collect keys in process context so that we don't have to disable
- * interrupts all over the place.
- *
- * key_put() schedules this rather than trying to do the cleanup itself, which
- * means key_put() doesn't have to sleep.
- */
-static void key_cleanup(struct work_struct *work)
-{
-       struct rb_node *_n;
-       struct key *key;
-
-go_again:
-       /* look for a dead key in the tree */
-       spin_lock(&key_serial_lock);
-
-       for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
-               key = rb_entry(_n, struct key, serial_node);
-
-               if (atomic_read(&key->usage) == 0)
-                       goto found_dead_key;
-       }
-
-       spin_unlock(&key_serial_lock);
-       return;
-
-found_dead_key:
-       /* we found a dead key - once we've removed it from the tree, we can
-        * drop the lock */
-       rb_erase(&key->serial_node, &key_serial_tree);
-       spin_unlock(&key_serial_lock);
-
-       key_check(key);
-
-       security_key_free(key);
-
-       /* deal with the user's key tracking and quota */
-       if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
-               spin_lock(&key->user->lock);
-               key->user->qnkeys--;
-               key->user->qnbytes -= key->quotalen;
-               spin_unlock(&key->user->lock);
-       }
-
-       atomic_dec(&key->user->nkeys);
-       if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
-               atomic_dec(&key->user->nikeys);
-
-       key_user_put(key->user);
-
-       /* now throw away the key memory */
-       if (key->type->destroy)
-               key->type->destroy(key);
-
-       kfree(key->description);
-
-#ifdef KEY_DEBUGGING
-       key->magic = KEY_DEBUG_MAGIC_X;
-#endif
-       kmem_cache_free(key_jar, key);
-
-       /* there may, of course, be more than one key to destroy */
-       goto go_again;
-}
-
 /**
  * key_put - Discard a reference to a key.
  * @key: The key to discard a reference from.
@@ -670,7 +597,7 @@ void key_put(struct key *key)
                key_check(key);
 
                if (atomic_dec_and_test(&key->usage))
-                       schedule_work(&key_cleanup_task);
+                       queue_work(system_nrt_wq, &key_gc_work);
        }
 }
 EXPORT_SYMBOL(key_put);
@@ -1048,49 +975,11 @@ EXPORT_SYMBOL(register_key_type);
  */
 void unregister_key_type(struct key_type *ktype)
 {
-       struct rb_node *_n;
-       struct key *key;
-
        down_write(&key_types_sem);
-
-       /* withdraw the key type */
        list_del_init(&ktype->link);
-
-       /* mark all the keys of this type dead */
-       spin_lock(&key_serial_lock);
-
-       for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
-               key = rb_entry(_n, struct key, serial_node);
-
-               if (key->type == ktype) {
-                       key->type = &key_type_dead;
-                       set_bit(KEY_FLAG_DEAD, &key->flags);
-               }
-       }
-
-       spin_unlock(&key_serial_lock);
-
-       /* make sure everyone revalidates their keys */
-       synchronize_rcu();
-
-       /* we should now be able to destroy the payloads of all the keys of
-        * this type with impunity */
-       spin_lock(&key_serial_lock);
-
-       for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
-               key = rb_entry(_n, struct key, serial_node);
-
-               if (key->type == ktype) {
-                       if (ktype->destroy)
-                               ktype->destroy(key);
-                       memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
-               }
-       }
-
-       spin_unlock(&key_serial_lock);
-       up_write(&key_types_sem);
-
-       key_schedule_gc(0);
+       downgrade_write(&key_types_sem);
+       key_gc_keytype(ktype);
+       up_read(&key_types_sem);
 }
 EXPORT_SYMBOL(unregister_key_type);
 
index 30e242f7bd0ec413bca908f1a0fdc5398b00a124..37a7f3b28852e098f57ab1cd98eb2eccdad5c7c5 100644 (file)
@@ -860,8 +860,7 @@ void __key_link(struct key *keyring, struct key *key,
 
        kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
 
-       klist = rcu_dereference_protected(keyring->payload.subscriptions,
-                                         rwsem_is_locked(&keyring->sem));
+       klist = rcu_dereference_locked_keyring(keyring);
 
        atomic_inc(&key->usage);
 
index a3063eb3dc232a16eab45161e28f0c88fc642841..1068cb1939b3122ca857b042ac0df9d304cda6a7 100644 (file)
@@ -270,7 +270,7 @@ static int install_session_keyring(struct key *keyring)
        if (!new)
                return -ENOMEM;
 
-       ret = install_session_keyring_to_cred(new, NULL);
+       ret = install_session_keyring_to_cred(new, keyring);
        if (ret < 0) {
                abort_creds(new);
                return ret;
@@ -589,12 +589,22 @@ try_again:
                        ret = install_user_keyrings();
                        if (ret < 0)
                                goto error;
-                       ret = install_session_keyring(
-                               cred->user->session_keyring);
+                       if (lflags & KEY_LOOKUP_CREATE)
+                               ret = join_session_keyring(NULL);
+                       else
+                               ret = install_session_keyring(
+                                       cred->user->session_keyring);
 
                        if (ret < 0)
                                goto error;
                        goto reget_creds;
+               } else if (cred->tgcred->session_keyring ==
+                          cred->user->session_keyring &&
+                          lflags & KEY_LOOKUP_CREATE) {
+                       ret = join_session_keyring(NULL);
+                       if (ret < 0)
+                               goto error;
+                       goto reget_creds;
                }
 
                rcu_read_lock();
index 0e4fccfef12cb495fcf059a4dc964e3b45ee136c..c1d69875db6c0a9fcc1a11b90d4d6711d794ff12 100644 (file)
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/ima.h>
+#include <linux/evm.h>
+
+#define MAX_LSM_EVM_XATTR      2
 
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
        CONFIG_DEFAULT_SECURITY;
 
-/* things that live in capability.c */
-extern void __init security_fixup_ops(struct security_operations *ops);
-
 static struct security_operations *security_ops;
 static struct security_operations default_security_ops = {
        .name   = "default",
@@ -334,20 +335,57 @@ int security_inode_alloc(struct inode *inode)
 
 void security_inode_free(struct inode *inode)
 {
-       ima_inode_free(inode);
+       integrity_inode_free(inode);
        security_ops->inode_free_security(inode);
 }
 
 int security_inode_init_security(struct inode *inode, struct inode *dir,
-                                const struct qstr *qstr, char **name,
-                                void **value, size_t *len)
+                                const struct qstr *qstr,
+                                const initxattrs initxattrs, void *fs_data)
 {
+       struct xattr new_xattrs[MAX_LSM_EVM_XATTR + 1];
+       struct xattr *lsm_xattr, *evm_xattr, *xattr;
+       int ret;
+
        if (unlikely(IS_PRIVATE(inode)))
-               return -EOPNOTSUPP;
+               return 0;
+
+       memset(new_xattrs, 0, sizeof new_xattrs);
+       if (!initxattrs)
+               return security_ops->inode_init_security(inode, dir, qstr,
+                                                        NULL, NULL, NULL);
+       lsm_xattr = new_xattrs;
+       ret = security_ops->inode_init_security(inode, dir, qstr,
+                                               &lsm_xattr->name,
+                                               &lsm_xattr->value,
+                                               &lsm_xattr->value_len);
+       if (ret)
+               goto out;
+
+       evm_xattr = lsm_xattr + 1;
+       ret = evm_inode_init_security(inode, lsm_xattr, evm_xattr);
+       if (ret)
+               goto out;
+       ret = initxattrs(inode, new_xattrs, fs_data);
+out:
+       for (xattr = new_xattrs; xattr->name != NULL; xattr++) {
+               kfree(xattr->name);
+               kfree(xattr->value);
+       }
+       return (ret == -EOPNOTSUPP) ? 0 : ret;
+}
+EXPORT_SYMBOL(security_inode_init_security);
+
+int security_old_inode_init_security(struct inode *inode, struct inode *dir,
+                                    const struct qstr *qstr, char **name,
+                                    void **value, size_t *len)
+{
+       if (unlikely(IS_PRIVATE(inode)))
+               return 0;
        return security_ops->inode_init_security(inode, dir, qstr, name, value,
                                                 len);
 }
-EXPORT_SYMBOL(security_inode_init_security);
+EXPORT_SYMBOL(security_old_inode_init_security);
 
 #ifdef CONFIG_SECURITY_PATH
 int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
@@ -523,9 +561,14 @@ int security_inode_permission(struct inode *inode, int mask)
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 {
+       int ret;
+
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return 0;
-       return security_ops->inode_setattr(dentry, attr);
+       ret = security_ops->inode_setattr(dentry, attr);
+       if (ret)
+               return ret;
+       return evm_inode_setattr(dentry, attr);
 }
 EXPORT_SYMBOL_GPL(security_inode_setattr);
 
@@ -539,9 +582,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 int security_inode_setxattr(struct dentry *dentry, const char *name,
                            const void *value, size_t size, int flags)
 {
+       int ret;
+
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return 0;
-       return security_ops->inode_setxattr(dentry, name, value, size, flags);
+       ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
+       if (ret)
+               return ret;
+       return evm_inode_setxattr(dentry, name, value, size);
 }
 
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
@@ -550,6 +598,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return;
        security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+       evm_inode_post_setxattr(dentry, name, value, size);
 }
 
 int security_inode_getxattr(struct dentry *dentry, const char *name)
@@ -568,9 +617,14 @@ int security_inode_listxattr(struct dentry *dentry)
 
 int security_inode_removexattr(struct dentry *dentry, const char *name)
 {
+       int ret;
+
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return 0;
-       return security_ops->inode_removexattr(dentry, name);
+       ret = security_ops->inode_removexattr(dentry, name);
+       if (ret)
+               return ret;
+       return evm_inode_removexattr(dentry, name);
 }
 
 int security_inode_need_killpriv(struct dentry *dentry)
index 90664385dead0df01f6eec2b7479c83f4f021160..e75dd94e2d2bd67b6a195850d72199de12067b9d 100644 (file)
@@ -12,6 +12,7 @@
  * as published by the Free Software Foundation.
  */
 #include <linux/module.h>
+#include <linux/selinux.h>
 
 #include "security.h"
 
index 266a2292451dcd247410fc3f0ffe8d12d82d8ad3..e545b9f6707210fa56b54a2e8257e57630776b36 100644 (file)
 #include "xfrm.h"
 #include "netlabel.h"
 #include "audit.h"
+#include "avc_ss.h"
 
 #define NUM_SEL_MNT_OPTS 5
 
-extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern struct security_operations *security_ops;
 
 /* SECMARK reference count */
-atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
+static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
 
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing;
@@ -279,10 +279,6 @@ static void superblock_free_security(struct super_block *sb)
        kfree(sbsec);
 }
 
-/* The security server must be initialized before
-   any labeling or access decisions can be provided. */
-extern int ss_initialized;
-
 /* The file system's label must be initialized prior to use. */
 
 static const char *labeling_behaviors[6] = {
@@ -2097,9 +2093,6 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
        return (atsecure || cap_bprm_secureexec(bprm));
 }
 
-extern struct vfsmount *selinuxfs_mount;
-extern struct dentry *selinux_null;
-
 /* Derived from fs/exec.c:flush_old_files. */
 static inline void flush_unauthorized_files(const struct cred *cred,
                                            struct files_struct *files)
@@ -5803,8 +5796,6 @@ static int selinux_disabled;
 
 int selinux_disable(void)
 {
-       extern void exit_sel_fs(void);
-
        if (ss_initialized) {
                /* Not permitted after initial policy load. */
                return -EINVAL;
index 4677aa519b0471b2f4dd7bad0cf79c737f93bd8d..d5c328452df0161acd82bd8ec295fecc00392b86 100644 (file)
@@ -18,5 +18,11 @@ struct security_class_mapping {
 
 extern struct security_class_mapping secclass_map[];
 
+/*
+ * The security server must be initialized before
+ * any labeling or access decisions can be provided.
+ */
+extern int ss_initialized;
+
 #endif /* _SELINUX_AVC_SS_H_ */
 
index 3ba4feba048a0158230620b83ca91d29c2e8ff26..d871e8ad2103c6d3a90a86c3c6bc6fa982de256c 100644 (file)
@@ -216,6 +216,14 @@ struct selinux_kernel_status {
 
 extern void selinux_status_update_setenforce(int enforcing);
 extern void selinux_status_update_policyload(int seqno);
+extern void selinux_complete_init(void);
+extern int selinux_disable(void);
+extern void exit_sel_fs(void);
+extern struct dentry *selinux_null;
+extern struct vfsmount *selinuxfs_mount;
+extern void selnl_notify_setenforce(int val);
+extern void selnl_notify_policyload(u32 seqno);
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 
 #endif /* _SELINUX_SECURITY_H_ */
 
index 36ac257cec9a523106d79aab69a19254eaf9f494..ce3f481558d8682421ddf410d3adb5f4787df732 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/selinux_netlink.h>
 #include <net/net_namespace.h>
 
+#include "security.h"
+
 static struct sock *selnl;
 
 static int selnl_msglen(int msgtype)
index 8b02b2137da25ce114e602d60a22b009dbbe82e1..0920ea3bf59936245ac1e495b25a1b7867bc614d 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "flask.h"
 #include "av_permissions.h"
+#include "security.h"
 
 struct nlmsg_perm {
        u16     nlmsg_type;
index 55d92cbb177acaf31a7b84282cf8d56b0c71fbe5..f46658722c78d2b798a2d77634a08df968b2d0da 100644 (file)
@@ -75,8 +75,6 @@ static char policy_opened;
 /* global data for policy capabilities */
 static struct dentry *policycap_dir;
 
-extern void selnl_notify_setenforce(int val);
-
 /* Check whether a task is allowed to use a security operation. */
 static int task_has_security(struct task_struct *tsk,
                             u32 perms)
@@ -278,7 +276,6 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
        char *page = NULL;
        ssize_t length;
        int new_value;
-       extern int selinux_disable(void);
 
        length = -ENOMEM;
        if (count >= PAGE_SIZE)
@@ -478,7 +475,7 @@ static struct vm_operations_struct sel_mmap_policy_ops = {
        .page_mkwrite = sel_mmap_policy_fault,
 };
 
-int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
+static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
 {
        if (vma->vm_flags & VM_SHARED) {
                /* do not allow mprotect to make mapping writable */
index a53373207fb459223e52fadd718e5a2cc2dbb666..2ec904177fe0bb65a522b110f642fd2f4ea0a5dd 100644 (file)
@@ -555,7 +555,7 @@ static int cond_write_av_list(struct policydb *p,
        return 0;
 }
 
-int cond_write_node(struct policydb *p, struct cond_node *node,
+static int cond_write_node(struct policydb *p, struct cond_node *node,
                    struct policy_file *fp)
 {
        struct cond_expr *cur_expr;
index 3f209c635295681f026874e332573577e743dea4..4d1f87466508f7adf60f2eb7f5ae89ec3c49aa29 100644 (file)
@@ -13,6 +13,7 @@
 #include "avtab.h"
 #include "symtab.h"
 #include "policydb.h"
+#include "../include/conditional.h"
 
 #define COND_EXPR_MAXDEPTH 10
 
index 2381d0ded228d492a207b2745a6bc59269114316..a7f61d52f05cd8cf5f9ad11dc10ab935ab19d097 100644 (file)
@@ -1743,8 +1743,6 @@ static int policydb_bounds_sanity_check(struct policydb *p)
        return 0;
 }
 
-extern int ss_initialized;
-
 u16 string_to_security_class(struct policydb *p, const char *name)
 {
        struct class_datum *cladatum;
index f6917bc0aa05322716d8fd5a4e0a46effd3be45c..185f849a26f6049a71fccf197cf5233ece3e3a99 100644 (file)
@@ -70,8 +70,6 @@
 #include "ebitmap.h"
 #include "audit.h"
 
-extern void selnl_notify_policyload(u32 seqno);
-
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;
 
@@ -1790,7 +1788,6 @@ static void security_load_policycaps(void)
                                                  POLICYDB_CAPABILITY_OPENPERM);
 }
 
-extern void selinux_complete_init(void);
 static int security_preserve_bools(struct policydb *p);
 
 /**
index 7c7f8c16c10fcd2260e4ba7af4e9f5a7e98bb8eb..8eb779b9d77f2ad56c7ccdc5db081bc4b0a05028 100644 (file)
@@ -1,8 +1,10 @@
 config SECURITY_TOMOYO
        bool "TOMOYO Linux Support"
        depends on SECURITY
+       depends on NET
        select SECURITYFS
        select SECURITY_PATH
+       select SECURITY_NETWORK
        default n
        help
          This selects TOMOYO Linux, pathname-based access control.
index 95278b71fc2155ccb4ac603d5b1e93da44d56f59..56a0c7be409eaa14524ea67c1ba204464eb904db 100644 (file)
@@ -1,4 +1,4 @@
-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-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o
 
 $(obj)/policy/profile.conf:
        @mkdir -p $(obj)/policy/
@@ -27,7 +27,7 @@ $(obj)/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 Generating built-in policy for TOMOYO 2.5.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
index 5dbb1f7617c0077550c79c1255b6fb3f943906ac..075c3a6d164985138194c3f94fc5b63e673c0b73 100644 (file)
@@ -313,6 +313,7 @@ static unsigned int tomoyo_log_count;
  */
 static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
                             const u8 profile, const u8 index,
+                            const struct tomoyo_acl_info *matched_acl,
                             const bool is_granted)
 {
        u8 mode;
@@ -324,6 +325,9 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
        p = tomoyo_profile(ns, profile);
        if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
                return false;
+       if (is_granted && matched_acl && matched_acl->cond &&
+           matched_acl->cond->grant_log != TOMOYO_GRANTLOG_AUTO)
+               return matched_acl->cond->grant_log == TOMOYO_GRANTLOG_YES;
        mode = p->config[index];
        if (mode == TOMOYO_CONFIG_USE_DEFAULT)
                mode = p->config[category];
@@ -350,7 +354,8 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
        char *buf;
        struct tomoyo_log *entry;
        bool quota_exceeded = false;
-       if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
+       if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type,
+                             r->matched_acl, r->granted))
                goto out;
        buf = tomoyo_init_log(r, len, fmt, args);
        if (!buf)
index 2e43aec1c36b04b6335b83d9942ada9d499c5ff8..dd552289ad9aa6d6e09818d60e729ab43a48166e 100644 (file)
@@ -20,6 +20,7 @@ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
 /* String table for /sys/kernel/security/tomoyo/profile */
 const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
                                       + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+       /* CONFIG::file group */
        [TOMOYO_MAC_FILE_EXECUTE]    = "execute",
        [TOMOYO_MAC_FILE_OPEN]       = "open",
        [TOMOYO_MAC_FILE_CREATE]     = "create",
@@ -43,7 +44,28 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
        [TOMOYO_MAC_FILE_MOUNT]      = "mount",
        [TOMOYO_MAC_FILE_UMOUNT]     = "unmount",
        [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
+       /* CONFIG::network group */
+       [TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       = "inet_stream_bind",
+       [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     = "inet_stream_listen",
+       [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    = "inet_stream_connect",
+       [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        = "inet_dgram_bind",
+       [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        = "inet_dgram_send",
+       [TOMOYO_MAC_NETWORK_INET_RAW_BIND]          = "inet_raw_bind",
+       [TOMOYO_MAC_NETWORK_INET_RAW_SEND]          = "inet_raw_send",
+       [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       = "unix_stream_bind",
+       [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     = "unix_stream_listen",
+       [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    = "unix_stream_connect",
+       [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        = "unix_dgram_bind",
+       [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        = "unix_dgram_send",
+       [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    = "unix_seqpacket_bind",
+       [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  = "unix_seqpacket_listen",
+       [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect",
+       /* CONFIG::misc group */
+       [TOMOYO_MAC_ENVIRON] = "env",
+       /* CONFIG group */
        [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+       [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+       [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc",
 };
 
 /* String table for conditions. */
@@ -130,10 +152,20 @@ const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
        [TOMOYO_TYPE_UMOUNT]     = "unmount",
 };
 
+/* String table for socket's operation. */
+const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = {
+       [TOMOYO_NETWORK_BIND]    = "bind",
+       [TOMOYO_NETWORK_LISTEN]  = "listen",
+       [TOMOYO_NETWORK_CONNECT] = "connect",
+       [TOMOYO_NETWORK_SEND]    = "send",
+};
+
 /* String table for categories. */
 static const char * const tomoyo_category_keywords
 [TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
-       [TOMOYO_MAC_CATEGORY_FILE]       = "file",
+       [TOMOYO_MAC_CATEGORY_FILE]    = "file",
+       [TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+       [TOMOYO_MAC_CATEGORY_MISC]    = "misc",
 };
 
 /* Permit policy management by non-root user? */
@@ -313,7 +345,7 @@ void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
                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;
+       ns->profile_version = 20110903;
        tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
        list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
 }
@@ -981,6 +1013,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
        return true;
 }
 
+/**
+ * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
+                             const struct tomoyo_acl_info *b)
+{
+       const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
+       const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
+       return p1->domainname == p2->domainname;
+}
+
+/**
+ * tomoyo_write_task - Update task related list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_task(struct tomoyo_acl_param *param)
+{
+       int error = -EINVAL;
+       if (tomoyo_str_starts(&param->data, "manual_domain_transition ")) {
+               struct tomoyo_task_acl e = {
+                       .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
+                       .domainname = tomoyo_get_domainname(param),
+               };
+               if (e.domainname)
+                       error = tomoyo_update_domain(&e.head, sizeof(e), param,
+                                                    tomoyo_same_task_acl,
+                                                    NULL);
+               tomoyo_put_name(e.domainname);
+       }
+       return error;
+}
+
 /**
  * tomoyo_delete_domain - Delete a domain.
  *
@@ -1039,11 +1113,16 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
        static const struct {
                const char *keyword;
                int (*write) (struct tomoyo_acl_param *);
-       } tomoyo_callback[1] = {
+       } tomoyo_callback[5] = {
                { "file ", tomoyo_write_file },
+               { "network inet ", tomoyo_write_inet_network },
+               { "network unix ", tomoyo_write_unix_network },
+               { "misc ", tomoyo_write_misc },
+               { "task ", tomoyo_write_task },
        };
        u8 i;
-       for (i = 0; i < 1; i++) {
+
+       for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) {
                if (!tomoyo_str_starts(&param.data,
                                       tomoyo_callback[i].keyword))
                        continue;
@@ -1127,6 +1206,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
        case 0:
                head->r.cond_index = 0;
                head->r.cond_step++;
+               if (cond->transit) {
+                       tomoyo_set_space(head);
+                       tomoyo_set_string(head, cond->transit->name);
+               }
                /* fall through */
        case 1:
                {
@@ -1239,6 +1322,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
                head->r.cond_step++;
                /* fall through */
        case 3:
+               if (cond->grant_log != TOMOYO_GRANTLOG_AUTO)
+                       tomoyo_io_printf(head, " grant_log=%s",
+                                        tomoyo_yesno(cond->grant_log ==
+                                                     TOMOYO_GRANTLOG_YES));
                tomoyo_set_lf(head);
                return true;
        }
@@ -1306,6 +1393,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
                if (first)
                        return true;
                tomoyo_print_name_union(head, &ptr->name);
+       } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
+               struct tomoyo_task_acl *ptr =
+                       container_of(acl, typeof(*ptr), head);
+               tomoyo_set_group(head, "task ");
+               tomoyo_set_string(head, "manual_domain_transition ");
+               tomoyo_set_string(head, ptr->domainname->name);
        } else if (head->r.print_transition_related_only) {
                return true;
        } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
@@ -1370,6 +1463,60 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
                tomoyo_print_number_union(head, &ptr->mode);
                tomoyo_print_number_union(head, &ptr->major);
                tomoyo_print_number_union(head, &ptr->minor);
+       } else if (acl_type == TOMOYO_TYPE_INET_ACL) {
+               struct tomoyo_inet_acl *ptr =
+                       container_of(acl, typeof(*ptr), head);
+               const u8 perm = ptr->perm;
+
+               for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+                       if (!(perm & (1 << bit)))
+                               continue;
+                       if (first) {
+                               tomoyo_set_group(head, "network inet ");
+                               tomoyo_set_string(head, tomoyo_proto_keyword
+                                                 [ptr->protocol]);
+                               tomoyo_set_space(head);
+                               first = false;
+                       } else {
+                               tomoyo_set_slash(head);
+                       }
+                       tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+               }
+               if (first)
+                       return true;
+               tomoyo_set_space(head);
+               if (ptr->address.group) {
+                       tomoyo_set_string(head, "@");
+                       tomoyo_set_string(head, ptr->address.group->group_name
+                                         ->name);
+               } else {
+                       char buf[128];
+                       tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
+                       tomoyo_io_printf(head, "%s", buf);
+               }
+               tomoyo_print_number_union(head, &ptr->port);
+       } else if (acl_type == TOMOYO_TYPE_UNIX_ACL) {
+               struct tomoyo_unix_acl *ptr =
+                       container_of(acl, typeof(*ptr), head);
+               const u8 perm = ptr->perm;
+
+               for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+                       if (!(perm & (1 << bit)))
+                               continue;
+                       if (first) {
+                               tomoyo_set_group(head, "network unix ");
+                               tomoyo_set_string(head, tomoyo_proto_keyword
+                                                 [ptr->protocol]);
+                               tomoyo_set_space(head);
+                               first = false;
+                       } else {
+                               tomoyo_set_slash(head);
+                       }
+                       tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+               }
+               if (first)
+                       return true;
+               tomoyo_print_name_union(head, &ptr->name);
        } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
                struct tomoyo_mount_acl *ptr =
                        container_of(acl, typeof(*ptr), head);
@@ -1378,6 +1525,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
                tomoyo_print_name_union(head, &ptr->dir_name);
                tomoyo_print_name_union(head, &ptr->fs_type);
                tomoyo_print_number_union(head, &ptr->flags);
+       } else if (acl_type == TOMOYO_TYPE_ENV_ACL) {
+               struct tomoyo_env_acl *ptr =
+                       container_of(acl, typeof(*ptr), head);
+
+               tomoyo_set_group(head, "misc env ");
+               tomoyo_set_string(head, ptr->env->name);
        }
        if (acl->cond) {
                head->r.print_cond_part = true;
@@ -1537,8 +1690,9 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
 
 /* String table for grouping keywords. */
 static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
-       [TOMOYO_PATH_GROUP]   = "path_group ",
-       [TOMOYO_NUMBER_GROUP] = "number_group ",
+       [TOMOYO_PATH_GROUP]    = "path_group ",
+       [TOMOYO_NUMBER_GROUP]  = "number_group ",
+       [TOMOYO_ADDRESS_GROUP] = "address_group ",
 };
 
 /**
@@ -1580,7 +1734,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
 }
 
 /**
- * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @idx:  Index number.
@@ -1617,6 +1771,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
                                                          (ptr,
                                                   struct tomoyo_number_group,
                                                           head)->number);
+                       } else if (idx == TOMOYO_ADDRESS_GROUP) {
+                               char buffer[128];
+
+                               struct tomoyo_address_group *member =
+                                       container_of(ptr, typeof(*member),
+                                                    head);
+                               tomoyo_print_ip(buffer, sizeof(buffer),
+                                               &member->address);
+                               tomoyo_io_printf(head, " %s", buffer);
                        }
                        tomoyo_set_lf(head);
                }
@@ -2066,27 +2229,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.4.0");
-               head->r.eof = true;
-       }
-}
-
-/**
- * tomoyo_read_self_domain - Get the current process's domainname.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns the current process's domainname.
- */
-static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
-{
-       if (!head->r.eof) {
-               /*
-                * tomoyo_domain()->domainname != NULL
-                * because every process belongs to a domain and
-                * the domain's name cannot be NULL.
-                */
-               tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
+               tomoyo_io_printf(head, "2.5.0");
                head->r.eof = true;
        }
 }
@@ -2221,10 +2364,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
                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_PROCESS_STATUS:
                /* /sys/kernel/security/tomoyo/.process_status */
                head->write = tomoyo_write_pid;
@@ -2562,11 +2701,11 @@ void tomoyo_check_profile(void)
        struct tomoyo_domain_info *domain;
        const int idx = tomoyo_read_lock();
        tomoyo_policy_loaded = true;
-       printk(KERN_INFO "TOMOYO: 2.4.0\n");
+       printk(KERN_INFO "TOMOYO: 2.5.0\n");
        list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
                const u8 profile = domain->profile;
                const struct tomoyo_policy_namespace *ns = domain->ns;
-               if (ns->profile_version != 20100903)
+               if (ns->profile_version != 20110903)
                        printk(KERN_ERR
                               "Profile version %u is not supported.\n",
                               ns->profile_version);
@@ -2577,9 +2716,9 @@ void tomoyo_check_profile(void)
                else
                        continue;
                printk(KERN_ERR
-                      "Userland tools for TOMOYO 2.4 must be installed and "
+                      "Userland tools for TOMOYO 2.5 must be installed and "
                       "policy must be initialized.\n");
-               printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
+               printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "
                       "for more information.\n");
                panic("STOP!");
        }
index f7fbaa66e443dcd95aaa4002eedd49f87ffedde1..a2bc33fc60b64ef9b3286dd9d470f0c08d567d4a 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Header file for TOMOYO.
  *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #ifndef _SECURITY_TOMOYO_COMMON_H
 #include <linux/poll.h>
 #include <linux/binfmts.h>
 #include <linux/highmem.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/un.h>
+#include <net/sock.h>
+#include <net/af_unix.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
 
 /********** Constants definitions. **********/
 
 #define TOMOYO_HASH_BITS  8
 #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
 
+/*
+ * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET.
+ * Therefore, we don't need SOCK_MAX.
+ */
+#define TOMOYO_SOCK_MAX 6
+
 #define TOMOYO_EXEC_TMPSIZE     4096
 
 /* Profile number is an integer between 0 and 255. */
@@ -136,6 +152,7 @@ enum tomoyo_mode_index {
 /* Index numbers for entry type. */
 enum tomoyo_policy_id {
        TOMOYO_ID_GROUP,
+       TOMOYO_ID_ADDRESS_GROUP,
        TOMOYO_ID_PATH_GROUP,
        TOMOYO_ID_NUMBER_GROUP,
        TOMOYO_ID_TRANSITION_CONTROL,
@@ -162,10 +179,21 @@ enum tomoyo_domain_info_flags_index {
        TOMOYO_MAX_DOMAIN_INFO_FLAGS
 };
 
+/* Index numbers for audit type. */
+enum tomoyo_grant_log {
+       /* Follow profile's configuration. */
+       TOMOYO_GRANTLOG_AUTO,
+       /* Do not generate grant log. */
+       TOMOYO_GRANTLOG_NO,
+       /* Generate grant_log. */
+       TOMOYO_GRANTLOG_YES,
+};
+
 /* Index numbers for group entries. */
 enum tomoyo_group_id {
        TOMOYO_PATH_GROUP,
        TOMOYO_NUMBER_GROUP,
+       TOMOYO_ADDRESS_GROUP,
        TOMOYO_MAX_GROUP
 };
 
@@ -196,6 +224,10 @@ enum tomoyo_acl_entry_type_index {
        TOMOYO_TYPE_PATH_NUMBER_ACL,
        TOMOYO_TYPE_MKDEV_ACL,
        TOMOYO_TYPE_MOUNT_ACL,
+       TOMOYO_TYPE_INET_ACL,
+       TOMOYO_TYPE_UNIX_ACL,
+       TOMOYO_TYPE_ENV_ACL,
+       TOMOYO_TYPE_MANUAL_TASK_ACL,
 };
 
 /* Index numbers for access controls with one pathname. */
@@ -228,6 +260,15 @@ enum tomoyo_mkdev_acl_index {
        TOMOYO_MAX_MKDEV_OPERATION
 };
 
+/* Index numbers for socket operations. */
+enum tomoyo_network_acl_index {
+       TOMOYO_NETWORK_BIND,    /* bind() operation. */
+       TOMOYO_NETWORK_LISTEN,  /* listen() operation. */
+       TOMOYO_NETWORK_CONNECT, /* connect() operation. */
+       TOMOYO_NETWORK_SEND,    /* send() operation. */
+       TOMOYO_MAX_NETWORK_OPERATION
+};
+
 /* Index numbers for access controls with two pathnames. */
 enum tomoyo_path2_acl_index {
        TOMOYO_TYPE_LINK,
@@ -255,7 +296,6 @@ enum tomoyo_securityfs_interface_index {
        TOMOYO_EXCEPTIONPOLICY,
        TOMOYO_PROCESS_STATUS,
        TOMOYO_STAT,
-       TOMOYO_SELFDOMAIN,
        TOMOYO_AUDIT,
        TOMOYO_VERSION,
        TOMOYO_PROFILE,
@@ -300,12 +340,30 @@ enum tomoyo_mac_index {
        TOMOYO_MAC_FILE_MOUNT,
        TOMOYO_MAC_FILE_UMOUNT,
        TOMOYO_MAC_FILE_PIVOT_ROOT,
+       TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
+       TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
+       TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
+       TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
+       TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
+       TOMOYO_MAC_NETWORK_INET_RAW_BIND,
+       TOMOYO_MAC_NETWORK_INET_RAW_SEND,
+       TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
+       TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
+       TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
+       TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
+       TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
+       TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+       TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+       TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+       TOMOYO_MAC_ENVIRON,
        TOMOYO_MAX_MAC_INDEX
 };
 
 /* Index numbers for category of functionality. */
 enum tomoyo_mac_category_index {
        TOMOYO_MAC_CATEGORY_FILE,
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       TOMOYO_MAC_CATEGORY_MISC,
        TOMOYO_MAX_MAC_CATEGORY_INDEX
 };
 
@@ -396,6 +454,25 @@ struct tomoyo_request_info {
                         */
                        u8 operation;
                } path_number;
+               struct {
+                       const struct tomoyo_path_info *name;
+               } environ;
+               struct {
+                       const __be32 *address;
+                       u16 port;
+                       /* One of values smaller than TOMOYO_SOCK_MAX. */
+                       u8 protocol;
+                       /* One of values in "enum tomoyo_network_acl_index". */
+                       u8 operation;
+                       bool is_ipv6;
+               } inet_network;
+               struct {
+                       const struct tomoyo_path_info *address;
+                       /* One of values smaller than TOMOYO_SOCK_MAX. */
+                       u8 protocol;
+                       /* One of values in "enum tomoyo_network_acl_index". */
+                       u8 operation;
+               } unix_network;
                struct {
                        const struct tomoyo_path_info *type;
                        const struct tomoyo_path_info *dir;
@@ -403,7 +480,11 @@ struct tomoyo_request_info {
                        unsigned long flags;
                        int need_dev;
                } mount;
+               struct {
+                       const struct tomoyo_path_info *domainname;
+               } task;
        } param;
+       struct tomoyo_acl_info *matched_acl;
        u8 param_type;
        bool granted;
        u8 retry;
@@ -442,7 +523,14 @@ struct tomoyo_number_union {
        u8 value_type[2];
 };
 
-/* Structure for "path_group"/"number_group" directive. */
+/* Structure for holding an IP address. */
+struct tomoyo_ipaddr_union {
+       struct in6_addr ip[2]; /* Big endian. */
+       struct tomoyo_group *group; /* Pointer to address group. */
+       bool is_ipv6; /* Valid only if @group == NULL. */
+};
+
+/* Structure for "path_group"/"number_group"/"address_group" directive. */
 struct tomoyo_group {
        struct tomoyo_shared_acl_head head;
        const struct tomoyo_path_info *group_name;
@@ -461,6 +549,13 @@ struct tomoyo_number_group {
        struct tomoyo_number_union number;
 };
 
+/* Structure for "address_group" directive. */
+struct tomoyo_address_group {
+       struct tomoyo_acl_head head;
+       /* Structure for holding an IP address. */
+       struct tomoyo_ipaddr_union address;
+};
+
 /* Subset of "struct stat". Used by conditional ACL and audit logs. */
 struct tomoyo_mini_stat {
        uid_t uid;
@@ -520,6 +615,7 @@ struct tomoyo_execve {
        struct tomoyo_request_info r;
        struct tomoyo_obj_info obj;
        struct linux_binprm *bprm;
+       const struct tomoyo_path_info *transition;
        /* For dumping argv[] and envp[]. */
        struct tomoyo_page_dump dump;
        /* For temporary use. */
@@ -554,6 +650,8 @@ struct tomoyo_condition {
        u16 names_count; /* Number of "struct tomoyo_name_union names". */
        u16 argc; /* Number of "struct tomoyo_argv". */
        u16 envc; /* Number of "struct tomoyo_envp". */
+       u8 grant_log; /* One of values in "enum tomoyo_grant_log". */
+       const struct tomoyo_path_info *transit; /* Maybe NULL. */
        /*
         * struct tomoyo_condition_element condition[condc];
         * struct tomoyo_number_union values[numbers_count];
@@ -586,6 +684,15 @@ struct tomoyo_domain_info {
        atomic_t users; /* Number of referring credentials. */
 };
 
+/*
+ * Structure for "task manual_domain_transition" directive.
+ */
+struct tomoyo_task_acl {
+       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */
+       /* Pointer to domainname. */
+       const struct tomoyo_path_info *domainname;
+};
+
 /*
  * Structure for "file execute", "file read", "file write", "file append",
  * "file unlink", "file getattr", "file rmdir", "file truncate",
@@ -638,6 +745,29 @@ struct tomoyo_mount_acl {
        struct tomoyo_number_union flags;
 };
 
+/* Structure for "misc env" directive in domain policy. */
+struct tomoyo_env_acl {
+       struct tomoyo_acl_info head;        /* type = TOMOYO_TYPE_ENV_ACL  */
+       const struct tomoyo_path_info *env; /* environment variable */
+};
+
+/* Structure for "network inet" directive. */
+struct tomoyo_inet_acl {
+       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_INET_ACL */
+       u8 protocol;
+       u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
+       struct tomoyo_ipaddr_union address;
+       struct tomoyo_number_union port;
+};
+
+/* Structure for "network unix" directive. */
+struct tomoyo_unix_acl {
+       struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_UNIX_ACL */
+       u8 protocol;
+       u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
+       struct tomoyo_name_union name;
+};
+
 /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
 struct tomoyo_acl_param {
        char *data;
@@ -773,7 +903,7 @@ struct tomoyo_policy_namespace {
        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. */
+       /* Profile version. Currently only 20110903 is defined. */
        unsigned int profile_version;
        /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
        const char *name;
@@ -781,6 +911,8 @@ struct tomoyo_policy_namespace {
 
 /********** Function prototypes. **********/
 
+bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
+                                 const struct tomoyo_group *group);
 bool tomoyo_compare_number_union(const unsigned long value,
                                 const struct tomoyo_number_union *ptr);
 bool tomoyo_condition(struct tomoyo_request_info *r,
@@ -796,6 +928,8 @@ bool tomoyo_memory_ok(void *ptr);
 bool tomoyo_number_matches_group(const unsigned long min,
                                 const unsigned long max,
                                 const struct tomoyo_group *group);
+bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
+                              struct tomoyo_ipaddr_union *ptr);
 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,
@@ -805,6 +939,7 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
 bool tomoyo_permstr(const char *string, const char *keyword);
 bool tomoyo_str_starts(char **src, const char *find);
 char *tomoyo_encode(const char *str);
+char *tomoyo_encode2(const char *str, int str_len);
 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);
@@ -814,12 +949,17 @@ 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_domainname
+(struct tomoyo_acl_param *param);
 const struct tomoyo_path_info *tomoyo_get_name(const char *name);
 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_close_control(struct tomoyo_io_buffer *head);
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env);
+int tomoyo_execute_permission(struct tomoyo_request_info *r,
+                             const struct tomoyo_path_info *filename);
 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);
@@ -842,6 +982,13 @@ 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_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+                                 int addr_len);
+int tomoyo_socket_connect_permission(struct socket *sock,
+                                    struct sockaddr *addr, int addr_len);
+int tomoyo_socket_listen_permission(struct socket *sock);
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
+                                    int size);
 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,
@@ -860,8 +1007,11 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
 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_misc(struct tomoyo_acl_param *param);
+int tomoyo_write_inet_network(struct tomoyo_acl_param *param);
 int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
                                    const u8 type);
+int tomoyo_write_unix_network(struct tomoyo_acl_param *param);
 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,
@@ -897,6 +1047,8 @@ 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_ip(char *buf, const unsigned int size,
+                    const struct tomoyo_ipaddr_union *ptr);
 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);
@@ -919,6 +1071,8 @@ 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 char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX];
+extern const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_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];
@@ -1097,6 +1251,21 @@ static inline bool tomoyo_same_number_union
                a->value_type[1] == b->value_type[1];
 }
 
+/**
+ * tomoyo_same_ipaddr_union - Check for duplicated "struct tomoyo_ipaddr_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_ipaddr_union".
+ * @b: Pointer to "struct tomoyo_ipaddr_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_ipaddr_union
+(const struct tomoyo_ipaddr_union *a, const struct tomoyo_ipaddr_union *b)
+{
+       return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group &&
+               a->is_ipv6 == b->is_ipv6;
+}
+
 /**
  * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
  *
index 8a05f71eaf6744a71116f4afe39f9a4a8fb23074..b854959c0fd4803c34b2a6bde0bdc3f474847bd8 100644 (file)
@@ -348,6 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
                a->numbers_count == b->numbers_count &&
                a->names_count == b->names_count &&
                a->argc == b->argc && a->envc == b->envc &&
+               a->grant_log == b->grant_log && a->transit == b->transit &&
                !memcmp(a + 1, b + 1, a->size - sizeof(*a));
 }
 
@@ -427,6 +428,46 @@ out:
        return entry;
 }
 
+/**
+ * tomoyo_get_transit_preference - Parse domain transition preference for execve().
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @e:     Pointer to "struct tomoyo_condition".
+ *
+ * Returns the condition string part.
+ */
+static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param,
+                                          struct tomoyo_condition *e)
+{
+       char * const pos = param->data;
+       bool flag;
+       if (*pos == '<') {
+               e->transit = tomoyo_get_domainname(param);
+               goto done;
+       }
+       {
+               char *cp = strchr(pos, ' ');
+               if (cp)
+                       *cp = '\0';
+               flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") ||
+                       !strcmp(pos, "initialize") || !strcmp(pos, "reset") ||
+                       !strcmp(pos, "child") || !strcmp(pos, "parent");
+               if (cp)
+                       *cp = ' ';
+       }
+       if (!flag)
+               return pos;
+       e->transit = tomoyo_get_name(tomoyo_read_token(param));
+done:
+       if (e->transit)
+               return param->data;
+       /*
+        * Return a bad read-only condition string that will let
+        * tomoyo_get_condition() return NULL.
+        */
+       return "/";
+}
+
 /**
  * tomoyo_get_condition - Parse condition part.
  *
@@ -443,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
        struct tomoyo_argv *argv = NULL;
        struct tomoyo_envp *envp = NULL;
        struct tomoyo_condition e = { };
-       char * const start_of_string = param->data;
+       char * const start_of_string =
+               tomoyo_get_transit_preference(param, &e);
        char * const end_of_string = start_of_string + strlen(start_of_string);
        char *pos;
 rerun:
@@ -486,6 +528,20 @@ rerun:
                        goto out;
                dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
                        is_not ? "!" : "", right_word);
+               if (!strcmp(left_word, "grant_log")) {
+                       if (entry) {
+                               if (is_not ||
+                                   entry->grant_log != TOMOYO_GRANTLOG_AUTO)
+                                       goto out;
+                               else if (!strcmp(right_word, "yes"))
+                                       entry->grant_log = TOMOYO_GRANTLOG_YES;
+                               else if (!strcmp(right_word, "no"))
+                                       entry->grant_log = TOMOYO_GRANTLOG_NO;
+                               else
+                                       goto out;
+                       }
+                       continue;
+               }
                if (!strncmp(left_word, "exec.argv[", 10)) {
                        if (!argv) {
                                e.argc++;
@@ -593,8 +649,9 @@ store_value:
                + e.envc * sizeof(struct tomoyo_envp);
        entry = kzalloc(e.size, GFP_NOFS);
        if (!entry)
-               return NULL;
+               goto out2;
        *entry = e;
+       e.transit = NULL;
        condp = (struct tomoyo_condition_element *) (entry + 1);
        numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
        names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
@@ -621,6 +678,8 @@ out:
                tomoyo_del_condition(&entry->head.list);
                kfree(entry);
        }
+out2:
+       tomoyo_put_name(e.transit);
        return NULL;
 }
 
index cd0f92d88bb43a35c99230c52effb311da0d5213..860390ee1fbe9548dae129d34b1904befa04ccb7 100644 (file)
@@ -102,6 +102,15 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
                new_entry->cond = tomoyo_get_condition(param);
                if (!new_entry->cond)
                        return -EINVAL;
+               /*
+                * Domain transition preference is allowed for only
+                * "file execute" entries.
+                */
+               if (new_entry->cond->transit &&
+                   !(new_entry->type == TOMOYO_TYPE_PATH_ACL &&
+                     container_of(new_entry, struct tomoyo_path_acl, head)
+                     ->perm == 1 << TOMOYO_TYPE_EXECUTE))
+                       goto out;
        }
        if (mutex_lock_interruptible(&tomoyo_policy_lock))
                goto out;
@@ -157,6 +166,7 @@ retry:
                        continue;
                if (!tomoyo_condition(r, ptr->cond))
                        continue;
+               r->matched_acl = ptr;
                r->granted = true;
                return;
        }
@@ -562,6 +572,92 @@ out:
        return entry;
 }
 
+/**
+ * tomoyo_environ - Check permission for environment variable names.
+ *
+ * @ee: Pointer to "struct tomoyo_execve".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_environ(struct tomoyo_execve *ee)
+{
+       struct tomoyo_request_info *r = &ee->r;
+       struct linux_binprm *bprm = ee->bprm;
+       /* env_page.data is allocated by tomoyo_dump_page(). */
+       struct tomoyo_page_dump env_page = { };
+       char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
+       int arg_len = 0;
+       unsigned long pos = bprm->p;
+       int offset = pos % PAGE_SIZE;
+       int argv_count = bprm->argc;
+       int envp_count = bprm->envc;
+       int error = -ENOMEM;
+
+       ee->r.type = TOMOYO_MAC_ENVIRON;
+       ee->r.profile = r->domain->profile;
+       ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile,
+                                    TOMOYO_MAC_ENVIRON);
+       if (!r->mode || !envp_count)
+               return 0;
+       arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+       if (!arg_ptr)
+               goto out;
+       while (error == -ENOMEM) {
+               if (!tomoyo_dump_page(bprm, pos, &env_page))
+                       goto out;
+               pos += PAGE_SIZE - offset;
+               /* Read. */
+               while (argv_count && offset < PAGE_SIZE) {
+                       if (!env_page.data[offset++])
+                               argv_count--;
+               }
+               if (argv_count) {
+                       offset = 0;
+                       continue;
+               }
+               while (offset < PAGE_SIZE) {
+                       const unsigned char c = env_page.data[offset++];
+
+                       if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+                               if (c == '=') {
+                                       arg_ptr[arg_len++] = '\0';
+                               } else 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;
+                       if (tomoyo_env_perm(r, arg_ptr)) {
+                               error = -EPERM;
+                               break;
+                       }
+                       if (!--envp_count) {
+                               error = 0;
+                               break;
+                       }
+                       arg_len = 0;
+               }
+               offset = 0;
+       }
+out:
+       if (r->mode != TOMOYO_CONFIG_ENFORCING)
+               error = 0;
+       kfree(env_page.data);
+       kfree(arg_ptr);
+       return error;
+}
+
 /**
  * tomoyo_find_next_domain - Find a domain.
  *
@@ -577,10 +673,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        struct tomoyo_domain_info *domain = NULL;
        const char *original_name = bprm->filename;
        int retval = -ENOMEM;
-       bool need_kfree = false;
        bool reject_on_transition_failure = false;
-       struct tomoyo_path_info rn = { }; /* real name */
+       const struct tomoyo_path_info *candidate;
+       struct tomoyo_path_info exename;
        struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
+
        if (!ee)
                return -ENOMEM;
        ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
@@ -594,40 +691,32 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        ee->bprm = bprm;
        ee->r.obj = &ee->obj;
        ee->obj.path1 = bprm->file->f_path;
- retry:
-       if (need_kfree) {
-               kfree(rn.name);
-               need_kfree = false;
-       }
        /* Get symlink's pathname of program. */
        retval = -ENOENT;
-       rn.name = tomoyo_realpath_nofollow(original_name);
-       if (!rn.name)
+       exename.name = tomoyo_realpath_nofollow(original_name);
+       if (!exename.name)
                goto out;
-       tomoyo_fill_path_info(&rn);
-       need_kfree = true;
-
+       tomoyo_fill_path_info(&exename);
+retry:
        /* Check 'aggregator' directive. */
        {
                struct tomoyo_aggregator *ptr;
                struct list_head *list =
                        &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
                /* Check 'aggregator' directive. */
+               candidate = &exename;
                list_for_each_entry_rcu(ptr, list, head.list) {
                        if (ptr->head.is_deleted ||
-                           !tomoyo_path_matches_pattern(&rn,
+                           !tomoyo_path_matches_pattern(&exename,
                                                         ptr->original_name))
                                continue;
-                       kfree(rn.name);
-                       need_kfree = false;
-                       /* This is OK because it is read only. */
-                       rn = *ptr->aggregated_name;
+                       candidate = ptr->aggregated_name;
                        break;
                }
        }
 
        /* Check execute permission. */
-       retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn);
+       retval = tomoyo_execute_permission(&ee->r, candidate);
        if (retval == TOMOYO_RETRY_REQUEST)
                goto retry;
        if (retval < 0)
@@ -638,20 +727,51 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
         * wildcard) rather than the pathname passed to execve()
         * (which never contains wildcard).
         */
-       if (ee->r.param.path.matched_path) {
-               if (need_kfree)
-                       kfree(rn.name);
-               need_kfree = false;
-               /* This is OK because it is read only. */
-               rn = *ee->r.param.path.matched_path;
-       }
+       if (ee->r.param.path.matched_path)
+               candidate = ee->r.param.path.matched_path;
 
-       /* Calculate domain to transit to. */
+       /*
+        * Check for domain transition preference if "file execute" matched.
+        * If preference is given, make do_execve() fail if domain transition
+        * has failed, for domain transition preference should be used with
+        * destination domain defined.
+        */
+       if (ee->transition) {
+               const char *domainname = ee->transition->name;
+               reject_on_transition_failure = true;
+               if (!strcmp(domainname, "keep"))
+                       goto force_keep_domain;
+               if (!strcmp(domainname, "child"))
+                       goto force_child_domain;
+               if (!strcmp(domainname, "reset"))
+                       goto force_reset_domain;
+               if (!strcmp(domainname, "initialize"))
+                       goto force_initialize_domain;
+               if (!strcmp(domainname, "parent")) {
+                       char *cp;
+                       strncpy(ee->tmp, old_domain->domainname->name,
+                               TOMOYO_EXEC_TMPSIZE - 1);
+                       cp = strrchr(ee->tmp, ' ');
+                       if (cp)
+                               *cp = '\0';
+               } else if (*domainname == '<')
+                       strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1);
+               else
+                       snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+                                old_domain->domainname->name, domainname);
+               goto force_jump_domain;
+       }
+       /*
+        * No domain transition preference specified.
+        * Calculate domain to transit to.
+        */
        switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
-                                      &rn)) {
+                                      candidate)) {
        case TOMOYO_TRANSITION_CONTROL_RESET:
+force_reset_domain:
                /* Transit to the root of specified namespace. */
-               snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
+               snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
+                        candidate->name);
                /*
                 * Make do_execve() fail if domain transition across namespaces
                 * has failed.
@@ -659,11 +779,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
                reject_on_transition_failure = true;
                break;
        case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
+force_initialize_domain:
                /* Transit to the child of current namespace's root. */
                snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
-                        old_domain->ns->name, rn.name);
+                        old_domain->ns->name, candidate->name);
                break;
        case TOMOYO_TRANSITION_CONTROL_KEEP:
+force_keep_domain:
                /* Keep current domain. */
                domain = old_domain;
                break;
@@ -677,13 +799,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
                         * before /sbin/init.
                         */
                        domain = old_domain;
-               } else {
-                       /* Normal domain transition. */
-                       snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
-                                old_domain->domainname->name, rn.name);
+                       break;
                }
+force_child_domain:
+               /* Normal domain transition. */
+               snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+                        old_domain->domainname->name, candidate->name);
                break;
        }
+force_jump_domain:
        if (!domain)
                domain = tomoyo_assign_domain(ee->tmp, true);
        if (domain)
@@ -711,8 +835,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        /* Update reference count on "struct tomoyo_domain_info". */
        atomic_inc(&domain->users);
        bprm->cred->security = domain;
-       if (need_kfree)
-               kfree(rn.name);
+       kfree(exename.name);
+       if (!retval) {
+               ee->r.domain = domain;
+               retval = tomoyo_environ(ee);
+       }
        kfree(ee->tmp);
        kfree(ee->dump.data);
        kfree(ee);
@@ -732,7 +859,8 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
                      struct tomoyo_page_dump *dump)
 {
        struct page *page;
-       /* dump->data is released by tomoyo_finish_execve(). */
+
+       /* dump->data is released by tomoyo_find_next_domain(). */
        if (!dump->data) {
                dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
                if (!dump->data)
@@ -753,6 +881,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
                 * So do I.
                 */
                char *kaddr = kmap_atomic(page, KM_USER0);
+
                dump->page = page;
                memcpy(dump->data + offset, kaddr + offset,
                       PAGE_SIZE - offset);
diff --git a/security/tomoyo/environ.c b/security/tomoyo/environ.c
new file mode 100644 (file)
index 0000000..ad4c6e1
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * security/tomoyo/environ.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+
+/**
+ * tomoyo_check_env_acl - Check permission for environment variable's name.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_env_acl(struct tomoyo_request_info *r,
+                                const struct tomoyo_acl_info *ptr)
+{
+       const struct tomoyo_env_acl *acl =
+               container_of(ptr, typeof(*acl), head);
+
+       return tomoyo_path_matches_pattern(r->param.environ.name, acl->env);
+}
+
+/**
+ * tomoyo_audit_env_log - Audit environment variable name log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_env_log(struct tomoyo_request_info *r)
+{
+       return tomoyo_supervisor(r, "misc env %s\n",
+                                r->param.environ.name->name);
+}
+
+/**
+ * tomoyo_env_perm - Check permission for environment variable's name.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @env: The name of environment variable.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env)
+{
+       struct tomoyo_path_info environ;
+       int error;
+
+       if (!env || !*env)
+               return 0;
+       environ.name = env;
+       tomoyo_fill_path_info(&environ);
+       r->param_type = TOMOYO_TYPE_ENV_ACL;
+       r->param.environ.name = &environ;
+       do {
+               tomoyo_check_acl(r, tomoyo_check_env_acl);
+               error = tomoyo_audit_env_log(r);
+       } while (error == TOMOYO_RETRY_REQUEST);
+       return error;
+}
+
+/**
+ * tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a,
+                               const struct tomoyo_acl_info *b)
+{
+       const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head);
+       const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head);
+
+       return p1->env == p2->env;
+}
+
+/**
+ * tomoyo_write_env - Write "struct tomoyo_env_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_env(struct tomoyo_acl_param *param)
+{
+       struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL };
+       int error = -ENOMEM;
+       const char *data = tomoyo_read_token(param);
+
+       if (!tomoyo_correct_word(data) || strchr(data, '='))
+               return -EINVAL;
+       e.env = tomoyo_get_name(data);
+       if (!e.env)
+               return error;
+       error = tomoyo_update_domain(&e.head, sizeof(e), param,
+                                 tomoyo_same_env_acl, NULL);
+       tomoyo_put_name(e.env);
+       return error;
+}
+
+/**
+ * tomoyo_write_misc - Update environment variable list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_misc(struct tomoyo_acl_param *param)
+{
+       if (tomoyo_str_starts(&param->data, "env "))
+               return tomoyo_write_env(param);
+       return -EINVAL;
+}
index 743c35f5084a1125c63e04043bdfb71bb8128651..b280c1bd652d409f1def2758545afdc165dbbb37 100644 (file)
@@ -570,15 +570,41 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
        do {
                tomoyo_check_acl(r, tomoyo_check_path_acl);
                error = tomoyo_audit_path_log(r);
-               /*
-                * Do not retry for execute request, for alias may have
-                * changed.
-                */
-       } while (error == TOMOYO_RETRY_REQUEST &&
-                operation != TOMOYO_TYPE_EXECUTE);
+       } while (error == TOMOYO_RETRY_REQUEST);
        return error;
 }
 
+/**
+ * tomoyo_execute_permission - Check permission for execute operation.
+ *
+ * @r:         Pointer to "struct tomoyo_request_info".
+ * @filename:  Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_execute_permission(struct tomoyo_request_info *r,
+                             const struct tomoyo_path_info *filename)
+{
+       /*
+        * Unlike other permission checks, this check is done regardless of
+        * profile mode settings in order to check for domain transition
+        * preference.
+        */
+       r->type = TOMOYO_MAC_FILE_EXECUTE;
+       r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);
+       r->param_type = TOMOYO_TYPE_PATH_ACL;
+       r->param.path.filename = filename;
+       r->param.path.operation = TOMOYO_TYPE_EXECUTE;
+       tomoyo_check_acl(r, tomoyo_check_path_acl);
+       r->ee->transition = r->matched_acl && r->matched_acl->cond ?
+               r->matched_acl->cond->transit : NULL;
+       if (r->mode != TOMOYO_CONFIG_DISABLED)
+               return tomoyo_audit_path_log(r);
+       return 0;
+}
+
 /**
  * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
  *
index ae135fbbbe955b922c0fe3dbe6d8d158dc6fa9d1..7747ceb9a2214350c43aebaad5ef5da2c11cccbb 100644 (file)
@@ -16,6 +16,7 @@ static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
 /* Size of an element. */
 static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
        [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
+       [TOMOYO_ID_ADDRESS_GROUP] = sizeof(struct tomoyo_address_group),
        [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
        [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
        [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
@@ -36,6 +37,9 @@ static const u8 tomoyo_acl_size[] = {
        [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
        [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
        [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
+       [TOMOYO_TYPE_INET_ACL] = sizeof(struct tomoyo_inet_acl),
+       [TOMOYO_TYPE_UNIX_ACL] = sizeof(struct tomoyo_unix_acl),
+       [TOMOYO_TYPE_ENV_ACL] = sizeof(struct tomoyo_env_acl),
 };
 
 /**
@@ -293,6 +297,31 @@ static void tomoyo_del_acl(struct list_head *element)
                        tomoyo_put_number_union(&entry->flags);
                }
                break;
+       case TOMOYO_TYPE_ENV_ACL:
+               {
+                       struct tomoyo_env_acl *entry =
+                               container_of(acl, typeof(*entry), head);
+
+                       tomoyo_put_name(entry->env);
+               }
+               break;
+       case TOMOYO_TYPE_INET_ACL:
+               {
+                       struct tomoyo_inet_acl *entry =
+                               container_of(acl, typeof(*entry), head);
+
+                       tomoyo_put_group(entry->address.group);
+                       tomoyo_put_number_union(&entry->port);
+               }
+               break;
+       case TOMOYO_TYPE_UNIX_ACL:
+               {
+                       struct tomoyo_unix_acl *entry =
+                               container_of(acl, typeof(*entry), head);
+
+                       tomoyo_put_name_union(&entry->name);
+               }
+               break;
        }
 }
 
@@ -421,6 +450,18 @@ static void tomoyo_del_group(struct list_head *element)
        tomoyo_put_name(group->group_name);
 }
 
+/**
+ * tomoyo_del_address_group - Delete members in "struct tomoyo_address_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_del_address_group(struct list_head *element)
+{
+       /* Nothing to do. */
+}
+
 /**
  * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group".
  *
@@ -518,9 +559,12 @@ static void tomoyo_collect_entry(void)
                        case 0:
                                id = TOMOYO_ID_PATH_GROUP;
                                break;
-                       default:
+                       case 1:
                                id = TOMOYO_ID_NUMBER_GROUP;
                                break;
+                       default:
+                               id = TOMOYO_ID_ADDRESS_GROUP;
+                               break;
                        }
                        list_for_each_entry(group, list, head.list) {
                                if (!tomoyo_collect_member
@@ -625,6 +669,9 @@ static bool tomoyo_kfree_entry(void)
                case TOMOYO_ID_PATH_GROUP:
                        tomoyo_del_path_group(element);
                        break;
+               case TOMOYO_ID_ADDRESS_GROUP:
+                       tomoyo_del_address_group(element);
+                       break;
                case TOMOYO_ID_GROUP:
                        tomoyo_del_group(element);
                        break;
@@ -660,7 +707,7 @@ static int tomoyo_gc_thread(void *unused)
        static DEFINE_MUTEX(tomoyo_gc_mutex);
        if (!mutex_trylock(&tomoyo_gc_mutex))
                goto out;
-       daemonize("GC for TOMOYO");
+
        do {
                tomoyo_collect_entry();
                if (list_empty(&tomoyo_gc_list))
index 5fb0e1298400e040bcc724f1196a4c9e02154f4c..50092534ec54083d0af18d852e9e0dc0dc9c154f 100644 (file)
@@ -42,7 +42,26 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
 }
 
 /**
- * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ * tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" 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_address_group(const struct tomoyo_acl_head *a,
+                                     const struct tomoyo_acl_head *b)
+{
+       const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1),
+                                                            head);
+       const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2),
+                                                            head);
+
+       return tomoyo_same_ipaddr_union(&p1->address, &p2->address);
+}
+
+/**
+ * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
  *
  * @param: Pointer to "struct tomoyo_acl_param".
  * @type:  Type of this group.
@@ -77,6 +96,14 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
                 * tomoyo_put_number_union() is not needed because
                 * param->data[0] != '@'.
                 */
+       } else {
+               struct tomoyo_address_group e = { };
+
+               if (param->data[0] == '@' ||
+                   !tomoyo_parse_ipaddr_union(param, &e.address))
+                       goto out;
+               error = tomoyo_update_policy(&e.head, sizeof(e), param,
+                                            tomoyo_same_address_group);
        }
 out:
        tomoyo_put_group(group);
@@ -137,3 +164,35 @@ bool tomoyo_number_matches_group(const unsigned long min,
        }
        return matched;
 }
+
+/**
+ * tomoyo_address_matches_group - Check whether the given address matches members of the given address group.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @address: An IPv4 or IPv6 address.
+ * @group:   Pointer to "struct tomoyo_address_group".
+ *
+ * Returns true if @address matches addresses in @group group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
+                                 const struct tomoyo_group *group)
+{
+       struct tomoyo_address_group *member;
+       bool matched = false;
+       const u8 size = is_ipv6 ? 16 : 4;
+
+       list_for_each_entry_rcu(member, &group->member_list, head.list) {
+               if (member->head.is_deleted)
+                       continue;
+               if (member->address.is_ipv6 != is_ipv6)
+                       continue;
+               if (memcmp(&member->address.ip[0], address, size) > 0 ||
+                   memcmp(address, &member->address.ip[1], size) > 0)
+                       continue;
+               matched = true;
+               break;
+       }
+       return matched;
+}
diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c
new file mode 100644 (file)
index 0000000..9752771
--- /dev/null
@@ -0,0 +1,771 @@
+/*
+ * security/tomoyo/network.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/* Structure for holding inet domain socket's address. */
+struct tomoyo_inet_addr_info {
+       __be16 port;           /* In network byte order. */
+       const __be32 *address; /* In network byte order. */
+       bool is_ipv6;
+};
+
+/* Structure for holding unix domain socket's address. */
+struct tomoyo_unix_addr_info {
+       u8 *addr; /* This may not be '\0' terminated string. */
+       unsigned int addr_len;
+};
+
+/* Structure for holding socket address. */
+struct tomoyo_addr_info {
+       u8 protocol;
+       u8 operation;
+       struct tomoyo_inet_addr_info inet;
+       struct tomoyo_unix_addr_info unix0;
+};
+
+/* String table for socket's protocols. */
+const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX] = {
+       [SOCK_STREAM]    = "stream",
+       [SOCK_DGRAM]     = "dgram",
+       [SOCK_RAW]       = "raw",
+       [SOCK_SEQPACKET] = "seqpacket",
+       [0] = " ", /* Dummy for avoiding NULL pointer dereference. */
+       [4] = " ", /* Dummy for avoiding NULL pointer dereference. */
+};
+
+/**
+ * tomoyo_parse_ipaddr_union - Parse an IP address.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_ipaddr_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
+                              struct tomoyo_ipaddr_union *ptr)
+{
+       u8 * const min = ptr->ip[0].in6_u.u6_addr8;
+       u8 * const max = ptr->ip[1].in6_u.u6_addr8;
+       char *address = tomoyo_read_token(param);
+       const char *end;
+
+       if (!strchr(address, ':') &&
+           in4_pton(address, -1, min, '-', &end) > 0) {
+               ptr->is_ipv6 = false;
+               if (!*end)
+                       ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0];
+               else if (*end++ != '-' ||
+                        in4_pton(end, -1, max, '\0', &end) <= 0 || *end)
+                       return false;
+               return true;
+       }
+       if (in6_pton(address, -1, min, '-', &end) > 0) {
+               ptr->is_ipv6 = true;
+               if (!*end)
+                       memmove(max, min, sizeof(u16) * 8);
+               else if (*end++ != '-' ||
+                        in6_pton(end, -1, max, '\0', &end) <= 0 || *end)
+                       return false;
+               return true;
+       }
+       return false;
+}
+
+/**
+ * tomoyo_print_ipv4 - Print an IPv4 address.
+ *
+ * @buffer:     Buffer to write to.
+ * @buffer_len: Size of @buffer.
+ * @min_ip:     Pointer to __be32.
+ * @max_ip:     Pointer to __be32.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_ipv4(char *buffer, const unsigned int buffer_len,
+                             const __be32 *min_ip, const __be32 *max_ip)
+{
+       snprintf(buffer, buffer_len, "%pI4%c%pI4", min_ip,
+                *min_ip == *max_ip ? '\0' : '-', max_ip);
+}
+
+/**
+ * tomoyo_print_ipv6 - Print an IPv6 address.
+ *
+ * @buffer:     Buffer to write to.
+ * @buffer_len: Size of @buffer.
+ * @min_ip:     Pointer to "struct in6_addr".
+ * @max_ip:     Pointer to "struct in6_addr".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_ipv6(char *buffer, const unsigned int buffer_len,
+                             const struct in6_addr *min_ip,
+                             const struct in6_addr *max_ip)
+{
+       snprintf(buffer, buffer_len, "%pI6c%c%pI6c", min_ip,
+                !memcmp(min_ip, max_ip, 16) ? '\0' : '-', max_ip);
+}
+
+/**
+ * tomoyo_print_ip - Print an IP address.
+ *
+ * @buf:  Buffer to write to.
+ * @size: Size of @buf.
+ * @ptr:  Pointer to "struct ipaddr_union".
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ip(char *buf, const unsigned int size,
+                    const struct tomoyo_ipaddr_union *ptr)
+{
+       if (ptr->is_ipv6)
+               tomoyo_print_ipv6(buf, size, &ptr->ip[0], &ptr->ip[1]);
+       else
+               tomoyo_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0],
+                                 &ptr->ip[1].s6_addr32[0]);
+}
+
+/*
+ * Mapping table from "enum tomoyo_network_acl_index" to
+ * "enum tomoyo_mac_index" for inet domain socket.
+ */
+static const u8 tomoyo_inet2mac
+[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
+       [SOCK_STREAM] = {
+               [TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
+               [TOMOYO_NETWORK_LISTEN]  =
+               TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
+               [TOMOYO_NETWORK_CONNECT] =
+               TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
+       },
+       [SOCK_DGRAM] = {
+               [TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
+               [TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
+       },
+       [SOCK_RAW]    = {
+               [TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_RAW_BIND,
+               [TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_RAW_SEND,
+       },
+};
+
+/*
+ * Mapping table from "enum tomoyo_network_acl_index" to
+ * "enum tomoyo_mac_index" for unix domain socket.
+ */
+static const u8 tomoyo_unix2mac
+[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
+       [SOCK_STREAM] = {
+               [TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
+               [TOMOYO_NETWORK_LISTEN]  =
+               TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
+               [TOMOYO_NETWORK_CONNECT] =
+               TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
+       },
+       [SOCK_DGRAM] = {
+               [TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
+               [TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
+       },
+       [SOCK_SEQPACKET] = {
+               [TOMOYO_NETWORK_BIND]    =
+               TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+               [TOMOYO_NETWORK_LISTEN]  =
+               TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+               [TOMOYO_NETWORK_CONNECT] =
+               TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+       },
+};
+
+/**
+ * tomoyo_same_inet_acl - Check for duplicated "struct tomoyo_inet_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool tomoyo_same_inet_acl(const struct tomoyo_acl_info *a,
+                                const struct tomoyo_acl_info *b)
+{
+       const struct tomoyo_inet_acl *p1 = container_of(a, typeof(*p1), head);
+       const struct tomoyo_inet_acl *p2 = container_of(b, typeof(*p2), head);
+
+       return p1->protocol == p2->protocol &&
+               tomoyo_same_ipaddr_union(&p1->address, &p2->address) &&
+               tomoyo_same_number_union(&p1->port, &p2->port);
+}
+
+/**
+ * tomoyo_same_unix_acl - Check for duplicated "struct tomoyo_unix_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool tomoyo_same_unix_acl(const struct tomoyo_acl_info *a,
+                                const struct tomoyo_acl_info *b)
+{
+       const struct tomoyo_unix_acl *p1 = container_of(a, typeof(*p1), head);
+       const struct tomoyo_unix_acl *p2 = container_of(b, typeof(*p2), head);
+
+       return p1->protocol == p2->protocol &&
+               tomoyo_same_name_union(&p1->name, &p2->name);
+}
+
+/**
+ * tomoyo_merge_inet_acl - Merge duplicated "struct tomoyo_inet_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool tomoyo_merge_inet_acl(struct tomoyo_acl_info *a,
+                                 struct tomoyo_acl_info *b,
+                                 const bool is_delete)
+{
+       u8 * const a_perm =
+               &container_of(a, struct tomoyo_inet_acl, head)->perm;
+       u8 perm = *a_perm;
+       const u8 b_perm = container_of(b, struct tomoyo_inet_acl, head)->perm;
+
+       if (is_delete)
+               perm &= ~b_perm;
+       else
+               perm |= b_perm;
+       *a_perm = perm;
+       return !perm;
+}
+
+/**
+ * tomoyo_merge_unix_acl - Merge duplicated "struct tomoyo_unix_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool tomoyo_merge_unix_acl(struct tomoyo_acl_info *a,
+                                 struct tomoyo_acl_info *b,
+                                 const bool is_delete)
+{
+       u8 * const a_perm =
+               &container_of(a, struct tomoyo_unix_acl, head)->perm;
+       u8 perm = *a_perm;
+       const u8 b_perm = container_of(b, struct tomoyo_unix_acl, head)->perm;
+
+       if (is_delete)
+               perm &= ~b_perm;
+       else
+               perm |= b_perm;
+       *a_perm = perm;
+       return !perm;
+}
+
+/**
+ * tomoyo_write_inet_network - Write "struct tomoyo_inet_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_inet_network(struct tomoyo_acl_param *param)
+{
+       struct tomoyo_inet_acl e = { .head.type = TOMOYO_TYPE_INET_ACL };
+       int error = -EINVAL;
+       u8 type;
+       const char *protocol = tomoyo_read_token(param);
+       const char *operation = tomoyo_read_token(param);
+
+       for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
+               if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
+                       break;
+       for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
+               if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
+                       e.perm |= 1 << type;
+       if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
+               return -EINVAL;
+       if (param->data[0] == '@') {
+               param->data++;
+               e.address.group =
+                       tomoyo_get_group(param, TOMOYO_ADDRESS_GROUP);
+               if (!e.address.group)
+                       return -ENOMEM;
+       } else {
+               if (!tomoyo_parse_ipaddr_union(param, &e.address))
+                       goto out;
+       }
+       if (!tomoyo_parse_number_union(param, &e.port) ||
+           e.port.values[1] > 65535)
+               goto out;
+       error = tomoyo_update_domain(&e.head, sizeof(e), param,
+                                    tomoyo_same_inet_acl,
+                                    tomoyo_merge_inet_acl);
+out:
+       tomoyo_put_group(e.address.group);
+       tomoyo_put_number_union(&e.port);
+       return error;
+}
+
+/**
+ * tomoyo_write_unix_network - Write "struct tomoyo_unix_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_unix_network(struct tomoyo_acl_param *param)
+{
+       struct tomoyo_unix_acl e = { .head.type = TOMOYO_TYPE_UNIX_ACL };
+       int error;
+       u8 type;
+       const char *protocol = tomoyo_read_token(param);
+       const char *operation = tomoyo_read_token(param);
+
+       for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
+               if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
+                       break;
+       for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
+               if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
+                       e.perm |= 1 << type;
+       if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
+               return -EINVAL;
+       if (!tomoyo_parse_name_union(param, &e.name))
+               return -EINVAL;
+       error = tomoyo_update_domain(&e.head, sizeof(e), param,
+                                    tomoyo_same_unix_acl,
+                                    tomoyo_merge_unix_acl);
+       tomoyo_put_name_union(&e.name);
+       return error;
+}
+
+/**
+ * tomoyo_audit_net_log - Audit network log.
+ *
+ * @r:         Pointer to "struct tomoyo_request_info".
+ * @family:    Name of socket family ("inet" or "unix").
+ * @protocol:  Name of protocol in @family.
+ * @operation: Name of socket operation.
+ * @address:   Name of address.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_net_log(struct tomoyo_request_info *r,
+                               const char *family, const u8 protocol,
+                               const u8 operation, const char *address)
+{
+       return tomoyo_supervisor(r, "network %s %s %s %s\n", family,
+                                tomoyo_proto_keyword[protocol],
+                                tomoyo_socket_keyword[operation], address);
+}
+
+/**
+ * tomoyo_audit_inet_log - Audit INET network log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_inet_log(struct tomoyo_request_info *r)
+{
+       char buf[128];
+       int len;
+       const __be32 *address = r->param.inet_network.address;
+
+       if (r->param.inet_network.is_ipv6)
+               tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *)
+                                 address, (const struct in6_addr *) address);
+       else
+               tomoyo_print_ipv4(buf, sizeof(buf), address, address);
+       len = strlen(buf);
+       snprintf(buf + len, sizeof(buf) - len, " %u",
+                r->param.inet_network.port);
+       return tomoyo_audit_net_log(r, "inet", r->param.inet_network.protocol,
+                                   r->param.inet_network.operation, buf);
+}
+
+/**
+ * tomoyo_audit_unix_log - Audit UNIX network log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_unix_log(struct tomoyo_request_info *r)
+{
+       return tomoyo_audit_net_log(r, "unix", r->param.unix_network.protocol,
+                                   r->param.unix_network.operation,
+                                   r->param.unix_network.address->name);
+}
+
+/**
+ * tomoyo_check_inet_acl - Check permission for inet domain socket operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_inet_acl(struct tomoyo_request_info *r,
+                                 const struct tomoyo_acl_info *ptr)
+{
+       const struct tomoyo_inet_acl *acl =
+               container_of(ptr, typeof(*acl), head);
+       const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4;
+
+       if (!(acl->perm & (1 << r->param.inet_network.operation)) ||
+           !tomoyo_compare_number_union(r->param.inet_network.port,
+                                        &acl->port))
+               return false;
+       if (acl->address.group)
+               return tomoyo_address_matches_group
+                       (r->param.inet_network.is_ipv6,
+                        r->param.inet_network.address, acl->address.group);
+       return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 &&
+               memcmp(&acl->address.ip[0],
+                      r->param.inet_network.address, size) <= 0 &&
+               memcmp(r->param.inet_network.address,
+                      &acl->address.ip[1], size) <= 0;
+}
+
+/**
+ * tomoyo_check_unix_acl - Check permission for unix domain socket operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_unix_acl(struct tomoyo_request_info *r,
+                                 const struct tomoyo_acl_info *ptr)
+{
+       const struct tomoyo_unix_acl *acl =
+               container_of(ptr, typeof(*acl), head);
+
+       return (acl->perm & (1 << r->param.unix_network.operation)) &&
+               tomoyo_compare_name_union(r->param.unix_network.address,
+                                         &acl->name);
+}
+
+/**
+ * tomoyo_inet_entry - Check permission for INET network operation.
+ *
+ * @address: Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_inet_entry(const struct tomoyo_addr_info *address)
+{
+       const int idx = tomoyo_read_lock();
+       struct tomoyo_request_info r;
+       int error = 0;
+       const u8 type = tomoyo_inet2mac[address->protocol][address->operation];
+
+       if (type && tomoyo_init_request_info(&r, NULL, type)
+           != TOMOYO_CONFIG_DISABLED) {
+               r.param_type = TOMOYO_TYPE_INET_ACL;
+               r.param.inet_network.protocol = address->protocol;
+               r.param.inet_network.operation = address->operation;
+               r.param.inet_network.is_ipv6 = address->inet.is_ipv6;
+               r.param.inet_network.address = address->inet.address;
+               r.param.inet_network.port = ntohs(address->inet.port);
+               do {
+                       tomoyo_check_acl(&r, tomoyo_check_inet_acl);
+                       error = tomoyo_audit_inet_log(&r);
+               } while (error == TOMOYO_RETRY_REQUEST);
+       }
+       tomoyo_read_unlock(idx);
+       return error;
+}
+
+/**
+ * tomoyo_check_inet_address - Check permission for inet domain socket's operation.
+ *
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ * @port:     Port number.
+ * @address:  Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_inet_address(const struct sockaddr *addr,
+                                    const unsigned int addr_len,
+                                    const u16 port,
+                                    struct tomoyo_addr_info *address)
+{
+       struct tomoyo_inet_addr_info *i = &address->inet;
+
+       switch (addr->sa_family) {
+       case AF_INET6:
+               if (addr_len < SIN6_LEN_RFC2133)
+                       goto skip;
+               i->is_ipv6 = true;
+               i->address = (__be32 *)
+                       ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr;
+               i->port = ((struct sockaddr_in6 *) addr)->sin6_port;
+               break;
+       case AF_INET:
+               if (addr_len < sizeof(struct sockaddr_in))
+                       goto skip;
+               i->is_ipv6 = false;
+               i->address = (__be32 *)
+                       &((struct sockaddr_in *) addr)->sin_addr;
+               i->port = ((struct sockaddr_in *) addr)->sin_port;
+               break;
+       default:
+               goto skip;
+       }
+       if (address->protocol == SOCK_RAW)
+               i->port = htons(port);
+       return tomoyo_inet_entry(address);
+skip:
+       return 0;
+}
+
+/**
+ * tomoyo_unix_entry - Check permission for UNIX network operation.
+ *
+ * @address: Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_unix_entry(const struct tomoyo_addr_info *address)
+{
+       const int idx = tomoyo_read_lock();
+       struct tomoyo_request_info r;
+       int error = 0;
+       const u8 type = tomoyo_unix2mac[address->protocol][address->operation];
+
+       if (type && tomoyo_init_request_info(&r, NULL, type)
+           != TOMOYO_CONFIG_DISABLED) {
+               char *buf = address->unix0.addr;
+               int len = address->unix0.addr_len - sizeof(sa_family_t);
+
+               if (len <= 0) {
+                       buf = "anonymous";
+                       len = 9;
+               } else if (buf[0]) {
+                       len = strnlen(buf, len);
+               }
+               buf = tomoyo_encode2(buf, len);
+               if (buf) {
+                       struct tomoyo_path_info addr;
+
+                       addr.name = buf;
+                       tomoyo_fill_path_info(&addr);
+                       r.param_type = TOMOYO_TYPE_UNIX_ACL;
+                       r.param.unix_network.protocol = address->protocol;
+                       r.param.unix_network.operation = address->operation;
+                       r.param.unix_network.address = &addr;
+                       do {
+                               tomoyo_check_acl(&r, tomoyo_check_unix_acl);
+                               error = tomoyo_audit_unix_log(&r);
+                       } while (error == TOMOYO_RETRY_REQUEST);
+                       kfree(buf);
+               } else
+                       error = -ENOMEM;
+       }
+       tomoyo_read_unlock(idx);
+       return error;
+}
+
+/**
+ * tomoyo_check_unix_address - Check permission for unix domain socket's operation.
+ *
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ * @address:  Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_unix_address(struct sockaddr *addr,
+                                    const unsigned int addr_len,
+                                    struct tomoyo_addr_info *address)
+{
+       struct tomoyo_unix_addr_info *u = &address->unix0;
+
+       if (addr->sa_family != AF_UNIX)
+               return 0;
+       u->addr = ((struct sockaddr_un *) addr)->sun_path;
+       u->addr_len = addr_len;
+       return tomoyo_unix_entry(address);
+}
+
+/**
+ * tomoyo_kernel_service - Check whether I'm kernel service or not.
+ *
+ * Returns true if I'm kernel service, false otherwise.
+ */
+static bool tomoyo_kernel_service(void)
+{
+       /* Nothing to do if I am a kernel service. */
+       return segment_eq(get_fs(), KERNEL_DS);
+}
+
+/**
+ * tomoyo_sock_family - Get socket's family.
+ *
+ * @sk: Pointer to "struct sock".
+ *
+ * Returns one of PF_INET, PF_INET6, PF_UNIX or 0.
+ */
+static u8 tomoyo_sock_family(struct sock *sk)
+{
+       u8 family;
+
+       if (tomoyo_kernel_service())
+               return 0;
+       family = sk->sk_family;
+       switch (family) {
+       case PF_INET:
+       case PF_INET6:
+       case PF_UNIX:
+               return family;
+       default:
+               return 0;
+       }
+}
+
+/**
+ * tomoyo_socket_listen_permission - Check permission for listening a socket.
+ *
+ * @sock: Pointer to "struct socket".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_listen_permission(struct socket *sock)
+{
+       struct tomoyo_addr_info address;
+       const u8 family = tomoyo_sock_family(sock->sk);
+       const unsigned int type = sock->type;
+       struct sockaddr_storage addr;
+       int addr_len;
+
+       if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET))
+               return 0;
+       {
+               const int error = sock->ops->getname(sock, (struct sockaddr *)
+                                                    &addr, &addr_len, 0);
+
+               if (error)
+                       return error;
+       }
+       address.protocol = type;
+       address.operation = TOMOYO_NETWORK_LISTEN;
+       if (family == PF_UNIX)
+               return tomoyo_check_unix_address((struct sockaddr *) &addr,
+                                                addr_len, &address);
+       return tomoyo_check_inet_address((struct sockaddr *) &addr, addr_len,
+                                        0, &address);
+}
+
+/**
+ * tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket.
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_connect_permission(struct socket *sock,
+                                    struct sockaddr *addr, int addr_len)
+{
+       struct tomoyo_addr_info address;
+       const u8 family = tomoyo_sock_family(sock->sk);
+       const unsigned int type = sock->type;
+
+       if (!family)
+               return 0;
+       address.protocol = type;
+       switch (type) {
+       case SOCK_DGRAM:
+       case SOCK_RAW:
+               address.operation = TOMOYO_NETWORK_SEND;
+               break;
+       case SOCK_STREAM:
+       case SOCK_SEQPACKET:
+               address.operation = TOMOYO_NETWORK_CONNECT;
+               break;
+       default:
+               return 0;
+       }
+       if (family == PF_UNIX)
+               return tomoyo_check_unix_address(addr, addr_len, &address);
+       return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
+                                        &address);
+}
+
+/**
+ * tomoyo_socket_bind_permission - Check permission for setting the local address of a socket.
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+                                 int addr_len)
+{
+       struct tomoyo_addr_info address;
+       const u8 family = tomoyo_sock_family(sock->sk);
+       const unsigned int type = sock->type;
+
+       if (!family)
+               return 0;
+       switch (type) {
+       case SOCK_STREAM:
+       case SOCK_DGRAM:
+       case SOCK_RAW:
+       case SOCK_SEQPACKET:
+               address.protocol = type;
+               address.operation = TOMOYO_NETWORK_BIND;
+               break;
+       default:
+               return 0;
+       }
+       if (family == PF_UNIX)
+               return tomoyo_check_unix_address(addr, addr_len, &address);
+       return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
+                                        &address);
+}
+
+/**
+ * tomoyo_socket_sendmsg_permission - Check permission for sending a datagram.
+ *
+ * @sock: Pointer to "struct socket".
+ * @msg:  Pointer to "struct msghdr".
+ * @size: Unused.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
+                                    int size)
+{
+       struct tomoyo_addr_info address;
+       const u8 family = tomoyo_sock_family(sock->sk);
+       const unsigned int type = sock->type;
+
+       if (!msg->msg_name || !family ||
+           (type != SOCK_DGRAM && type != SOCK_RAW))
+               return 0;
+       address.protocol = type;
+       address.operation = TOMOYO_NETWORK_SEND;
+       if (family == PF_UNIX)
+               return tomoyo_check_unix_address((struct sockaddr *)
+                                                msg->msg_name,
+                                                msg->msg_namelen, &address);
+       return tomoyo_check_inet_address((struct sockaddr *) msg->msg_name,
+                                        msg->msg_namelen,
+                                        sock->sk->sk_protocol, &address);
+}
index 6c601bd300f386a9faff38e335c4f9c6dbdfa541..738bbdf8d4c77ceba3ba3abfe733a90dc56cd4fb 100644 (file)
 #include "../../fs/internal.h"
 
 /**
- * tomoyo_encode: Convert binary string to ascii string.
+ * tomoyo_encode2 - Encode binary string to ascii string.
  *
- * @str: String in binary format.
+ * @str:     String in binary format.
+ * @str_len: Size of @str in byte.
  *
  * Returns pointer to @str in ascii format on success, NULL otherwise.
  *
  * This function uses kzalloc(), so caller must kfree() if this function
  * didn't return NULL.
  */
-char *tomoyo_encode(const char *str)
+char *tomoyo_encode2(const char *str, int str_len)
 {
+       int i;
        int len = 0;
        const char *p = str;
        char *cp;
@@ -33,8 +35,9 @@ char *tomoyo_encode(const char *str)
 
        if (!p)
                return NULL;
-       while (*p) {
-               const unsigned char c = *p++;
+       for (i = 0; i < str_len; i++) {
+               const unsigned char c = p[i];
+
                if (c == '\\')
                        len += 2;
                else if (c > ' ' && c < 127)
@@ -49,8 +52,8 @@ char *tomoyo_encode(const char *str)
                return NULL;
        cp0 = cp;
        p = str;
-       while (*p) {
-               const unsigned char c = *p++;
+       for (i = 0; i < str_len; i++) {
+               const unsigned char c = p[i];
 
                if (c == '\\') {
                        *cp++ = '\\';
@@ -67,6 +70,21 @@ char *tomoyo_encode(const char *str)
        return cp0;
 }
 
+/**
+ * tomoyo_encode - Encode binary string to ascii string.
+ *
+ * @str: String in binary format.
+ *
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_encode(const char *str)
+{
+       return str ? tomoyo_encode2(str, strlen(str)) : NULL;
+}
+
 /**
  * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
  *
index a49c3bfd4dd5825ce960e32c75e357ebdbcb1b92..d08296a4882b07631749d3b21a25ebfa3eb615d1 100644 (file)
@@ -7,6 +7,124 @@
 #include <linux/security.h>
 #include "common.h"
 
+/**
+ * tomoyo_check_task_acl - Check permission for task operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_task_acl(struct tomoyo_request_info *r,
+                                 const struct tomoyo_acl_info *ptr)
+{
+       const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
+                                                        head);
+       return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
+}
+
+/**
+ * tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Domainname to transit to.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ *
+ * If domain transition was permitted but the domain transition failed, this
+ * function returns error rather than terminating current thread with SIGKILL.
+ */
+static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
+                             size_t count, loff_t *ppos)
+{
+       char *data;
+       int error;
+       if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
+               return -ENOMEM;
+       data = kzalloc(count + 1, GFP_NOFS);
+       if (!data)
+               return -ENOMEM;
+       if (copy_from_user(data, buf, count)) {
+               error = -EFAULT;
+               goto out;
+       }
+       tomoyo_normalize_line(data);
+       if (tomoyo_correct_domain(data)) {
+               const int idx = tomoyo_read_lock();
+               struct tomoyo_path_info name;
+               struct tomoyo_request_info r;
+               name.name = data;
+               tomoyo_fill_path_info(&name);
+               /* Check "task manual_domain_transition" permission. */
+               tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+               r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL;
+               r.param.task.domainname = &name;
+               tomoyo_check_acl(&r, tomoyo_check_task_acl);
+               if (!r.granted)
+                       error = -EPERM;
+               else {
+                       struct tomoyo_domain_info *new_domain =
+                               tomoyo_assign_domain(data, true);
+                       if (!new_domain) {
+                               error = -ENOENT;
+                       } else {
+                               struct cred *cred = prepare_creds();
+                               if (!cred) {
+                                       error = -ENOMEM;
+                               } else {
+                                       struct tomoyo_domain_info *old_domain =
+                                               cred->security;
+                                       cred->security = new_domain;
+                                       atomic_inc(&new_domain->users);
+                                       atomic_dec(&old_domain->users);
+                                       commit_creds(cred);
+                                       error = 0;
+                               }
+                       }
+               }
+               tomoyo_read_unlock(idx);
+       } else
+               error = -EINVAL;
+out:
+       kfree(data);
+       return error ? error : count;
+}
+
+/**
+ * tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Domainname which current thread belongs to.
+ * @count: Size of @buf.
+ * @ppos:  Bytes read by now.
+ *
+ * Returns read size on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read_self(struct file *file, char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       const char *domain = tomoyo_domain()->domainname->name;
+       loff_t len = strlen(domain);
+       loff_t pos = *ppos;
+       if (pos >= len || !count)
+               return 0;
+       len -= pos;
+       if (count < len)
+               len = count;
+       if (copy_to_user(buf, domain + pos, len))
+               return -EFAULT;
+       *ppos += len;
+       return len;
+}
+
+/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */
+static const struct file_operations tomoyo_self_operations = {
+       .write = tomoyo_write_self,
+       .read  = tomoyo_read_self,
+};
+
 /**
  * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
  *
@@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void)
                            TOMOYO_EXCEPTIONPOLICY);
        tomoyo_create_entry("audit",            0400, tomoyo_dir,
                            TOMOYO_AUDIT);
-       tomoyo_create_entry("self_domain",      0400, tomoyo_dir,
-                           TOMOYO_SELFDOMAIN);
        tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
                            TOMOYO_PROCESS_STATUS);
        tomoyo_create_entry("stat",             0644, tomoyo_dir,
@@ -147,6 +263,8 @@ static int __init tomoyo_initerface_init(void)
                            TOMOYO_MANAGER);
        tomoyo_create_entry("version",          0400, tomoyo_dir,
                            TOMOYO_VERSION);
+       securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL,
+                              &tomoyo_self_operations);
        return 0;
 }
 
index f776400a8f3136aefb705aa2893e250ec4122939..4b327b6917451bbe8f4ad2fc2cccad113a9cbf46 100644 (file)
@@ -442,6 +442,64 @@ static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
        return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
 }
 
+/**
+ * tomoyo_socket_listen - Check permission for listen().
+ *
+ * @sock:    Pointer to "struct socket".
+ * @backlog: Backlog parameter.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_listen(struct socket *sock, int backlog)
+{
+       return tomoyo_socket_listen_permission(sock);
+}
+
+/**
+ * tomoyo_socket_connect - Check permission for connect().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr,
+                                int addr_len)
+{
+       return tomoyo_socket_connect_permission(sock, addr, addr_len);
+}
+
+/**
+ * tomoyo_socket_bind - Check permission for bind().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *addr,
+                             int addr_len)
+{
+       return tomoyo_socket_bind_permission(sock, addr, addr_len);
+}
+
+/**
+ * tomoyo_socket_sendmsg - Check permission for sendmsg().
+ *
+ * @sock: Pointer to "struct socket".
+ * @msg:  Pointer to "struct msghdr".
+ * @size: Size of message.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+                                int size)
+{
+       return tomoyo_socket_sendmsg_permission(sock, msg, size);
+}
+
 /*
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
@@ -472,6 +530,10 @@ static struct security_operations tomoyo_security_ops = {
        .sb_mount            = tomoyo_sb_mount,
        .sb_umount           = tomoyo_sb_umount,
        .sb_pivotroot        = tomoyo_sb_pivotroot,
+       .socket_bind         = tomoyo_socket_bind,
+       .socket_connect      = tomoyo_socket_connect,
+       .socket_listen       = tomoyo_socket_listen,
+       .socket_sendmsg      = tomoyo_socket_sendmsg,
 };
 
 /* Lock for GC. */
index c36bd1107fc8bbf549dbbc11b7ca7c21b61276a1..50e9b4c73cebd0f8d9e99ebce29841c8a08e8d95 100644 (file)
@@ -42,6 +42,39 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
        [TOMOYO_MAC_FILE_MOUNT]      = TOMOYO_MAC_CATEGORY_FILE,
        [TOMOYO_MAC_FILE_UMOUNT]     = TOMOYO_MAC_CATEGORY_FILE,
        [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
+       /* CONFIG::network group */
+       [TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_INET_RAW_BIND]          =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_INET_RAW_SEND]          =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] =
+       TOMOYO_MAC_CATEGORY_NETWORK,
+       /* CONFIG::misc group */
+       [TOMOYO_MAC_ENVIRON]         = TOMOYO_MAC_CATEGORY_MISC,
 };
 
 /**
@@ -125,6 +158,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param)
        return pos;
 }
 
+/**
+ * tomoyo_get_domainname - Read a domainname from a line.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns a domainname on success, NULL otherwise.
+ */
+const struct tomoyo_path_info *tomoyo_get_domainname
+(struct tomoyo_acl_param *param)
+{
+       char *start = param->data;
+       char *pos = start;
+       while (*pos) {
+               if (*pos++ != ' ' || *pos++ == '/')
+                       continue;
+               pos -= 2;
+               *pos++ = '\0';
+               break;
+       }
+       param->data = pos;
+       if (tomoyo_correct_domain(start))
+               return tomoyo_get_name(start);
+       return NULL;
+}
+
 /**
  * tomoyo_parse_ulong - Parse an "unsigned long" value.
  *
@@ -920,14 +978,17 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
                    const u8 index)
 {
        u8 mode;
-       const u8 category = TOMOYO_MAC_CATEGORY_FILE;
+       struct tomoyo_profile *p;
+
        if (!tomoyo_policy_loaded)
                return TOMOYO_CONFIG_DISABLED;
-       mode = tomoyo_profile(ns, profile)->config[index];
+       p = tomoyo_profile(ns, profile);
+       mode = p->config[index];
        if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-               mode = tomoyo_profile(ns, profile)->config[category];
+               mode = p->config[tomoyo_index2category[index]
+                                + TOMOYO_MAX_MAC_INDEX];
        if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-               mode = tomoyo_profile(ns, profile)->default_config;
+               mode = p->default_config;
        return mode & 3;
 }