From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
IMA-Measure creates a new measurement entry every time a file is measured, unless the same entry is already in the measurement list.
This patch introduces a new type of measurement list, recognizable by the PCR number specified with the new ima_digest_list_pcr= kernel option. This type of measurement list includes measurements of digest lists and files not found in those lists.
The benefit of this patch is the availability of a predictable PCR that can be used to seal data or TPM keys to the OS software. Unlike standard measurements, digest list measurements only indicate that files with a digest in those lists could have been accessed, but not if and when. With standard measurements, however, the chosen PCR is unlikely predictable.
Both standard and digest list measurements can be generated at the same time by adding '+' as a prefix to the value of ima_digest_list_pcr= (example: with ima_digest_list_pcr=+11, IMA generates standard measurements with PCR 10 and digest list measurements with PCR 11).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: zhoushuiqing zhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 9 +++ security/integrity/ima/ima.h | 12 ++++ security/integrity/ima/ima_api.c | 57 +++++++++++++++++++ security/integrity/ima/ima_digest_list.c | 25 ++++++++ security/integrity/ima/ima_init.c | 4 ++ security/integrity/ima/ima_main.c | 28 ++++++++- security/integrity/ima/ima_policy.c | 5 ++ security/integrity/integrity.h | 3 + 8 files changed, 142 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 6a9143e0103bb..4394c25a0e4d0 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1962,6 +1962,15 @@ Use the canonical format for the binary runtime measurements, instead of host native format.
+ ima_digest_list_pcr= + [IMA] + Specify which PCR is extended when file digests are + not found in the loaded digest lists. If '+' is not + specified, no measurement entry is created if the + digest is found. Otherwise, IMA creates also entries + with PCR 10, according to the existing behavior. + Format: { [+]<unsigned int> } + ima_hash= [IMA] Format: { md5 | sha1 | rmd160 | sha256 | sha384 | sha512 | ... } diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index aeabdb45c72c9..41879cb5bea32 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -58,6 +58,8 @@ extern int ima_appraise; extern struct tpm_chip *ima_tpm_chip; extern const char boot_aggregate_name[]; #ifdef CONFIG_IMA_DIGEST_LIST +extern int ima_digest_list_pcr; +extern bool ima_plus_standard_pcr; extern int ima_digest_list_actions; #endif
@@ -293,7 +295,12 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_template_desc *template_desc, + struct ima_digest *digest); +#else struct ima_template_desc *template_desc); +#endif int process_buffer_measurement(struct mnt_idmap *idmap, struct inode *inode, const void *buf, int size, const char *eventname, enum ima_hooks func, @@ -305,8 +312,13 @@ int ima_alloc_init_template(struct ima_event_data *event_data, struct ima_template_entry **entry, struct ima_template_desc *template_desc); int ima_store_template(struct ima_template_entry *entry, int violation, +#ifdef CONFIG_IMA_DIGEST_LIST + struct inode *inode, const unsigned char *filename, + int pcr, struct ima_digest *digest); +#else struct inode *inode, const unsigned char *filename, int pcr); +#endif void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index d3662f4acadc1..84ae37c7af559 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -102,11 +102,19 @@ int ima_alloc_init_template(struct ima_event_data *event_data, */ int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, +#ifdef CONFIG_IMA_DIGEST_LIST + const unsigned char *filename, int pcr, + struct ima_digest *digest) +#else const unsigned char *filename, int pcr) +#endif { static const char op[] = "add_template_measure"; static const char audit_cause[] = "hashing_error"; char *template_name = entry->template_desc->name; +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_template_entry *duplicated_entry = NULL; +#endif int result;
if (!violation) { @@ -119,8 +127,27 @@ int ima_store_template(struct ima_template_entry *entry, return result; } } +#ifdef CONFIG_IMA_DIGEST_LIST + if (ima_plus_standard_pcr && !digest) { + duplicated_entry = kmemdup(entry, + sizeof(*entry) + entry->template_desc->num_fields * + sizeof(struct ima_field_data), GFP_KERNEL); + if (duplicated_entry) + duplicated_entry->pcr = ima_digest_list_pcr; + } else if (!ima_plus_standard_pcr && ima_digest_list_pcr >= 0) { + pcr = ima_digest_list_pcr; + } +#endif entry->pcr = pcr; result = ima_add_template_entry(entry, violation, op, inode, filename); +#ifdef CONFIG_IMA_DIGEST_LIST + if (!result && duplicated_entry) { + result = ima_add_template_entry(duplicated_entry, violation, op, + inode, filename); + if (result < 0) + kfree(duplicated_entry); + } +#endif return result; }
@@ -152,8 +179,13 @@ void ima_add_violation(struct file *file, const unsigned char *filename, result = -ENOMEM; goto err_out; } +#ifdef CONFIG_IMA_DIGEST_LIST + result = ima_store_template(entry, violation, inode, filename, + CONFIG_IMA_MEASURE_PCR_IDX, NULL); +#else result = ima_store_template(entry, violation, inode, filename, CONFIG_IMA_MEASURE_PCR_IDX); +#endif if (result < 0) ima_free_template_entry(entry); err_out: @@ -341,13 +373,22 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_template_desc *template_desc, + struct ima_digest *digest) +#else struct ima_template_desc *template_desc) +#endif { static const char op[] = "add_template_measure"; static const char audit_cause[] = "ENOMEM"; int result = -ENOMEM; struct inode *inode = file_inode(file); +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_template_entry *entry = NULL; +#else struct ima_template_entry *entry; +#endif struct ima_event_data event_data = { .iint = iint, .file = file, .filename = filename, @@ -365,6 +406,12 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (iint->measured_pcrs & (0x1 << pcr) && !modsig) return;
+#ifdef CONFIG_IMA_DIGEST_LIST + if (digest && !ima_plus_standard_pcr && ima_digest_list_pcr >= 0) { + result = -EEXIST; + goto out; + } +#endif result = ima_alloc_init_template(&event_data, &entry, template_desc); if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, @@ -372,12 +419,22 @@ void ima_store_measurement(struct integrity_iint_cache *iint, return; }
+#ifdef CONFIG_IMA_DIGEST_LIST + result = ima_store_template(entry, violation, inode, filename, pcr, + digest); +out: +#else result = ima_store_template(entry, violation, inode, filename, pcr); +#endif if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { iint->flags |= IMA_MEASURED; iint->measured_pcrs |= (0x1 << pcr); } +#ifdef CONFIG_IMA_DIGEST_LIST + if (result < 0 && entry) +#else if (result < 0) +#endif ima_free_template_entry(entry); }
diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 91c70562aa86d..42b2306e43ccd 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -29,6 +29,31 @@ struct ima_h_table ima_digests_htable = { .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT };
+static int __init digest_list_pcr_setup(char *str) +{ + int pcr, ret; + + ret = kstrtouint(str, 10, &pcr); + if (ret) { + pr_err("Invalid PCR number %s\n", str); + return 1; + } + + if (pcr == CONFIG_IMA_MEASURE_PCR_IDX) { + pr_err("Default PCR cannot be used for digest lists\n"); + return 1; + } + + ima_digest_list_pcr = pcr; + ima_digest_list_actions |= IMA_MEASURE; + + if (*str == '+') + ima_plus_standard_pcr = true; + + return 1; +} +__setup("ima_digest_list_pcr=", digest_list_pcr_setup); + /************************* * Get/add/del functions * *************************/ diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 63979aefc95f7..0a4833daf4da0 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -85,7 +85,11 @@ static int __init ima_add_boot_aggregate(void)
result = ima_store_template(entry, violation, NULL, boot_aggregate_name, +#ifdef CONFIG_IMA_DIGEST_LIST + CONFIG_IMA_MEASURE_PCR_IDX, NULL); +#else CONFIG_IMA_MEASURE_PCR_IDX); +#endif if (result < 0) { ima_free_template_entry(entry); audit_cause = "store_entry"; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index e40975b2cdfdf..8004412249919 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -28,6 +28,9 @@ #include <linux/fs.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST +#include "ima_digest_list.h" +#endif
#ifdef CONFIG_IMA_APPRAISE int ima_appraise = IMA_APPRAISE_ENFORCE; @@ -40,6 +43,10 @@ int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1; #ifdef CONFIG_IMA_DIGEST_LIST /* Actions (measure/appraisal) for which digest lists can be used */ int ima_digest_list_actions; +/* PCR used for digest list measurements */ +int ima_digest_list_pcr = -1; +/* Flag to include standard measurement if digest list PCR is specified */ +bool ima_plus_standard_pcr; #endif static int hash_setup_done;
@@ -182,6 +189,8 @@ static enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_v || sig->hash_algo >= HASH_ALGO__LAST) return ima_hash_algo; return sig->hash_algo; + case EVM_IMA_XATTR_DIGEST_LIST: + fallthrough; case IMA_XATTR_DIGEST_NG: /* first byte contains algorithm id */ ret = xattr_value->data[0]; @@ -217,7 +226,7 @@ static int ima_read_xattr(struct dentry *dentry, ret = 0; return ret; } -#endif +#endif /* CONFIG_IMA_DIGEST_LIST */ static void ima_check_last_writer(struct integrity_iint_cache *iint, struct inode *inode, struct file *file) { @@ -276,6 +285,9 @@ static int process_measurement(struct file *file, const struct cred *cred, const char *pathname = NULL; int rc = 0, action, must_appraise = 0; int pcr = CONFIG_IMA_MEASURE_PCR_IDX; +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_digest *found_digest; +#endif struct evm_ima_xattr_data *xattr_value = NULL; struct modsig *modsig = NULL; int xattr_len = 0; @@ -410,10 +422,20 @@ static int process_measurement(struct file *file, const struct cred *cred, if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ pathname = ima_d_path(&file->f_path, &pathbuf, filename);
+#ifdef CONFIG_IMA_DIGEST_LIST + found_digest = ima_lookup_digest(iint->ima_hash->digest, hash_algo, + COMPACT_FILE); +#endif if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, xattr_value, xattr_len, modsig, pcr, +#ifdef CONFIG_IMA_DIGEST_LIST + template_desc, + ima_digest_allow(found_digest, + IMA_MEASURE)); +#else template_desc); +#endif if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { rc = ima_check_blacklist(iint, modsig, pcr); if (rc != -EPERM) { @@ -1067,7 +1089,11 @@ int process_buffer_measurement(struct mnt_idmap *idmap, goto out; }
+#ifdef CONFIG_IMA_DIGEST_LIST + ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr, NULL); +#else ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr); +#endif if (ret < 0) { audit_cause = "store_entry"; ima_free_template_entry(entry); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 9d9290b6313cd..53a035ef069be 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1872,7 +1872,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ima_log_string(ab, "pcr", args[0].from);
result = kstrtoint(args[0].from, 10, &entry->pcr); +#ifdef CONFIG_IMA_DIGEST_LIST + if (result || INVALID_PCR(entry->pcr) || + entry->pcr == ima_digest_list_pcr) +#else if (result || INVALID_PCR(entry->pcr)) +#endif result = -EINVAL; else entry->flags |= IMA_PCR; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 6cbc872dceada..00688b5bddf86 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -84,6 +84,9 @@ enum evm_ima_xattr_type { IMA_XATTR_DIGEST_NG, EVM_XATTR_PORTABLE_DIGSIG, IMA_VERITY_DIGSIG, +#ifdef CONFIG_IMA_DIGEST_LIST + EVM_IMA_XATTR_DIGEST_LIST, +#endif IMA_XATTR_LAST };