diff options
Diffstat (limited to 'security/integrity/ima')
-rw-r--r-- | security/integrity/ima/Kconfig | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 24 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 10 | ||||
-rw-r--r-- | security/integrity/ima/ima_appraise.c | 17 | ||||
-rw-r--r-- | security/integrity/ima/ima_crypto.c | 20 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 25 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 123 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 23 | ||||
-rw-r--r-- | security/integrity/ima/ima_template.c | 30 |
9 files changed, 146 insertions, 128 deletions
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 08758fbd496f..e099875643c5 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 diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 57da4bd7ba0c..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,12 +149,6 @@ 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, FIRMWARE_CHECK, POST_SETATTR }; @@ -164,20 +156,22 @@ 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_FIRMWARE 0x08 +#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, @@ -193,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; } diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index d9cd5ce14d2b..86885979918c 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -179,11 +179,6 @@ int ima_get_action(struct inode *inode, int mask, int function) 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,10 +325,9 @@ 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 = kmalloc(PATH_MAX, GFP_KERNEL); if (*pathbuf) { - pathname = d_path(path, *pathbuf, PATH_MAX + 11); + pathname = d_absolute_path(path, *pathbuf, PATH_MAX); if (IS_ERR(pathname)) { kfree(*pathbuf); *pathbuf = NULL; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 86bfd5c5df85..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; @@ -183,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"; @@ -192,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; @@ -202,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; } @@ -315,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; @@ -353,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 0bd732843fe7..d34e7dfc1118 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -80,24 +80,24 @@ static int ima_kernel_read(struct file *file, loff_t offset, { mm_segment_t old_fs; char __user *buf = addr; - ssize_t ret; + ssize_t ret = -EINVAL; if (!(file->f_mode & FMODE_READ)) return -EBADF; - if (!file->f_op->read && !file->f_op->aio_read) - 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 + else if (file->f_op->aio_read) ret = do_sync_read(file, buf, count, &offset); + else if (file->f_op->read_iter) + ret = new_sync_read(file, buf, count, &offset); set_fs(old_fs); return ret; } -int ima_init_crypto(void) +int __init ima_init_crypto(void) { long rc; @@ -116,7 +116,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); @@ -200,7 +203,10 @@ static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo) struct crypto_ahash *tfm = ima_ahash_tfm; int rc; - if ((algo != ima_hash_algo && algo < HASH_ALGO__LAST) || !tfm) { + 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) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index e8f9d70a465d..9164fc8cac84 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -43,7 +43,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,17 +72,23 @@ 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; } int __init ima_init(void) @@ -98,6 +104,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 +115,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 2917f980bf30..62f59eca32d3 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -77,42 +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; 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; } 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, @@ -124,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); } @@ -154,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 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 @@ -174,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, @@ -199,15 +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; } template_desc = ima_template_desc_current(); - if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { - if (action & IMA_APPRAISE_SUBMASK) - xattr_ptr = &xattr_value; - } else + 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); @@ -217,23 +229,26 @@ 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: + kfree(pathbuf); out: mutex_unlock(&inode->i_mutex); - kfree(xattr_value); if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) return -EACCES; return 0; @@ -253,7 +268,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; } @@ -272,10 +287,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); } /** @@ -288,12 +300,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); @@ -316,7 +327,7 @@ 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) @@ -327,7 +338,7 @@ int ima_fw_from_file(struct file *file, char *buf, size_t size) return -EACCES; /* INTEGRITY_UNKNOWN */ return 0; } - return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK); + return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0); } static int __init init_ima(void) @@ -336,14 +347,10 @@ static int __init init_ima(void) hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); - if (error) - goto out; - - error = ima_init_keyring(INTEGRITY_KEYRING_IMA); - if (error) - goto out; - ima_initialized = 1; -out: + 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 07099a8bc283..cdc620b2152f 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 @@ -295,6 +297,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. * @@ -341,6 +363,7 @@ void ima_update_policy(void) if (ima_rules == &ima_default_rules) { ima_rules = &ima_policy_rules; + ima_update_policy_flag(); cause = "complete"; result = 0; } diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index a076a967ec47..e854862c9337 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -152,24 +152,6 @@ out: return result; } -static int init_defined_templates(void) -{ - int i = 0; - int result = 0; - - /* Init defined templates. */ - for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { - struct ima_template_desc *template = &defined_templates[i]; - - result = template_desc_init_fields(template->fmt, - &(template->fields), - &(template->num_fields)); - if (result < 0) - return result; - } - return result; -} - struct ima_template_desc *ima_template_desc_current(void) { if (!ima_template) @@ -178,13 +160,11 @@ struct ima_template_desc *ima_template_desc_current(void) return ima_template; } -int ima_init_template(void) +int __init ima_init_template(void) { - int result; - - result = init_defined_templates(); - if (result < 0) - return result; + struct ima_template_desc *template = ima_template_desc_current(); - return 0; + return template_desc_init_fields(template->fmt, + &(template->fields), + &(template->num_fields)); } |