ima: add ima_inode_setxattr/removexattr function and calls
authorMimi Zohar <zohar@linux.vnet.ibm.com>
Thu, 10 Mar 2011 23:54:15 +0000 (18:54 -0500)
committerMimi Zohar <zohar@linux.vnet.ibm.com>
Fri, 7 Sep 2012 18:57:47 +0000 (14:57 -0400)
Based on xattr_permission comments, the restriction to modify 'security'
xattr is left up to the underlying fs or lsm. Ensure that not just anyone
can modify or remove 'security.ima'.

Changelog v1:
- Unless IMA-APPRAISE is configured, use stub ima_inode_removexattr()/setxattr()
  functions.  (Moved ima_inode_removexattr()/setxattr() to ima_appraise.c)

Changelog:
  - take i_mutex to fix locking (Dmitry Kasatkin)
  - ima_reset_appraise_flags should only be called when modifying or
    removing the 'security.ima' xattr. Requires CAP_SYS_ADMIN privilege.
    (Incorporated fix from Roberto Sassu)
  - Even if allowed to update security.ima, reset the appraisal flags,
    forcing re-appraisal.
  - Replace CAP_MAC_ADMIN with CAP_SYS_ADMIN
  - static inline ima_inode_setxattr()/ima_inode_removexattr() stubs
  - ima_protect_xattr should be static

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
include/linux/ima.h
security/integrity/ima/ima_appraise.c
security/security.c

index e2bfbb1e9af6a722dad8d32e5d29d1da027c8e1d..2c7223d7e73b720de899756e3a0433a813c2640e 100644 (file)
@@ -44,10 +44,27 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
 
 #ifdef CONFIG_IMA_APPRAISE
 extern void ima_inode_post_setattr(struct dentry *dentry);
+extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+                      const void *xattr_value, size_t xattr_value_len);
+extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
 #else
 static inline void ima_inode_post_setattr(struct dentry *dentry)
 {
        return;
 }
+
+static inline int ima_inode_setxattr(struct dentry *dentry,
+                                    const char *xattr_name,
+                                    const void *xattr_value,
+                                    size_t xattr_value_len)
+{
+       return 0;
+}
+
+static inline int ima_inode_removexattr(struct dentry *dentry,
+                                       const char *xattr_name)
+{
+       return 0;
+}
 #endif /* CONFIG_IMA_APPRAISE_H */
 #endif /* _LINUX_IMA_H */
index 681cb6e7225760a92b7987ebc9e30909718eb77b..becc7e09116d7712045b7581696505ff08e6697e 100644 (file)
@@ -169,3 +169,60 @@ void ima_inode_post_setattr(struct dentry *dentry)
                rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
        return;
 }
+
+/*
+ * ima_protect_xattr - protect 'security.ima'
+ *
+ * Ensure that not just anyone can modify or remove 'security.ima'.
+ */
+static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
+                            const void *xattr_value, size_t xattr_value_len)
+{
+       if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               return 1;
+       }
+       return 0;
+}
+
+static void ima_reset_appraise_flags(struct inode *inode)
+{
+       struct integrity_iint_cache *iint;
+
+       if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
+               return;
+
+       iint = integrity_iint_find(inode);
+       if (!iint)
+               return;
+
+       iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED);
+       return;
+}
+
+int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+                      const void *xattr_value, size_t xattr_value_len)
+{
+       int result;
+
+       result = ima_protect_xattr(dentry, xattr_name, xattr_value,
+                                  xattr_value_len);
+       if (result == 1) {
+               ima_reset_appraise_flags(dentry->d_inode);
+               result = 0;
+       }
+       return result;
+}
+
+int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+       int result;
+
+       result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
+       if (result == 1) {
+               ima_reset_appraise_flags(dentry->d_inode);
+               result = 0;
+       }
+       return result;
+}
index 68c1b9b45d93e23576be375027012f3c3a45e252..d23b43522a5a3206d87a663175550b9acea7e777 100644 (file)
@@ -571,6 +571,9 @@ int security_inode_setxattr(struct dentry *dentry, const char *name,
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return 0;
        ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
+       if (ret)
+               return ret;
+       ret = ima_inode_setxattr(dentry, name, value, size);
        if (ret)
                return ret;
        return evm_inode_setxattr(dentry, name, value, size);
@@ -606,6 +609,9 @@ int security_inode_removexattr(struct dentry *dentry, const char *name)
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return 0;
        ret = security_ops->inode_removexattr(dentry, name);
+       if (ret)
+               return ret;
+       ret = ima_inode_removexattr(dentry, name);
        if (ret)
                return ret;
        return evm_inode_removexattr(dentry, name);