diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:17:52 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:17:52 +0000 |
commit | 3afb00d3f86d3d924f88b56fa8285d4e9db85852 (patch) | |
tree | 95a985d3019522cea546b7d8df621369bc44fc6c /security/integrity/ima | |
parent | Adding debian version 6.9.12-1. (diff) | |
download | linux-3afb00d3f86d3d924f88b56fa8285d4e9db85852.tar.xz linux-3afb00d3f86d3d924f88b56fa8285d4e9db85852.zip |
Merging upstream version 6.10.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/integrity/ima')
-rw-r--r-- | security/integrity/ima/ima.h | 12 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 16 | ||||
-rw-r--r-- | security/integrity/ima/ima_appraise.c | 4 | ||||
-rw-r--r-- | security/integrity/ima/ima_crypto.c | 7 | ||||
-rw-r--r-- | security/integrity/ima/ima_fs.c | 133 | ||||
-rw-r--r-- | security/integrity/ima/ima_iint.c | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 6 | ||||
-rw-r--r-- | security/integrity/ima/ima_kexec.c | 1 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 44 | ||||
-rw-r--r-- | security/integrity/ima/ima_template_lib.c | 10 |
10 files changed, 188 insertions, 47 deletions
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 0a4f274f77..c51e24d24d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -49,11 +49,19 @@ extern int ima_policy_flag; /* bitset of digests algorithms allowed in the setxattr hook */ extern atomic_t ima_setxattr_allowed_hash_algorithms; +/* IMA hash algorithm description */ +struct ima_algo_desc { + struct crypto_shash *tfm; + enum hash_algo algo; +}; + /* set during initialization */ extern int ima_hash_algo __ro_after_init; extern int ima_sha1_idx __ro_after_init; extern int ima_hash_algo_idx __ro_after_init; extern int ima_extra_slots __ro_after_init; +extern struct ima_algo_desc *ima_algo_array __ro_after_init; + extern int ima_appraise; extern struct tpm_chip *ima_tpm_chip; extern const char boot_aggregate_name[]; @@ -175,12 +183,10 @@ struct ima_kexec_hdr { /* IMA integrity metadata associated with an inode */ struct ima_iint_cache { struct mutex mutex; /* protects: version, flags, digest */ - u64 version; /* track inode changes */ + struct integrity_inode_attributes real_inode; unsigned long flags; unsigned long measured_pcrs; unsigned long atomic_flags; - unsigned long real_ino; - dev_t real_dev; enum integrity_status ima_file_status:4; enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 1856981e33..984e861f6e 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -246,6 +246,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, struct inode *inode = file_inode(file); struct inode *real_inode = d_real_inode(file_dentry(file)); struct ima_max_digest_data hash; + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct name_snapshot filename; struct kstat stat; int result = 0; @@ -286,9 +288,9 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, result = -ENODATA; } } else if (buf) { - result = ima_calc_buffer_hash(buf, size, &hash.hdr); + result = ima_calc_buffer_hash(buf, size, hash_hdr); } else { - result = ima_calc_file_hash(file, &hash.hdr); + result = ima_calc_file_hash(file, hash_hdr); } if (result && result != -EBADF && result != -EINVAL) @@ -303,11 +305,11 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, iint->ima_hash = tmpbuf; memcpy(iint->ima_hash, &hash, length); - iint->version = i_version; - if (real_inode != inode) { - iint->real_ino = real_inode->i_ino; - iint->real_dev = real_inode->i_sb->s_dev; - } + if (real_inode == inode) + iint->real_inode.version = i_version; + else + integrity_inode_attrs_store(&iint->real_inode, i_version, + real_inode); /* Possibly temporary failure due to type of read (eg. O_DIRECT) */ if (!result) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 3497741cae..656c709b97 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -378,7 +378,9 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, } rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo, - iint->ima_hash->digest, &hash.hdr); + iint->ima_hash->digest, + container_of(&hash.hdr, + struct ima_digest_data, hdr)); if (rc) { *cause = "sigv3-hashing-error"; *status = INTEGRITY_FAIL; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index f3738b2c8b..6f5696d999 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -57,11 +57,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); static struct crypto_shash *ima_shash_tfm; static struct crypto_ahash *ima_ahash_tfm; -struct ima_algo_desc { - struct crypto_shash *tfm; - enum hash_algo algo; -}; - int ima_sha1_idx __ro_after_init; int ima_hash_algo_idx __ro_after_init; /* @@ -70,7 +65,7 @@ int ima_hash_algo_idx __ro_after_init; */ int ima_extra_slots __ro_after_init; -static struct ima_algo_desc *ima_algo_array; +struct ima_algo_desc *ima_algo_array __ro_after_init; static int __init ima_init_ima_crypto(void) { diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index cd1683dad3..e4a79a9b2d 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -116,9 +116,31 @@ void ima_putc(struct seq_file *m, void *data, int datalen) seq_putc(m, *(char *)data++); } +static struct dentry **ascii_securityfs_measurement_lists __ro_after_init; +static struct dentry **binary_securityfs_measurement_lists __ro_after_init; +static int securityfs_measurement_list_count __ro_after_init; + +static void lookup_template_data_hash_algo(int *algo_idx, enum hash_algo *algo, + struct seq_file *m, + struct dentry **lists) +{ + struct dentry *dentry; + int i; + + dentry = file_dentry(m->file); + + for (i = 0; i < securityfs_measurement_list_count; i++) { + if (dentry == lists[i]) { + *algo_idx = i; + *algo = ima_algo_array[i].algo; + break; + } + } +} + /* print format: * 32bit-le=pcr# - * char[20]=template digest + * char[n]=template digest * 32bit-le=template name size * char[n]=template name * [eventdata length] @@ -132,7 +154,15 @@ int ima_measurements_show(struct seq_file *m, void *v) char *template_name; u32 pcr, namelen, template_data_len; /* temporary fields */ bool is_ima_template = false; - int i; + enum hash_algo algo; + int i, algo_idx; + + algo_idx = ima_sha1_idx; + algo = HASH_ALGO_SHA1; + + if (m->file != NULL) + lookup_template_data_hash_algo(&algo_idx, &algo, m, + binary_securityfs_measurement_lists); /* get entry */ e = qe->entry; @@ -151,7 +181,7 @@ int ima_measurements_show(struct seq_file *m, void *v) ima_putc(m, &pcr, sizeof(e->pcr)); /* 2nd: template digest */ - ima_putc(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE); + ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]); /* 3rd: template name size */ namelen = !ima_canonical_fmt ? strlen(template_name) : @@ -220,7 +250,15 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) struct ima_queue_entry *qe = v; struct ima_template_entry *e; char *template_name; - int i; + enum hash_algo algo; + int i, algo_idx; + + algo_idx = ima_sha1_idx; + algo = HASH_ALGO_SHA1; + + if (m->file != NULL) + lookup_template_data_hash_algo(&algo_idx, &algo, m, + ascii_securityfs_measurement_lists); /* get entry */ e = qe->entry; @@ -233,8 +271,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) /* 1st: PCR used (config option) */ seq_printf(m, "%2d ", e->pcr); - /* 2nd: SHA1 template hash */ - ima_print_digest(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE); + /* 2nd: template hash */ + ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]); /* 3th: template name */ seq_printf(m, " %s", template_name); @@ -379,6 +417,69 @@ static const struct seq_operations ima_policy_seqops = { }; #endif +static void __init remove_securityfs_measurement_lists(struct dentry **lists) +{ + int i; + + if (lists) { + for (i = 0; i < securityfs_measurement_list_count; i++) + securityfs_remove(lists[i]); + + kfree(lists); + } +} + +static int __init create_securityfs_measurement_lists(void) +{ + char file_name[NAME_MAX + 1]; + struct dentry *dentry; + u16 algo; + int i; + + securityfs_measurement_list_count = NR_BANKS(ima_tpm_chip); + + if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip)) + securityfs_measurement_list_count++; + + ascii_securityfs_measurement_lists = + kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *), + GFP_KERNEL); + if (!ascii_securityfs_measurement_lists) + return -ENOMEM; + + binary_securityfs_measurement_lists = + kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *), + GFP_KERNEL); + if (!binary_securityfs_measurement_lists) + return -ENOMEM; + + for (i = 0; i < securityfs_measurement_list_count; i++) { + algo = ima_algo_array[i].algo; + + sprintf(file_name, "ascii_runtime_measurements_%s", + hash_algo_name[algo]); + dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, + ima_dir, NULL, + &ima_ascii_measurements_ops); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + ascii_securityfs_measurement_lists[i] = dentry; + + sprintf(file_name, "binary_runtime_measurements_%s", + hash_algo_name[algo]); + dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, + ima_dir, NULL, + &ima_measurements_ops); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + binary_securityfs_measurement_lists[i] = dentry; + } + + return 0; +} + /* * ima_open_policy: sequentialize access to the policy file */ @@ -454,6 +555,9 @@ int __init ima_fs_init(void) { int ret; + ascii_securityfs_measurement_lists = NULL; + binary_securityfs_measurement_lists = NULL; + ima_dir = securityfs_create_dir("ima", integrity_dir); if (IS_ERR(ima_dir)) return PTR_ERR(ima_dir); @@ -465,19 +569,21 @@ int __init ima_fs_init(void) goto out; } + ret = create_securityfs_measurement_lists(); + if (ret != 0) + goto out; + binary_runtime_measurements = - securityfs_create_file("binary_runtime_measurements", - S_IRUSR | S_IRGRP, ima_dir, NULL, - &ima_measurements_ops); + securityfs_create_symlink("binary_runtime_measurements", ima_dir, + "binary_runtime_measurements_sha1", NULL); if (IS_ERR(binary_runtime_measurements)) { ret = PTR_ERR(binary_runtime_measurements); goto out; } ascii_runtime_measurements = - securityfs_create_file("ascii_runtime_measurements", - S_IRUSR | S_IRGRP, ima_dir, NULL, - &ima_ascii_measurements_ops); + securityfs_create_symlink("ascii_runtime_measurements", ima_dir, + "ascii_runtime_measurements_sha1", NULL); if (IS_ERR(ascii_runtime_measurements)) { ret = PTR_ERR(ascii_runtime_measurements); goto out; @@ -515,6 +621,9 @@ out: securityfs_remove(runtime_measurements_count); securityfs_remove(ascii_runtime_measurements); securityfs_remove(binary_runtime_measurements); + remove_securityfs_measurement_lists(ascii_securityfs_measurement_lists); + remove_securityfs_measurement_lists(binary_securityfs_measurement_lists); + securityfs_measurement_list_count = 0; securityfs_remove(ima_symlink); securityfs_remove(ima_dir); diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c index e7c9c216c1..e23412a2c5 100644 --- a/security/integrity/ima/ima_iint.c +++ b/security/integrity/ima/ima_iint.c @@ -59,7 +59,7 @@ static void ima_iint_init_always(struct ima_iint_cache *iint, struct inode *inode) { iint->ima_hash = NULL; - iint->version = 0; + iint->real_inode.version = 0; iint->flags = 0UL; iint->atomic_flags = 0UL; iint->ima_file_status = INTEGRITY_UNKNOWN; diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 393f5c7912..4e208239a4 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -48,12 +48,14 @@ static int __init ima_add_boot_aggregate(void) struct ima_event_data event_data = { .iint = iint, .filename = boot_aggregate_name }; struct ima_max_digest_data hash; + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); int result = -ENOMEM; int violation = 0; memset(iint, 0, sizeof(*iint)); memset(&hash, 0, sizeof(hash)); - iint->ima_hash = &hash.hdr; + iint->ima_hash = hash_hdr; iint->ima_hash->algo = ima_hash_algo; iint->ima_hash->length = hash_digest_size[ima_hash_algo]; @@ -70,7 +72,7 @@ static int __init ima_add_boot_aggregate(void) * is not found. */ if (ima_tpm_chip) { - result = ima_calc_boot_aggregate(&hash.hdr); + result = ima_calc_boot_aggregate(hash_hdr); if (result < 0) { audit_cause = "hashing_error"; goto err_out; diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index dadc1d1381..52e00332de 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -30,6 +30,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, goto out; } + file.file = NULL; file.size = segment_size; file.read_pos = 0; file.count = sizeof(khdr); /* reserved space */ diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index c84e8c5533..f04f43af65 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -26,6 +26,7 @@ #include <linux/ima.h> #include <linux/fs.h> #include <linux/iversion.h> +#include <linux/evm.h> #include "ima.h" @@ -173,7 +174,7 @@ static void ima_check_last_writer(struct ima_iint_cache *iint, STATX_CHANGE_COOKIE, AT_STATX_SYNC_AS_STAT) || !(stat.result_mask & STATX_CHANGE_COOKIE) || - stat.change_cookie != iint->version) { + stat.change_cookie != iint->real_inode.version) { iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->measured_pcrs = 0; if (update) @@ -208,9 +209,10 @@ static int process_measurement(struct file *file, const struct cred *cred, u32 secid, char *buf, loff_t size, int mask, enum ima_hooks func) { - struct inode *backing_inode, *inode = file_inode(file); + struct inode *real_inode, *inode = file_inode(file); struct ima_iint_cache *iint = NULL; struct ima_template_desc *template_desc = NULL; + struct inode *metadata_inode; char *pathbuf = NULL; char filename[NAME_MAX]; const char *pathname = NULL; @@ -285,17 +287,28 @@ static int process_measurement(struct file *file, const struct cred *cred, iint->measured_pcrs = 0; } - /* Detect and re-evaluate changes made to the backing file. */ - backing_inode = d_real_inode(file_dentry(file)); - if (backing_inode != inode && + /* + * On stacked filesystems, detect and re-evaluate file data and + * metadata changes. + */ + real_inode = d_real_inode(file_dentry(file)); + if (real_inode != inode && (action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) { - if (!IS_I_VERSION(backing_inode) || - backing_inode->i_sb->s_dev != iint->real_dev || - backing_inode->i_ino != iint->real_ino || - !inode_eq_iversion(backing_inode, iint->version)) { + if (!IS_I_VERSION(real_inode) || + integrity_inode_attrs_changed(&iint->real_inode, + real_inode)) { iint->flags &= ~IMA_DONE_MASK; iint->measured_pcrs = 0; } + + /* + * Reset the EVM status when metadata changed. + */ + metadata_inode = d_inode(d_real(file_dentry(file), + D_REAL_METADATA)); + if (evm_metadata_changed(inode, metadata_inode)) + iint->flags &= ~(IMA_APPRAISED | + IMA_APPRAISED_SUBMASK); } /* Determine if already appraised/measured based on bitmask @@ -902,6 +915,13 @@ static int ima_post_load_data(char *buf, loff_t size, return 0; } + /* + * Measure the init_module syscall buffer containing the ELF image. + */ + if (load_id == LOADING_MODULE) + ima_measure_critical_data("modules", "init_module", + buf, size, true, NULL, 0); + return 0; } @@ -941,6 +961,8 @@ int process_buffer_measurement(struct mnt_idmap *idmap, .buf_len = size}; struct ima_template_desc *template; struct ima_max_digest_data hash; + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); char digest_hash[IMA_MAX_DIGEST_SIZE]; int digest_hash_len = hash_digest_size[ima_hash_algo]; int violation = 0; @@ -979,7 +1001,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap, if (!pcr) pcr = CONFIG_IMA_MEASURE_PCR_IDX; - iint.ima_hash = &hash.hdr; + iint.ima_hash = hash_hdr; iint.ima_hash->algo = ima_hash_algo; iint.ima_hash->length = hash_digest_size[ima_hash_algo]; @@ -990,7 +1012,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap, } if (buf_hash) { - memcpy(digest_hash, hash.hdr.digest, digest_hash_len); + memcpy(digest_hash, hash_hdr->digest, digest_hash_len); ret = ima_calc_buffer_hash(digest_hash, digest_hash_len, iint.ima_hash); diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 3b2cb8f100..4183956c53 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -339,6 +339,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data, struct ima_field_data *field_data) { struct ima_max_digest_data hash; + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); u8 *cur_digest = NULL; u32 cur_digestsize = 0; struct inode *inode; @@ -358,7 +360,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data, if ((const char *)event_data->filename == boot_aggregate_name) { if (ima_tpm_chip) { hash.hdr.algo = HASH_ALGO_SHA1; - result = ima_calc_boot_aggregate(&hash.hdr); + result = ima_calc_boot_aggregate(hash_hdr); /* algo can change depending on available PCR banks */ if (!result && hash.hdr.algo != HASH_ALGO_SHA1) @@ -368,7 +370,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data, memset(&hash, 0, sizeof(hash)); } - cur_digest = hash.hdr.digest; + cur_digest = hash_hdr->digest; cur_digestsize = hash_digest_size[HASH_ALGO_SHA1]; goto out; } @@ -379,14 +381,14 @@ int ima_eventdigest_init(struct ima_event_data *event_data, inode = file_inode(event_data->file); hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? ima_hash_algo : HASH_ALGO_SHA1; - result = ima_calc_file_hash(event_data->file, &hash.hdr); + result = ima_calc_file_hash(event_data->file, hash_hdr); if (result) { integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, event_data->filename, "collect_data", "failed", result, 0); return result; } - cur_digest = hash.hdr.digest; + cur_digest = hash_hdr->digest; cur_digestsize = hash.hdr.length; out: return ima_eventdigest_init_common(cur_digest, cur_digestsize, |