summaryrefslogtreecommitdiffstats
path: root/security/integrity/ima
diff options
context:
space:
mode:
Diffstat (limited to 'security/integrity/ima')
-rw-r--r--security/integrity/ima/ima.h21
-rw-r--r--security/integrity/ima/ima_api.c27
-rw-r--r--security/integrity/ima/ima_appraise.c92
-rw-r--r--security/integrity/ima/ima_crypto.c81
-rw-r--r--security/integrity/ima/ima_init.c3
-rw-r--r--security/integrity/ima/ima_main.c138
-rw-r--r--security/integrity/ima/ima_policy.c138
7 files changed, 341 insertions, 159 deletions
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 079a85dc37b2..a41c9c18e5e0 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -84,11 +84,12 @@ 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);
-int ima_calc_hash(struct file *file, char *digest);
-int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_file_hash(struct file *file, char *digest);
+int ima_calc_buffer_hash(const void *data, int len, char *digest);
int ima_calc_boot_aggregate(char *digest);
void ima_add_violation(struct inode *inode, const unsigned char *filename,
const char *op, const char *cause);
+int ima_init_crypto(void);
/*
* used to protect h_table and sha_table
@@ -119,6 +120,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
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);
+const char *ima_d_path(struct path *path, char **pathbuf);
/* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode.
@@ -127,7 +129,7 @@ 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, MODULE_CHECK, POST_SETATTR };
+enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
int flags);
@@ -142,13 +144,16 @@ void ima_delete_rules(void);
#define IMA_APPRAISE_MODULES 0x04
#ifdef CONFIG_IMA_APPRAISE
-int ima_appraise_measurement(struct integrity_iint_cache *iint,
+int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename);
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,
+ int func);
#else
-static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
+static inline int ima_appraise_measurement(int func,
+ struct integrity_iint_cache *iint,
struct file *file,
const unsigned char *filename)
{
@@ -165,6 +170,12 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint,
struct file *file)
{
}
+
+static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
+ *iint, int func)
+{
+ return INTEGRITY_UNKNOWN;
+}
#endif
/* LSM based policy rules require audit */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 0cea3db21657..d9030b29d84d 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -50,8 +50,8 @@ int ima_store_template(struct ima_template_entry *entry,
entry->template_len = sizeof(entry->template);
if (!violation) {
- result = ima_calc_template_hash(entry->template_len,
- &entry->template,
+ result = ima_calc_buffer_hash(&entry->template,
+ entry->template_len,
entry->digest);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
@@ -100,12 +100,12 @@ err_out:
* ima_get_action - 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, MODULE_CHECK)
+ * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK)
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
- * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP | MODULE_CHECK
+ * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
* mask: contains the permission mask
* fsmagic: hex value
*
@@ -148,7 +148,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
u64 i_version = file->f_dentry->d_inode->i_version;
iint->ima_xattr.type = IMA_XATTR_DIGEST;
- result = ima_calc_hash(file, iint->ima_xattr.digest);
+ result = ima_calc_file_hash(file, iint->ima_xattr.digest);
if (!result) {
iint->version = i_version;
iint->flags |= IMA_COLLECTED;
@@ -237,3 +237,20 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
iint->flags |= IMA_AUDITED;
}
+
+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);
+ if (*pathbuf) {
+ pathname = d_path(path, *pathbuf, PATH_MAX + 11);
+ if (IS_ERR(pathname)) {
+ kfree(*pathbuf);
+ *pathbuf = NULL;
+ pathname = NULL;
+ }
+ }
+ return pathname;
+}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index bdc8ba1d1d27..2d4becab8918 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -42,12 +42,69 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
return ima_match_policy(inode, func, mask, IMA_APPRAISE);
}
-static void ima_fix_xattr(struct dentry *dentry,
+static int ima_fix_xattr(struct dentry *dentry,
struct integrity_iint_cache *iint)
{
iint->ima_xattr.type = IMA_XATTR_DIGEST;
- __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
- sizeof iint->ima_xattr, 0);
+ return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
+ (u8 *)&iint->ima_xattr,
+ sizeof(iint->ima_xattr), 0);
+}
+
+/* Return specific func appraised cached result */
+enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+ int func)
+{
+ switch(func) {
+ case MMAP_CHECK:
+ return iint->ima_mmap_status;
+ case BPRM_CHECK:
+ return iint->ima_bprm_status;
+ case MODULE_CHECK:
+ return iint->ima_module_status;
+ case FILE_CHECK:
+ default:
+ return iint->ima_file_status;
+ }
+}
+
+static void ima_set_cache_status(struct integrity_iint_cache *iint,
+ int func, enum integrity_status status)
+{
+ switch(func) {
+ case MMAP_CHECK:
+ iint->ima_mmap_status = status;
+ break;
+ case BPRM_CHECK:
+ iint->ima_bprm_status = status;
+ break;
+ case MODULE_CHECK:
+ iint->ima_module_status = status;
+ break;
+ case FILE_CHECK:
+ default:
+ iint->ima_file_status = status;
+ break;
+ }
+}
+
+static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
+{
+ switch(func) {
+ case MMAP_CHECK:
+ iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
+ break;
+ case BPRM_CHECK:
+ iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
+ break;
+ case MODULE_CHECK:
+ iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED);
+ break;
+ case FILE_CHECK:
+ default:
+ iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
+ break;
+ }
}
/*
@@ -58,7 +115,7 @@ static void ima_fix_xattr(struct dentry *dentry,
*
* Return 0 on success, error code otherwise
*/
-int ima_appraise_measurement(struct integrity_iint_cache *iint,
+int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename)
{
struct dentry *dentry = file->f_dentry;
@@ -74,9 +131,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
if (!inode->i_op->getxattr)
return INTEGRITY_UNKNOWN;
- if (iint->flags & IMA_APPRAISED)
- return iint->ima_status;
-
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
0, GFP_NOFS);
if (rc <= 0) {
@@ -98,19 +152,18 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
cause = "invalid-HMAC";
goto out;
}
-
switch (xattr_value->type) {
case IMA_XATTR_DIGEST:
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ cause = "IMA signature required";
+ status = INTEGRITY_FAIL;
+ break;
+ }
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
IMA_DIGEST_SIZE);
if (rc) {
cause = "invalid-hash";
status = INTEGRITY_FAIL;
- print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
- xattr_value, sizeof(*xattr_value));
- print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
- (u8 *)&iint->ima_xattr,
- sizeof iint->ima_xattr);
break;
}
status = INTEGRITY_PASS;
@@ -141,15 +194,15 @@ out:
if ((ima_appraise & IMA_APPRAISE_FIX) &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
- ima_fix_xattr(dentry, iint);
- status = INTEGRITY_PASS;
+ if (!ima_fix_xattr(dentry, iint))
+ status = INTEGRITY_PASS;
}
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
op, cause, rc, 0);
} else {
- iint->flags |= IMA_APPRAISED;
+ ima_cache_flags(iint, func);
}
- iint->ima_status = status;
+ ima_set_cache_status(iint, func, status);
kfree(xattr_value);
return status;
}
@@ -195,10 +248,11 @@ void ima_inode_post_setattr(struct dentry *dentry)
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
iint = integrity_iint_find(inode);
if (iint) {
+ iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
+ IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
+ IMA_ACTION_FLAGS);
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);
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index b21ee5b5495a..b691e0f3830c 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -19,38 +19,41 @@
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <crypto/hash.h>
#include "ima.h"
-static int init_desc(struct hash_desc *desc)
+static struct crypto_shash *ima_shash_tfm;
+
+int ima_init_crypto(void)
{
- int rc;
+ long rc;
- desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(desc->tfm)) {
- pr_info("IMA: failed to load %s transform: %ld\n",
- ima_hash, PTR_ERR(desc->tfm));
- rc = PTR_ERR(desc->tfm);
+ ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0);
+ if (IS_ERR(ima_shash_tfm)) {
+ rc = PTR_ERR(ima_shash_tfm);
+ pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc);
return rc;
}
- desc->flags = 0;
- rc = crypto_hash_init(desc);
- if (rc)
- crypto_free_hash(desc->tfm);
- return rc;
+ return 0;
}
/*
* Calculate the MD5/SHA1 file digest
*/
-int ima_calc_hash(struct file *file, char *digest)
+int ima_calc_file_hash(struct file *file, char *digest)
{
- struct hash_desc desc;
- struct scatterlist sg[1];
loff_t i_size, offset = 0;
char *rbuf;
int rc, read = 0;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(ima_shash_tfm)];
+ } desc;
- rc = init_desc(&desc);
+ desc.shash.tfm = ima_shash_tfm;
+ desc.shash.flags = 0;
+
+ rc = crypto_shash_init(&desc.shash);
if (rc != 0)
return rc;
@@ -75,41 +78,34 @@ int ima_calc_hash(struct file *file, char *digest)
if (rbuf_len == 0)
break;
offset += rbuf_len;
- sg_init_one(sg, rbuf, rbuf_len);
- rc = crypto_hash_update(&desc, sg, rbuf_len);
+ rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len);
if (rc)
break;
}
kfree(rbuf);
if (!rc)
- rc = crypto_hash_final(&desc, digest);
+ rc = crypto_shash_final(&desc.shash, digest);
if (read)
file->f_mode &= ~FMODE_READ;
out:
- crypto_free_hash(desc.tfm);
return rc;
}
/*
- * Calculate the hash of a given template
+ * Calculate the hash of a given buffer
*/
-int ima_calc_template_hash(int template_len, void *template, char *digest)
+int ima_calc_buffer_hash(const void *data, int len, char *digest)
{
- struct hash_desc desc;
- struct scatterlist sg[1];
- int rc;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(ima_shash_tfm)];
+ } desc;
- rc = init_desc(&desc);
- if (rc != 0)
- return rc;
+ desc.shash.tfm = ima_shash_tfm;
+ desc.shash.flags = 0;
- sg_init_one(sg, template, template_len);
- rc = crypto_hash_update(&desc, sg, template_len);
- if (!rc)
- rc = crypto_hash_final(&desc, digest);
- crypto_free_hash(desc.tfm);
- return rc;
+ return crypto_shash_digest(&desc.shash, data, len, digest);
}
static void __init ima_pcrread(int idx, u8 *pcr)
@@ -126,12 +122,17 @@ static void __init ima_pcrread(int idx, u8 *pcr)
*/
int __init ima_calc_boot_aggregate(char *digest)
{
- struct hash_desc desc;
- struct scatterlist sg;
u8 pcr_i[IMA_DIGEST_SIZE];
int rc, i;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(ima_shash_tfm)];
+ } desc;
+
+ desc.shash.tfm = ima_shash_tfm;
+ desc.shash.flags = 0;
- rc = init_desc(&desc);
+ rc = crypto_shash_init(&desc.shash);
if (rc != 0)
return rc;
@@ -139,11 +140,9 @@ int __init ima_calc_boot_aggregate(char *digest)
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
ima_pcrread(i, pcr_i);
/* now accumulate with current aggregate */
- sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
- rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+ rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE);
}
if (!rc)
- crypto_hash_final(&desc, digest);
- crypto_free_hash(desc.tfm);
+ crypto_shash_final(&desc.shash, digest);
return rc;
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index b5dfd534f13d..162ea723db3d 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -85,6 +85,9 @@ int __init ima_init(void)
if (!ima_used_chip)
pr_info("IMA: No TPM chip found, activating TPM-bypass!\n");
+ rc = ima_init_crypto();
+ if (rc)
+ return rc;
ima_add_boot_aggregate(); /* boot aggregate must be first entry */
ima_init_policy();
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index dba965de90d3..5127afcc4b89 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -61,7 +61,8 @@ static void ima_rdwr_violation_check(struct file *file)
fmode_t mode = file->f_mode;
int must_measure;
bool send_tomtou = false, send_writers = false;
- unsigned char *pathname = NULL, *pathbuf = NULL;
+ char *pathbuf = NULL;
+ const char *pathname;
if (!S_ISREG(inode->i_mode) || !ima_initialized)
return;
@@ -86,22 +87,15 @@ out:
if (!send_tomtou && !send_writers)
return;
- /* We will allow 11 spaces for ' (deleted)' to be appended */
- pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
- if (pathbuf) {
- pathname = d_path(&file->f_path, pathbuf, PATH_MAX + 11);
- if (IS_ERR(pathname))
- pathname = NULL;
- else if (strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
- pathname = NULL;
- }
+ pathname = ima_d_path(&file->f_path, &pathbuf);
+ if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
+ pathname = dentry->d_name.name;
+
if (send_tomtou)
- ima_add_violation(inode,
- !pathname ? dentry->d_name.name : pathname,
+ ima_add_violation(inode, pathname,
"invalid_pcr", "ToMToU");
if (send_writers)
- ima_add_violation(inode,
- !pathname ? dentry->d_name.name : pathname,
+ ima_add_violation(inode, pathname,
"invalid_pcr", "open_writers");
kfree(pathbuf);
}
@@ -145,25 +139,31 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}
-static int process_measurement(struct file *file, const unsigned char *filename,
+static int process_measurement(struct file *file, const char *filename,
int mask, int function)
{
struct inode *inode = file->f_dentry->d_inode;
struct integrity_iint_cache *iint;
- unsigned char *pathname = NULL, *pathbuf = NULL;
- int rc = -ENOMEM, action, must_appraise;
+ char *pathbuf = NULL;
+ const char *pathname = NULL;
+ int rc = -ENOMEM, action, must_appraise, _func;
if (!ima_initialized || !S_ISREG(inode->i_mode))
return 0;
- /* Determine if in appraise/audit/measurement policy,
- * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */
+ /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
+ * bitmask based on the appraise/audit/measurement policy.
+ * Included is the appraise submask.
+ */
action = ima_get_action(inode, mask, function);
if (!action)
return 0;
must_appraise = action & IMA_APPRAISE;
+ /* Is the appraise rule hook specific? */
+ _func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function;
+
mutex_lock(&inode->i_mutex);
iint = integrity_inode_get(inode);
@@ -171,44 +171,45 @@ static int process_measurement(struct file *file, const unsigned char *filename,
goto out;
/* Determine if already appraised/measured based on bitmask
- * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
- * IMA_AUDIT, IMA_AUDITED) */
+ * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
+ * IMA_AUDIT, IMA_AUDITED)
+ */
iint->flags |= action;
+ action &= IMA_DO_MASK;
action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
/* Nothing to do, just return existing appraised status */
if (!action) {
- if (iint->flags & IMA_APPRAISED)
- rc = iint->ima_status;
- goto out;
+ if (must_appraise)
+ rc = ima_get_cache_status(iint, _func);
+ goto out_digsig;
}
rc = ima_collect_measurement(iint, file);
if (rc != 0)
- goto out;
+ goto out_digsig;
+
+ if (function != BPRM_CHECK)
+ pathname = ima_d_path(&file->f_path, &pathbuf);
+
+ if (!pathname)
+ pathname = filename;
- if (function != BPRM_CHECK) {
- /* We will allow 11 spaces for ' (deleted)' to be appended */
- pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
- if (pathbuf) {
- pathname =
- d_path(&file->f_path, pathbuf, PATH_MAX + 11);
- if (IS_ERR(pathname))
- pathname = NULL;
- }
- }
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);
+ ima_store_measurement(iint, file, pathname);
+ if (action & IMA_APPRAISE_SUBMASK)
+ rc = ima_appraise_measurement(_func, iint, file, pathname);
if (action & IMA_AUDIT)
- ima_audit_measurement(iint, !pathname ? filename : pathname);
+ ima_audit_measurement(iint, pathname);
kfree(pathbuf);
+out_digsig:
+ if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
+ rc = -EACCES;
out:
mutex_unlock(&inode->i_mutex);
- return (rc && must_appraise) ? -EACCES : 0;
+ if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
+ return -EACCES;
+ return 0;
}
/**
@@ -219,19 +220,15 @@ out:
* Measure files being mmapped executable based on the ima_must_measure()
* policy decision.
*
- * Return 0 on success, an error code on failure.
- * (Based on the results of appraise_measurement().)
+ * 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_mmap(struct file *file, unsigned long prot)
{
- 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 (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
+ if (file && (prot & PROT_EXEC))
+ return process_measurement(file, file->f_dentry->d_name.name,
+ MAY_EXEC, MMAP_CHECK);
+ return 0;
}
/**
@@ -244,18 +241,15 @@ int ima_file_mmap(struct file *file, unsigned long prot)
* So we can be certain that what we verify and measure here is actually
* what is being executed.
*
- * Return 0 on success, an error code on failure.
- * (Based on the results of appraise_measurement().)
+ * 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_bprm_check(struct linux_binprm *bprm)
{
- int rc;
-
- rc = process_measurement(bprm->file,
+ return process_measurement(bprm->file,
(strcmp(bprm->filename, bprm->interp) == 0) ?
bprm->filename : bprm->interp,
MAY_EXEC, BPRM_CHECK);
- return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
/**
@@ -265,18 +259,15 @@ int ima_bprm_check(struct linux_binprm *bprm)
*
* Measure files based on the ima_must_measure() policy decision.
*
- * Always return 0 and audit dentry_open failures.
- * (Return code will be based upon measurement appraisal.)
+ * 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 rc;
-
ima_rdwr_violation_check(file);
- rc = process_measurement(file, file->f_dentry->d_name.name,
+ return process_measurement(file, file->f_dentry->d_name.name,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
FILE_CHECK);
- return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
EXPORT_SYMBOL_GPL(ima_file_check);
@@ -286,23 +277,20 @@ EXPORT_SYMBOL_GPL(ima_file_check);
*
* Measure/appraise kernel modules based on policy.
*
- * Always return 0 and audit dentry_open failures.
- * Return code is based upon measurement appraisal.
+ * 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_module_check(struct file *file)
{
- int rc = 0;
-
if (!file) {
- if (ima_appraise & IMA_APPRAISE_MODULES) {
#ifndef CONFIG_MODULE_SIG_FORCE
- rc = -EACCES; /* INTEGRITY_UNKNOWN */
+ if (ima_appraise & IMA_APPRAISE_MODULES)
+ return -EACCES; /* INTEGRITY_UNKNOWN */
#endif
- }
- } else
- rc = process_measurement(file, file->f_dentry->d_name.name,
- MAY_EXEC, MODULE_CHECK);
- return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
+ return 0; /* We rely on module signature checking */
+ }
+ return process_measurement(file, file->f_dentry->d_name.name,
+ MAY_EXEC, MODULE_CHECK);
}
static int __init init_ima(void)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 479fca940bb5..b27535a13a79 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -16,6 +16,7 @@
#include <linux/magic.h>
#include <linux/parser.h>
#include <linux/slab.h>
+#include <linux/genhd.h>
#include "ima.h"
@@ -25,6 +26,7 @@
#define IMA_FSMAGIC 0x0004
#define IMA_UID 0x0008
#define IMA_FOWNER 0x0010
+#define IMA_FSUUID 0x0020
#define UNKNOWN 0
#define MEASURE 0x0001 /* same as IMA_MEASURE */
@@ -45,10 +47,12 @@ struct ima_rule_entry {
enum ima_hooks func;
int mask;
unsigned long fsmagic;
+ u8 fsuuid[16];
kuid_t uid;
kuid_t fowner;
struct {
void *rule; /* LSM file metadata specific */
+ void *args_p; /* audit value */
int type; /* audit type */
} lsm[MAX_LSM_RULES];
};
@@ -74,7 +78,7 @@ static struct ima_rule_entry default_rules[] = {
{.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
- {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+ {.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
@@ -119,6 +123,35 @@ static int __init default_appraise_policy_setup(char *str)
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);
+/*
+ * Although the IMA policy does not change, the LSM policy can be
+ * reloaded, leaving the IMA LSM based rules referring to the old,
+ * stale LSM policy.
+ *
+ * Update the IMA LSM based rules to reflect the reloaded LSM policy.
+ * We assume the rules still exist; and BUG_ON() if they don't.
+ */
+static void ima_lsm_update_rules(void)
+{
+ struct ima_rule_entry *entry, *tmp;
+ int result;
+ int i;
+
+ mutex_lock(&ima_rules_mutex);
+ list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ if (!entry->lsm[i].rule)
+ continue;
+ result = security_filter_rule_init(entry->lsm[i].type,
+ Audit_equal,
+ entry->lsm[i].args_p,
+ &entry->lsm[i].rule);
+ BUG_ON(!entry->lsm[i].rule);
+ }
+ }
+ mutex_unlock(&ima_rules_mutex);
+}
+
/**
* ima_match_rules - determine whether an inode matches the measure rule.
* @rule: a pointer to a rule
@@ -142,6 +175,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
if ((rule->flags & IMA_FSMAGIC)
&& rule->fsmagic != inode->i_sb->s_magic)
return false;
+ if ((rule->flags & IMA_FSUUID) &&
+ memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid)))
+ return false;
if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
return false;
if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
@@ -149,10 +185,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
u32 osid, sid;
+ int retried = 0;
if (!rule->lsm[i].rule)
continue;
-
+retry:
switch (i) {
case LSM_OBJ_USER:
case LSM_OBJ_ROLE:
@@ -176,12 +213,39 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
default:
break;
}
+ if ((rc < 0) && (!retried)) {
+ retried = 1;
+ ima_lsm_update_rules();
+ goto retry;
+ }
if (!rc)
return false;
}
return true;
}
+/*
+ * In addition to knowing that we need to appraise the file in general,
+ * we need to differentiate between calling hooks, for hook specific rules.
+ */
+static int get_subaction(struct ima_rule_entry *rule, int func)
+{
+ if (!(rule->flags & IMA_FUNC))
+ return IMA_FILE_APPRAISE;
+
+ switch(func) {
+ case MMAP_CHECK:
+ return IMA_MMAP_APPRAISE;
+ case BPRM_CHECK:
+ return IMA_BPRM_APPRAISE;
+ case MODULE_CHECK:
+ return IMA_MODULE_APPRAISE;
+ case FILE_CHECK:
+ default:
+ return IMA_FILE_APPRAISE;
+ }
+}
+
/**
* ima_match_policy - decision based on LSM and other conditions
* @inode: pointer to an inode for which the policy decision is being made
@@ -209,7 +273,12 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
if (!ima_match_rules(entry, inode, func, mask))
continue;
+ action |= entry->flags & IMA_ACTION_FLAGS;
+
action |= entry->action & IMA_DO_MASK;
+ if (entry->action & IMA_APPRAISE)
+ action |= get_subaction(entry, func);
+
if (entry->action & IMA_DO_MASK)
actmask &= ~(entry->action | entry->action << 1);
else
@@ -282,7 +351,8 @@ enum {
Opt_audit,
Opt_obj_user, Opt_obj_role, Opt_obj_type,
Opt_subj_user, Opt_subj_role, Opt_subj_type,
- Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner
+ Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner,
+ Opt_appraise_type, Opt_fsuuid
};
static match_table_t policy_tokens = {
@@ -300,25 +370,35 @@ static match_table_t policy_tokens = {
{Opt_func, "func=%s"},
{Opt_mask, "mask=%s"},
{Opt_fsmagic, "fsmagic=%s"},
+ {Opt_fsuuid, "fsuuid=%s"},
{Opt_uid, "uid=%s"},
{Opt_fowner, "fowner=%s"},
+ {Opt_appraise_type, "appraise_type=%s"},
{Opt_err, NULL}
};
static int ima_lsm_rule_init(struct ima_rule_entry *entry,
- char *args, int lsm_rule, int audit_type)
+ substring_t *args, int lsm_rule, int audit_type)
{
int result;
if (entry->lsm[lsm_rule].rule)
return -EINVAL;
+ entry->lsm[lsm_rule].args_p = match_strdup(args);
+ if (!entry->lsm[lsm_rule].args_p)
+ return -ENOMEM;
+
entry->lsm[lsm_rule].type = audit_type;
result = security_filter_rule_init(entry->lsm[lsm_rule].type,
- Audit_equal, args,
+ Audit_equal,
+ entry->lsm[lsm_rule].args_p,
&entry->lsm[lsm_rule].rule);
- if (!entry->lsm[lsm_rule].rule)
+ if (!entry->lsm[lsm_rule].rule) {
+ kfree(entry->lsm[lsm_rule].args_p);
return -EINVAL;
+ }
+
return result;
}
@@ -404,8 +484,9 @@ 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, "FILE_MMAP") == 0)
- entry->func = FILE_MMAP;
+ else if ((strcmp(args[0].from, "FILE_MMAP") == 0)
+ || (strcmp(args[0].from, "MMAP_CHECK") == 0))
+ entry->func = MMAP_CHECK;
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
entry->func = BPRM_CHECK;
else
@@ -445,6 +526,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
if (!result)
entry->flags |= IMA_FSMAGIC;
break;
+ case Opt_fsuuid:
+ ima_log_string(ab, "fsuuid", args[0].from);
+
+ if (memchr_inv(entry->fsuuid, 0x00,
+ sizeof(entry->fsuuid))) {
+ result = -EINVAL;
+ break;
+ }
+
+ part_pack_uuid(args[0].from, entry->fsuuid);
+ entry->flags |= IMA_FSUUID;
+ result = 0;
+ break;
case Opt_uid:
ima_log_string(ab, "uid", args[0].from);
@@ -481,40 +575,52 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
case Opt_obj_user:
ima_log_string(ab, "obj_user", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_OBJ_USER,
AUDIT_OBJ_USER);
break;
case Opt_obj_role:
ima_log_string(ab, "obj_role", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_OBJ_ROLE,
AUDIT_OBJ_ROLE);
break;
case Opt_obj_type:
ima_log_string(ab, "obj_type", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_OBJ_TYPE,
AUDIT_OBJ_TYPE);
break;
case Opt_subj_user:
ima_log_string(ab, "subj_user", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_USER,
AUDIT_SUBJ_USER);
break;
case Opt_subj_role:
ima_log_string(ab, "subj_role", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_ROLE,
AUDIT_SUBJ_ROLE);
break;
case Opt_subj_type:
ima_log_string(ab, "subj_type", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_TYPE,
AUDIT_SUBJ_TYPE);
break;
+ case Opt_appraise_type:
+ if (entry->action != APPRAISE) {
+ result = -EINVAL;
+ break;
+ }
+
+ ima_log_string(ab, "appraise_type", args[0].from);
+ if ((strcmp(args[0].from, "imasig")) == 0)
+ entry->flags |= IMA_DIGSIG_REQUIRED;
+ else
+ result = -EINVAL;
+ break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -590,9 +696,13 @@ ssize_t ima_parse_add_rule(char *rule)
void ima_delete_rules(void)
{
struct ima_rule_entry *entry, *tmp;
+ int i;
mutex_lock(&ima_rules_mutex);
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+ for (i = 0; i < MAX_LSM_RULES; i++)
+ kfree(entry->lsm[i].args_p);
+
list_del(&entry->list);
kfree(entry);
}
OpenPOWER on IntegriCloud