diff options
Diffstat (limited to 'security/integrity/ima/ima_main.c')
| -rw-r--r-- | security/integrity/ima/ima_main.c | 387 |
1 files changed, 191 insertions, 196 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 203de979d30..09baa335ebc 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -22,240 +22,224 @@ #include <linux/mount.h> #include <linux/mman.h> #include <linux/slab.h> +#include <linux/xattr.h> +#include <linux/ima.h> +#include <crypto/hash_info.h> #include "ima.h" int ima_initialized; -char *ima_hash = "sha1"; +#ifdef CONFIG_IMA_APPRAISE +int ima_appraise = IMA_APPRAISE_ENFORCE; +#else +int ima_appraise; +#endif + +int ima_hash_algo = HASH_ALGO_SHA1; +static int hash_setup_done; + static int __init hash_setup(char *str) { - if (strncmp(str, "md5", 3) == 0) - ima_hash = "md5"; - return 1; -} -__setup("ima_hash=", hash_setup); + struct ima_template_desc *template_desc = ima_template_desc_current(); + int i; -struct ima_imbalance { - struct hlist_node node; - unsigned long fsmagic; -}; + if (hash_setup_done) + return 1; -/* - * ima_limit_imbalance - emit one imbalance message per filesystem type - * - * Maintain list of filesystem types that do not measure files properly. - * Return false if unknown, true if known. - */ -static bool ima_limit_imbalance(struct file *file) -{ - static DEFINE_SPINLOCK(ima_imbalance_lock); - static HLIST_HEAD(ima_imbalance_list); - - struct super_block *sb = file->f_dentry->d_sb; - struct ima_imbalance *entry; - struct hlist_node *node; - bool found = false; - - rcu_read_lock(); - hlist_for_each_entry_rcu(entry, node, &ima_imbalance_list, node) { - if (entry->fsmagic == sb->s_magic) { - found = true; + if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { + if (strncmp(str, "sha1", 4) == 0) + ima_hash_algo = HASH_ALGO_SHA1; + else if (strncmp(str, "md5", 3) == 0) + ima_hash_algo = HASH_ALGO_MD5; + goto out; + } + + for (i = 0; i < HASH_ALGO__LAST; i++) { + if (strcmp(str, hash_algo_name[i]) == 0) { + ima_hash_algo = i; break; } } - rcu_read_unlock(); - if (found) - goto out; - - entry = kmalloc(sizeof(*entry), GFP_NOFS); - if (!entry) - goto out; - entry->fsmagic = sb->s_magic; - spin_lock(&ima_imbalance_lock); - /* - * we could have raced and something else might have added this fs - * to the list, but we don't really care - */ - hlist_add_head_rcu(&entry->node, &ima_imbalance_list); - spin_unlock(&ima_imbalance_lock); - printk(KERN_INFO "IMA: unmeasured files on fsmagic: %lX\n", - entry->fsmagic); out: - return found; + hash_setup_done = 1; + return 1; } +__setup("ima_hash=", hash_setup); /* - * ima_counts_get - increment file counts + * ima_rdwr_violation_check * - * Maintain read/write counters for all files, but only - * invalidate the PCR for measured files: - * - Opening a file for write when already open for read, + * Only invalidate the PCR for measured files: + * - Opening a file for write when already open for read, * results in a time of measure, time of use (ToMToU) error. * - Opening a file for read when already open for write, - * could result in a file measurement error. + * could result in a file measurement error. * */ -void ima_counts_get(struct file *file) +static void ima_rdwr_violation_check(struct file *file) { - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file_inode(file); fmode_t mode = file->f_mode; - int rc; bool send_tomtou = false, send_writers = false; + char *pathbuf = NULL; + const char *pathname; - if (!S_ISREG(inode->i_mode)) + if (!S_ISREG(inode->i_mode) || !ima_initialized) return; - spin_lock(&inode->i_lock); - - if (!ima_initialized) - goto out; + mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */ if (mode & FMODE_WRITE) { - if (inode->i_readcount && IS_IMA(inode)) - send_tomtou = true; - goto out; + if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { + struct integrity_iint_cache *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)) + send_writers = true; } - rc = ima_must_measure(NULL, inode, MAY_READ, FILE_CHECK); - if (rc < 0) - goto out; + mutex_unlock(&inode->i_mutex); - if (atomic_read(&inode->i_writecount) > 0) - send_writers = true; -out: - /* remember the vfs deals with i_writecount */ - if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) - inode->i_readcount++; + if (!send_tomtou && !send_writers) + return; - spin_unlock(&inode->i_lock); + pathname = ima_d_path(&file->f_path, &pathbuf); if (send_tomtou) - ima_add_violation(inode, dentry->d_name.name, "invalid_pcr", - "ToMToU"); + ima_add_violation(file, pathname, "invalid_pcr", "ToMToU"); if (send_writers) - ima_add_violation(inode, dentry->d_name.name, "invalid_pcr", - "open_writers"); + ima_add_violation(file, pathname, + "invalid_pcr", "open_writers"); + kfree(pathbuf); } -/* - * Decrement ima counts - */ -static void ima_dec_counts(struct inode *inode, struct file *file) +static void ima_check_last_writer(struct integrity_iint_cache *iint, + struct inode *inode, struct file *file) { - mode_t mode = file->f_mode; - - assert_spin_locked(&inode->i_lock); - - if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { - if (unlikely(inode->i_readcount == 0)) { - if (!ima_limit_imbalance(file)) { - printk(KERN_INFO "%s: open/free imbalance (r:%u)\n", - __func__, inode->i_readcount); - dump_stack(); - } - return; - } - inode->i_readcount--; - } -} - -static void ima_check_last_writer(struct ima_iint_cache *iint, - struct inode *inode, - struct file *file) -{ - mode_t mode = file->f_mode; - - BUG_ON(!mutex_is_locked(&iint->mutex)); - assert_spin_locked(&inode->i_lock); - - if (mode & FMODE_WRITE && - atomic_read(&inode->i_writecount) == 1 && - iint->version != inode->i_version) - iint->flags &= ~IMA_MEASURED; -} - -static void ima_file_free_iint(struct ima_iint_cache *iint, struct inode *inode, - struct file *file) -{ - mutex_lock(&iint->mutex); - spin_lock(&inode->i_lock); - - ima_dec_counts(inode, file); - ima_check_last_writer(iint, inode, file); - - spin_unlock(&inode->i_lock); - mutex_unlock(&iint->mutex); -} - -static void ima_file_free_noiint(struct inode *inode, struct file *file) -{ - spin_lock(&inode->i_lock); + fmode_t mode = file->f_mode; - ima_dec_counts(inode, file); + if (!(mode & FMODE_WRITE)) + return; - spin_unlock(&inode->i_lock); + 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); + } + mutex_unlock(&inode->i_mutex); } /** * ima_file_free - called on __fput() * @file: pointer to file structure being freed * - * Flag files that changed, based on i_version; - * and decrement the i_readcount. + * Flag files that changed, based on i_version */ void ima_file_free(struct file *file) { - struct inode *inode = file->f_dentry->d_inode; - struct ima_iint_cache *iint; + struct inode *inode = file_inode(file); + struct integrity_iint_cache *iint; if (!iint_initialized || !S_ISREG(inode->i_mode)) return; - iint = ima_iint_find(inode); - - if (iint) - ima_file_free_iint(iint, inode, file); - else - ima_file_free_noiint(inode, file); + iint = integrity_iint_find(inode); + if (!iint) + return; + 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 ima_iint_cache *iint; - int rc = 0; + struct inode *inode = file_inode(file); + struct integrity_iint_cache *iint; + struct ima_template_desc *template_desc = ima_template_desc_current(); + char *pathbuf = NULL; + const char *pathname = NULL; + int rc = -ENOMEM, action, must_appraise, _func; + struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; + int xattr_len = 0; if (!ima_initialized || !S_ISREG(inode->i_mode)) return 0; - rc = ima_must_measure(NULL, inode, mask, function); - if (rc != 0) - return rc; -retry: - iint = ima_iint_find(inode); - if (!iint) { - rc = ima_inode_alloc(inode); - if (!rc || rc == -EEXIST) - goto retry; - return rc; - } + /* 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(&iint->mutex); + mutex_lock(&inode->i_mutex); - rc = ima_must_measure(iint, inode, mask, function); - if (rc != 0) + iint = integrity_inode_get(inode); + if (!iint) goto out; - rc = ima_collect_measurement(iint, file); - if (!rc) - ima_store_measurement(iint, file, filename); + /* Determine if already appraised/measured based on bitmask + * (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 (must_appraise) + rc = ima_get_cache_status(iint, _func); + goto out_digsig; + } + + if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { + if (action & IMA_APPRAISE_SUBMASK) + xattr_ptr = &xattr_value; + } else + xattr_ptr = &xattr_value; + + rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); + if (rc != 0) { + if (file->f_flags & O_DIRECT) + rc = (iint->flags & IMA_PERMIT_DIRECTIO) ? 0 : -EACCES; + goto out_digsig; + } + + pathname = filename ?: 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); + if (action & IMA_AUDIT) + ima_audit_measurement(iint, pathname); + kfree(pathbuf); +out_digsig: + if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) + rc = -EACCES; out: - mutex_unlock(&iint->mutex); - return rc; + mutex_unlock(&inode->i_mutex); + kfree(xattr_value); + if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; + return 0; } /** @@ -266,18 +250,13 @@ 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; - - if (!file) - return 0; - if (prot & PROT_EXEC) - rc = process_measurement(file, file->f_dentry->d_name.name, - MAY_EXEC, FILE_MMAP); + if (file && (prot & PROT_EXEC)) + return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK); return 0; } @@ -291,16 +270,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, bprm->filename, - MAY_EXEC, BPRM_CHECK); - return 0; + return process_measurement(bprm->file, + (strcmp(bprm->filename, bprm->interp) == 0) ? + bprm->filename : bprm->interp, + MAY_EXEC, BPRM_CHECK); } /** @@ -310,34 +288,51 @@ 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; - - rc = process_measurement(file, file->f_dentry->d_name.name, - mask & (MAY_READ | MAY_WRITE | MAY_EXEC), - FILE_CHECK); - return 0; + ima_rdwr_violation_check(file); + return process_measurement(file, NULL, + mask & (MAY_READ | MAY_WRITE | MAY_EXEC), + FILE_CHECK); } EXPORT_SYMBOL_GPL(ima_file_check); +/** + * ima_module_check - based on policy, collect/store/appraise measurement. + * @file: pointer to the file to be measured/appraised + * + * Measure/appraise kernel modules based on policy. + * + * 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) +{ + if (!file) { +#ifndef CONFIG_MODULE_SIG_FORCE + if ((ima_appraise & IMA_APPRAISE_MODULES) && + (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; /* INTEGRITY_UNKNOWN */ +#endif + return 0; /* We rely on module signature checking */ + } + return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); +} + static int __init init_ima(void) { int error; + hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); - ima_initialized = 1; + if (!error) + ima_initialized = 1; return error; } -static void __exit cleanup_ima(void) -{ - ima_cleanup(); -} - late_initcall(init_ima); /* Start IMA after the TPM is available */ MODULE_DESCRIPTION("Integrity Measurement Architecture"); |
