diff options
Diffstat (limited to 'security/integrity/ima')
-rw-r--r-- | security/integrity/ima/ima.h | 21 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 27 | ||||
-rw-r--r-- | security/integrity/ima/ima_appraise.c | 92 | ||||
-rw-r--r-- | security/integrity/ima/ima_crypto.c | 81 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 3 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 139 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 139 |
7 files changed, 343 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 27cb9eb42cc8..1c03e8f1e0e1 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_inode(file)->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 81dcaa26401e..a02e0791cf15 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 e7a147f7d371..3b3b7e6bf8da 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_inode(file); 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,21 @@ 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) && + (ima_appraise & IMA_APPRAISE_ENFORCE)) + 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..399433ad614e 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,20 @@ 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; + } + + result = blk_part_pack_uuid(args[0].from, + entry->fsuuid); + if (!result) + entry->flags |= IMA_FSUUID; + break; case Opt_uid: ima_log_string(ab, "uid", args[0].from); @@ -481,40 +576,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 +697,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); } |