Path: blob/master/security/integrity/ima/ima_queue.c
10817 views
/*1* Copyright (C) 2005,2006,2007,2008 IBM Corporation2*3* Authors:4* Serge Hallyn <[email protected]>5* Reiner Sailer <[email protected]>6* Mimi Zohar <[email protected]>7*8* This program is free software; you can redistribute it and/or9* modify it under the terms of the GNU General Public License as10* published by the Free Software Foundation, version 2 of the11* License.12*13* File: ima_queue.c14* Implements queues that store template measurements and15* maintains aggregate over the stored measurements16* in the pre-configured TPM PCR (if available).17* The measurement list is append-only. No entry is18* ever removed or changed during the boot-cycle.19*/20#include <linux/module.h>21#include <linux/rculist.h>22#include <linux/slab.h>23#include "ima.h"2425LIST_HEAD(ima_measurements); /* list of all measurements */2627/* key: inode (before secure-hashing a file) */28struct ima_h_table ima_htable = {29.len = ATOMIC_LONG_INIT(0),30.violations = ATOMIC_LONG_INIT(0),31.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT32};3334/* mutex protects atomicity of extending measurement list35* and extending the TPM PCR aggregate. Since tpm_extend can take36* long (and the tpm driver uses a mutex), we can't use the spinlock.37*/38static DEFINE_MUTEX(ima_extend_list_mutex);3940/* lookup up the digest value in the hash table, and return the entry */41static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)42{43struct ima_queue_entry *qe, *ret = NULL;44unsigned int key;45struct hlist_node *pos;46int rc;4748key = ima_hash_key(digest_value);49rcu_read_lock();50hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {51rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE);52if (rc == 0) {53ret = qe;54break;55}56}57rcu_read_unlock();58return ret;59}6061/* ima_add_template_entry helper function:62* - Add template entry to measurement list and hash table.63*64* (Called with ima_extend_list_mutex held.)65*/66static int ima_add_digest_entry(struct ima_template_entry *entry)67{68struct ima_queue_entry *qe;69unsigned int key;7071qe = kmalloc(sizeof(*qe), GFP_KERNEL);72if (qe == NULL) {73pr_err("IMA: OUT OF MEMORY ERROR creating queue entry.\n");74return -ENOMEM;75}76qe->entry = entry;7778INIT_LIST_HEAD(&qe->later);79list_add_tail_rcu(&qe->later, &ima_measurements);8081atomic_long_inc(&ima_htable.len);82key = ima_hash_key(entry->digest);83hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);84return 0;85}8687static int ima_pcr_extend(const u8 *hash)88{89int result = 0;9091if (!ima_used_chip)92return result;9394result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);95if (result != 0)96pr_err("IMA: Error Communicating to TPM chip\n");97return result;98}99100/* Add template entry to the measurement list and hash table,101* and extend the pcr.102*/103int ima_add_template_entry(struct ima_template_entry *entry, int violation,104const char *op, struct inode *inode)105{106u8 digest[IMA_DIGEST_SIZE];107const char *audit_cause = "hash_added";108int audit_info = 1;109int result = 0;110111mutex_lock(&ima_extend_list_mutex);112if (!violation) {113memcpy(digest, entry->digest, sizeof digest);114if (ima_lookup_digest_entry(digest)) {115audit_cause = "hash_exists";116goto out;117}118}119120result = ima_add_digest_entry(entry);121if (result < 0) {122audit_cause = "ENOMEM";123audit_info = 0;124goto out;125}126127if (violation) /* invalidate pcr */128memset(digest, 0xff, sizeof digest);129130result = ima_pcr_extend(digest);131if (result != 0) {132audit_cause = "TPM error";133audit_info = 0;134}135out:136mutex_unlock(&ima_extend_list_mutex);137integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,138entry->template.file_name,139op, audit_cause, result, audit_info);140return result;141}142143144