summaryrefslogtreecommitdiffstats
path: root/security/integrity/ima
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-07 13:18:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-07 13:18:06 +0000
commit638a9e433ecd61e64761352dbec1fa4f5874c941 (patch)
treefdbff74a238d7a5a7d1cef071b7230bc064b9f25 /security/integrity/ima
parentReleasing progress-linux version 6.9.12-1~progress7.99u1. (diff)
downloadlinux-638a9e433ecd61e64761352dbec1fa4f5874c941.tar.xz
linux-638a9e433ecd61e64761352dbec1fa4f5874c941.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.h12
-rw-r--r--security/integrity/ima/ima_api.c16
-rw-r--r--security/integrity/ima/ima_appraise.c4
-rw-r--r--security/integrity/ima/ima_crypto.c7
-rw-r--r--security/integrity/ima/ima_fs.c133
-rw-r--r--security/integrity/ima/ima_iint.c2
-rw-r--r--security/integrity/ima/ima_init.c6
-rw-r--r--security/integrity/ima/ima_kexec.c1
-rw-r--r--security/integrity/ima/ima_main.c44
-rw-r--r--security/integrity/ima/ima_template_lib.c10
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,