This patch applies against linux-2.6.12-rc6-mm1 and provides the main Integrity Measurement Architecture code (LSM-based). Signed-off-by: Reiner Sailer <sailer@private> --- diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_fs.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_fs.c --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_fs.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_fs.c 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Kylene Hall <kjhall@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_fs.c + * implemenents imafs + * for reporting measurement log and userspace measure requests + */ + +#include <linux/init.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/pagemap.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/security.h> +#include <linux/major.h> +#include <linux/seq_file.h> +#include <linux/percpu.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> +#include <linux/file.h> +#include <linux/parser.h> +#include <linux/device.h> + +#include "ima.h" + +struct measure_request { + int fd; + u16 label; +}; + +extern struct h_table htable; + +/* based on selinux pseudo filesystem */ + +#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_clean_inode_hits(struct file *filp, + char __user * buf, + size_t count, + loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, + &htable.clean_inode_hits); +} +static struct file_operations ima_htable_clean_inode_hits_ops = { + .read = ima_show_htable_clean_inode_hits +}; + +static ssize_t ima_show_htable_clean_table_hits(struct file *filp, + char __user * buf, + size_t count, + loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, + &htable.clean_table_hits); +} +static struct file_operations ima_htable_clean_table_hits_ops = { + .read = ima_show_htable_clean_table_hits +}; + +static ssize_t ima_show_htable_dirty_table_hits(struct file *filp, + char __user * buf, + size_t count, + loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, + &htable.dirty_table_hits); +} +static struct file_operations ima_htable_dirty_table_hits_ops = { + .read = ima_show_htable_dirty_table_hits +}; + +static ssize_t ima_show_htable_changed_files(struct file *filp, + char __user * buf, + size_t count, loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, + &htable.changed_files); +} +static struct file_operations ima_htable_changed_files_ops = { + .read = ima_show_htable_changed_files +}; + +static ssize_t ima_show_htable_user_measure(struct file *filp, + char __user * buf, + size_t count, loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, + &htable.user_measure); +} +static struct file_operations ima_htable_user_measure_ops = { + .read = ima_show_htable_user_measure +}; + +static ssize_t ima_show_htable_kernel_measure(struct file *filp, + char __user * buf, + size_t count, loff_t * ppos) +{ + return ima_show_htable_value(buf, count, ppos, + &htable.kernel_measure); +} +static struct file_operations ima_htable_kernel_measure_ops = { + .read = ima_show_htable_kernel_measure +}; + +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 +}; + +extern int measure_user_file(struct file *, u32 measure_flags); +extern int ima_enabled; + +enum ima_inos { + IMA_ROOT_INO = 1, + IMA_MEASURE, /* userspace measurement request */ + IMA_MEASUREMENTS, /* measurement log in binary format */ + IMA_MEASUREMENTS_COUNT, /* number of measurements in log */ + IMA_HTABLE_CLEAN_INODE_HITS, + IMA_HTABLE_CLEAN_TABLE_HITS, + IMA_HTABLE_DIRTY_TABLE_HITS, + IMA_HTABLE_CHANGED_FILES, + IMA_HTABLE_USER_MEASURE, + IMA_HTABLE_KERNEL_MEASURE, + IMA_HTABLE_VIOLATIONS, +}; + +#define IMA_MAX_EVENT_SIZE 69 +/* print format: 32bit-le=pcr#||char[20]=digest||flags||filename||'\0' flags bits: 32-16 application flags, 15-3 kernel flags, 2-0 hook len(filename)<=40*/ +static int print_measure_event_entry(struct measure_entry *e, char *buf, + int count) +{ + void *ptr = (void *) buf; + int filename_len = strlen(e->file_name); + + /* 1st: PCR used is always the same (config option) in little-endian format */ + *((u32 *) ptr) = (u32) CONFIG_IMA_MEASURE_PCR_IDX; + ptr += 4; + + /* 2nd: SHA1 */ + memcpy(ptr, e->digest, 20); + ptr += 20; + + /* 3rd: flags */ + *((u32 *)ptr) = e->measure_flags; + ptr += 4; + + /* 4th: filename <= 40 + \'0' delimiter */ + if (filename_len > TCG_EVENT_NAME_LEN_MAX) + filename_len = TCG_EVENT_NAME_LEN_MAX; + + memcpy(ptr, e->file_name, filename_len); + ptr += filename_len; + + /* 4th: delimiter */ + *((char *) ptr) = '\0'; + ptr += 1; + + return ((u32) ptr - (u32) buf); +} + +/* Position pointer is overrided to mean entry # rather than size in bytes */ +static ssize_t ima_measurements_read(struct file *filp, char __user * buf, + size_t count, loff_t * ppos) +{ + struct queue_entry *qe; + char *tmpbuf; + int tmpsiz, i, ret = 0, len; + loff_t pos = 0; + + if (count < 0) + return -EINVAL; + + tmpsiz = (count < PAGE_SIZE) ? count : PAGE_SIZE; + tmpbuf = kmalloc(tmpsiz, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + + + down(&h_table_mutex); + + /* fast forward to correct measurement for requested position */ + for (qe = first_measurement, i = 0; qe && qe->entry && i < *ppos; + qe = qe->later, i++); + + + /* make sure the next entry fits completely */ + while ((tmpsiz >= IMA_MAX_EVENT_SIZE) && qe && qe->entry) { + /* now fill rest of page */ + len = + print_measure_event_entry(qe->entry, tmpbuf + ret, + count); + qe = qe->later; + tmpsiz -= len; + ret += len; + *ppos += 1; + } + up(&h_table_mutex); + len = simple_read_from_buffer(buf, count, &pos, tmpbuf, ret); + + kfree(tmpbuf); + return len; +} + +static ssize_t ima_measure_write(struct file *file, + const char __user * buf, size_t count, + loff_t * ppos) +{ + struct measure_request *mr; + struct file *meas_file; + int error = -EINVAL; + char tmpbuf[sizeof(struct measure_request)]; + + atomic_inc(&htable.user_measure); + if (count != sizeof(struct measure_request)) { + ima_error("illegal request size (%d, expected %d).\n", + count, sizeof(struct measure_request)); + return -EIO; + } + + if (copy_from_user(tmpbuf, buf, count)) { + ima_error("trouble copying request\n"); + return -EIO; + } + + mr = (struct measure_request *) tmpbuf; + if (mr->fd < 0) { + ima_error("bad descriptor request\n"); + return -EBADF; + } + + meas_file = fget(mr->fd); + if (!meas_file) { + ima_error("could not open request\n"); + return -EACCES; + } + + error = measure_user_file(meas_file, (u32)(((mr->label) << 16) | USER_MEASURE_FLAG)); + fput(meas_file); + if (error) { + ima_error("problem measuring request\n"); + return error; + } else + return count; +} + +static struct file_operations ima_measure_ops = { + .write = ima_measure_write, +}; + +static struct file_operations ima_measurements_ops = { + .read = ima_measurements_read +}; + +enum { Opt_uid, Opt_gid }; + +static match_table_t tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"} +}; + +static int ima_remount(struct super_block *sb, int *flags, char *data) +{ + char *p; + int option; + int changed = 0, uid = 0, gid = 0; + struct inode *inode; + + if (!data) + return 0; + + while ((p = strsep(&data, ",")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int token; + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + + case Opt_uid: + if (match_int(args, &option)) + return -EINVAL; + uid = option; + changed = 1; + break; + + case Opt_gid: + if (match_int(args, &option)) + return -EINVAL; + gid = option; + changed = 1; + break; + + default: + ima_error("ima_fs: unrecognized mount option\n"); + return -EINVAL; + } + } + + if (changed) { + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { + inode->i_uid = uid; + inode->i_gid = gid; + } + } + + return 0; +} + +/* imafs Filenames and Permissions are set here -- Double CHECK */ +static int ima_fill_super(struct super_block *sb, void *data, int silent) +{ + static struct tree_descr ima_files[] = { + [IMA_MEASURE] = + {"measurereq", &ima_measure_ops, S_IWUSR | S_IWGRP}, + [IMA_MEASUREMENTS] = + {"binary_measurements", &ima_measurements_ops, + S_IRUSR | S_IRGRP}, + [IMA_MEASUREMENTS_COUNT] = + {"binary_measurements_count", + &ima_measurements_count_ops, + S_IRUSR | S_IRGRP}, + [IMA_HTABLE_CLEAN_INODE_HITS] = + {"clean_inode_hits", &ima_htable_clean_inode_hits_ops, + S_IRUSR | S_IRGRP}, + [IMA_HTABLE_CLEAN_TABLE_HITS] = + {"clean_hashtable_hits", + &ima_htable_clean_table_hits_ops, + S_IRUSR | S_IRGRP}, + [IMA_HTABLE_DIRTY_TABLE_HITS] = + {"dirty_hashtable_hits", + &ima_htable_dirty_table_hits_ops, + S_IRUSR | S_IRGRP}, + [IMA_HTABLE_CHANGED_FILES] = + {"changed_files", &ima_htable_changed_files_ops, + S_IRUSR | S_IRGRP}, + [IMA_HTABLE_USER_MEASURE] = + {"user_count", &ima_htable_user_measure_ops, + S_IRUSR | S_IRGRP}, + [IMA_HTABLE_KERNEL_MEASURE] = + {"kernel_count", &ima_htable_kernel_measure_ops, + S_IRUSR | S_IRGRP}, + [IMA_HTABLE_VIOLATIONS] = + {"violations", &ima_htable_violations_ops, + S_IRUSR | S_IRGRP}, + + /* last one */ {""} + }; + + return simple_fill_super(sb, IMA_MAGIC, ima_files); +} + +static struct super_block *ima_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + struct super_block *sb; + sb = get_sb_single(fs_type, flags, data, ima_fill_super); + + sb->s_op->remount_fs = ima_remount; + + return sb; +} + +static struct file_system_type ima_fs_type = { + .name = "imafs", + .get_sb = ima_get_sb, + .kill_sb = kill_litter_super, +}; + +struct vfsmount *imafs_mount; + +void ima_fs_init(void) +{ + int err; + + if (!ima_enabled) + return; + + err = register_filesystem(&ima_fs_type); + if (!err) { + imafs_mount = kern_mount(&ima_fs_type); + if (IS_ERR(imafs_mount)) { + ima_error("imafs: could not mount!\n"); + err = PTR_ERR(imafs_mount); + imafs_mount = NULL; + } + } + return; +} diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima.h linux-2.6.12-rc6-mm1-ima/security/ima/ima.h --- linux-2.6.12-rc6-mm1_orig/security/ima/ima.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima.h 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,242 @@ +/* + * 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> + +#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 cannot tolerate panic for the sake of attestation guarantees, + * then redefine IMA_PANIC to, e g., ima_error (see INSTALL documentation) */ +#define IMA_PANIC \ + panic + +/* set during registering as lsm */ +extern unsigned char ima_terminating; +void invalidate_pcr(char *); + +#define IMA_MEASURE_MODULE_NAME "IMA" +#define TCG_EVENT_NAME_LEN_MAX 40 + +/* file systems we expect to change without + * our inode_permission hook being called (nfs, remote fs) */ +#define NFS_SUPER_MAGIC 0x6969 + +/* file systems we won't measure */ +#define IMA_MAGIC 0x9999 + +/* Flags for measurement entries (identifying hook) */ +#define FLAG_HOOK_MASK 0x0f +#define MMAP_MEASURE_FLAG 0x01 +#define MODULE_MEASURE_FLAG 0x02 +#define USER_MEASURE_FLAG 0x04 + +#define MEASURE_HTABLE_SIZE 512 +#define HASH_KEY(inode_number) ((inode_number) % MEASURE_HTABLE_SIZE) +#define SHA_KEY(sha_value) (((sha_value)[18] << 8 | (sha_value)[19]) % MEASURE_HTABLE_SIZE) +typedef enum { CLEAN, DIRTY, CHANGED } ima_entry_flags; + +/* security structure appended to inodes */ +struct ima_inode { + atomic_t measure_count; /* # processes currently using this file in measure-mode */ + ima_entry_flags dirty; +}; + +/* security structure appended to measured files*/ +struct ima_file { + char is_measuring; /* identify fds that are "measuring" */ +}; + +/* get/store security state information; + * if stacking were to be implemented, this would be the place */ +#define ima_get_inode_security(inode) \ + ((struct ima_inode *) ((inode)->i_security)) + +#define ima_store_inode_security(inode,sec_struct) \ + ((inode)->i_security = (sec_struct)) + +#define ima_get_file_security(file) \ + ((struct ima_file *) ((file)->f_security)) + +#define ima_store_file_security(file, sec_struct) \ + ((file)->f_security = (sec_struct)) + +struct measure_entry { + u32 measure_flags; + unsigned long inode_nr; + dev_t dev_id; + ima_entry_flags dirty; + u8 digest[20]; /* sha1 measurement hash */ + char file_name[TCG_EVENT_NAME_LEN_MAX+1]; /* name + \0 */ + struct super_block *super_block; /* super block link (for umount-dirty flagging) */ +}; + +struct sha_entry { + struct sha_entry *next; + u8 *digest; + struct measure_entry *m_entry; +}; + +struct queue_entry { + struct queue_entry *next; + struct queue_entry *later; + struct measure_entry *entry; +}; + +extern struct queue_entry *first_measurement; /* for printing */ +extern struct queue_entry *latest_measurement; /* for adding */ + +/* hash table to keep fast access to past measurements + * uses one global lock for now (read/write) */ +extern struct semaphore h_table_mutex; + +struct h_table { + atomic_t len; + atomic_t user_measure; /* # measurements requested from userspace */ + atomic_t kernel_measure; /* # measurements performed from kernel */ + atomic_t clean_inode_hits; /* times we find an inode clean when measuring */ + atomic_t clean_table_hits; /* times we find a clean htable hit */ + atomic_t dirty_table_hits; /* times we find a dirty htable hit */ + atomic_t changed_files; /* times we realize a dirty marked entry really changed */ + atomic_t violations; + unsigned int max_htable_size; + struct queue_entry *queue[MEASURE_HTABLE_SIZE]; + atomic_t queue_len[MEASURE_HTABLE_SIZE]; +}; + +struct sha_table { + atomic_t len; + unsigned int max_htable_size; + struct sha_entry *queue[MEASURE_HTABLE_SIZE]; + atomic_t queue_len[MEASURE_HTABLE_SIZE]; +}; + +#define MEM_MINOR 1 +#define KMEM_MINOR 2 +#ifdef CONFIG_IMA_KMEM_BYPASS_PROTECTION +static inline void check_kmem_bypass(struct inode *inode) +{ + if ((imajor(inode) == MEM_MAJOR) + && S_ISCHR(inode->i_mode) && (iminor(inode) == KMEM_MINOR)) + invalidate_pcr("/dev/kmem write violation"); +} +#else +static inline void check_kmem_bypass(struct inode *inode) +{ + return; +} +#endif + +#ifdef CONFIG_IMA_MEM_BYPASS_PROTECTION +static inline void check_mem_bypass(struct inode *inode) +{ + if ((imajor(inode) == MEM_MAJOR) + && S_ISCHR(inode->i_mode) && (iminor(inode) == MEM_MINOR)) + invalidate_pcr("/dev/mmem write violation"); +} +#else +static inline void check_mem_bypass(struct inode *inode) +{ + return; +} +#endif + +#ifdef CONFIG_IMA_RAM_BYPASS_PROTECTION +static inline void check_ram_bypass(struct inode *inode) +{ + if ((imajor(inode) == RAMDISK_MAJOR) && S_ISBLK(inode->i_mode)) + invalidate_pcr("/dev/ram write violation"); +} +#else +static inline void check_ram_bypass(struct inode *inode) +{ + return; +} +#endif + +#ifdef CONFIG_IMA_HD_SD_BYPASS_PROTECTION +static inline void check_hd_sd_bypass(struct inode *inode) +{ + if ((imajor(inode) == HD_MAJOR) && S_ISBLK(inode->i_mode)) + invalidate_pcr("/dev/hdx write violation"); + else if ((imajor(inode) == SCSI_DISK0_MAJOR) && S_ISBLK(inode->i_mode)) + invalidate_pcr("/dev/sdx write violation"); +} +#else +static inline void check_hd_sd_bypass(struct inode *inode) +{ + return; +} +#endif + +/* configuration options*/ +extern int ima_test_mode; +extern int skip_boot_aggregate; + +static inline void read_configs(void) +{ +#ifdef CONFIG_IMA_TEST_MODE + ima_test_mode = 1; +#else + ima_test_mode = 0; +#endif + +#ifdef CONFIG_IMA_SKIP_BOOT_AGGREGATE + skip_boot_aggregate = 1; +#else + skip_boot_aggregate = 0; +#endif +} + +#ifdef CONFIG_TCG_TPM +struct tpm_chip; + +extern ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz); + +extern struct tpm_chip *tpm_chip_lookup(int chip_num); +#else +struct tpm_chip { + char dummy; +}; + +static inline ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz) +{ + return 0; +} + +static inline struct tpm_chip *tpm_chip_lookup(int chip_num) +{ + return NULL; +} +#endif + + +#endif diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_init.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_init.c --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_init.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_init.c 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Contributions: + * Leendert van Doorn <leendert@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_init.c + * init functions to start up IBM IMA as LSM + */ +#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" +#include "ima_tpm_pcrread.h" +#include "ima_tpm_extend.h" + +/* name for boot aggregate entry */ +char *boot_aggregate_name = "boot_aggregate"; + +extern struct h_table htable; + +/* These identify the driver base version and may not be removed. */ +static const char version[] = "v4.0 06/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 }; + +/* configuration parameters */ +int ima_test_mode; +int skip_boot_aggregate; + +void create_htable(void); +void create_sha_htable(void); +void ima_lsm_init(void); +void ima_fs_init(void); +int ima_add_measure_entry(struct measure_entry *); + +int ima_enabled = 0; +struct tpm_chip *ima_used_chip; + +static int __init ima_enabled_setup(char *str) +{ + ima_enabled = simple_strtol(str, NULL, 0); + return 1; +} + +__setup("ima=", ima_enabled_setup); + + +void ima_add_boot_aggregate(void) +{ + /* cumulative sha1 the first 8 tpm registers */ + struct measure_entry *entry; + size_t count; + + /* 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->inode_nr = 0; /* 0,0 are special (no files) */ + entry->dev_id = 0; + entry->measure_flags = 0; + entry->dirty = DIRTY; + entry->super_block = NULL; + 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 != NULL) { + int i; + u8 pcr_i[20]; + struct crypto_tfm *tfm; + + tfm = crypto_alloc_tfm("sha1", 0); + if (tfm == NULL) { + ima_error("Digest init failed ERROR.\n"); + return; + } + crypto_digest_init(tfm); + + for (i = 0; i < 8; i++) { + tpm_pcrread(i, 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 */ + if (ima_add_measure_entry(entry) < 0) { + kfree(entry); + invalidate_pcr("error adding boot aggregate"); + } else + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, entry->digest); +} + + +/* 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); + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, illegal_pcr); + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, illegal_pcr); + atomic_inc(&htable.violations); /* can overflow into 0; this is an indicator only */ +} + + +static int __init measure_init(void) +{ + struct security_operations null_ops; + + printk(KERN_INFO "IBM Integrity Measurement Architecture (IBM IMA %s).\n", + version); + read_configs(); + + /* check pre-conditions and dependencies */ + if (!ima_test_mode) + ima_enabled = 1; /* unconditionally */ + else { + if (!ima_enabled) { + printk(KERN_INFO " IMA (not enabled in kernel command line) aborting!\n"); + return 0; + } + printk(KERN_INFO " IMA (test mode)\n"); + } + ima_used_chip = tpm_chip_lookup(0); + if (ima_used_chip == NULL) { + if (ima_test_mode) + printk(KERN_INFO " IMA (TPM/BYPASS - no TPM chip found)\n"); + else + /* no way to invalidate pcr and inform remote party */ + IMA_PANIC("IMA: TPM/no support and IMA not in test mode!\n"); + } + /* check for LSM availability */ + memset(&null_ops, 0, sizeof(struct security_operations)); + if (!register_security(&null_ops)) + unregister_security(&null_ops); + else { + if (ima_test_mode) { + ima_enabled = 0; + printk(KERN_INFO " IMA (LSM/not free) aborting!\n"); + return -EFAULT; + } else + invalidate_pcr("LSM/not free in real mode!\n"); + } + create_htable(); /* for measurements */ + create_sha_htable(); + /* boot aggregate must be very first entry */ + if (!skip_boot_aggregate) + ima_add_boot_aggregate(); + ima_lsm_init(); + ima_fs_init(); + return 0; +} + +__initcall(measure_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Reiner Sailer <sailer@private>"); +MODULE_DESCRIPTION + ("Run-time LSM-based IBM Integrity Measurement Architecture"); diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_lsmhooks.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_lsmhooks.c --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_lsmhooks.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_lsmhooks.c 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,181 @@ +/* + * 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_lsmhooks.c + * implements Linux Security Modules hooks that call into + * into the measurement functions + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <asm/mman.h> +#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/namei.h> +#include "ima.h" + +extern struct h_table htable; + +/* if set, then hooks do nothing + * (controls non-lsm module hook as well) */ +unsigned char ima_terminating = 1; + +struct measure_entry *ima_lookup_measure_entry(unsigned long, dev_t); +void measure_mmap_file(struct file *, u32 flags); +int measure_dirty_flag_super(struct super_block *); + +/* measure files mmapped with exec permission */ +int ima_file_mmap(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) +{ + /* filter interesting calls that actually map files executable */ + if (!(reqprot & PROT_EXEC)) + return 0; + + /* now check protection */ + if (reqprot & MAP_SHARED & PROT_EXEC & PROT_WRITE) { + ima_error("MMAP protection flag error!!!\n"); + invalidate_pcr("MMAP protection flag violation!"); + } + atomic_inc(&htable.kernel_measure); + measure_mmap_file(file, (u32)MMAP_MEASURE_FLAG); + /* IMA is non-intrusive, so we always map */ + return 0; +} + +/* dirty flags on open with MAY_WRITE|MAY_APPEND */ +int ima_inode_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + struct ima_inode *i_security = NULL; + struct measure_entry *entry; + + /* dirty-flagging applies to changing files */ + if (!(mask & (MAY_WRITE | MAY_APPEND)) || !inode) + return 0; + + /* general checks against bypassing dirty-flagging */ + check_kmem_bypass(inode); + check_mem_bypass(inode); + check_ram_bypass(inode); + check_hd_sd_bypass(inode); + + /* files that are written to are usually not executed (measured), + optimize this path */ + down(&h_table_mutex); + if ((entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev)) == NULL) + goto out; /* not a measured file */ + if (entry->dirty == CLEAN) + entry->dirty = DIRTY; + /* dirty flag inode */ + if ((i_security = ima_get_inode_security(inode)) != NULL) { + if (atomic_read(&(i_security->measure_count))) { + /* write permission on measured file was granted! */ + invalidate_pcr("ToMToU violation"); + ima_error("VIOLATION: Writing to measured file (%s) while it is being used!\n", + entry->file_name); + } + if (i_security->dirty == CLEAN) + i_security->dirty = DIRTY; + } + out: + up(&h_table_mutex); + return 0; +} + +/* dirty flag files on an umounted file system */ +static int ima_sb_umount(struct vfsmount *mnt, int flags) +{ + /* mark all clean entries with this superblock dirty */ + struct queue_entry *qe; + struct super_block *super = mnt->mnt_sb; + int j; + + down(&h_table_mutex); + for (j = 0; j < htable.max_htable_size; j++) { + qe = htable.queue[j]; + while (qe != NULL) { + if (qe->entry->super_block == super) + if (qe->entry->dirty == CLEAN) + qe->entry->dirty = DIRTY; + qe = qe->next; + } + } + up(&h_table_mutex); + return 0; +} + +/* free security structure if applies */ +static void ima_inode_free_security(struct inode *inode) +{ + struct ima_inode *i_security = ima_get_inode_security(inode); + + if (i_security) { + kfree(i_security); + ima_store_inode_security(inode, NULL); + } +} + +static void ima_file_free_security(struct file *file) +{ + struct ima_file *f_security; + struct ima_inode *i_security = NULL; + + if ((f_security = ima_get_file_security(file)) == NULL) + return; + /* decrease measure count if file is measured */ + i_security = ima_get_inode_security(file->f_dentry->d_inode); + if (i_security && (f_security->is_measuring)) + atomic_dec(&(i_security->measure_count)); + kfree(f_security); + ima_store_file_security(file, NULL); +} + +/* module stacking operations */ +int ima_register_security(const char *name, struct security_operations *ops) +{ + /* no stacking */ + return -EFAULT; +} + +int ima_unregister_security(const char *name, struct security_operations *ops) +{ + /* no stacking */ + return -EFAULT; +} + +struct security_operations ima_ops; + +/* IMA requires early initialization in order measure + all executables etc from the very beginning. */ +void ima_lsm_init(void) +{ + /* prepare ima_ops struct */ + memset(&ima_ops, 0, sizeof(struct security_operations)); + /* set the few non-null elements */ + ima_ops.file_mmap = ima_file_mmap; + ima_ops.file_free_security = ima_file_free_security; + ima_ops.inode_permission = ima_inode_permission; + ima_ops.inode_free_security = ima_inode_free_security; + ima_ops.sb_umount = ima_sb_umount; + ima_ops.register_security = ima_register_security; + ima_ops.unregister_security = ima_unregister_security; + /* rest will be taken care of by registration (fixup) */ + if (register_security(&ima_ops)) { + invalidate_pcr("IMA: Unable to register with kernel.\n"); + return; + } + /* module measurement hook becomes hot */ + ima_terminating = 0; +} diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_main.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_main.c --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_main.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_main.c 2005-06-14 21:58:13.000000000 -0400 @@ -0,0 +1,462 @@ +/* + * 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_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" +#include "ima_tpm_extend.h" + +extern struct h_table htable; +extern struct sha_table sha_htable; + +struct sha_entry *ima_lookup_sha_entry(u8 * sha_value); +struct measure_entry *ima_lookup_measure_entry(unsigned long, dev_t); +int ima_add_measure_entry(struct measure_entry *); +int measure_dirty_flag_inode(struct inode *); +extern struct tpm_chip *ima_used_chip; + +DEFINE_SPINLOCK(ima_measure_file_lock); + +/* + * Returns the dirty flag setting for an inode + * (nfs, since we don't control changes) + */ +static inline ima_entry_flags get_default_dirty_setting(struct inode *inode) +{ + switch (inode->i_sb->s_magic) { + case NFS_SUPER_MAGIC: + return DIRTY; /* dirty */ + break; + default: /* local fs etc. */ + return CLEAN; /* clean */ + } +} + +/* returns >0 if measurement must be skipped + * returns =0 if measurement allowed + */ +static inline int skip_measurement(struct inode *inode) +{ + /* measuring only regular files; can't measure IMA files */ + if (S_ISREG(inode->i_mode) && (inode->i_sb->s_magic != IMA_MAGIC)) + return 0; /* measure */ + else + return 1; /* skip */ +} + + +/* measures new file and adds it to measurement list */ +static struct measure_entry *do_measure_file(struct file *file, struct inode *inode) +{ + struct ima_inode *i_security = NULL; + mm_segment_t oldfs; + int error = 0; + loff_t offset = 0; + size_t count; + struct crypto_tfm *tfm; + struct measure_entry *entry; + + char *bufp = NULL; + /* create read buffer */ + if ((bufp = + (char *) kmalloc(PAGE_SIZE, GFP_KERNEL)) == 0) { + ima_error("no memory for read buffer\n"); + error = -ENOMEM; + goto out; /* invalidate pcr */ + } + /* create new entry and measure */ + entry = (struct measure_entry *) + kmalloc(sizeof(struct measure_entry), GFP_KERNEL); + if (entry == NULL) { + error = -ENOMEM; + ima_error("error allocating new measurement entry"); + kfree(bufp); + goto out; /* invalidate pcr */ + } + entry->inode_nr = inode->i_ino; + entry->dev_id = inode->i_rdev; + entry->dirty = get_default_dirty_setting(inode); + entry->super_block = inode->i_sb; + if ((count = file->f_dentry->d_name.len) > TCG_EVENT_NAME_LEN_MAX) + count = TCG_EVENT_NAME_LEN_MAX; + memcpy(entry->file_name, file->f_dentry->d_name.name, count); + entry->file_name[count] = '\0'; /* ez-print */ + error = 0; + /* second add sha1 over file contents */ + /* init context */ + tfm = crypto_alloc_tfm("sha1", 0); + if (tfm == NULL) { + ima_error("Digest init failed ERROR.\n"); + goto outm; + } + crypto_digest_init(tfm); + + /* set fs so that kernel writes into kernel segment */ + oldfs = get_fs(); + set_fs(KERNEL_DS); + do { + if ((count = + (file->f_op->read) (file, + (char __user *) bufp, + PAGE_SIZE, + &offset)) < 0) { + error = count; + ima_error("Error reading from file (%d)\n", error); + goto outf; + } + /* update hash with this part */ + tfm->__crt_alg->cra_digest.dia_update(crypto_tfm_ctx(tfm), + bufp, count); + } while (count); + set_fs(oldfs); + + /* complete hash */ + crypto_digest_final(tfm, entry->digest); + crypto_free_tfm(tfm); + /* before returning, replicate important information into inode->i_security */ + i_security = ima_get_inode_security(inode); + if (i_security != NULL) { + /* update */ + i_security->dirty = entry->dirty; + } else { + ima_error("error No security structure in measure!\n"); + goto outm; + } + + kfree(bufp); + return (entry); + + /* error exits */ + outf: + set_fs(oldfs); + outm: + kfree(entry); + kfree(bufp); + out: + return (NULL); +} + +/* measure memory (kernel module; still the exact copy of the object file) */ +int do_measure_memory(void *start, unsigned long len, u32 measure_flags, char *name) +{ + struct crypto_tfm *tfm; + u8 mem_digest[20]; + int error = 0; + struct measure_entry *entry; + int count; + + /* init context */ + tfm = crypto_alloc_tfm("sha1", 0); + if (tfm == NULL) { + invalidate_pcr("No SHA1 available"); + return -EFAULT; + } + crypto_digest_init(tfm); + /* now measure the memory ... */ + tfm->__crt_alg->cra_digest.dia_update(crypto_tfm_ctx(tfm), start, + len); + crypto_digest_final(tfm, mem_digest); + crypto_free_tfm(tfm); + + down(&h_table_mutex); + if (!ima_lookup_sha_entry(mem_digest)) { + /* create new entry and measure */ + entry = (struct measure_entry *) + kmalloc(sizeof(struct measure_entry), GFP_KERNEL); + if (entry == NULL) { + invalidate_pcr("OUT OF MEMORY"); + error = -EFAULT; + goto out; + } + entry->inode_nr = 0; /* special entries, no file entries */ + entry->dev_id = 0; + entry->dirty = DIRTY; + entry->super_block = NULL; + memcpy(entry->digest, mem_digest, 20); + if ((count = strlen(name)) > TCG_EVENT_NAME_LEN_MAX) + count = TCG_EVENT_NAME_LEN_MAX; + strncpy(entry->file_name, name, count); + entry->file_name[count] = '\0'; + entry->measure_flags = measure_flags; + if ((error = ima_add_measure_entry(entry)) < 0) { + kfree(entry); + invalidate_pcr + ("error adding new measurement entry");; + goto out; + } else { /* extend PCR */ + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, entry->digest); + } + } /* else we already have this hash value from an exec/file that was running earlier */ + up(&h_table_mutex); + return 0; + out: + up(&h_table_mutex); + return -EFAULT; +} + +static unsigned int find_mod_sec(Elf_Ehdr * hdr, Elf_Shdr * sechdrs, const char *secstrings, const char *name) +{ + unsigned int i; + for (i = 1; i < hdr->e_shnum; i++) + /* Alloc bit cleared means "here is nothing to look for (ignore)" */ + if ((sechdrs[i].sh_flags & SHF_ALLOC) + && strcmp(secstrings + sechdrs[i].sh_name, name) == 0) + return i; + return 0; +} + +/* Measure kernel modules in-memory before relocation */ +void measure_kernel_module(void *start, unsigned long len, const char __user * uargs) +{ + Elf_Ehdr *hdr; + Elf_Shdr *sechdrs; + struct module *mod; + unsigned int modindex; + char *args, *secstrings; + long arglen; + + arglen = strlen_user(uargs); + if (!arglen) { + invalidate_pcr("ERROR measuring kernel module!"); + return; + } + args = kmalloc(arglen, GFP_KERNEL); + if (!args) { + invalidate_pcr("OUT OF MEMORY measuring kernel module!"); + return; + } + if (copy_from_user(args, uargs, arglen) != 0) { + invalidate_pcr("ERROR measuring kernel module!"); + return; + } + /* get the module name for entry */ + hdr = (Elf_Ehdr *) start; + sechdrs = (void *) hdr + hdr->e_shoff; + secstrings = (void *) hdr + sechdrs[hdr->e_shstrndx].sh_offset; + + modindex = find_mod_sec(hdr, sechdrs, secstrings, + ".gnu.linkonce.this_module"); + if (!modindex) { + ima_error("No module found in object\n"); + invalidate_pcr("Module without name?!"); + return; + } + mod = (void *) ((size_t) hdr + sechdrs[modindex].sh_offset); + atomic_inc(&htable.kernel_measure); /* CHECK */ + do_measure_memory(start, len, (u32)MODULE_MEASURE_FLAG, mod->name); + return; +} + + + +static void measure_file (struct file *file, u32 measure_flags, struct inode *inode, struct ima_inode *i_security) +{ + struct measure_entry *entry, *new_entry; + + down(&h_table_mutex); + entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev); + if ((entry != NULL) && (entry->dirty == CLEAN)) { + i_security->dirty = CLEAN; + atomic_inc(&htable.clean_table_hits); + goto out; /* done */ + } + new_entry = do_measure_file(file, inode); + /* now we adjust the entry table: + * -- if there was no entry, we just add the new one + * -- if there was one but different hash, we add the new one + * -- if there was one and same hash, we clear dirty bit on existing one + */ + if (!new_entry) { + /* internal error, make sure attestation fails from now on */ + invalidate_pcr("error measuring file"); + goto out; + } + new_entry->measure_flags = measure_flags; + if (entry == NULL) { /* no old entry for this inode found */ + /* add if this hash is new (i.e., no copy measured yet) */ + if (!ima_lookup_sha_entry(new_entry->digest)) { + if (ima_add_measure_entry(new_entry) < 0) { + kfree(new_entry); + invalidate_pcr("error adding measurement entry"); + } else + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, new_entry->digest); + } + goto out; + } + /* old entry exists (!= clean) */ + if (!memcmp(entry->digest, new_entry->digest, 20)) { + /* set with default (no clean-flag for nfs) */ + entry->dirty = get_default_dirty_setting(inode); + i_security->dirty = entry->dirty; + atomic_inc(&htable.dirty_table_hits); + kfree(new_entry); + } else { + /* dirty and look whether to add new entry */ + entry->dirty = CHANGED; + atomic_inc(&htable.changed_files); + if (!ima_lookup_sha_entry(new_entry->digest)) { + if (ima_add_measure_entry(new_entry) < 0) { + kfree(new_entry); + invalidate_pcr("error adding measurement entry"); + } else + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, new_entry->digest); + } + } + out: + up(&h_table_mutex); +} + + +/* Measure user space file descriptor, protect file from being + * written until all measureing processes have closed the file + */ +int measure_user_file(struct file *file, u32 measure_flags) +{ + struct inode *inode; + struct ima_file *f_security = NULL; + struct ima_inode *i_security = NULL; + + if (!file || !file->f_op || !file->f_dentry || !file->f_dentry->d_inode) + return -EACCES; + + inode = file->f_dentry->d_inode; + + /* here we skip unnecessary measurements */ + if (skip_measurement(inode)) + return -EACCES; /* not allowed to measure; user apps to handle error */ + + /* a) if there is already a writer on this file --> error! */ + if (atomic_read(&(inode->i_writecount)) > 0) { + struct measure_entry *entry; + invalidate_pcr("ToMToU violation"); + down(&h_table_mutex); + entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev); + ima_error("VIOLATION: Measured file (%s) has writers!\n", + (entry != NULL) ? entry->file_name : "most likely measuring file opened rw"); + up(&h_table_mutex); + return -EACCES; + } + inode = file->f_dentry->d_inode; + /* mark this file as measuring (increases measurement-refcount on inode) */ + if ((f_security = ima_get_file_security(file)) != NULL) { + i_security = ima_get_inode_security(file->f_dentry->d_inode); + if (i_security == NULL) { + invalidate_pcr("Internal error (f_security not free but no i_security).\n"); + return -EFAULT; + } + } else { + /* create f_security and if necessary i_security */ + f_security = kmalloc(sizeof(struct ima_file), GFP_KERNEL); + if (f_security == NULL) { + invalidate_pcr("out of memory error"); + return -ENOMEM; + } else { + f_security->is_measuring = 1; + ima_store_file_security(file, f_security); + } + /* we maintain an inode copy of clean etc. to speed up clean hits */ + i_security = ima_get_inode_security(inode); + if (i_security != NULL) + atomic_inc(&(i_security->measure_count)); + else { + spin_lock(&ima_measure_file_lock); + if ((i_security = ima_get_inode_security(inode))) + goto dontalloc; + i_security = + kmalloc(sizeof(struct ima_inode), GFP_KERNEL); + if (i_security == NULL) { + spin_unlock(&ima_measure_file_lock); + invalidate_pcr("out of memory error"); + return -EFAULT; + } else { + i_security->dirty = DIRTY; + /* is reset later after measuring file */ + atomic_set(&(i_security->measure_count), 1); + ima_store_inode_security(inode, i_security); + } +dontalloc: + spin_unlock(&ima_measure_file_lock); + } + } + /* catch most cases */ + if (i_security->dirty == CLEAN) + atomic_inc(&htable.clean_inode_hits); + else + measure_file(file, measure_flags, inode, i_security); + return 0; +} + + +/* Measure files mapped as executable */ +void measure_mmap_file(struct file *file, u32 measure_flags) +{ + struct inode *inode; + struct ima_inode *i_security = NULL; + + if (!file || !file->f_op || !file->f_dentry || !file->f_dentry->d_inode) + return; + + inode = file->f_dentry->d_inode; + + /* here we skip non-allowed measurements */ + if (skip_measurement(inode)) + return; + + /* if there is already a writer on this file --> error! */ + if (atomic_read(&(inode->i_writecount)) > 0) { + invalidate_pcr("Measured file has writers."); + return; + } + /* we maintain an inode copy of clean etc. to speed up clean hits */ + i_security = ima_get_inode_security(inode); + if (!i_security) { + spin_lock(&ima_measure_file_lock); + if ((i_security = ima_get_inode_security(inode))) + goto dontalloc; + i_security = kmalloc(sizeof(struct ima_inode), GFP_KERNEL); + if (i_security == NULL) { + spin_unlock(&ima_measure_file_lock); + invalidate_pcr("out of memory error"); + return; + } else { + i_security->dirty = DIRTY; + /* is reset later after measuring file */ + atomic_set(&(i_security->measure_count), 0); + ima_store_inode_security(inode, i_security); + } +dontalloc: + spin_unlock(&ima_measure_file_lock); + } + /* catch most cases */ + if (i_security->dirty == CLEAN) + atomic_inc(&htable.clean_inode_hits); + else + measure_file(file, measure_flags, inode, i_security); +} diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_queue.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_queue.c --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_queue.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_queue.c 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,185 @@ +/* + * 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_queue.c + * implements queues for run-time measurement + * functions based on SHA1 + */ +#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" + +/* pointer to very first and latest measurement (time-ordered) */ +struct queue_entry *first_measurement = NULL; /* for printing */ +struct queue_entry *latest_measurement = NULL; /* for adding */ + +struct h_table htable; /* key: inode (before secure-hashing a file) */ +struct sha_table sha_htable; /* key: hash (after secure-hashing a file) */ +int ima_add_sha_entry(struct measure_entry *); + +DECLARE_MUTEX_LOCKED(h_table_mutex); + +void create_sha_htable(void) +{ + int i; + + atomic_set(&sha_htable.len, 0); + sha_htable.max_htable_size = MEASURE_HTABLE_SIZE; + for (i = 0; i < sha_htable.max_htable_size; i++) { + sha_htable.queue[i] = NULL; + atomic_set(&sha_htable.queue_len[i], 0); + } +} + +void create_htable(void) +{ + int i; + + init_MUTEX_LOCKED(&h_table_mutex); + first_measurement = NULL; + latest_measurement = NULL; + atomic_set(&htable.len, 0); + atomic_set(&htable.user_measure, 0); + atomic_set(&htable.kernel_measure, 0); + atomic_set(&htable.clean_inode_hits, 0); + atomic_set(&htable.clean_table_hits, 0); + atomic_set(&htable.dirty_table_hits, 0); + atomic_set(&htable.changed_files, 0); + atomic_set(&htable.violations, 0); + htable.max_htable_size = MEASURE_HTABLE_SIZE; + for (i = 0; i < htable.max_htable_size; i++) { + htable.queue[i] = NULL; + atomic_set(&htable.queue_len[i], 0); + } + up(&h_table_mutex); +} + +/* + * also sets clean and dirty table hit marks + */ +struct measure_entry *ima_lookup_measure_entry(unsigned long inode_number, dev_t dev_number) +{ + struct queue_entry *qe; + struct measure_entry *me; + + /* fill in later */ + qe = htable.queue[HASH_KEY(inode_number)]; + while ((qe != NULL) && ((qe->entry->inode_nr != inode_number) + || (qe->entry->dev_id != dev_number))) + qe = qe->next; + + if (qe != NULL) { + if (qe->entry->dirty != CLEAN) + atomic_inc(&htable.dirty_table_hits); + else + atomic_inc(&htable.clean_table_hits); + + me = qe->entry; + } else { + me = NULL; + } + return me; +} + + + +struct sha_entry *ima_lookup_sha_entry(u8 * sha_value) +{ + struct sha_entry *se; + unsigned int key; + + key = SHA_KEY(sha_value); + se = sha_htable.queue[key]; + while ((se != NULL) && (memcmp(se->digest, sha_value, 20))) + se = se->next; + return se; +} + + +int ima_add_measure_entry(struct measure_entry *entry) +{ + unsigned int key; + struct queue_entry *qe; + int error = 0; + + /* calculate key */ + key = HASH_KEY(entry->inode_nr); + + /* create queue_entry */ + if ((qe = kmalloc(sizeof(struct queue_entry), GFP_KERNEL)) == NULL) { + ima_error("OUT OF MEMORY in %s.\n", __func__); + error = -ENOMEM; + goto out; + } + qe->entry = entry; + + /* insert entry at beginning of queue */ + qe->next = htable.queue[key]; + qe->later = NULL; + htable.queue[key] = qe; + atomic_inc(&htable.queue_len[key]); + /* update later list */ + if (first_measurement == NULL) + first_measurement = qe; + else + latest_measurement->later = qe; + + latest_measurement = qe; + atomic_inc(&htable.len); + /* now add to sha hash table, too */ + if (ima_add_sha_entry(entry)) + error = -ENOMEM; + out: + return error; +} + + + +int ima_add_sha_entry(struct measure_entry *entry) +{ + unsigned int key; + struct sha_entry *se; + + /* calculate key */ + key = SHA_KEY(entry->digest); + /* create queue_entry */ + if ((se = kmalloc(sizeof(struct sha_entry), GFP_KERNEL)) == NULL) + goto out; + se->m_entry = entry; + se->digest = entry->digest; + se->next = NULL; + + /* insert entry at beginning of queue */ + se->next = sha_htable.queue[key]; + sha_htable.queue[key] = se; + atomic_inc(&sha_htable.queue_len[key]); + /* update later list */ + atomic_inc(&sha_htable.len); + return 0; + + out: + ima_error("OUT OF MEMORY ERROR creating queue entry.\n"); + return -ENOMEM; +} diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_extend.h linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_extend.h --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_extend.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_extend.h 2005-06-14 20:32:39.000000000 -0400 @@ -0,0 +1,65 @@ +/* + * 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_tpm_extend.h + * implements glue code to connect IMA to the TPM driver + * (glues to tpmdd on www.sourceforge.net/tpmdd) + */ +#ifndef __LINUX_IMA_TPM_EXTEND_H +#define __LINUX_IMA_TPM_EXTEND_H + +#define TPM_BUFSIZE 2048 + +extern struct tpm_chip *ima_used_chip; + +static const u8 extend[] = { + 0, 193, /* TPM_TAG_RQU_COMMAND */ + 0, 0, 0, 34, /* length */ + 0, 0, 0, 20, /* TPM_ORD_Extend */ + 0, 0, 0, 0 /* PCR index */ +}; + +static void tpm_extend(int index, const u8 * digest) +{ + u8 *data; + u32 i; + int len; + + if (ima_used_chip == NULL) + return; + + if ((data = kmalloc(TPM_BUFSIZE, GFP_KERNEL)) == NULL) + goto error; + + memcpy(data, extend, sizeof(extend)); + i = cpu_to_be32(index); + memcpy(data+10, &i, 4); + memcpy(data + 14, digest, 20); + if ((len = tpm_transmit(ima_used_chip, data, TPM_BUFSIZE)) >= 30) { + memcpy(&i, data + 6, 4); /* return code */ + if (be32_to_cpu(i) == 0) + goto out; /* ok */ + } + error: + if (!ima_test_mode) + IMA_PANIC("IMA: Error Communicating to TPM chip and IMA not in test mode!\n"); + else + ima_error("Error Communicating to TPM chip\n"); + out: + if (data != NULL) + kfree(data); +} + +#endif diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_pcrread.h linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_pcrread.h --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_pcrread.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_pcrread.h 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,67 @@ +/* + * 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_tpm_pcrread.h + * implements glue code to connect IMA to the TPM driver + * (glues to tpmdd on www.sourceforge.net/tpmdd) + */ +#ifndef __LINUX_IMA_TPM_PCRREAD_H +#define __LINUX_IMA_TPM_PCRREAD_H + +#define TPM_BUFSIZE 2048 + +extern struct tpm_chip *ima_used_chip; + +static const u8 pcrread[] = { + 0, 193, /* TPM_TAG_RQU_COMMAND */ + 0, 0, 0, 14, /* length */ + 0, 0, 0, 21, /* TPM_ORD_PcrRead */ + 0, 0, 0, 0 /* PCR index */ +}; + + +static void tpm_pcrread(int index, u8 * hash) +{ + u8 *data; + u32 i; + ssize_t len; + + if (ima_used_chip == NULL) + return; + + if ((data = kmalloc(TPM_BUFSIZE, GFP_KERNEL)) == NULL) + goto error; + + memcpy(data, pcrread, sizeof(pcrread)); + i = cpu_to_be32(index); + memcpy(data+10, &i, 4); + if ((len = tpm_transmit(ima_used_chip, data, TPM_BUFSIZE)) >= 30) { + memcpy(&i, data + 6, 4); /* return code */ + if (be32_to_cpu(i) == 0) { + memcpy(hash, data + 10, 20); + goto out; /* ok */ + } + } + error: + if (!ima_test_mode) + IMA_PANIC("IMA: Error Communicating to TPM chip and IMA not in test mode!\n"); + else + ima_error("Error Communicating to TPM chip\n"); + out: + if (data != NULL) + kfree(data); +} + +#endif diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/Kconfig linux-2.6.12-rc6-mm1-ima/security/ima/Kconfig --- linux-2.6.12-rc6-mm1_orig/security/ima/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/Kconfig 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,90 @@ +# +# 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) + help + To measure executable code running on this + system, say Y. If you say Y, you must disable + any other security modules because LSM are + currently not stackable.To actually start IMA, + you need to set a kernel boot parameter "ima=1". + If unsure, say N. + +config IMA_TEST_MODE + bool "IMA test mode" + depends on IMA_MEASURE + default y + help + If you would like to test the measurement + architecture but you do not have a TPM hardware + on your system, say Y. Otherwise say N. If you say + Y and IMA does not find a TPM chip it will just bypass + +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. + +config IMA_SKIP_BOOT_AGGREGATE + bool "Skip Boot Aggregate Creation" + depends on IMA_MEASURE + help + If y, the usual aggregate over the boot PCRs + of the TPM is not calculated and not added to + the measurement list. If unsure, say N. + +config IMA_KMEM_BYPASS_PROTECTION + bool "Invalidate PCR on /dev/kmem write" + depends on IMA_MEASURE + help + This setting enforces TPM PCR invalidation if /dev/kmem + is written (bypass of measurements possible). Usually, + this does not restrict normal systems. + If unsure, say Y. + +config IMA_RAM_BYPASS_PROTECTION + bool "Invalidate PCR on /dev/ram write" + depends on IMA_MEASURE + help + This setting enforces TPM PCR invalidation if /dev/ram + is written (bypass of measurements possible). If you use + ramdisk, you might have a problem. + If unsure, say N. + +config IMA_HD_SD_BYPASS_PROTECTION + bool "Invalidate PCR on /dev/hdx /dev/sdx write" + depends on IMA_MEASURE + help + This setting enforces TPM PCR invalidation if /dev/hda, + /dev/hdb ... or /dev/sda, /dev/sdb ... are written + directly (bypass of measurement dirty flagging possible). + This requires some changes in /etc/rc.sysinit: + * check filesystems readonly (in rc.sysinit add "-n" fsck + option, remove -a where it appears + * switch off swapping (kernel controlled open on rw) + otherwise the PCRs will usually be invalidated. + If unsure, say N. + +config IMA_MEM_BYPASS_PROTECTION + bool "Invalidate PCR on /dev/mem write" + depends on IMA_MEASURE + help + This setting enforces TPM PCR invalidation if /dev/mem + is written (bypass of measurements possible). X needs + currently to write directly to /dev/mem. For client systems, + you might want to chose N here. For server systems not running X, + it is safe to say yes. + If unsure, say N. + +#endmenu + diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/Makefile linux-2.6.12-rc6-mm1-ima/security/ima/Makefile --- linux-2.6.12-rc6-mm1_orig/security/ima/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc6-mm1-ima/security/ima/Makefile 2005-06-14 16:25:05.000000000 -0400 @@ -0,0 +1,5 @@ + +obj-$(CONFIG_IMA_MEASURE) += ima_init.o ima_main.o \ + ima_queue.o ima_lsmhooks.o ima_fs.o + + diff -uprN linux-2.6.12-rc6-mm1_orig/security/Kconfig linux-2.6.12-rc6-mm1-ima/security/Kconfig --- linux-2.6.12-rc6-mm1_orig/security/Kconfig 2005-06-06 11:22:29.000000000 -0400 +++ linux-2.6.12-rc6-mm1-ima/security/Kconfig 2005-06-14 16:25:05.000000000 -0400 @@ -86,6 +86,7 @@ config SECURITY_SECLVL If you are unsure how to answer this question, answer N. source security/selinux/Kconfig +source security/ima/Kconfig endmenu diff -uprN linux-2.6.12-rc6-mm1_orig/security/Makefile linux-2.6.12-rc6-mm1-ima/security/Makefile --- linux-2.6.12-rc6-mm1_orig/security/Makefile 2005-06-06 11:22:29.000000000 -0400 +++ linux-2.6.12-rc6-mm1-ima/security/Makefile 2005-06-14 16:25:05.000000000 -0400 @@ -4,6 +4,7 @@ obj-$(CONFIG_KEYS) += keys/ subdir-$(CONFIG_SECURITY_SELINUX) += selinux +subdir-$(CONFIG_IMA_MEASURE) += ima # if we don't select a security model, use the default capabilities ifneq ($(CONFIG_SECURITY),y) @@ -14,6 +15,7 @@ endif obj-$(CONFIG_SECURITY) += security.o dummy.o # Must precede capability.o in order to stack properly. obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o +obj-$(CONFIG_IMA_MEASURE) += ima/built-in.o obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o obj-$(CONFIG_SECURITY_SECLVL) += seclvl.o
This archive was generated by hypermail 2.1.3 : Wed Jun 15 2005 - 07:38:53 PDT