Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2005,2006,2007,2008 IBM Corporation * * Authors: * Serge Hallyn <serue@us.ibm.com> * Reiner Sailer <sailer@watson.ibm.com> * Mimi Zohar <zohar@us.ibm.com> * * File: ima_queue.c * Implements queues that store template measurements and * maintains aggregate over the stored measurements * in the pre-configured TPM PCR (if available). * The measurement list is append-only. No entry is * ever removed or changed during the boot-cycle. */
#include <linux/rculist.h>#include <linux/reboot.h>#include <linux/slab.h>#include "ima.h"
#define AUDIT_CAUSE_LEN_MAX 32
bool ima_flush_htable;
static int __init ima_flush_htable_setup(char *str){ if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) { pr_warn("Hash table not enabled, ignoring request to flush\n"); return 1; }
ima_flush_htable = true; return 1;}__setup("ima_flush_htable", ima_flush_htable_setup);
/* pre-allocated array of tpm_digest structures to extend a PCR */static struct tpm_digest *digests;
LIST_HEAD(ima_measurements); /* list of all measurements */LIST_HEAD(ima_measurements_staged); /* list of staged measurements */#ifdef CONFIG_IMA_KEXECstatic unsigned long binary_runtime_size[BINARY__LAST];#elsestatic unsigned long binary_runtime_size[BINARY__LAST] = { [0 ... BINARY__LAST - 1] = ULONG_MAX};#endif
atomic_long_t ima_num_records[BINARY__LAST] = { [0 ... BINARY__LAST - 1] = ATOMIC_LONG_INIT(0)};atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0);
/* key: inode (before secure-hashing a file) */struct hlist_head __rcu *ima_htable;
/* mutex protects atomicity of extending and staging measurement list * and extending the TPM PCR aggregate. Since tpm_extend can take * long (and the tpm driver uses a mutex), we can't use the spinlock. */static DEFINE_MUTEX(ima_extend_list_mutex);
/* * Used internally by the kernel to suspend measurements. * Protected by ima_extend_list_mutex. */static bool ima_measurements_suspended;
/* Callers must call synchronize_rcu() and free the hash table. */static struct hlist_head *ima_alloc_replace_htable(void){ struct hlist_head *old_htable, *new_htable;
/* Initializing to zeros is equivalent to call HLIST_HEAD_INIT. */ new_htable = kcalloc(IMA_MEASURE_HTABLE_SIZE, sizeof(struct hlist_head), GFP_KERNEL); if (!new_htable) return ERR_PTR(-ENOMEM);
old_htable = rcu_replace_pointer(ima_htable, new_htable, lockdep_is_held(&ima_extend_list_mutex));
return old_htable;}
int __init ima_init_htable(void){ struct hlist_head *old_htable;
mutex_lock(&ima_extend_list_mutex); old_htable = ima_alloc_replace_htable(); mutex_unlock(&ima_extend_list_mutex);
if (IS_ERR(old_htable)) return PTR_ERR(old_htable);
/* Synchronize_rcu() and kfree() not necessary, only for robustness. */ synchronize_rcu(); kfree(old_htable); return 0;}
/* lookup up the digest value in the hash table, and return the entry */static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, int pcr){ struct ima_queue_entry *qe, *ret = NULL; struct hlist_head *htable; unsigned int key; int rc;
key = ima_hash_key(digest_value); rcu_read_lock(); htable = rcu_dereference(ima_htable); hlist_for_each_entry_rcu(qe, &htable[key], hnext) { rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest, digest_value, hash_digest_size[ima_hash_algo]); if ((rc == 0) && (qe->entry->pcr == pcr)) { ret = qe; break; } } rcu_read_unlock(); return ret;}
/* * Calculate the memory required for serializing a single * binary_runtime_measurement list entry, which contains a * couple of variable length fields (e.g template name and data). */static int get_binary_runtime_size(struct ima_template_entry *entry){ int size = 0;
size += sizeof(u32); /* pcr */ size += TPM_DIGEST_SIZE; size += sizeof(int); /* template name size field */ size += strlen(entry->template_desc->name); size += sizeof(entry->template_data_len); size += entry->template_data_len; return size;}
static void ima_update_binary_runtime_size(struct ima_template_entry *entry, enum binary_lists binary_list){ int size;
if (binary_runtime_size[binary_list] == ULONG_MAX) return;
size = get_binary_runtime_size(entry); binary_runtime_size[binary_list] = (binary_runtime_size[binary_list] < ULONG_MAX - size) ? binary_runtime_size[binary_list] + size : ULONG_MAX;}
/* ima_add_template_entry helper function: * - Add template entry to the measurement list and hash table, for * all entries except those carried across kexec. * * (Called with ima_extend_list_mutex held.) */static int ima_add_digest_entry(struct ima_template_entry *entry, bool update_htable){ struct ima_queue_entry *qe; struct hlist_head *htable; unsigned int key;
qe = kmalloc_obj(*qe); if (qe == NULL) { pr_err("OUT OF MEMORY ERROR creating queue entry\n"); return -ENOMEM; } qe->entry = entry;
INIT_LIST_HEAD(&qe->later); list_add_tail_rcu(&qe->later, &ima_measurements);
htable = rcu_dereference_protected(ima_htable, lockdep_is_held(&ima_extend_list_mutex));
atomic_long_inc(&ima_num_records[BINARY]); atomic_long_inc(&ima_num_records[BINARY_FULL]);
if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); hlist_add_head_rcu(&qe->hnext, &htable[key]); }
ima_update_binary_runtime_size(entry, BINARY); ima_update_binary_runtime_size(entry, BINARY_FULL);
return 0;}
/* * Return the amount of memory required for serializing the * entire binary_runtime_measurement list, including the ima_kexec_hdr * structure. */unsigned long ima_get_binary_runtime_size(enum binary_lists binary_list){ unsigned long val;
mutex_lock(&ima_extend_list_mutex); val = binary_runtime_size[binary_list]; mutex_unlock(&ima_extend_list_mutex);
if (val >= (ULONG_MAX - sizeof(struct ima_kexec_hdr))) return ULONG_MAX; else return val + sizeof(struct ima_kexec_hdr);}
static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr){ int result = 0;
if (!ima_tpm_chip) return result;
result = tpm_pcr_extend(ima_tpm_chip, pcr, digests_arg); if (result != 0) pr_err("Error Communicating to TPM chip, result: %d\n", result); return result;}
/* * Add template entry to the measurement list and hash table, and * extend the pcr. * * On systems which support carrying the IMA measurement list across * kexec, maintain the total memory size required for serializing the * binary_runtime_measurements. */int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, const unsigned char *filename){ u8 *digest = entry->digests[ima_hash_algo_idx].digest; struct tpm_digest *digests_arg = entry->digests; const char *audit_cause = "hash_added"; char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX]; int audit_info = 1; int result = 0, tpmresult = 0;
mutex_lock(&ima_extend_list_mutex);
/* * Avoid appending to the measurement log when the TPM subsystem has * been shut down while preparing for system reboot. */ if (ima_measurements_suspended) { audit_cause = "measurements_suspended"; audit_info = 0; result = -ENODEV; goto out; }
if (!violation && !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) { if (ima_lookup_digest_entry(digest, entry->pcr)) { audit_cause = "hash_exists"; result = -EEXIST; goto out; } }
result = ima_add_digest_entry(entry, !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)); if (result < 0) { audit_cause = "ENOMEM"; audit_info = 0; goto out; }
if (violation) /* invalidate pcr */ digests_arg = digests;
tpmresult = ima_pcr_extend(digests_arg, entry->pcr); if (tpmresult != 0) { snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)", tpmresult); audit_cause = tpm_audit_cause; audit_info = 0; }out: mutex_unlock(&ima_extend_list_mutex); integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, op, audit_cause, result, audit_info); return result;}
/** * ima_queue_stage - Stage all measurements * * If the staged measurements list is empty, the current measurements list is * not empty, and measurement is not suspended, move the measurements from the * current list to the staged one, and update the number of records and binary * run-time size accordingly. * * Do not allow staging after measurement is suspended, so that dumping * measurements can be done in a lockless way. * * Return: Zero on success, a negative value otherwise. */int ima_queue_stage(void){ int ret = 0;
mutex_lock(&ima_extend_list_mutex); if (!list_empty(&ima_measurements_staged)) { ret = -EEXIST; goto out_unlock; }
if (list_empty(&ima_measurements)) { ret = -ENOENT; goto out_unlock; }
if (ima_measurements_suspended) { ret = -EACCES; goto out_unlock; }
list_replace(&ima_measurements, &ima_measurements_staged); INIT_LIST_HEAD(&ima_measurements);
atomic_long_set(&ima_num_records[BINARY_STAGED], atomic_long_read(&ima_num_records[BINARY])); atomic_long_set(&ima_num_records[BINARY], 0);
if (IS_ENABLED(CONFIG_IMA_KEXEC)) { binary_runtime_size[BINARY_STAGED] = binary_runtime_size[BINARY]; binary_runtime_size[BINARY] = 0; }out_unlock: mutex_unlock(&ima_extend_list_mutex); return ret;}
static void ima_queue_delete(struct list_head *head, bool flush_htable);
/** * ima_queue_staged_delete_all - Delete staged measurements * * Move staged measurements to a temporary list, ima_measurements_trim, update * the number of records and the binary run-time size accordingly. Finally, * delete measurements in the temporary list. * * Refuse to delete staged measurements if measurement is suspended, so that * dump can be done in a lockless way and user space is notified about staged * measurements being carried over to the secondary kernel, so that it does not * save them twice. * * Return: Zero on success, a negative value otherwise. */int ima_queue_staged_delete_all(void){ struct hlist_head *old_queue = NULL; LIST_HEAD(ima_measurements_trim);
mutex_lock(&ima_extend_list_mutex); if (list_empty(&ima_measurements_staged)) { mutex_unlock(&ima_extend_list_mutex); return -ENOENT; }
if (ima_measurements_suspended) { mutex_unlock(&ima_extend_list_mutex); return -ESTALE; }
list_replace(&ima_measurements_staged, &ima_measurements_trim); INIT_LIST_HEAD(&ima_measurements_staged);
atomic_long_set(&ima_num_records[BINARY_STAGED], 0);
if (IS_ENABLED(CONFIG_IMA_KEXEC)) binary_runtime_size[BINARY_STAGED] = 0;
if (ima_flush_htable) { old_queue = ima_alloc_replace_htable(); if (IS_ERR(old_queue)) { mutex_unlock(&ima_extend_list_mutex); return PTR_ERR(old_queue); } }
mutex_unlock(&ima_extend_list_mutex);
if (ima_flush_htable) { synchronize_rcu(); kfree(old_queue); }
ima_queue_delete(&ima_measurements_trim, ima_flush_htable); return 0;}
/** * ima_queue_delete_partial - Delete current measurements * @req_value: Number of measurements to delete * * Delete the requested number of measurements from the current measurements * list, and update the number of records and the binary run-time size * accordingly. * * Refuse to delete current measurements if measurement is suspended, so that * dump can be done in a lockless way and user space is notified about current * measurements being carried over to the secondary kernel, so that it does not * save them twice. * * Return: Zero on success, a negative value otherwise. */int ima_queue_delete_partial(unsigned long req_value){ unsigned long req_value_copy = req_value; unsigned long size_to_remove = 0, num_to_remove = 0; LIST_HEAD(ima_measurements_trim); struct ima_queue_entry *qe; int ret = 0;
/* * list_for_each_entry_rcu() without rcu_read_lock() is fine because * only list append can happen concurrently. No list replace due to the * staging/delete writers mutual exclusion. */ list_for_each_entry_rcu(qe, &ima_measurements, later, true) { size_to_remove += get_binary_runtime_size(qe->entry); num_to_remove++;
if (--req_value_copy == 0) break; }
/* Not enough records to delete. */ if (req_value_copy > 0) return -ENOENT;
mutex_lock(&ima_extend_list_mutex); if (ima_measurements_suspended) { mutex_unlock(&ima_extend_list_mutex); return -ESTALE; }
/* * qe remains valid because ima_fs.c enforces single-writer exclusion. */ __list_cut_position(&ima_measurements_trim, &ima_measurements, &qe->later);
atomic_long_sub(num_to_remove, &ima_num_records[BINARY]);
if (IS_ENABLED(CONFIG_IMA_KEXEC)) binary_runtime_size[BINARY] -= size_to_remove;
mutex_unlock(&ima_extend_list_mutex);
ima_queue_delete(&ima_measurements_trim, false); return ret;}
/** * ima_queue_delete - Delete measurements * @head: List head measurements are deleted from * @flush_htable: Whether or not the hash table is being flushed * * Delete the measurements from the passed list head completely if the * hash table is not enabled or is being flushed, or partially (only the * template data), if the hash table is used. */static void ima_queue_delete(struct list_head *head, bool flush_htable){ struct ima_queue_entry *qe, *qe_tmp; unsigned int i;
list_for_each_entry_safe(qe, qe_tmp, head, later) { /* * Safe to free template_data here without synchronize_rcu() * because the only htable reader, ima_lookup_digest_entry(), * accesses only entry->digests, not template_data. If new * htable readers are added that access template_data, a * synchronize_rcu() is required here. */ for (i = 0; i < qe->entry->template_desc->num_fields; i++) { kfree(qe->entry->template_data[i].data); qe->entry->template_data[i].data = NULL; qe->entry->template_data[i].len = 0; }
list_del(&qe->later);
/* No leak if condition is false, referenced by ima_htable. */ if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE) || flush_htable) { kfree(qe->entry->digests); kfree(qe->entry); kfree(qe); } }}
int ima_restore_measurement_entry(struct ima_template_entry *entry){ int result = 0;
mutex_lock(&ima_extend_list_mutex); result = ima_add_digest_entry(entry, 0); mutex_unlock(&ima_extend_list_mutex); return result;}
static void ima_measurements_suspend(void){ mutex_lock(&ima_extend_list_mutex); ima_measurements_suspended = true; mutex_unlock(&ima_extend_list_mutex);}
static int ima_reboot_notifier(struct notifier_block *nb, unsigned long action, void *data){#ifdef CONFIG_IMA_KEXEC if (action == SYS_RESTART && data && !strcmp(data, "kexec reboot")) ima_measure_kexec_event("kexec_execute");#endif
ima_measurements_suspend();
return NOTIFY_DONE;}
static struct notifier_block ima_reboot_nb = { .notifier_call = ima_reboot_notifier,};
void __init ima_init_reboot_notifier(void){ register_reboot_notifier(&ima_reboot_nb);}
int __init ima_init_digests(void){ u16 digest_size; u16 crypto_id; int i;
if (!ima_tpm_chip) return 0;
digests = kzalloc_objs(*digests, ima_tpm_chip->nr_allocated_banks, GFP_NOFS); if (!digests) return -ENOMEM;
for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) { digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id; digest_size = ima_tpm_chip->allocated_banks[i].digest_size; crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id;
/* for unmapped TPM algorithms digest is still a padded SHA1 */ if (crypto_id == HASH_ALGO__LAST) digest_size = SHA1_DIGEST_SIZE;
memset(digests[i].digest, 0xff, digest_size); }
return 0;}