diff --git a/include/linux/ima.h b/include/linux/ima.h index 0ab1dbf34c47ab731c1a6831928cf1fefb71d5d2..12738e37f714dc0e939708fbd8936f92f0071c64 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -233,6 +233,7 @@ struct ima_namespace { struct ima_policy_data *policy_data; struct integrity_iint_tree *iint_tree; struct list_head ns_measurements; + atomic_long_t ml_len; /* number of stored measurements in the list */ } __randomize_layout; extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index f757aedea66f595961d2f3c085d54460af300a3e..0e864ef8087436f66af7d7ede5fe37340890dac4 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -190,12 +190,14 @@ extern spinlock_t ima_queue_lock; extern spinlock_t ima_htable_lock; struct ima_h_table { - atomic_long_t len; /* number of stored measurements in the list */ + atomic_long_t len; /* number of stored measurements in the list */ atomic_long_t violations; struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; }; extern struct ima_h_table ima_htable; +extern atomic_long_t ima_ml_len; + static inline unsigned int ima_hash_key(u8 *digest) { /* there is no point in taking a hash of part of a digest */ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 2ae4adfbaa8246dca9d5185952233be8d43cfbf0..f8a19751c80dc24671da423590f8492760a5eba9 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -58,11 +58,12 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, atomic_long_t *val = NULL; char tmpbuf[32]; /* greater than largest 'long' string value */ ssize_t len; + struct ima_namespace *ima_ns = get_current_ns(); if (filp->f_path.dentry == violations) val = &ima_htable.violations; else if (filp->f_path.dentry == runtime_measurements_count) - val = &ima_htable.len; + val = (ima_ns == &init_ima_ns) ? &ima_ml_len : &ima_ns->ml_len; #ifdef CONFIG_IMA_DIGEST_LIST else if (filp->f_path.dentry == digests_count) val = &ima_digests_htable.len; @@ -82,32 +83,57 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos) { loff_t l = *pos; struct ima_queue_entry *qe; + struct ima_namespace *ima_ns = get_current_ns(); - /* we need a lock since pos could point beyond last element */ - rcu_read_lock(); - list_for_each_entry_rcu(qe, &ima_measurements, later) { - if (!l--) { - rcu_read_unlock(); - return qe; + if (ima_ns == &init_ima_ns) { + /* we need a lock since pos could point beyond last element */ + rcu_read_lock(); + list_for_each_entry_rcu(qe, &ima_measurements, later) { + if (!l--) { + rcu_read_unlock(); + return qe; + } + } + rcu_read_unlock(); + } else { + rcu_read_lock(); + list_for_each_entry_rcu(qe, &ima_ns->ns_measurements, ns_later) { + if (!l--) { + rcu_read_unlock(); + return qe; + } } + rcu_read_unlock(); } - rcu_read_unlock(); + return NULL; } static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) { struct ima_queue_entry *qe = v; + struct ima_namespace *ima_ns = get_current_ns(); /* lock protects when reading beyond last element * against concurrent list-extension */ - rcu_read_lock(); - qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later); - rcu_read_unlock(); - (*pos)++; + if (ima_ns == &init_ima_ns) { + rcu_read_lock(); + qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, + later); + rcu_read_unlock(); + (*pos)++; + + return (&qe->later == &ima_measurements) ? NULL : qe; + } else { + rcu_read_lock(); + qe = list_entry_rcu(qe->ns_later.next, struct ima_queue_entry, + ns_later); + rcu_read_unlock(); + (*pos)++; - return (&qe->later == &ima_measurements) ? NULL : qe; + return (&qe->ns_later == &ima_ns->ns_measurements) ? NULL : qe; + } } static void ima_measurements_stop(struct seq_file *m, void *v) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 00c8dfea6ba8691bbb310e911c92fb3b2a10f7a0..99b9643e6763d20f8227a748864d787ceeb149df 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -36,6 +36,7 @@ struct ima_namespace init_ima_ns = { .policy_data = &init_policy_data, .iint_tree = &init_iint_tree, .ns_measurements = LIST_HEAD_INIT(init_ima_ns.ns_measurements), + .ml_len = ATOMIC_LONG_INIT(0), }; EXPORT_SYMBOL(init_ima_ns); diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 885f12043cb3cd18670bf0d7d78b8525da1b49d2..2a4b7a23f9a7d40ae80430aba6cfaf77c60e610f 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -127,6 +127,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, ns->user_ns = get_user_ns(user_ns); ns->ucounts = ucounts; ns->frozen = false; + atomic_long_set(&ns->ml_len, 0); rwlock_init(&ns->iint_tree->lock); ns->iint_tree->root = RB_ROOT; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 3aeff7b5e036c49f433cc0f2568f6263e05c038e..89b9c373489021f159a7e4bb690a40da74da3b6c 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -25,6 +25,7 @@ /* pre-allocated array of tpm_digest structures to extend a PCR */ static struct tpm_digest *digests; +atomic_long_t ima_ml_len = ATOMIC_LONG_INIT(0); /* number of stored measurements in the list */ LIST_HEAD(ima_measurements); /* list of all measurements */ #ifdef CONFIG_IMA_KEXEC static unsigned long binary_runtime_size; @@ -113,6 +114,8 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, list_add_tail_rcu(&qe->ns_later, &ima_ns->ns_measurements); atomic_long_inc(&ima_htable.len); + atomic_long_inc(&ima_ml_len); + atomic_long_inc(&ima_ns->ml_len); if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); spin_lock(&ima_htable_lock);