Merge branch 'next-evm' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/ima...
authorJames Morris <jmorris@namei.org>
Tue, 9 Aug 2011 00:31:03 +0000 (10:31 +1000)
committerJames Morris <jmorris@namei.org>
Tue, 9 Aug 2011 00:31:03 +0000 (10:31 +1000)
Conflicts:
fs/attr.c

Resolve conflict manually.

Signed-off-by: James Morris <jmorris@namei.org>
39 files changed:
Documentation/ABI/testing/evm [new file with mode: 0644]
Documentation/kernel-parameters.txt
fs/attr.c
fs/btrfs/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/linux-2.6/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
mm/shmem.c
security/Kconfig
security/Makefile
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_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_iint.c [deleted file]
security/integrity/ima/ima_main.c
security/integrity/integrity.h [new file with mode: 0644]
security/security.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 e279b72..cd7c861 100644 (file)
@@ -48,6 +48,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.
        GCOV    GCOV profiling is enabled.
        HW      Appropriate hardware is enabled.
@@ -758,6 +759,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 538e279..7ee7ba4 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 d733b9c..6196e1a 100644 (file)
@@ -374,36 +374,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 5d979b4..c922adc 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 b8d9f83..3c218b8 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 007c3bf..34e4350 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 900cf98..6525b80 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 cfeb716..0f20208 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 e87fede..26683e1 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 81ecf9c..194fb22 100644 (file)
@@ -7185,20 +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 = {
-               .enable = 1,
-       };
 
-       ret = ocfs2_init_security_get(inode, dir, qstr, &si);
+       ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
        if (!ret) {
-               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;
        }
@@ -7255,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,
@@ -7263,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 ef66c18..534668f 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 f060663..67583de 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 b9c172b..3719460 100644 (file)
@@ -94,37 +94,38 @@ xfs_mark_inode_dirty(
                mark_inode_dirty(inode);
 }
 
+
+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..db5556d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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);
+#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 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 -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_EVM_H */
+#endif /* LINUX_EVM_H */
index 09e6e62..6ac8e50 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..9684433
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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_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 ebd2a53..1c528b1 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 {
@@ -1704,8 +1709,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,9 +2042,8 @@ 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;
 }
index aed54c5..b20cb96 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
 
@@ -67,6 +70,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 +87,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 32f6763..2d35772 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 e0f08b5..22847a8 100644 (file)
@@ -186,7 +186,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 8bb0fe9..a5e502f 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
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..73f6540
--- /dev/null
@@ -0,0 +1,12 @@
+config EVM
+       boolean "EVM support"
+       depends on SECURITY && KEYS && ENCRYPTED_KEYS
+       select CRYPTO_HMAC
+       select CRYPTO_MD5
+       select CRYPTO_SHA1
+       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..0787d26
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for building the Extended Verification Module(EVM)
+#
+obj-$(CONFIG_EVM) += evm.o
+
+evm-y := evm_main.o evm_crypto.o evm_secfs.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..8fc5b5d
--- /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;
+       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)
+               goto err_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)
+               goto err_out;
+       evm_status = INTEGRITY_PASS;
+       goto out;
+
+err_out:
+       switch (rc) {
+       case -ENODATA:          /* file not labelled */
+               evm_status = INTEGRITY_NOLABEL;
+               break;
+       default:
+               evm_status = 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_protect_xattr - protect the EVM extended attribute
+ *
+ * Prevent security.evm from being modified or removed.
+ */
+static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
+                            const void *xattr_value, size_t xattr_value_len)
+{
+       if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+       }
+       return 0;
+}
+
+/*
+ * 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_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)
+{
+
+       enum integrity_status evm_status;
+       int ret;
+
+       ret = evm_protect_xattr(dentry, xattr_name, xattr_value,
+                               xattr_value_len);
+       if (ret)
+               return ret;
+       evm_status = evm_verify_current_integrity(dentry);
+       return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * 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)
+{
+       enum integrity_status evm_status;
+       int ret;
+
+       ret = evm_protect_xattr(dentry, xattr_name, NULL, 0);
+       if (ret)
+               return ret;
+       evm_status = evm_verify_current_integrity(dentry);
+       return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * 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))
+               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);
+       return evm_status == INTEGRITY_PASS ? 0 : -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 -EOPNOTSUPP;
+
+       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_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..0a23e07
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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;
+       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 b6ecfd4..19c053b 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 787c4cb..5690c02 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 08408bd..29d97af 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
@@ -96,34 +98,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 da36d2c..0d50df0 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";
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 26b46ff..42dc270 100644 (file)
@@ -82,7 +82,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 +105,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 +121,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 +131,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..880bbee
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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);
index 0e4fccf..a632842 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] =
@@ -334,20 +338,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;
+
+       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 -EOPNOTSUPP;
        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 +564,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 +585,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 +601,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 +620,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)