summaryrefslogtreecommitdiffstats
path: root/security/integrity/ima/ima_main.c
diff options
context:
space:
mode:
authorMimi Zohar <zohar@linux.vnet.ibm.com>2012-02-13 16:15:05 +0100
committerMimi Zohar <zohar@linux.vnet.ibm.com>2012-09-07 20:57:44 +0200
commit2fe5d6def1672ae6635dd71867bf36dcfaa7434b (patch)
treef83878d309605440b5bc2d2d43a16ccece64c645 /security/integrity/ima/ima_main.c
parentvfs: move ima_file_free before releasing the file (diff)
downloadlinux-2fe5d6def1672ae6635dd71867bf36dcfaa7434b.tar.xz
linux-2fe5d6def1672ae6635dd71867bf36dcfaa7434b.zip
ima: integrity appraisal extension
IMA currently maintains an integrity measurement list used to assert the integrity of the running system to a third party. The IMA-appraisal extension adds local integrity validation and enforcement of the measurement against a "good" value stored as an extended attribute 'security.ima'. The initial methods for validating 'security.ima' are hashed based, which provides file data integrity, and digital signature based, which in addition to providing file data integrity, provides authenticity. This patch creates and maintains the 'security.ima' xattr, containing the file data hash measurement. Protection of the xattr is provided by EVM, if enabled and configured. Based on policy, IMA calls evm_verifyxattr() to verify a file's metadata integrity and, assuming success, compares the file's current hash value with the one stored as an extended attribute in 'security.ima'. Changelov v4: - changed iint cache flags to hex values Changelog v3: - change appraisal default for filesystems without xattr support to fail Changelog v2: - fix audit msg 'res' value - removed unused 'ima_appraise=' values Changelog v1: - removed unused iint mutex (Dmitry Kasatkin) - setattr hook must not reset appraised (Dmitry Kasatkin) - evm_verifyxattr() now differentiates between no 'security.evm' xattr (INTEGRITY_NOLABEL) and no EVM 'protected' xattrs included in the 'security.evm' (INTEGRITY_NOXATTRS). - replace hash_status with ima_status (Dmitry Kasatkin) - re-initialize slab element ima_status on free (Dmitry Kasatkin) - include 'security.ima' in EVM if CONFIG_IMA_APPRAISE, not CONFIG_IMA - merged half "ima: ima_must_appraise_or_measure API change" (Dmitry Kasatkin) - removed unnecessary error variable in process_measurement() (Dmitry Kasatkin) - use ima_inode_post_setattr() stub function, if IMA_APPRAISE not configured (moved ima_inode_post_setattr() to ima_appraise.c) - make sure ima_collect_measurement() can read file Changelog: - add 'iint' to evm_verifyxattr() call (Dimitry Kasatkin) - fix the race condition between chmod, which takes the i_mutex and then iint->mutex, and ima_file_free() and process_measurement(), which take the locks in the reverse order, by eliminating iint->mutex. (Dmitry Kasatkin) - cleanup of ima_appraise_measurement() (Dmitry Kasatkin) - changes as a result of the iint not allocated for all regular files, but only for those measured/appraised. - don't try to appraise new/empty files - expanded ima_appraisal description in ima/Kconfig - IMA appraise definitions required even if IMA_APPRAISE not enabled - add return value to ima_must_appraise() stub - unconditionally set status = INTEGRITY_PASS *after* testing status, not before. (Found by Joe Perches) Signed-off-by: Mimi Zohar <zohar@us.ibm.com> Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
Diffstat (limited to 'security/integrity/ima/ima_main.c')
-rw-r--r--security/integrity/ima/ima_main.c79
1 files changed, 54 insertions, 25 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index be8294915cf7..6eb28d47e74b 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -22,12 +22,19 @@
#include <linux/mount.h>
#include <linux/mman.h>
#include <linux/slab.h>
+#include <linux/xattr.h>
#include <linux/ima.h>
#include "ima.h"
int ima_initialized;
+#ifdef CONFIG_IMA_APPRAISE
+int ima_appraise = IMA_APPRAISE_ENFORCE;
+#else
+int ima_appraise;
+#endif
+
char *ima_hash = "sha1";
static int __init hash_setup(char *str)
{
@@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file)
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
fmode_t mode = file->f_mode;
- int rc;
+ int must_measure;
bool send_tomtou = false, send_writers = false;
unsigned char *pathname = NULL, *pathbuf = NULL;
@@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file)
goto out;
}
- rc = ima_must_measure(inode, MAY_READ, FILE_CHECK);
- if (rc < 0)
+ must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
+ if (!must_measure)
goto out;
if (atomic_read(&inode->i_writecount) > 0)
@@ -100,17 +107,21 @@ out:
}
static void ima_check_last_writer(struct integrity_iint_cache *iint,
- struct inode *inode,
- struct file *file)
+ struct inode *inode, struct file *file)
{
fmode_t mode = file->f_mode;
- mutex_lock(&iint->mutex);
- if (mode & FMODE_WRITE &&
- atomic_read(&inode->i_writecount) == 1 &&
- iint->version != inode->i_version)
- iint->flags &= ~IMA_MEASURED;
- mutex_unlock(&iint->mutex);
+ if (!(mode & FMODE_WRITE))
+ return;
+
+ mutex_lock(&inode->i_mutex);
+ if (atomic_read(&inode->i_writecount) == 1 &&
+ iint->version != inode->i_version) {
+ iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED);
+ if (iint->flags & IMA_APPRAISE)
+ ima_update_xattr(iint, file);
+ }
+ mutex_unlock(&inode->i_mutex);
}
/**
@@ -140,14 +151,17 @@ static int process_measurement(struct file *file, const unsigned char *filename,
struct inode *inode = file->f_dentry->d_inode;
struct integrity_iint_cache *iint;
unsigned char *pathname = NULL, *pathbuf = NULL;
- int rc = 0;
+ int rc = -ENOMEM, action, must_appraise;
if (!ima_initialized || !S_ISREG(inode->i_mode))
return 0;
- rc = ima_must_measure(inode, mask, function);
- if (rc != 0)
- return rc;
+ /* Determine if in appraise/measurement policy,
+ * returns IMA_MEASURE, IMA_APPRAISE bitmask. */
+ action = ima_must_appraise_or_measure(inode, mask, function);
+ if (!action)
+ return 0;
+
retry:
iint = integrity_iint_find(inode);
if (!iint) {
@@ -157,11 +171,21 @@ retry:
return rc;
}
- mutex_lock(&iint->mutex);
+ must_appraise = action & IMA_APPRAISE;
- rc = iint->flags & IMA_MEASURED ? 1 : 0;
- if (rc != 0)
+ mutex_lock(&inode->i_mutex);
+
+ /* Determine if already appraised/measured based on bitmask
+ * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */
+ iint->flags |= action;
+ action &= ~((iint->flags & (IMA_MEASURED | IMA_APPRAISED)) >> 1);
+
+ /* Nothing to do, just return existing appraised status */
+ if (!action) {
+ if (iint->flags & IMA_APPRAISED)
+ rc = iint->ima_status;
goto out;
+ }
rc = ima_collect_measurement(iint, file);
if (rc != 0)
@@ -177,11 +201,16 @@ retry:
pathname = NULL;
}
}
- ima_store_measurement(iint, file, !pathname ? filename : pathname);
+ if (action & IMA_MEASURE)
+ ima_store_measurement(iint, file,
+ !pathname ? filename : pathname);
+ if (action & IMA_APPRAISE)
+ rc = ima_appraise_measurement(iint, file,
+ !pathname ? filename : pathname);
kfree(pathbuf);
out:
- mutex_unlock(&iint->mutex);
- return rc;
+ mutex_unlock(&inode->i_mutex);
+ return (rc && must_appraise) ? -EACCES : 0;
}
/**
@@ -197,14 +226,14 @@ out:
*/
int ima_file_mmap(struct file *file, unsigned long prot)
{
- int rc;
+ int rc = 0;
if (!file)
return 0;
if (prot & PROT_EXEC)
rc = process_measurement(file, file->f_dentry->d_name.name,
MAY_EXEC, FILE_MMAP);
- return 0;
+ return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
/**
@@ -228,7 +257,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
(strcmp(bprm->filename, bprm->interp) == 0) ?
bprm->filename : bprm->interp,
MAY_EXEC, BPRM_CHECK);
- return 0;
+ return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
/**
@@ -249,7 +278,7 @@ int ima_file_check(struct file *file, int mask)
rc = process_measurement(file, file->f_dentry->d_name.name,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
FILE_CHECK);
- return 0;
+ return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
EXPORT_SYMBOL_GPL(ima_file_check);