Patch 3/3 - IMA (Integrity Measurement Architecture) IMA is an optional part of the SLIM module, which is called when SLIM determines that a security significant file is about to be opened or executed. IMA submits the measurement (hash) of the file (normally provided by EVM), to the TPM chip, for inclusion in one of the chip's Platform Configuration Registers (PCR). IMA also keeps a list of all file names and hashes that have been submitted to the TPM, and this list can be viewed through securityfs. By separately requesting a TPM_Quote from the chip, an application can get a chip-signed value of the PCR, which along with the list of measurements from IMA, can be used to attest, or prove to a third party, the validity of the hash list. (The tpm-3.0.3 package includes example TPM applications for creating keys, and performing the TPM_Quote operation.) IMA was separately submitted earlier, and this integration with EVM/SLIM, and resubmission, hopefully addresses the technical issues raised earlier. In particular, IMA was criticized as not being an access control module. This is true, and the reason for including it as an optional part of SLIM, which does perform access control, is simply one of efficiency. Since EVM has already hashed the files, and SLIM has already determined which files are security critical, IMA complexity and overhead is greatly reduced by integration into SLIM. A second issue with IMA was that in the earlier version, IMA measured only executables and a predefined list of other files/scripts. Thus a system could be compromised, without detection by IMA, by adding malicious, sensitive scripts which were not in the list of files to be measured. In this version, SLIM uses the integrity labels on all files to determine which hashes are important for IMA attestation, so that executables, scripts, and config files which are sensitive, will all be measured. IMA can be included or excluded in kernel configuration. If included in the SLIM module, it can also be enabled or disabled at module load time with the slm_enable_ima module parameter. modprobe slim slm_enable_ima=[0|1] SLIM accepts a module parameter indicating whether or not to invoke the additional IMA (Integrity Measurement Architecture) processing, which sends EVM measurements for SYSTEM files to the TPM for attestation measurement. The default is IMA processing on. diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima/ima_biosmeasurements.c linux-2.6.14.2.tc/security/evm/slim/ima/ima_biosmeasurements.c --- linux-2.6.14.2.orig/security/evm/slim/ima/ima_biosmeasurements.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima/ima_biosmeasurements.c 2005-11-11 14:20:28.000000000 -0500 @@ -0,0 +1,563 @@ +/* + * ima_biosmeasurements.c + * + * Device driver to access the eventlog extended by the TCG BIOS of PC platform + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * Revision history: + * + * 2003/10/29 initial implementation Seiji Munetoh + * Adding experimental EventLog support code. + * Requirements + * - Kernel was configured to support ACPI (CONFIG_ACPI=y) + * Other ACPI config options should be 'n'. + * + * EventLog Initialization + * Post-O/S EventLog is stored in the ACPI table. At first we retrive this + * table with ACPI driver's help. + * + * Size of theEventLog + * ACPI table size is 0x10000(64K) bytes. + * Minimum size of event is 32 bytes, so this hold max 2048 events. + * For a while, this is enough size!? + * 'tcg_eventlog' holds the copy of ACPI table and new EventLogs. + * + * Stefan Berger 07/2005 + * - adapt it to IMA + * + * Reiner Sailer 08/2005 + * - major rewrite, move to seq file / security fs + * + */ + +#include <linux/config.h> + +#ifdef CONFIG_ACPI + +#include <linux/version.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/seq_file.h> +#include <linux/fs.h> +#include <asm/uaccess.h> +#include <acpi/acpi.h> +#include <acpi/actypes.h> +#include <acpi/actbl.h> +#include "ima.h" + +/* + + TCPA/TCG Eventlog + + EventLog is stored in ACPI table. Current linux ACPI driver + does not support the TCPA table under RSDT. and I/F functions + + driver/acpi/tables/tbxfroot.c + ***************************************************************************** + * + * FUNCTION: Acpi_get_firmware_table + * + * PARAMETERS: Signature - Any ACPI table signature + * Instance - the non zero instance of the table, allows + * support for multiple tables of the same type + * Flags - 0: Physical/Virtual support + * Ret_buffer - pointer to a structure containing a buffer to + * receive the table + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get an ACPI table. The caller + * supplies an Out_buffer large enough to contain the entire ACPI + * table. Upon completion + * the Out_buffer->Length field will indicate the number of bytes + * copied into the Out_buffer->Buf_ptr buffer. This table will be + * a complete table including the header. + * + ******************************************************************************/ + +#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ + +#define PACK __attribute__ ((__packed__)) + +struct acpi_tcpa { + struct acpi_table_header hdr; + u16 reserved; + u32 log_max_len PACK; + u32 log_start_addr PACK; +}; + +struct tcpa_event { + u32 PCRIndex; + u32 eventType; + u8 PCRValue[20]; /* SHA1 */ + u32 eventSize; + /* (eventSize) bytes of event data follows */ +}; + +/* (Binary) bios measurement buffer */ +static void *tcg_eventlog = NULL; +static void *tcg_eventlog_addr_limit = NULL; /* MAX */ + +#define MAX_TEXT_EVENT 1000 /* Max event string length */ + +static u32 be_decode_u32(u8 * buf) +{ + u32 val = buf[3]; + val = (val << 8) | (u8) buf[2]; + val = (val << 8) | (u8) buf[1]; + val = (val << 8) | (u8) buf[0]; + return val; +} + +/* returns pointer to start of pos. entry of tcg log */ +static void *ima_bios_measurements_start(struct seq_file *m, loff_t * pos) +{ + loff_t i; + void *addr = tcg_eventlog; + struct tcpa_event *event; + + /* read over *pos measurements */ + for (i = 0; i < *pos; i++) { + event = (struct tcpa_event *) addr; + + if ((addr + sizeof(struct tcpa_event)) < + tcg_eventlog_addr_limit) { + if (event->eventType == 0 && event->eventSize == 0) + return NULL; + addr += sizeof(struct tcpa_event) + event->eventSize; + } + } + + /* now check if current entry is valid */ + if ((addr + sizeof(struct tcpa_event)) >= tcg_eventlog_addr_limit) + return NULL; + + event = (struct tcpa_event *) addr; + + if ((event->eventType == 0 && event->eventSize == 0) || + ((addr + sizeof(struct tcpa_event) + event->eventSize) >= + tcg_eventlog_addr_limit)) + return NULL; + + return addr; +} + +static void *ima_bios_measurements_next(struct seq_file *m, void *v, + loff_t * pos) +{ + struct tcpa_event *event = (struct tcpa_event *) v; + + v += sizeof(struct tcpa_event) + event->eventSize; + + /* now check if current entry is valid */ + if ((v + sizeof(struct tcpa_event)) >= tcg_eventlog_addr_limit) + return NULL; + + event = (struct tcpa_event *) v; + + if (event->eventType == 0 && event->eventSize == 0) + return NULL; + + if ((event->eventType == 0 && event->eventSize == 0) || + ((v + sizeof(struct tcpa_event) + event->eventSize) >= + tcg_eventlog_addr_limit)) + return NULL; + + (*pos)++; + return v; +} + +static void ima_bios_measurements_stop(struct seq_file *m, void *v) +{ +} + +static int ima_binary_bios_measurements_show(struct seq_file *m, void *v) +{ + + char *eventname; + u32 event_id, event_data_size; + char data[4]; + u32 help; + int i, len; + struct tcpa_event *event = (struct tcpa_event *) v; + unsigned char *event_entry = + (unsigned char *) (v + sizeof(struct tcpa_event)); + + eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); + if (!eventname) { + printk(KERN_ERR "%s: ERROR - No Memory for event name\n ", + __func__); + return -EFAULT; + } + + /* 1st: PCR used is in little-endian format (4 bytes) */ + help = event->PCRIndex; + memcpy(data, &help, 4); + for (i = 0; i < 4; i++) + seq_putc(m, data[i]); + + /* 2nd: SHA1 (20 bytes) */ + for (i = 0; i < 20; i++) + seq_putc(m, event->PCRValue[i]); + + /* 3rd: event type identifier (4 bytes) */ + help = event->eventType; + memcpy(data, &help, 4); + for (i = 0; i < 4; i++) + seq_putc(m, data[i]); + + len = 0; + /* gather event name */ + if (event->eventType == 0) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]"); + } else if (event->eventType == 1) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]"); + } else if (event->eventType == 2) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "[XML CONFIG]"); + } else if (event->eventType == 3) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "[NO_ACTION]"); + } else if (event->eventType == 4) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "["); + if (MAX_TEXT_EVENT - len > event->eventSize) { + memcpy(eventname + len, event_entry, + event->eventSize); + len += event->eventSize; + } + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]"); + } else if (event->eventType == 5) { + len += + snprintf(eventname + len, MAX_TEXT_EVENT - len, "["); + if (MAX_TEXT_EVENT - len > event->eventSize) { + memcpy(eventname + len, event_entry, + event->eventSize); + len += event->eventSize; + } + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]"); + } else if (event->eventType == 6) { /* PC Specific */ + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "["); + + event_id = be_decode_u32(event_entry); + event_data_size = be_decode_u32(&event_entry[4]); + + /* ToDo Row data -> Base64 */ + + switch (event_id) { + case 1: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "SMBIOS"); + break; + case 2: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "BIS Certificate"); + break; + case 3: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "POST BIOS "); + for (i = 0; i < 20; i++) { + len += snprintf(eventname + len, + MAX_TEXT_EVENT - len, "%02X", + event_entry[8 + i]); + } + break; + case 4: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "ESCD "); + for (i = 0; i < 20; i++) { + len += + snprintf(eventname + len, + MAX_TEXT_EVENT - len, "%02X", + event_entry[8 + i]); + } + break; + case 5: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "CMOS"); + break; + case 6: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "NVRAM"); + break; + case 7: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "Option ROM"); + break; + case 8: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "Option ROM microcode"); + break; + + default: + len += + snprintf(eventname + len, MAX_TEXT_EVENT - len, + "N/A"); + break; + } + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]"); + } else { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "BINARY N/A]"); + } + + /* 4th: filename <= 255 + \'0' delimiter */ + if (len > TCG_EVENT_NAME_LEN_MAX) + len = TCG_EVENT_NAME_LEN_MAX; + + for (i = 0; i < len; i++) + seq_putc(m, eventname[i]); + + /* 5th: delimiter */ + seq_putc(m, '\0'); + + return 0; +} + +static int ima_bios_measurements_release(struct inode *inode, + struct file *file) +{ + if (tcg_eventlog) { + kfree(tcg_eventlog); + tcg_eventlog = NULL; + } + return seq_release(inode, file); +} + +static int ima_ascii_bios_measurements_show(struct seq_file *m, void *v) +{ + int len = 0; + int i; + char *eventname; + u32 event_id, event_data_size; + + struct tcpa_event *event = (struct tcpa_event *) v; + unsigned char *event_entry = + (unsigned char *) (v + sizeof(struct tcpa_event)); + + eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); + if (!eventname) { + printk(KERN_ERR "%s: ERROR - No Memory for event name\n ", + __func__); + return -EFAULT; + } + + seq_printf(m, "%2d ", event->PCRIndex); + + /* 2nd: SHA1 */ + for (i = 0; i < 20; i++) + seq_printf(m, "%02x", event->PCRValue[i]); + + /* 3rd: event type identifier */ + seq_printf(m, " %02x", event->eventType); + + if (event->eventType == 0) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]"); + } else if (event->eventType == 1) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]"); + } else if (event->eventType == 2) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "[XML CONFIG]"); + } else if (event->eventType == 3) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "[NO_ACTION]"); + } else if (event->eventType == 4) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "["); + if (MAX_TEXT_EVENT - len > event->eventSize) { + memcpy(eventname + len, event_entry, + event->eventSize); + len += event->eventSize; + } + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]"); + } else if (event->eventType == 5) { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "["); + if (MAX_TEXT_EVENT - len > event->eventSize) { + memcpy(eventname + len, event_entry, + event->eventSize); + len += event->eventSize; + } + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]"); + } else if (event->eventType == 6) { /* PC Specific */ + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "["); + + event_id = be_decode_u32(event_entry); + event_data_size = be_decode_u32(&event_entry[4]); + + /* ToDo Row data -> Base64 */ + + switch (event_id) { + case 1: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "SMBIOS"); + break; + case 2: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "BIS Certificate"); + break; + case 3: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "POST BIOS "); + for (i = 0; i < 20; i++) { + len += snprintf(eventname + len, + MAX_TEXT_EVENT - len, "%02X", + event_entry[8 + i]); + } + break; + case 4: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "ESCD "); + for (i = 0; i < 20; i++) { + len += snprintf(eventname + len, + MAX_TEXT_EVENT - len, "%02X", + event_entry[8 + i]); + } + break; + case 5: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "CMOS"); + break; + case 6: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "NVRAM"); + break; + case 7: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "Option ROM"); + break; + case 8: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "Option ROM microcode"); + break; + + default: + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "N/A"); + break; + } + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]"); + } else { + len += snprintf(eventname + len, MAX_TEXT_EVENT - len, + "BINARY N/A]"); + } + + /* 4th: eventname <= max + \'0' delimiter */ + seq_printf(m, " %s\n", eventname); + + return 0; +} + + +static struct seq_operations ima_ascii_b_measurments_seqops = { + .start = ima_bios_measurements_start, + .next = ima_bios_measurements_next, + .stop = ima_bios_measurements_stop, + .show = ima_ascii_bios_measurements_show +}; + +static struct seq_operations ima_binary_b_measurments_seqops = { + .start = ima_bios_measurements_start, + .next = ima_bios_measurements_next, + .stop = ima_bios_measurements_stop, + .show = ima_binary_bios_measurements_show +}; + +/* read binary bios log */ +static int read_log(void) +{ + struct acpi_tcpa *buff; + acpi_status status; + void *virt; + + if (tcg_eventlog != NULL) { + printk(KERN_ERR + "%s: ERROR - Eventlog already initialized\n", + __func__); + return -EFAULT; + } + + /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ + status = acpi_get_firmware_table(ACPI_TCPA_SIG, 1, + ACPI_LOGICAL_ADDRESSING, + (struct acpi_table_header **) + &buff); + + if (ACPI_FAILURE(status)) { + printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", + __func__); + return -EIO; + } + + if (buff->log_max_len == 0) { + printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", + __func__); + return -EIO; + } + + /* malloc EventLog space */ + tcg_eventlog = kmalloc(buff->log_max_len, GFP_KERNEL); + if (!tcg_eventlog) { + printk + ("%s: ERROR - Not enough Memory for BIOS measurements\n", + __func__); + return -EFAULT; + } + + tcg_eventlog_addr_limit = tcg_eventlog + buff->log_max_len; + + acpi_os_map_memory(buff->log_start_addr, buff->log_max_len, &virt); + + memcpy((void *) tcg_eventlog, virt, buff->log_max_len); + + acpi_os_unmap_memory(virt, buff->log_max_len); + return 0; +} + +static int ima_ascii_bios_measurements_open(struct inode *inode, + struct file *file) +{ + int err; + if ((err = read_log())) + return err; + + /* now register seq file */ + return seq_open(file, &ima_ascii_b_measurments_seqops); +} + +struct file_operations ima_ascii_bios_measurements_ops = { + .open = ima_ascii_bios_measurements_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ima_bios_measurements_release, +}; + +static int ima_binary_bios_measurements_open(struct inode *inode, + struct file *file) +{ + int err; + + if ((err = read_log())) + return err; + + /* now register seq file */ + return seq_open(file, &ima_binary_b_measurments_seqops); +} + +struct file_operations ima_binary_bios_measurements_ops = { + .open = ima_binary_bios_measurements_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ima_bios_measurements_release, +}; + +EXPORT_SYMBOL_GPL(ima_ascii_bios_measurements_ops); +EXPORT_SYMBOL_GPL(ima_binary_bios_measurements_ops); + +#endif +/* CONFIG_ACPI */ diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima/ima_fs.c linux-2.6.14.2.tc/security/evm/slim/ima/ima_fs.c --- linux-2.6.14.2.orig/security/evm/slim/ima/ima_fs.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima/ima_fs.c 2005-11-11 14:22:42.000000000 -0500 @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Kylene Hall <kjhall@private> + * + * Reiner Sailer <sailer@private> + * Adaptation to RCU lists 07/15/05 + * Including ascii runtime and boot measurements 08/15/05 + * + * IBM Integrity Measurement Architecture. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_fs.c + * implemenents imafs for reporting measurement + * log and userspace measure requests + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/file.h> +#include <linux/seq_file.h> +#include <linux/parser.h> +#include <linux/device.h> +#include <asm/uaccess.h> + +#include "ima.h" + +#ifdef CONFIG_ACPI +extern struct file_operations ima_ascii_bios_measurements_ops; +#endif + + +#define TMPBUFLEN 12 +static ssize_t ima_show_htable_value(char __user * buf, size_t count, + loff_t * ppos, atomic_t * val) +{ + char tmpbuf[TMPBUFLEN]; + ssize_t len; + + len = scnprintf(tmpbuf, TMPBUFLEN, "%i\n", atomic_read(val)); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); +} + +static ssize_t ima_show_htable_call_count(struct file *filp, + char __user * buf, + size_t count, loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, &htable.call_count); +} +static struct file_operations ima_htable_call_count_ops = { + .read = ima_show_htable_call_count +}; + +static ssize_t ima_show_htable_violations(struct file *filp, + char __user * buf, + size_t count, loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, &htable.violations); +} +static struct file_operations ima_htable_violations_ops = { + .read = ima_show_htable_violations +}; + +static ssize_t ima_show_measurements_count(struct file *filp, + char __user * buf, + size_t count, loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, &htable.len); + +} +static struct file_operations ima_measurements_count_ops = { + .read = ima_show_measurements_count +}; + +/* returns pointer to hlist_node */ +static void *ima_measurements_start(struct seq_file *m, loff_t * pos) +{ + struct list_head *lpos; + loff_t l = *pos; + /* we need a lock since pos could point beyond last element */ + rcu_read_lock(); + list_for_each_rcu(lpos, &ima_measurements) { + if (!l--) { + rcu_read_unlock(); + return lpos; + } + } + rcu_read_unlock(); /* can we unlock before printing? */ + return NULL; +} + +static void *ima_measurements_next(struct seq_file *m, void *v, + loff_t * pos) +{ + /* lock protects when reading beyond last element + * against concurrent list-extension */ + struct list_head *lpos = (struct list_head *) v; + + rcu_read_lock(); + lpos = rcu_dereference(lpos->next); + rcu_read_unlock(); + (*pos)++; + + return (lpos == &ima_measurements) ? NULL : lpos; +} + +static void ima_measurements_stop(struct seq_file *m, void *v) +{ +} + + +/* print format: 32bit-le=pcr#||char[20]=digest||flags||filename||'\0' + * flags bits: 32-16 application flags, 15-3 kernel flags, + * 2-0 hook + */ +static int ima_measurements_show(struct seq_file *m, void *v) +{ + /* the list never shrinks, so we don't need a lock here */ + struct list_head *lpos = v; + struct queue_entry *qe; + struct measure_entry *e; + int filename_len; + int i; + u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; + char data[4]; + + /* get entry */ + qe = list_entry(lpos, struct queue_entry, later); + if (qe == NULL) + return -1; + + e = qe->entry; + if (e == NULL) + return -1; + + /* PCR used is always the same in little-endian format */ + memcpy(data, &pcr, 4); + for (i = 0; i < 4; i++) + seq_putc(m, data[i]); + + /* 2nd: SHA1 */ + for (i = 0; i < 20; i++) + seq_putc(m, e->digest[i]); + + /* 3rd: flags */ + memcpy(data, &e->measure_flags, 4); + for (i = 0; i < 4; i++) + seq_putc(m, data[i]); + + /* 4th: filename + \'0' delimiter */ + filename_len = strlen(e->file_name); + if (filename_len > TCG_EVENT_NAME_LEN_MAX) + filename_len = TCG_EVENT_NAME_LEN_MAX; + + for (i = 0; i < filename_len; i++) + seq_putc(m, e->file_name[i]); + + /* 5th: delimiter */ + seq_putc(m, '\0'); + return 0; +} + +static struct seq_operations ima_measurments_seqops = { + .start = ima_measurements_start, + .next = ima_measurements_next, + .stop = ima_measurements_stop, + .show = ima_measurements_show +}; + +static int ima_measurements_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ima_measurments_seqops); +} + +static struct file_operations ima_measurements_ops = { + .open = ima_measurements_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +/* print in ascii */ +static int ima_ascii_measurements_show(struct seq_file *m, void *v) +{ + /* the list never shrinks, so we don't need a lock here */ + struct list_head *lpos = v; + struct queue_entry *qe; + struct measure_entry *e; + int i; + + /* get entry */ + qe = list_entry(lpos, struct queue_entry, later); + if (qe == NULL) + return -1; + + e = qe->entry; + if (e == NULL) + return -1; + + /* 1st: PCR used (config option) */ + seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX); + + /* 2nd: SHA1 */ + for (i = 0; i < 20; i++) + seq_printf(m, "%02x", e->digest[i]); + + /* 3th: filename <= max + \'0' delimiter */ + seq_printf(m, " %s\n", e->file_name); + + return 0; +} + +static struct seq_operations ima_ascii_measurements_seqops = { + .start = ima_measurements_start, + .next = ima_measurements_next, + .stop = ima_measurements_stop, + .show = ima_ascii_measurements_show +}; + +static int ima_ascii_measurements_open(struct inode *inode, + struct file *file) +{ + return seq_open(file, &ima_ascii_measurements_seqops); +} + +static struct file_operations ima_ascii_measurements_ops = { + .open = ima_ascii_measurements_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +void ima_fs_init(void) +{ + struct dentry *ima_dir, *binary_runtime_measurements, + *ascii_runtime_measurements, *runtime_measurements_count, + *binary_bios_measurements, *ascii_bios_measurements, + *call_count, *violations; + + if ((ima_dir = securityfs_create_dir("ima", NULL)) == NULL) + return; + + binary_runtime_measurements = + securityfs_create_file("binary_runtime_measurements", + S_IRUSR | S_IRGRP, ima_dir, NULL, + &ima_measurements_ops); + if (binary_runtime_measurements == NULL) + goto out6; + + ascii_runtime_measurements = + securityfs_create_file("ascii_runtime_measurements", + S_IRUSR | S_IRGRP, ima_dir, NULL, + &ima_ascii_measurements_ops); + if (ascii_runtime_measurements == NULL) + goto out5; + + runtime_measurements_count = + securityfs_create_file("runtime_measurements_count", + S_IRUSR | S_IRGRP, ima_dir, NULL, + &ima_measurements_count_ops); + if (runtime_measurements_count == NULL) + goto out4; +#ifdef CONFIG_ACPI + binary_bios_measurements = + securityfs_create_file("binary_bios_measurements", + S_IRUSR | S_IRGRP, ima_dir, NULL, + &ima_binary_bios_measurements_ops); + if (binary_bios_measurements == NULL) + goto out3; + + ascii_bios_measurements = + securityfs_create_file("ascii_bios_measurements", + S_IRUSR | S_IRGRP, ima_dir, NULL, + &ima_ascii_bios_measurements_ops); + if (ascii_bios_measurements == NULL) + goto out2; +#endif + call_count = + securityfs_create_file("ima_call_count", S_IRUSR | S_IRGRP, + ima_dir, NULL, + &ima_htable_call_count_ops); + if (call_count == NULL) + goto out1; + + violations = + securityfs_create_file("violations", S_IRUSR | S_IRGRP, + ima_dir, NULL, + &ima_htable_violations_ops); + if (violations == NULL) + goto out; + return; + + + out: + securityfs_remove(call_count); + out1: +#ifdef CONFIG_ACPI + securityfs_remove(ascii_bios_measurements); + out2: + securityfs_remove(binary_bios_measurements); + out3: +#endif + securityfs_remove(runtime_measurements_count); + out4: + securityfs_remove(ascii_runtime_measurements); + out5: + securityfs_remove(binary_runtime_measurements); + out6: + securityfs_remove(ima_dir); +} diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima/ima.h linux-2.6.14.2.tc/security/evm/slim/ima/ima.h --- linux-2.6.14.2.orig/security/evm/slim/ima/ima.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima/ima.h 2005-11-11 14:23:16.000000000 -0500 @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: Reiner Sailer <sailer@private> + * + * LSM IBM Integrity Measurement Architecture. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima.h + * defs + */ +#ifndef __LINUX_IMA_H +#define __LINUX_IMA_H + +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/major.h> +#include <linux/crypto.h> +#include <linux/security.h> +#include <linux/hash.h> +#include <linux/tpm.h> + +#define ima_printk(level, format, arg...) \ + printk(level "ima (%s): " format ,__func__, ## arg) + +#define ima_error(format, arg...) \ + ima_printk(KERN_ERR, format, ## arg) + +#define ima_info(format, arg...) \ + ima_printk(KERN_INFO, format, ## arg) + +/* if you can tolerate panic for the sake of attestation guarantees, + * then redefine IMA_PANIC to, panic (see INSTALL documentation) */ +#define IMA_PANIC \ + ima_error + +/* digest size for IMA, fits SHA1 or MD5 */ +#define IMA_DIGEST_SIZE 20 +#define IMA_MEASURE_MODULE_NAME "IMA" +#define TCG_EVENT_NAME_LEN_MAX 255 + +/* file systems we won't measure */ +#define IMA_MAGIC 0x9999 + +#define IMA_HASH_BITS 9 +#define MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) +#define HASH_KEY(digest) (hash_long( \ + (unsigned long)(*digest), IMA_HASH_BITS)); + +/* set during registering as lsm */ +extern unsigned char ima_terminating; +extern int ima_used_chip; + +struct measure_entry { + u32 measure_flags; + u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ + char file_name[TCG_EVENT_NAME_LEN_MAX+1]; /* name + \0 */ +}; + +struct queue_entry { + struct hlist_node hnext; /* place in hash collision list */ + struct list_head later; /* place in ima_measurements list */ + struct measure_entry *entry; +}; + +extern struct list_head ima_measurements; /* list of all measurements */ + +/* + * used to protect h_table and sha_table + */ +extern spinlock_t ima_queue_lock; + +struct h_table { + atomic_t len; /* number of stored measurements in the list */ + atomic_t call_count; /* # measure requests (calls into ima) */ + atomic_t changed_files; /* times dirty marked entry really changed */ + atomic_t violations; + unsigned int max_htable_size; + struct hlist_head queue[MEASURE_HTABLE_SIZE]; + atomic_t queue_len[MEASURE_HTABLE_SIZE]; +}; +extern struct h_table htable; + +extern struct file_operations ima_ascii_bios_measurements_ops; +extern struct file_operations ima_ascii_bios_measurements_ops; + +/* should move into a kconfig somewhen */ +#define CONFIG_IMA_MEASURE_PCR_IDX 10 + +/* protos */ +void invalidate_pcr(char *); +int ima_add_measure_entry(struct measure_entry *entry); +extern void ima_measure(const unsigned char *func, const unsigned char *name, + struct inode *inode, int mask, char *hash); +extern int _ima_measure(const unsigned char *func, const unsigned char *name, + struct inode *inode, int mask, int hash_len, char *hash); +extern struct file_operations ima_binary_bios_measurements_ops; +extern void ima_fs_init(void); +extern void create_htable(void); +extern struct queue_entry *ima_lookup_digest_entry(u8 * digest); +extern void ima_init(void); +extern int _ima_init(void); + +/* TPM "Glue" definitions */ + +#define IMA_TPM TPM_HW_TYPE<<16|TPM_ANY_NUM +static inline void ima_extend(const u8* hash) +{ + if (!ima_used_chip) + return; + + if ( tpm_pcr_extend( IMA_TPM, CONFIG_IMA_MEASURE_PCR_IDX, hash ) != 0 ) + ima_error("Error Communicating to TPM chip\n"); +} + +static inline void ima_pcrread(int idx, u8* pcr, int pcr_size) +{ + if (!ima_used_chip) + return; + + if ( tpm_pcr_read( IMA_TPM, idx, pcr, pcr_size ) != 0 ) { + ima_error("Error Communicating to TPM chip\n"); + } +} +#endif diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima/ima_init.c linux-2.6.14.2.tc/security/evm/slim/ima/ima_init.c --- linux-2.6.14.2.orig/security/evm/slim/ima/ima_init.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima/ima_init.c 2005-11-11 14:24:08.000000000 -0500 @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Contributions: + * Leendert van Doorn <leendert@private> + * + * IBM Integrity Measurement Architecture for TLC/EVM/SLIM + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_init.c + * init functions to start up IBM IMA + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/linkage.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <asm/uaccess.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/crypto.h> +#include <linux/fs.h> +#include <linux/init.h> +#include "ima.h" + +/* name for boot aggregate entry */ +static char *boot_aggregate_name = "boot_aggregate"; + +/* These identify the driver base version and may not be removed. */ +static const char version[] = "v5.5 08/15/2005"; +static const char illegal_pcr[20] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +int ima_used_chip = 0; + +static void ima_add_boot_aggregate(void) +{ + /* cumulative sha1 the first 8 tpm registers */ + struct measure_entry *entry; + size_t count; + int err; + + /* create new entry for boot aggregate */ + entry = (struct measure_entry *) + kmalloc(sizeof(struct measure_entry), GFP_KERNEL); + if (entry == NULL) { + invalidate_pcr("error allocating new measurement entry"); + return; + } + entry->measure_flags = 0; + memset(entry->digest, 0, 20); + if ((count = strlen(boot_aggregate_name)) > TCG_EVENT_NAME_LEN_MAX) + count = TCG_EVENT_NAME_LEN_MAX; + memcpy(entry->file_name, boot_aggregate_name, count); + entry->file_name[count] = '\0'; /* ez-print */ + if (ima_used_chip) { + int i; + u8 pcr_i[20]; + struct crypto_tfm *tfm; + + tfm = crypto_alloc_tfm("sha1", 0); + if (tfm == NULL) { + kfree(entry); + ima_error("Digest init failed ERROR.\n"); + return; + } + crypto_digest_init(tfm); + + for (i = 0; i < 8; i++) { + ima_pcrread(i, pcr_i, sizeof(pcr_i)); + /* now accumulate with current aggregate */ + tfm->__crt_alg->cra_digest. + dia_update(crypto_tfm_ctx(tfm), pcr_i, 20); + } + crypto_digest_final(tfm, entry->digest); + crypto_free_tfm(tfm); + } else + memset(entry->digest, 0xff, 20); + + /* now add measurement; if TPM bypassed, we have a ff..ff entry */ + err = ima_add_measure_entry(entry); + if (err < 0) { + kfree(entry); + if (err != -EEXIST) + invalidate_pcr("error adding boot aggregate"); + } +} + + +/* general invalidation function called by the measurement code */ +void invalidate_pcr(char *cause) +{ + /* extend pcr with illegal digest (no digest yields 0) */ + /* extending twice is obviously flagging the exception condition. */ + ima_error("INVALIDATING PCR AGGREGATE. Cause=%s.\n", cause); + ima_extend(illegal_pcr); + ima_extend(illegal_pcr); + atomic_inc(&htable.violations); /* can overflow; indicator only */ +} + + +int _ima_init(void) +{ + printk(KERN_INFO + "IBM Integrity Measurement Architecture (IBM IMA %s).\n", + version); + + ima_used_chip = 0; + if (tpm_pcr_read(IMA_TPM, 0, NULL, 0) == 0) + ima_used_chip = 1; + + if (!ima_used_chip) + printk(KERN_INFO + " IMA (TPM/BYPASS - no TPM chip found)\n"); + + create_htable(); /* for measurements */ + + /* boot aggregate must be very first entry */ + ima_add_boot_aggregate(); + + ima_fs_init(); + return 0; +} + +/* +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Reiner Sailer <sailer@private>"); +MODULE_DESCRIPTION("Run-time LSM-based IBM Integrity Measurement Architecture"); +*/ diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima/ima_main.c linux-2.6.14.2.tc/security/evm/slim/ima/ima_main.c --- linux-2.6.14.2.orig/security/evm/slim/ima/ima_main.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima/ima_main.c 2005-11-11 14:24:23.000000000 -0500 @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * EVM/SLIM implementation: Reiner Sailer <sailer@private> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_main.c + * implements run-time measurements + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/linkage.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <asm/uaccess.h> +#include <asm/atomic.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/stat.h> + +#include "ima.h" + +struct queue_entry *ima_lookup_digest_entry(u8 * digest); + +/* Add a file that is scheduled for measurement */ +int +_ima_measure(const unsigned char *func, const unsigned char *name, + struct inode *inode, int mask, int hash_len, char *hash) +{ + struct measure_entry *entry; + u8 digest[IMA_DIGEST_SIZE]; + int err = 0, count; + + atomic_inc(&htable.call_count); + + if (hash_len > IMA_DIGEST_SIZE) { + printk("%s: SLIM digest too long. Cutting to %x bytes.\n", + __func__, IMA_DIGEST_SIZE); + hash_len = IMA_DIGEST_SIZE; + } + + memset(digest, 0, IMA_DIGEST_SIZE); + + if (!memcmp(digest, hash, IMA_DIGEST_SIZE)) + invalidate_pcr("Error, NULL hash value!\n"); + + memcpy(digest, hash, hash_len); + + /* check if hash already exists in the measurement list */ + if (ima_lookup_digest_entry(digest)) { + /* printk("%s: Entry for %s already exists. skipping.\n", + __func__, name); */ + goto out; + } + + /* create new entry and add to measurement list */ + entry = kmalloc(sizeof(struct measure_entry), GFP_KERNEL); + if (!entry) { + invalidate_pcr("error allocating new measurement entry"); + err = -ENOMEM; + goto out; /* invalidate pcr */ + } + + memset(entry, sizeof(struct measure_entry), 0); + entry->measure_flags = 0; + if ((count = strlen(name)) > TCG_EVENT_NAME_LEN_MAX) + count = TCG_EVENT_NAME_LEN_MAX; + + memcpy(entry->file_name, name, count); + entry->file_name[count] = '\0'; /* ez-print */ + memcpy(entry->digest, digest, IMA_DIGEST_SIZE); + + err = ima_add_measure_entry(entry); + if (err < 0) { + kfree(entry); + if (err != -EEXIST) + invalidate_pcr("error adding measurement entry"); + else { + printk("%s: Double entry (%s).\n", __func__, name); + err = 0; + } + goto out; + } + out: + return err; +} diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima/ima_queue.c linux-2.6.14.2.tc/security/evm/slim/ima/ima_queue.c --- linux-2.6.14.2.orig/security/evm/slim/ima/ima_queue.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima/ima_queue.c 2005-11-11 14:24:37.000000000 -0500 @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Serge Hallyn <serue@private> + * Reiner Sailer <sailer@private> + * + * Adapted to SLIM: Reiner Sailer 07/27/05 + * + * Maintained by: Reiner Sailer <sailer@private> + * + * IBM Integrity Measurement Architecture. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_queue.c + * implements queues for run-time measurement + * functions based on SHA1 (MD5 for SLIM) + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/linkage.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <asm/uaccess.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/crypto.h> + +#include "ima.h" + +struct list_head ima_measurements; /* list of all measurements */ + +struct h_table htable; /* key: inode (before secure-hashing a file) */ + +/* Spinlock protects list for rarely occurring, critical operations + * (extend, dirty-flag). For scalability, we use RCU protection during + * normal operation (lookup entries). */ +DEFINE_SPINLOCK(ima_queue_lock); + +/* mutex protects atomicity of extending 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 DECLARE_MUTEX(ima_extend_list_mutex); + +void create_htable(void) +{ + int i; + + spin_lock(&ima_queue_lock); + INIT_LIST_HEAD(&ima_measurements); + atomic_set(&htable.len, 0); + atomic_set(&htable.violations, 0); + atomic_set(&htable.call_count, 0); + + htable.max_htable_size = MEASURE_HTABLE_SIZE; + for (i = 0; i < htable.max_htable_size; i++) { + INIT_HLIST_HEAD(&htable.queue[i]); + atomic_set(&htable.queue_len[i], 0); + } + + init_MUTEX(&ima_extend_list_mutex); + spin_unlock(&ima_queue_lock); +} + +struct queue_entry *ima_lookup_digest_entry(u8 * digest_value) +{ + struct queue_entry *qe, *ret = NULL; + unsigned int key; + struct hlist_node *pos; + + key = HASH_KEY(digest_value); + rcu_read_lock(); + hlist_for_each_entry_rcu(qe, pos, &htable.queue[key], hnext) { + if (memcmp(qe->entry->digest, digest_value, 20) == 0) { + ret = qe; + break; + } + } + rcu_read_unlock(); + return ret; +} + +/* Called with ima_queue_lock held */ +static int ima_add_digest_entry(struct measure_entry *entry) +{ + struct queue_entry *qe; + unsigned int key; + + key = HASH_KEY(entry->digest); + qe = kmalloc(sizeof(struct queue_entry), GFP_KERNEL); + if (qe == NULL) { + ima_error("OUT OF MEMORY ERROR creating queue entry.\n"); + return -ENOMEM; + } + qe->entry = entry; + + hlist_add_head_rcu(&qe->hnext, &htable.queue[key]); + atomic_inc(&htable.queue_len[key]); + return 0; +} + +int ima_add_measure_entry(struct measure_entry *entry) +{ + struct queue_entry *qe; + int error = 0; + + down(&ima_extend_list_mutex); + spin_lock(&ima_queue_lock); + if (ima_lookup_digest_entry(entry->digest)) { + error = -EEXIST; + spin_unlock(&ima_queue_lock); + goto out; + } + qe = kmalloc(sizeof(struct queue_entry), GFP_KERNEL); + if (qe == NULL) { + ima_error("OUT OF MEMORY in %s.\n", __func__); + error = -ENOMEM; + spin_unlock(&ima_queue_lock); + goto out; + } + qe->entry = entry; + + INIT_LIST_HEAD(&qe->later); + list_add_tail_rcu(&qe->later, &ima_measurements); + atomic_inc(&htable.len); + + if (ima_add_digest_entry(entry)) { + error = -ENOMEM; + spin_unlock(&ima_queue_lock); + goto out; + } + spin_unlock(&ima_queue_lock); + ima_extend(entry->digest); + out: + up(&ima_extend_list_mutex); + return error; +} diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima/Kconfig linux-2.6.14.2.tc/security/evm/slim/ima/Kconfig --- linux-2.6.14.2.orig/security/evm/slim/ima/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima/Kconfig 2005-11-11 14:24:47.000000000 -0500 @@ -0,0 +1,25 @@ +# +# IBM Integrity Measurement Architecture +# + +#menu "TPM-based Integrity Measurement Architecture" + +config IMA_MEASURE + bool "TCG run-time Integrity Measurement Architecture" + depends on SECURITY && (CRYPTO_SHA1=y) && SECURITY_SLIM + help + To measure executable code running on this + system, say Y. + If unsure, say N. + +config IMA_MEASURE_PCR_IDX + int "PCR for Aggregate (8<= Index <= 15)" + depends on IMA_MEASURE + range 8 15 + default 10 + help + This determines the PCR index used for aggregating the + measurement list into the TPM hardware. + If unsure, use the default 10. +#endmenu + diff -rupN linux-2.6.14.2.orig/security/evm/slim/ima.h linux-2.6.14.2.tc/security/evm/slim/ima.h --- linux-2.6.14.2.orig/security/evm/slim/ima.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.14.2.tc/security/evm/slim/ima.h 2005-11-11 14:20:28.000000000 -0500 @@ -0,0 +1,32 @@ +/* + IMA + */ + +int ima_init(void); +void _ima_init(void); +extern unsigned int slm_enable_ima; + +#ifdef CONFIG_IMA_MEASURE +int ima_init() +{ + printk(KERN_INFO "%s: slm_enable_ima %d\n", + __FUNCTION__, slm_enable_ima); + if (slm_enable_ima) + _ima_init(); + return 0; +} +#else +int ima_init() +{ + slm_enable_ima = 0; + return 0; +} +#endif + +void slm_measure(const unsigned char *func, const unsigned char *debug_name, + struct nameidata *nd, struct file *file, + struct inode *inode, int mask); +void slm_reset_measure(struct inode *inode, unsigned const char *name); +void ima_cleanup(void); +void ima_measure(const unsigned char *func, const unsigned char *name, + struct inode *inode, int mask, char *hash);
This archive was generated by hypermail 2.1.3 : Tue Nov 15 2005 - 06:17:20 PST