summaryrefslogtreecommitdiff
path: root/security/integrity/ima
diff options
context:
space:
mode:
Diffstat (limited to 'security/integrity/ima')
-rw-r--r--security/integrity/ima/Kconfig37
-rw-r--r--security/integrity/ima/ima.h37
-rw-r--r--security/integrity/ima/ima_api.c15
-rw-r--r--security/integrity/ima/ima_appraise.c27
-rw-r--r--security/integrity/ima/ima_crypto.c347
-rw-r--r--security/integrity/ima/ima_fs.c37
-rw-r--r--security/integrity/ima/ima_init.c42
-rw-r--r--security/integrity/ima/ima_main.c134
-rw-r--r--security/integrity/ima/ima_policy.c72
-rw-r--r--security/integrity/ima/ima_template.c117
10 files changed, 649 insertions, 216 deletions
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 81a27971d884..b80a93ec1ccc 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -2,8 +2,6 @@
#
config IMA
bool "Integrity Measurement Architecture(IMA)"
- depends on SECURITY
- select INTEGRITY
select SECURITYFS
select CRYPTO
select CRYPTO_HMAC
@@ -123,3 +121,38 @@ config IMA_APPRAISE
For more information on integrity appraisal refer to:
<http://linux-ima.sourceforge.net>
If unsure, say N.
+
+config IMA_TRUSTED_KEYRING
+ bool "Require all keys on the .ima keyring be signed"
+ depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ select KEYS_DEBUG_PROC_KEYS
+ default y
+ help
+ This option requires that all keys added to the .ima
+ keyring be signed by a key on the system trusted keyring.
+
+config IMA_LOAD_X509
+ bool "Load X509 certificate onto the '.ima' trusted keyring"
+ depends on IMA_TRUSTED_KEYRING
+ default n
+ help
+ File signature verification is based on the public keys
+ loaded on the .ima trusted keyring. These public keys are
+ X509 certificates signed by a trusted key on the
+ .system keyring. This option enables X509 certificate
+ loading from the kernel onto the '.ima' trusted keyring.
+
+config IMA_X509_PATH
+ string "IMA X509 certificate path"
+ depends on IMA_LOAD_X509
+ default "/etc/keys/x509_ima.der"
+ help
+ This option defines IMA X509 certificate path.
+
+config IMA_APPRAISE_SIGNED_INIT
+ bool "Require signed user-space initialization"
+ depends on IMA_LOAD_X509
+ default n
+ help
+ This option requires user-space init to be signed.
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index f79fa8be203c..8ee997dff139 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -43,6 +43,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
#define IMA_TEMPLATE_IMA_NAME "ima"
#define IMA_TEMPLATE_IMA_FMT "d|n"
+/* current content of the policy */
+extern int ima_policy_flag;
+
/* set during initialization */
extern int ima_initialized;
extern int ima_used_chip;
@@ -90,10 +93,7 @@ extern struct list_head ima_measurements; /* list of all measurements */
/* Internal IMA function definitions */
int ima_init(void);
-void ima_cleanup(void);
int ima_fs_init(void);
-void ima_fs_cleanup(void);
-int ima_inode_alloc(struct inode *inode);
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
const unsigned char *filename);
@@ -110,8 +110,6 @@ void ima_print_digest(struct seq_file *m, u8 *digest, int size);
struct ima_template_desc *ima_template_desc_current(void);
int ima_init_template(void);
-int ima_init_template(void);
-
/*
* used to protect h_table and sha_table
*/
@@ -151,32 +149,29 @@ int ima_store_template(struct ima_template_entry *entry, int violation,
void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(struct path *path, char **pathbuf);
-/* 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);
-
/* IMA policy related functions */
-enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
+enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR };
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);
+void ima_update_policy_flag(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
-#define IMA_APPRAISE_MODULES 0x04
+#define IMA_APPRAISE_LOG 0x04
+#define IMA_APPRAISE_MODULES 0x08
+#define IMA_APPRAISE_FIRMWARE 0x10
#ifdef CONFIG_IMA_APPRAISE
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len);
+ int xattr_len, int opened);
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
@@ -192,7 +187,7 @@ static inline int ima_appraise_measurement(int func,
struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len)
+ int xattr_len, int opened)
{
return INTEGRITY_UNKNOWN;
}
@@ -249,4 +244,16 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
return -EINVAL;
}
#endif /* CONFIG_IMA_LSM_RULES */
+
+#ifdef CONFIG_IMA_TRUSTED_KEYRING
+static inline int ima_init_keyring(const unsigned int id)
+{
+ return integrity_init_keyring(id);
+}
+#else
+static inline int ima_init_keyring(const unsigned int id)
+{
+ return 0;
+}
+#endif /* CONFIG_IMA_TRUSTED_KEYRING */
#endif
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index d9cd5ce14d2b..b0dc922d8be3 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -173,17 +173,11 @@ int ima_get_action(struct inode *inode, int mask, int function)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
- if (!ima_appraise)
- flags &= ~IMA_APPRAISE;
+ flags &= ima_policy_flag;
return ima_match_policy(inode, function, mask, flags);
}
-int ima_must_measure(struct inode *inode, int mask, int function)
-{
- return ima_match_policy(inode, function, mask, IMA_MEASURE);
-}
-
/*
* ima_collect_measurement - collect file measurement
*
@@ -330,12 +324,11 @@ const char *ima_d_path(struct path *path, char **pathbuf)
{
char *pathname = NULL;
- /* We will allow 11 spaces for ' (deleted)' to be appended */
- *pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
+ *pathbuf = __getname();
if (*pathbuf) {
- pathname = d_path(path, *pathbuf, PATH_MAX + 11);
+ pathname = d_absolute_path(path, *pathbuf, PATH_MAX);
if (IS_ERR(pathname)) {
- kfree(*pathbuf);
+ __putname(*pathbuf);
*pathbuf = NULL;
pathname = NULL;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index d3113d4aaa3c..922685483bd3 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -23,6 +23,8 @@ static int __init default_appraise_setup(char *str)
{
if (strncmp(str, "off", 3) == 0)
ima_appraise = 0;
+ else if (strncmp(str, "log", 3) == 0)
+ ima_appraise = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0)
ima_appraise = IMA_APPRAISE_FIX;
return 1;
@@ -75,6 +77,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
return iint->ima_bprm_status;
case MODULE_CHECK:
return iint->ima_module_status;
+ case FIRMWARE_CHECK:
+ return iint->ima_firmware_status;
case FILE_CHECK:
default:
return iint->ima_file_status;
@@ -94,6 +98,9 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
case MODULE_CHECK:
iint->ima_module_status = status;
break;
+ case FIRMWARE_CHECK:
+ iint->ima_firmware_status = status;
+ break;
case FILE_CHECK:
default:
iint->ima_file_status = status;
@@ -113,6 +120,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
case MODULE_CHECK:
iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED);
break;
+ case FIRMWARE_CHECK:
+ iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED);
+ break;
case FILE_CHECK:
default:
iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
@@ -175,7 +185,7 @@ int ima_read_xattr(struct dentry *dentry,
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len)
+ int xattr_len, int opened)
{
static const char op[] = "appraise_data";
char *cause = "unknown";
@@ -184,8 +194,6 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len, hash_start = 0;
- if (!ima_appraise)
- return 0;
if (!inode->i_op->getxattr)
return INTEGRITY_UNKNOWN;
@@ -194,8 +202,11 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
goto out;
cause = "missing-hash";
- status =
- (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
+ status = INTEGRITY_NOLABEL;
+ if (opened & FILE_CREATED) {
+ iint->flags |= IMA_NEW_FILE;
+ status = INTEGRITY_PASS;
+ }
goto out;
}
@@ -214,7 +225,7 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
hash_start = 1;
case IMA_XATTR_DIGEST:
if (iint->flags & IMA_DIGSIG_REQUIRED) {
- cause = "IMA signature required";
+ cause = "IMA-signature-required";
status = INTEGRITY_FAIL;
break;
}
@@ -307,7 +318,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
struct integrity_iint_cache *iint;
int must_appraise, rc;
- if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
+ if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|| !inode->i_op->removexattr)
return;
@@ -345,7 +356,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
struct integrity_iint_cache *iint;
- if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
+ if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
return;
iint = integrity_iint_find(inode);
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index ccd0ac8fa9a0..5df4d960d4dc 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -16,6 +16,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/ratelimit.h>
#include <linux/file.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
@@ -25,39 +27,47 @@
#include <crypto/hash_info.h>
#include "ima.h"
-static struct crypto_shash *ima_shash_tfm;
+struct ahash_completion {
+ struct completion completion;
+ int err;
+};
-/**
- * ima_kernel_read - read file content
- *
- * This is a function for reading file content instead of kernel_read().
- * It does not perform locking checks to ensure it cannot be blocked.
- * It does not perform security checks because it is irrelevant for IMA.
- *
- */
-static int ima_kernel_read(struct file *file, loff_t offset,
- char *addr, unsigned long count)
+/* minimum file size for ahash use */
+static unsigned long ima_ahash_minsize;
+module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644);
+MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use");
+
+/* default is 0 - 1 page. */
+static int ima_maxorder;
+static unsigned int ima_bufsize = PAGE_SIZE;
+
+static int param_set_bufsize(const char *val, const struct kernel_param *kp)
{
- mm_segment_t old_fs;
- char __user *buf = addr;
- ssize_t ret;
+ unsigned long long size;
+ int order;
- if (!(file->f_mode & FMODE_READ))
- return -EBADF;
- if (!file->f_op->read && !file->f_op->aio_read)
+ size = memparse(val, NULL);
+ order = get_order(size);
+ if (order >= MAX_ORDER)
return -EINVAL;
-
- old_fs = get_fs();
- set_fs(get_ds());
- if (file->f_op->read)
- ret = file->f_op->read(file, buf, count, &offset);
- else
- ret = do_sync_read(file, buf, count, &offset);
- set_fs(old_fs);
- return ret;
+ ima_maxorder = order;
+ ima_bufsize = PAGE_SIZE << order;
+ return 0;
}
-int ima_init_crypto(void)
+static struct kernel_param_ops param_ops_bufsize = {
+ .set = param_set_bufsize,
+ .get = param_get_uint,
+};
+#define param_check_bufsize(name, p) __param_check(name, p, unsigned int)
+
+module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644);
+MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
+
+static struct crypto_shash *ima_shash_tfm;
+static struct crypto_ahash *ima_ahash_tfm;
+
+int __init ima_init_crypto(void)
{
long rc;
@@ -76,7 +86,10 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
struct crypto_shash *tfm = ima_shash_tfm;
int rc;
- if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) {
+ if (algo < 0 || algo >= HASH_ALGO__LAST)
+ algo = ima_hash_algo;
+
+ if (algo != ima_hash_algo) {
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
if (IS_ERR(tfm)) {
rc = PTR_ERR(tfm);
@@ -93,9 +106,250 @@ static void ima_free_tfm(struct crypto_shash *tfm)
crypto_free_shash(tfm);
}
-/*
- * Calculate the MD5/SHA1 file digest
+/**
+ * ima_alloc_pages() - Allocate contiguous pages.
+ * @max_size: Maximum amount of memory to allocate.
+ * @allocated_size: Returned size of actual allocation.
+ * @last_warn: Should the min_size allocation warn or not.
+ *
+ * Tries to do opportunistic allocation for memory first trying to allocate
+ * max_size amount of memory and then splitting that until zero order is
+ * reached. Allocation is tried without generating allocation warnings unless
+ * last_warn is set. Last_warn set affects only last allocation of zero order.
+ *
+ * By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL)
+ *
+ * Return pointer to allocated memory, or NULL on failure.
*/
+static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size,
+ int last_warn)
+{
+ void *ptr;
+ int order = ima_maxorder;
+ gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY;
+
+ if (order)
+ order = min(get_order(max_size), order);
+
+ for (; order; order--) {
+ ptr = (void *)__get_free_pages(gfp_mask, order);
+ if (ptr) {
+ *allocated_size = PAGE_SIZE << order;
+ return ptr;
+ }
+ }
+
+ /* order is zero - one page */
+
+ gfp_mask = GFP_KERNEL;
+
+ if (!last_warn)
+ gfp_mask |= __GFP_NOWARN;
+
+ ptr = (void *)__get_free_pages(gfp_mask, 0);
+ if (ptr) {
+ *allocated_size = PAGE_SIZE;
+ return ptr;
+ }
+
+ *allocated_size = 0;
+ return NULL;
+}
+
+/**
+ * ima_free_pages() - Free pages allocated by ima_alloc_pages().
+ * @ptr: Pointer to allocated pages.
+ * @size: Size of allocated buffer.
+ */
+static void ima_free_pages(void *ptr, size_t size)
+{
+ if (!ptr)
+ return;
+ free_pages((unsigned long)ptr, get_order(size));
+}
+
+static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo)
+{
+ struct crypto_ahash *tfm = ima_ahash_tfm;
+ int rc;
+
+ if (algo < 0 || algo >= HASH_ALGO__LAST)
+ algo = ima_hash_algo;
+
+ if (algo != ima_hash_algo || !tfm) {
+ tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0);
+ if (!IS_ERR(tfm)) {
+ if (algo == ima_hash_algo)
+ ima_ahash_tfm = tfm;
+ } else {
+ rc = PTR_ERR(tfm);
+ pr_err("Can not allocate %s (reason: %d)\n",
+ hash_algo_name[algo], rc);
+ }
+ }
+ return tfm;
+}
+
+static void ima_free_atfm(struct crypto_ahash *tfm)
+{
+ if (tfm != ima_ahash_tfm)
+ crypto_free_ahash(tfm);
+}
+
+static void ahash_complete(struct crypto_async_request *req, int err)
+{
+ struct ahash_completion *res = req->data;
+
+ if (err == -EINPROGRESS)
+ return;
+ res->err = err;
+ complete(&res->completion);
+}
+
+static int ahash_wait(int err, struct ahash_completion *res)
+{
+ switch (err) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ wait_for_completion(&res->completion);
+ reinit_completion(&res->completion);
+ err = res->err;
+ /* fall through */
+ default:
+ pr_crit_ratelimited("ahash calculation failed: err: %d\n", err);
+ }
+
+ return err;
+}
+
+static int ima_calc_file_hash_atfm(struct file *file,
+ struct ima_digest_data *hash,
+ struct crypto_ahash *tfm)
+{
+ loff_t i_size, offset;
+ char *rbuf[2] = { NULL, };
+ int rc, read = 0, rbuf_len, active = 0, ahash_rc = 0;
+ struct ahash_request *req;
+ struct scatterlist sg[1];
+ struct ahash_completion res;
+ size_t rbuf_size[2];
+
+ hash->length = crypto_ahash_digestsize(tfm);
+
+ req = ahash_request_alloc(tfm, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ init_completion(&res.completion);
+ ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ ahash_complete, &res);
+
+ rc = ahash_wait(crypto_ahash_init(req), &res);
+ if (rc)
+ goto out1;
+
+ i_size = i_size_read(file_inode(file));
+
+ if (i_size == 0)
+ goto out2;
+
+ /*
+ * Try to allocate maximum size of memory.
+ * Fail if even a single page cannot be allocated.
+ */
+ rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1);
+ if (!rbuf[0]) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+
+ /* Only allocate one buffer if that is enough. */
+ if (i_size > rbuf_size[0]) {
+ /*
+ * Try to allocate secondary buffer. If that fails fallback to
+ * using single buffering. Use previous memory allocation size
+ * as baseline for possible allocation size.
+ */
+ rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0],
+ &rbuf_size[1], 0);
+ }
+
+ if (!(file->f_mode & FMODE_READ)) {
+ file->f_mode |= FMODE_READ;
+ read = 1;
+ }
+
+ for (offset = 0; offset < i_size; offset += rbuf_len) {
+ if (!rbuf[1] && offset) {
+ /* Not using two buffers, and it is not the first
+ * read/request, wait for the completion of the
+ * previous ahash_update() request.
+ */
+ rc = ahash_wait(ahash_rc, &res);
+ if (rc)
+ goto out3;
+ }
+ /* read buffer */
+ rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
+ rc = integrity_kernel_read(file, offset, rbuf[active],
+ rbuf_len);
+ if (rc != rbuf_len)
+ goto out3;
+
+ if (rbuf[1] && offset) {
+ /* Using two buffers, and it is not the first
+ * read/request, wait for the completion of the
+ * previous ahash_update() request.
+ */
+ rc = ahash_wait(ahash_rc, &res);
+ if (rc)
+ goto out3;
+ }
+
+ sg_init_one(&sg[0], rbuf[active], rbuf_len);
+ ahash_request_set_crypt(req, sg, NULL, rbuf_len);
+
+ ahash_rc = crypto_ahash_update(req);
+
+ if (rbuf[1])
+ active = !active; /* swap buffers, if we use two */
+ }
+ /* wait for the last update request to complete */
+ rc = ahash_wait(ahash_rc, &res);
+out3:
+ if (read)
+ file->f_mode &= ~FMODE_READ;
+ ima_free_pages(rbuf[0], rbuf_size[0]);
+ ima_free_pages(rbuf[1], rbuf_size[1]);
+out2:
+ if (!rc) {
+ ahash_request_set_crypt(req, NULL, hash->digest, 0);
+ rc = ahash_wait(crypto_ahash_final(req), &res);
+ }
+out1:
+ ahash_request_free(req);
+ return rc;
+}
+
+static int ima_calc_file_ahash(struct file *file, struct ima_digest_data *hash)
+{
+ struct crypto_ahash *tfm;
+ int rc;
+
+ tfm = ima_alloc_atfm(hash->algo);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ rc = ima_calc_file_hash_atfm(file, hash, tfm);
+
+ ima_free_atfm(tfm);
+
+ return rc;
+}
+
static int ima_calc_file_hash_tfm(struct file *file,
struct ima_digest_data *hash,
struct crypto_shash *tfm)
@@ -134,7 +388,7 @@ static int ima_calc_file_hash_tfm(struct file *file,
while (offset < i_size) {
int rbuf_len;
- rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE);
+ rbuf_len = integrity_kernel_read(file, offset, rbuf, PAGE_SIZE);
if (rbuf_len < 0) {
rc = rbuf_len;
break;
@@ -156,7 +410,7 @@ out:
return rc;
}
-int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
+static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash)
{
struct crypto_shash *tfm;
int rc;
@@ -173,6 +427,35 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
}
/*
+ * ima_calc_file_hash - calculate file hash
+ *
+ * Asynchronous hash (ahash) allows using HW acceleration for calculating
+ * a hash. ahash performance varies for different data sizes on different
+ * crypto accelerators. shash performance might be better for smaller files.
+ * The 'ima.ahash_minsize' module parameter allows specifying the best
+ * minimum file size for using ahash on the system.
+ *
+ * If the ima.ahash_minsize parameter is not specified, this function uses
+ * shash for the hash calculation. If ahash fails, it falls back to using
+ * shash.
+ */
+int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
+{
+ loff_t i_size;
+ int rc;
+
+ i_size = i_size_read(file_inode(file));
+
+ if (ima_ahash_minsize && i_size >= ima_ahash_minsize) {
+ rc = ima_calc_file_ahash(file, hash);
+ if (!rc)
+ return 0;
+ }
+
+ return ima_calc_file_shash(file, hash);
+}
+
+/*
* Calculate the hash of template data
*/
static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index da92fcc08d15..461215e5fd31 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -118,6 +118,7 @@ static int ima_measurements_show(struct seq_file *m, void *v)
/* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
+ char *template_name;
int namelen;
u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
bool is_ima_template = false;
@@ -128,6 +129,9 @@ static int ima_measurements_show(struct seq_file *m, void *v)
if (e == NULL)
return -1;
+ template_name = (e->template_desc->name[0] != '\0') ?
+ e->template_desc->name : e->template_desc->fmt;
+
/*
* 1st: PCRIndex
* PCR used is always the same (config option) in
@@ -139,14 +143,14 @@ static int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, e->digest, TPM_DIGEST_SIZE);
/* 3rd: template name size */
- namelen = strlen(e->template_desc->name);
+ namelen = strlen(template_name);
ima_putc(m, &namelen, sizeof(namelen));
/* 4th: template name */
- ima_putc(m, e->template_desc->name, namelen);
+ ima_putc(m, template_name, namelen);
/* 5th: template length (except for 'ima' template) */
- if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0)
+ if (strcmp(template_name, IMA_TEMPLATE_IMA_NAME) == 0)
is_ima_template = true;
if (!is_ima_template)
@@ -200,6 +204,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
/* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
+ char *template_name;
int i;
/* get entry */
@@ -207,6 +212,9 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
if (e == NULL)
return -1;
+ template_name = (e->template_desc->name[0] != '\0') ?
+ e->template_desc->name : e->template_desc->fmt;
+
/* 1st: PCR used (config option) */
seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
@@ -214,7 +222,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);
/* 3th: template name */
- seq_printf(m, " %s", e->template_desc->name);
+ seq_printf(m, " %s", template_name);
/* 4th: template specific data */
for (i = 0; i < e->template_desc->num_fields; i++) {
@@ -288,7 +296,12 @@ static struct dentry *runtime_measurements_count;
static struct dentry *violations;
static struct dentry *ima_policy;
-static atomic_t policy_opencount = ATOMIC_INIT(1);
+enum ima_fs_flags {
+ IMA_FS_BUSY,
+};
+
+static unsigned long ima_fs_flags;
+
/*
* ima_open_policy: sequentialize access to the policy file
*/
@@ -297,9 +310,9 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
/* No point in being allowed to open it if you aren't going to write */
if (!(filp->f_flags & O_WRONLY))
return -EACCES;
- if (atomic_dec_and_test(&policy_opencount))
- return 0;
- return -EBUSY;
+ if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
+ return -EBUSY;
+ return 0;
}
/*
@@ -311,10 +324,16 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
*/
static int ima_release_policy(struct inode *inode, struct file *file)
{
+ const char *cause = valid_policy ? "completed" : "failed";
+
+ pr_info("IMA: policy update %s\n", cause);
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
+ "policy_update", cause, !valid_policy, 0);
+
if (!valid_policy) {
ima_delete_rules();
valid_policy = 1;
- atomic_set(&policy_opencount, 1);
+ clear_bit(IMA_FS_BUSY, &ima_fs_flags);
return 0;
}
ima_update_policy();
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index e8f9d70a465d..5e4c29d174ee 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -24,6 +24,12 @@
#include <crypto/hash_info.h>
#include "ima.h"
+#ifdef CONFIG_IMA_X509_PATH
+#define IMA_X509_PATH CONFIG_IMA_X509_PATH
+#else
+#define IMA_X509_PATH "/etc/keys/x509_ima.der"
+#endif
+
/* name for boot aggregate entry */
static const char *boot_aggregate_name = "boot_aggregate";
int ima_used_chip;
@@ -43,7 +49,7 @@ int ima_used_chip;
* a different value.) Violations add a zero entry to the measurement
* list and extend the aggregate PCR value with ff...ff's.
*/
-static void __init ima_add_boot_aggregate(void)
+static int __init ima_add_boot_aggregate(void)
{
static const char op[] = "add_boot_aggregate";
const char *audit_cause = "ENOMEM";
@@ -72,18 +78,35 @@ static void __init ima_add_boot_aggregate(void)
result = ima_alloc_init_template(iint, NULL, boot_aggregate_name,
NULL, 0, &entry);
- if (result < 0)
- return;
+ if (result < 0) {
+ audit_cause = "alloc_entry";
+ goto err_out;
+ }
result = ima_store_template(entry, violation, NULL,
boot_aggregate_name);
- if (result < 0)
+ if (result < 0) {
ima_free_template_entry(entry);
- return;
+ audit_cause = "store_entry";
+ goto err_out;
+ }
+ return 0;
err_out:
integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
audit_cause, result, 0);
+ return result;
+}
+
+#ifdef CONFIG_IMA_LOAD_X509
+void __init ima_load_x509(void)
+{
+ int unset_flags = ima_policy_flag & IMA_APPRAISE;
+
+ ima_policy_flag &= ~unset_flags;
+ integrity_load_x509(INTEGRITY_KEYRING_IMA, IMA_X509_PATH);
+ ima_policy_flag |= unset_flags;
}
+#endif
int __init ima_init(void)
{
@@ -98,6 +121,10 @@ int __init ima_init(void)
if (!ima_used_chip)
pr_info("No TPM chip found, activating TPM-bypass!\n");
+ rc = ima_init_keyring(INTEGRITY_KEYRING_IMA);
+ if (rc)
+ return rc;
+
rc = ima_init_crypto();
if (rc)
return rc;
@@ -105,7 +132,10 @@ int __init ima_init(void)
if (rc != 0)
return rc;
- ima_add_boot_aggregate(); /* boot aggregate must be first entry */
+ rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
+ if (rc != 0)
+ return rc;
+
ima_init_policy();
return ima_fs_init();
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 09baa335ebc7..eeee00dce729 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -77,46 +77,39 @@ __setup("ima_hash=", hash_setup);
* could result in a file measurement error.
*
*/
-static void ima_rdwr_violation_check(struct file *file)
+static void ima_rdwr_violation_check(struct file *file,
+ struct integrity_iint_cache *iint,
+ int must_measure,
+ char **pathbuf,
+ const char **pathname)
{
struct inode *inode = file_inode(file);
fmode_t mode = file->f_mode;
bool send_tomtou = false, send_writers = false;
- char *pathbuf = NULL;
- const char *pathname;
-
- if (!S_ISREG(inode->i_mode) || !ima_initialized)
- return;
-
- mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */
if (mode & FMODE_WRITE) {
if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
- struct integrity_iint_cache *iint;
- iint = integrity_iint_find(inode);
+ if (!iint)
+ iint = integrity_iint_find(inode);
/* IMA_MEASURE is set from reader side */
if (iint && (iint->flags & IMA_MEASURE))
send_tomtou = true;
}
} else {
- if ((atomic_read(&inode->i_writecount) > 0) &&
- ima_must_measure(inode, MAY_READ, FILE_CHECK))
+ if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
send_writers = true;
}
- mutex_unlock(&inode->i_mutex);
-
if (!send_tomtou && !send_writers)
return;
- pathname = ima_d_path(&file->f_path, &pathbuf);
+ *pathname = ima_d_path(&file->f_path, pathbuf);
if (send_tomtou)
- ima_add_violation(file, pathname, "invalid_pcr", "ToMToU");
+ ima_add_violation(file, *pathname, "invalid_pcr", "ToMToU");
if (send_writers)
- ima_add_violation(file, pathname,
+ ima_add_violation(file, *pathname,
"invalid_pcr", "open_writers");
- kfree(pathbuf);
}
static void ima_check_last_writer(struct integrity_iint_cache *iint,
@@ -128,11 +121,13 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
return;
mutex_lock(&inode->i_mutex);
- if (atomic_read(&inode->i_writecount) == 1 &&
- iint->version != inode->i_version) {
- iint->flags &= ~IMA_DONE_MASK;
- if (iint->flags & IMA_APPRAISE)
- ima_update_xattr(iint, file);
+ if (atomic_read(&inode->i_writecount) == 1) {
+ if ((iint->version != inode->i_version) ||
+ (iint->flags & IMA_NEW_FILE)) {
+ iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
+ if (iint->flags & IMA_APPRAISE)
+ ima_update_xattr(iint, file);
+ }
}
mutex_unlock(&inode->i_mutex);
}
@@ -148,7 +143,7 @@ void ima_file_free(struct file *file)
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint;
- if (!iint_initialized || !S_ISREG(inode->i_mode))
+ if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return;
iint = integrity_iint_find(inode);
@@ -158,19 +153,20 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}
-static int process_measurement(struct file *file, const char *filename,
- int mask, int function)
+static int process_measurement(struct file *file, int mask, int function,
+ int opened)
{
struct inode *inode = file_inode(file);
- struct integrity_iint_cache *iint;
- struct ima_template_desc *template_desc = ima_template_desc_current();
+ struct integrity_iint_cache *iint = NULL;
+ struct ima_template_desc *template_desc;
char *pathbuf = NULL;
const char *pathname = NULL;
- int rc = -ENOMEM, action, must_appraise, _func;
+ int rc = -ENOMEM, action, must_appraise;
struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL;
int xattr_len = 0;
+ bool violation_check;
- if (!ima_initialized || !S_ISREG(inode->i_mode))
+ if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return 0;
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
@@ -178,19 +174,33 @@ static int process_measurement(struct file *file, const char *filename,
* Included is the appraise submask.
*/
action = ima_get_action(inode, mask, function);
- if (!action)
+ violation_check = ((function == FILE_CHECK || function == MMAP_CHECK) &&
+ (ima_policy_flag & IMA_MEASURE));
+ if (!action && !violation_check)
return 0;
must_appraise = action & IMA_APPRAISE;
/* Is the appraise rule hook specific? */
- _func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function;
+ if (action & IMA_FILE_APPRAISE)
+ function = FILE_CHECK;
mutex_lock(&inode->i_mutex);
- iint = integrity_inode_get(inode);
- if (!iint)
- goto out;
+ if (action) {
+ iint = integrity_inode_get(inode);
+ if (!iint)
+ goto out;
+ }
+
+ if (violation_check) {
+ ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
+ &pathbuf, &pathname);
+ if (!action) {
+ rc = 0;
+ goto out_free;
+ }
+ }
/* Determine if already appraised/measured based on bitmask
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
@@ -203,14 +213,13 @@ static int process_measurement(struct file *file, const char *filename,
/* Nothing to do, just return existing appraised status */
if (!action) {
if (must_appraise)
- rc = ima_get_cache_status(iint, _func);
+ rc = ima_get_cache_status(iint, function);
goto out_digsig;
}
- if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) {
- if (action & IMA_APPRAISE_SUBMASK)
- xattr_ptr = &xattr_value;
- } else
+ template_desc = ima_template_desc_current();
+ if ((action & IMA_APPRAISE_SUBMASK) ||
+ strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
xattr_ptr = &xattr_value;
rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len);
@@ -220,23 +229,27 @@ static int process_measurement(struct file *file, const char *filename,
goto out_digsig;
}
- pathname = filename ?: ima_d_path(&file->f_path, &pathbuf);
+ if (!pathname) /* ima_rdwr_violation possibly pre-fetched */
+ pathname = ima_d_path(&file->f_path, &pathbuf);
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len);
if (action & IMA_APPRAISE_SUBMASK)
- rc = ima_appraise_measurement(_func, iint, file, pathname,
- xattr_value, xattr_len);
+ rc = ima_appraise_measurement(function, iint, file, pathname,
+ xattr_value, xattr_len, opened);
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname);
- kfree(pathbuf);
+
out_digsig:
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
rc = -EACCES;
+ kfree(xattr_value);
+out_free:
+ if (pathbuf)
+ __putname(pathbuf);
out:
mutex_unlock(&inode->i_mutex);
- kfree(xattr_value);
if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
return -EACCES;
return 0;
@@ -256,7 +269,7 @@ out:
int ima_file_mmap(struct file *file, unsigned long prot)
{
if (file && (prot & PROT_EXEC))
- return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK);
+ return process_measurement(file, MAY_EXEC, MMAP_CHECK, 0);
return 0;
}
@@ -275,10 +288,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
*/
int ima_bprm_check(struct linux_binprm *bprm)
{
- return process_measurement(bprm->file,
- (strcmp(bprm->filename, bprm->interp) == 0) ?
- bprm->filename : bprm->interp,
- MAY_EXEC, BPRM_CHECK);
+ return process_measurement(bprm->file, MAY_EXEC, BPRM_CHECK, 0);
}
/**
@@ -291,12 +301,11 @@ int ima_bprm_check(struct linux_binprm *bprm)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_file_check(struct file *file, int mask)
+int ima_file_check(struct file *file, int mask, int opened)
{
- ima_rdwr_violation_check(file);
- return process_measurement(file, NULL,
+ return process_measurement(file,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
- FILE_CHECK);
+ FILE_CHECK, opened);
}
EXPORT_SYMBOL_GPL(ima_file_check);
@@ -319,7 +328,18 @@ int ima_module_check(struct file *file)
#endif
return 0; /* We rely on module signature checking */
}
- return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK);
+ return process_measurement(file, MAY_EXEC, MODULE_CHECK, 0);
+}
+
+int ima_fw_from_file(struct file *file, char *buf, size_t size)
+{
+ if (!file) {
+ if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
+ (ima_appraise & IMA_APPRAISE_ENFORCE))
+ return -EACCES; /* INTEGRITY_UNKNOWN */
+ return 0;
+ }
+ return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0);
}
static int __init init_ima(void)
@@ -328,8 +348,10 @@ static int __init init_ima(void)
hash_setup(CONFIG_IMA_DEFAULT_HASH);
error = ima_init();
- if (!error)
+ if (!error) {
ima_initialized = 1;
+ ima_update_policy_flag();
+ }
return error;
}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 40a7488f6721..d1eefb9d65fb 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -35,6 +35,8 @@
#define DONT_APPRAISE 0x0008
#define AUDIT 0x0040
+int ima_policy_flag;
+
#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
@@ -84,6 +86,7 @@ static struct ima_rule_entry default_rules[] = {
{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID,
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
{.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC},
+ {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC},
};
static struct ima_rule_entry default_appraise_rules[] = {
@@ -97,7 +100,13 @@ static struct ima_rule_entry default_appraise_rules[] = {
{.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+#ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT
{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER},
+#else
+ /* force signature */
+ {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID,
+ .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED},
+#endif
};
static LIST_HEAD(ima_default_rules);
@@ -241,6 +250,8 @@ static int get_subaction(struct ima_rule_entry *rule, int func)
return IMA_BPRM_APPRAISE;
case MODULE_CHECK:
return IMA_MODULE_APPRAISE;
+ case FIRMWARE_CHECK:
+ return IMA_FIRMWARE_APPRAISE;
case FILE_CHECK:
default:
return IMA_FILE_APPRAISE;
@@ -292,6 +303,26 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
return action;
}
+/*
+ * Initialize the ima_policy_flag variable based on the currently
+ * loaded policy. Based on this flag, the decision to short circuit
+ * out of a function or not call the function in the first place
+ * can be made earlier.
+ */
+void ima_update_policy_flag(void)
+{
+ struct ima_rule_entry *entry;
+
+ ima_policy_flag = 0;
+ list_for_each_entry(entry, ima_rules, list) {
+ if (entry->action & IMA_DO_MASK)
+ ima_policy_flag |= entry->action;
+ }
+
+ if (!ima_appraise)
+ ima_policy_flag &= ~IMA_APPRAISE;
+}
+
/**
* ima_init_policy - initialize the default measure rules.
*
@@ -331,18 +362,8 @@ void __init ima_init_policy(void)
*/
void ima_update_policy(void)
{
- static const char op[] = "policy_update";
- const char *cause = "already exists";
- int result = 1;
- int audit_info = 0;
-
- if (ima_rules == &ima_default_rules) {
- ima_rules = &ima_policy_rules;
- cause = "complete";
- result = 0;
- }
- integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
- NULL, op, cause, result, audit_info);
+ ima_rules = &ima_policy_rules;
+ ima_update_policy_flag();
}
enum {
@@ -486,6 +507,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = FILE_CHECK;
else if (strcmp(args[0].from, "MODULE_CHECK") == 0)
entry->func = MODULE_CHECK;
+ else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0)
+ entry->func = FIRMWARE_CHECK;
else if ((strcmp(args[0].from, "FILE_MMAP") == 0)
|| (strcmp(args[0].from, "MMAP_CHECK") == 0))
entry->func = MMAP_CHECK;
@@ -636,6 +659,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
result = -EINVAL;
else if (entry->func == MODULE_CHECK)
ima_appraise |= IMA_APPRAISE_MODULES;
+ else if (entry->func == FIRMWARE_CHECK)
+ ima_appraise |= IMA_APPRAISE_FIRMWARE;
audit_log_format(ab, "res=%d", !result);
audit_log_end(ab);
return result;
@@ -656,13 +681,12 @@ ssize_t ima_parse_add_rule(char *rule)
ssize_t result, len;
int audit_info = 0;
- /* Prevent installed policy from changing */
- if (ima_rules != &ima_default_rules) {
- integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
- NULL, op, "already exists",
- -EACCES, audit_info);
- return -EACCES;
- }
+ p = strsep(&rule, "\n");
+ len = strlen(p) + 1;
+ p += strspn(p, " \t");
+
+ if (*p == '#' || *p == '\0')
+ return len;
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
@@ -673,19 +697,11 @@ ssize_t ima_parse_add_rule(char *rule)
INIT_LIST_HEAD(&entry->list);
- p = strsep(&rule, "\n");
- len = strlen(p) + 1;
-
- if (*p == '#') {
- kfree(entry);
- return len;
- }
-
result = ima_parse_rule(p, entry);
if (result) {
kfree(entry);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
- NULL, op, "invalid policy", result,
+ NULL, op, "invalid-policy", result,
audit_info);
return result;
}
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index a076a967ec47..0b7404ebfa80 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -24,6 +24,7 @@ static struct ima_template_desc defined_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
+ {.name = "", .fmt = ""}, /* placeholder for a custom format */
};
static struct ima_template_field supported_fields[] = {
@@ -41,19 +42,28 @@ static struct ima_template_field supported_fields[] = {
static struct ima_template_desc *ima_template;
static struct ima_template_desc *lookup_template_desc(const char *name);
+static int template_desc_init_fields(const char *template_fmt,
+ struct ima_template_field ***fields,
+ int *num_fields);
static int __init ima_template_setup(char *str)
{
struct ima_template_desc *template_desc;
int template_len = strlen(str);
+ if (ima_template)
+ return 1;
+
/*
* Verify that a template with the supplied name exists.
* If not, use CONFIG_IMA_DEFAULT_TEMPLATE.
*/
template_desc = lookup_template_desc(str);
- if (!template_desc)
+ if (!template_desc) {
+ pr_err("template %s not found, using %s\n",
+ str, CONFIG_IMA_DEFAULT_TEMPLATE);
return 1;
+ }
/*
* Verify whether the current hash algorithm is supported
@@ -70,6 +80,25 @@ static int __init ima_template_setup(char *str)
}
__setup("ima_template=", ima_template_setup);
+static int __init ima_template_fmt_setup(char *str)
+{
+ int num_templates = ARRAY_SIZE(defined_templates);
+
+ if (ima_template)
+ return 1;
+
+ if (template_desc_init_fields(str, NULL, NULL) < 0) {
+ pr_err("format string '%s' not valid, using template %s\n",
+ str, CONFIG_IMA_DEFAULT_TEMPLATE);
+ return 1;
+ }
+
+ defined_templates[num_templates - 1].fmt = str;
+ ima_template = defined_templates + num_templates - 1;
+ return 1;
+}
+__setup("ima_template_fmt=", ima_template_fmt_setup);
+
static struct ima_template_desc *lookup_template_desc(const char *name)
{
int i;
@@ -113,61 +142,46 @@ static int template_desc_init_fields(const char *template_fmt,
struct ima_template_field ***fields,
int *num_fields)
{
- char *c, *template_fmt_copy, *template_fmt_ptr;
+ const char *template_fmt_ptr;
+ struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];
int template_num_fields = template_fmt_size(template_fmt);
- int i, result = 0;
+ int i, len;
- if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX)
+ if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) {
+ pr_err("format string '%s' contains too many fields\n",
+ template_fmt);
return -EINVAL;
-
- /* copying is needed as strsep() modifies the original buffer */
- template_fmt_copy = kstrdup(template_fmt, GFP_KERNEL);
- if (template_fmt_copy == NULL)
- return -ENOMEM;
-
- *fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL);
- if (*fields == NULL) {
- result = -ENOMEM;
- goto out;
}
- template_fmt_ptr = template_fmt_copy;
- for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL &&
- i < template_num_fields; i++) {
- struct ima_template_field *f = lookup_template_field(c);
+ for (i = 0, template_fmt_ptr = template_fmt; i < template_num_fields;
+ i++, template_fmt_ptr += len + 1) {
+ char tmp_field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN + 1];
- if (!f) {
- result = -ENOENT;
- goto out;
+ len = strchrnul(template_fmt_ptr, '|') - template_fmt_ptr;
+ if (len == 0 || len > IMA_TEMPLATE_FIELD_ID_MAX_LEN) {
+ pr_err("Invalid field with length %d\n", len);
+ return -EINVAL;
}
- (*fields)[i] = f;
- }
- *num_fields = i;
-out:
- if (result < 0) {
- kfree(*fields);
- *fields = NULL;
- }
- kfree(template_fmt_copy);
- return result;
-}
-static int init_defined_templates(void)
-{
- int i = 0;
- int result = 0;
+ memcpy(tmp_field_id, template_fmt_ptr, len);
+ tmp_field_id[len] = '\0';
+ found_fields[i] = lookup_template_field(tmp_field_id);
+ if (!found_fields[i]) {
+ pr_err("field '%s' not found\n", tmp_field_id);
+ return -ENOENT;
+ }
+ }
- /* Init defined templates. */
- for (i = 0; i < ARRAY_SIZE(defined_templates); i++) {
- struct ima_template_desc *template = &defined_templates[i];
+ if (fields && num_fields) {
+ *fields = kmalloc_array(i, sizeof(*fields), GFP_KERNEL);
+ if (*fields == NULL)
+ return -ENOMEM;
- result = template_desc_init_fields(template->fmt,
- &(template->fields),
- &(template->num_fields));
- if (result < 0)
- return result;
+ memcpy(*fields, found_fields, i * sizeof(*fields));
+ *num_fields = i;
}
- return result;
+
+ return 0;
}
struct ima_template_desc *ima_template_desc_current(void)
@@ -178,13 +192,18 @@ struct ima_template_desc *ima_template_desc_current(void)
return ima_template;
}
-int ima_init_template(void)
+int __init ima_init_template(void)
{
+ struct ima_template_desc *template = ima_template_desc_current();
int result;
- result = init_defined_templates();
+ result = template_desc_init_fields(template->fmt,
+ &(template->fields),
+ &(template->num_fields));
if (result < 0)
- return result;
+ pr_err("template %s init failed, result: %d\n",
+ (strlen(template->name) ?
+ template->name : template->fmt), result);
- return 0;
+ return result;
}