ihash_entries= [KNL]
Set number of hash buckets for inode cache.
+ ima_appraise= [IMA] appraise integrity measurements
+ Format: { "off" | "enforce" | "fix" }
+ default: "enforce"
+
ima_audit= [IMA]
Format: { "0" | "1" }
0 -- integrity auditing messages. (Default)
#define XATTR_EVM_SUFFIX "evm"
#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
+#define XATTR_IMA_SUFFIX "ima"
+#define XATTR_NAME_IMA XATTR_SECURITY_PREFIX XATTR_IMA_SUFFIX
+
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
#endif
#ifdef CONFIG_SECURITY_SMACK
XATTR_NAME_SMACK,
+#endif
+#ifdef CONFIG_IMA_APPRAISE
+ XATTR_NAME_IMA,
#endif
XATTR_NAME_CAPS,
NULL
{
iint->version = 0;
iint->flags = 0UL;
+ iint->ima_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
kmem_cache_free(iint_cache, iint);
}
memset(iint, 0, sizeof *iint);
iint->version = 0;
iint->flags = 0UL;
- mutex_init(&iint->mutex);
+ iint->ima_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
}
default y
help
Disabling this option will disregard LSM based policy rules.
+
+config IMA_APPRAISE
+ bool "Appraise integrity measurements"
+ depends on IMA
+ default n
+ help
+ This option enables local measurement integrity appraisal.
+ It requires the system to be labeled with a security extended
+ attribute containing the file hash measurement. To protect
+ the security extended attributes from offline attack, enable
+ and configure EVM.
+
+ For more information on integrity appraisal refer to:
+ <http://linux-ima.sourceforge.net>
+ If unsure, say N.
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o
ima-$(CONFIG_IMA_AUDIT) += ima_audit.o
+ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
extern int ima_initialized;
extern int ima_used_chip;
extern char *ima_hash;
+extern int ima_appraise;
/* IMA inode template definition */
struct ima_template_data {
}
/* LIM API function definitions */
+int ima_must_appraise_or_measure(struct inode *inode, int mask, int function);
int ima_must_measure(struct inode *inode, int mask, int function);
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file);
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
/* IMA policy related functions */
-enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR };
-int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
+ int flags);
void ima_init_policy(void);
void ima_update_policy(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
+/* Appraise integrity measurements */
+#define IMA_APPRAISE_ENFORCE 0x01
+#define IMA_APPRAISE_FIX 0x02
+
+#ifdef CONFIG_IMA_APPRAISE
+int ima_appraise_measurement(struct integrity_iint_cache *iint,
+ struct file *file, const unsigned char *filename);
+int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask);
+void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
+
+#else
+static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
+ struct file *file,
+ const unsigned char *filename)
+{
+ return INTEGRITY_UNKNOWN;
+}
+
+static inline int ima_must_appraise(struct inode *inode,
+ enum ima_hooks func, int mask)
+{
+ return 0;
+}
+
+static inline void ima_update_xattr(struct integrity_iint_cache *iint,
+ struct file *file)
+{
+}
+#endif
+
/* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES
* License.
*
* File: ima_api.c
- * Implements must_measure, collect_measurement, store_measurement,
- * and store_template.
+ * Implements must_appraise_or_measure, collect_measurement,
+ * appraise_measurement, store_measurement and store_template.
*/
#include <linux/module.h>
#include <linux/slab.h>
-
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/evm.h>
#include "ima.h"
+
static const char *IMA_TEMPLATE_NAME = "ima";
/*
}
/**
- * ima_must_measure - measure decision based on policy.
+ * ima_must_appraise_or_measure - appraise & measure decision based on policy.
* @inode: pointer to inode to measure
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
* @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP)
* mask: contains the permission mask
* fsmagic: hex value
*
- * Return 0 to measure. For matching a DONT_MEASURE policy, no policy,
- * or other error, return an error code.
-*/
-int ima_must_measure(struct inode *inode, int mask, int function)
+ * Returns IMA_MEASURE, IMA_APPRAISE mask.
+ *
+ */
+int ima_must_appraise_or_measure(struct inode *inode, int mask, int function)
{
- int must_measure;
+ int flags = IMA_MEASURE | IMA_APPRAISE;
+
+ if (!ima_appraise)
+ flags &= ~IMA_APPRAISE;
+
+ return ima_match_policy(inode, function, mask, flags);
+}
- must_measure = ima_match_policy(inode, function, mask);
- return must_measure ? 0 : -EACCES;
+int ima_must_measure(struct inode *inode, int mask, int function)
+{
+ return ima_match_policy(inode, function, mask, IMA_MEASURE);
}
/*
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file)
{
- int result = -EEXIST;
+ struct inode *inode = file->f_dentry->d_inode;
+ const char *filename = file->f_dentry->d_name.name;
+ int result = 0;
- if (!(iint->flags & IMA_MEASURED)) {
+ if (!(iint->flags & IMA_COLLECTED)) {
u64 i_version = file->f_dentry->d_inode->i_version;
memset(iint->digest, 0, IMA_DIGEST_SIZE);
result = ima_calc_hash(file, iint->digest);
- if (!result)
+ if (!result) {
iint->version = i_version;
+ iint->flags |= IMA_COLLECTED;
+ }
}
+ if (result)
+ integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
+ filename, "collect_data", "failed",
+ result, 0);
return result;
}
struct ima_template_entry *entry;
int violation = 0;
+ if (iint->flags & IMA_MEASURED)
+ return;
+
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
--- /dev/null
+/*
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/magic.h>
+#include <linux/ima.h>
+#include <linux/evm.h>
+
+#include "ima.h"
+
+static int __init default_appraise_setup(char *str)
+{
+ if (strncmp(str, "off", 3) == 0)
+ ima_appraise = 0;
+ else if (strncmp(str, "fix", 3) == 0)
+ ima_appraise = IMA_APPRAISE_FIX;
+ return 1;
+}
+
+__setup("ima_appraise=", default_appraise_setup);
+
+/*
+ * ima_must_appraise - set appraise flag
+ *
+ * Return 1 to appraise
+ */
+int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask)
+{
+ return 0;
+}
+
+static void ima_fix_xattr(struct dentry *dentry,
+ struct integrity_iint_cache *iint)
+{
+ iint->digest[0] = IMA_XATTR_DIGEST;
+ __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
+ iint->digest, IMA_DIGEST_SIZE + 1, 0);
+}
+
+/*
+ * ima_appraise_measurement - appraise file measurement
+ *
+ * Call evm_verifyxattr() to verify the integrity of 'security.ima'.
+ * Assuming success, compare the xattr hash with the collected measurement.
+ *
+ * Return 0 on success, error code otherwise
+ */
+int ima_appraise_measurement(struct integrity_iint_cache *iint,
+ struct file *file, const unsigned char *filename)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ u8 xattr_value[IMA_DIGEST_SIZE];
+ enum integrity_status status = INTEGRITY_UNKNOWN;
+ const char *op = "appraise_data";
+ char *cause = "unknown";
+ int rc;
+
+ if (!ima_appraise)
+ return 0;
+ if (!inode->i_op->getxattr)
+ return INTEGRITY_UNKNOWN;
+
+ if (iint->flags & IMA_APPRAISED)
+ return iint->ima_status;
+
+ rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, xattr_value,
+ IMA_DIGEST_SIZE);
+ if (rc <= 0) {
+ if (rc && rc != -ENODATA)
+ goto out;
+
+ cause = "missing-hash";
+ status =
+ (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
+ goto out;
+ }
+
+ status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
+ if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
+ if ((status == INTEGRITY_NOLABEL)
+ || (status == INTEGRITY_NOXATTRS))
+ cause = "missing-HMAC";
+ else if (status == INTEGRITY_FAIL)
+ cause = "invalid-HMAC";
+ goto out;
+ }
+
+ rc = memcmp(xattr_value, iint->digest, IMA_DIGEST_SIZE);
+ if (rc) {
+ status = INTEGRITY_FAIL;
+ cause = "invalid-hash";
+ print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
+ xattr_value, IMA_DIGEST_SIZE);
+ print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
+ iint->digest, IMA_DIGEST_SIZE);
+ goto out;
+ }
+ status = INTEGRITY_PASS;
+ iint->flags |= IMA_APPRAISED;
+out:
+ if (status != INTEGRITY_PASS) {
+ if (ima_appraise & IMA_APPRAISE_FIX) {
+ ima_fix_xattr(dentry, iint);
+ status = INTEGRITY_PASS;
+ }
+ integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
+ op, cause, rc, 0);
+ }
+ iint->ima_status = status;
+ return status;
+}
+
+/*
+ * ima_update_xattr - update 'security.ima' hash value
+ */
+void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ int rc = 0;
+
+ rc = ima_collect_measurement(iint, file);
+ if (rc < 0)
+ return;
+ ima_fix_xattr(dentry, iint);
+}
+
+/**
+ * ima_inode_post_setattr - reflect file metadata changes
+ * @dentry: pointer to the affected dentry
+ *
+ * Changes to a dentry's metadata might result in needing to appraise.
+ *
+ * This function is called from notify_change(), which expects the caller
+ * to lock the inode's i_mutex.
+ */
+void ima_inode_post_setattr(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct integrity_iint_cache *iint;
+ int must_appraise, rc;
+
+ if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
+ || !inode->i_op->removexattr)
+ return;
+
+ must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
+ iint = integrity_iint_find(inode);
+ if (iint) {
+ if (must_appraise)
+ iint->flags |= IMA_APPRAISE;
+ else
+ iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
+ }
+ if (!must_appraise)
+ rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
+ return;
+}
struct scatterlist sg[1];
loff_t i_size, offset = 0;
char *rbuf;
- int rc;
+ int rc, read = 0;
rc = init_desc(&desc);
if (rc != 0)
rc = -ENOMEM;
goto out;
}
+ if (!(file->f_mode & FMODE_READ)) {
+ file->f_mode |= FMODE_READ;
+ read = 1;
+ }
i_size = i_size_read(file->f_dentry->d_inode);
while (offset < i_size) {
int rbuf_len;
kfree(rbuf);
if (!rc)
rc = crypto_hash_final(&desc, digest);
+ if (read)
+ file->f_mode &= ~FMODE_READ;
out:
crypto_free_hash(desc.tfm);
return rc;
#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)
{
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;
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)
}
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);
}
/**
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) {
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)
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;
}
/**
*/
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;
}
/**
(strcmp(bprm->filename, bprm->interp) == 0) ?
bprm->filename : bprm->interp,
MAY_EXEC, BPRM_CHECK);
- return 0;
+ return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
/**
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);
#define IMA_FSMAGIC 0x0004
#define IMA_UID 0x0008
-enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
+#define UNKNOWN 0
+#define MEASURE 1 /* same as IMA_MEASURE */
+#define DONT_MEASURE 2
+#define MEASURE_MASK 3
+#define APPRAISE 4 /* same as IMA_APPRAISE */
+#define DONT_APPRAISE 8
+#define APPRAISE_MASK 12
#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
struct ima_measure_rule_entry {
struct list_head list;
- enum ima_action action;
+ int action;
unsigned int flags;
enum ima_hooks func;
int mask;
* as elements in the list are never deleted, nor does the list
* change.)
*/
-int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
+ int flags)
{
struct ima_measure_rule_entry *entry;
+ int action = 0, actmask = flags | (flags << 1);
list_for_each_entry(entry, ima_measure, list) {
- bool rc;
- rc = ima_match_rules(entry, inode, func, mask);
- if (rc)
- return entry->action;
+ if (!(entry->action & actmask))
+ continue;
+
+ if (!ima_match_rules(entry, inode, func, mask))
+ continue;
+
+ action |= (entry->action & (IMA_APPRAISE | IMA_MEASURE));
+ actmask &= (entry->action & APPRAISE_MASK) ?
+ ~APPRAISE_MASK : ~MEASURE_MASK;
+ if (!actmask)
+ break;
}
- return 0;
+
+ return action;
}
/**
#include <crypto/sha.h>
/* iint cache flags */
-#define IMA_MEASURED 0x01
+#define IMA_MEASURE 0x01
+#define IMA_MEASURED 0x02
+#define IMA_APPRAISE 0x04
+#define IMA_APPRAISED 0x08
+#define IMA_COLLECTED 0x10
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
u64 version; /* track inode changes */
unsigned char flags;
u8 digest[SHA1_DIGEST_SIZE];
- struct mutex mutex; /* protects: version, flags, digest */
+ enum integrity_status ima_status;
enum integrity_status evm_status;
};