diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 0cd50491c0a4..802a3fd9e485 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1292,7 +1292,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Set number of hash buckets for inode cache. ima_appraise= [IMA] appraise integrity measurements - Format: { "off" | "enforce" | "fix" } + Format: { "off" | "enforce" | "fix" | "log" } default: "enforce" ima_appraise_tcb [IMA] diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8e4bb883fc13..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; @@ -153,14 +156,16 @@ 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, diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 65c41a968cc1..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 * diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 013ec3f0e42d..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; @@ -316,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; @@ -354,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_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 673a37e92ba3..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, @@ -160,15 +157,16 @@ 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; 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 @@ -176,7 +174,9 @@ static int process_measurement(struct file *file, int mask, int function, * 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; @@ -187,9 +187,20 @@ static int process_measurement(struct file *file, int mask, int function, 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, @@ -218,7 +229,8 @@ static int process_measurement(struct file *file, int mask, int function, goto out_digsig; } - pathname = 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, @@ -228,13 +240,15 @@ static int process_measurement(struct file *file, int mask, int function, 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; @@ -288,7 +302,6 @@ int ima_bprm_check(struct linux_binprm *bprm) */ int ima_file_check(struct file *file, int mask, int opened) { - ima_rdwr_violation_check(file); return process_measurement(file, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK, opened); @@ -334,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; }