This is the 3rd of 4 patches that constitute the IBM Integrity Measurement Architecture (IMA). This patch includes the main IMA functionality as a Linux Security Module. This patch applies to the clean 2.6.12-rc4 test kernel. Signed-off-by: Reiner Sailer <sailer@private> --- diff -uprN linux-2.6.12-rc4/security/ima/ima.h linux-2.6.12-rc4-ima/security/ima/ima.h --- linux-2.6.12-rc4/security/ima/ima.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima.h 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: TBD + * + * 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/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) + +/* set during registering as lsm */ +extern unsigned char ima_terminating; + +#define IMA_MEASURE_MODULE_NAME "IMA" +#define IMA_MEASURE_PROC_NAME "ima" + +/* 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 + (invalidate TPM PCR when executing one of these) */ +#define DEVFS_SUPER_MAGIC 0x1373 +#define PROC_SUPER_MAGIC 0x9fa0 +#define SYSFS_MAGIC 0x62656572 + +/* + * request structure fd: file descriptor + * label: sec label defined in user space or in kernel + * flags: store the hook that initiated measurement and more + */ +#define FLAG_HOOK_MASK 0x0f +#define MMAP_MEASURE_FLAG 0x01 +#define MODULE_MEASURE_FLAG 0x02 +#define USER_MEASURE_FLAG 0x04 +struct measure_request { + int fd; + unsigned short label; + unsigned long flags; +}; + +#define MEASURE_HTABLE_SIZE 512 +/* key = lowest two bytes of inode_number */ +#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 */ +#define IMA_MAGIC 0x9999 +struct ima_inode { + unsigned short magic; + atomic_t measure_count; /* # processes currently using this file in measure-mode */ + ima_entry_flags dirty; + char *file_name; /* points to measure entry->fileName */ +}; + +/* security structure appended to measured files*/ +struct ima_file { + unsigned short magic; /* identify our struct format */ + 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)) + +#define ENTRY_MAXFILENAME 50 +struct measure_entry { + struct measure_request mr; /* keep info from measure request if applies */ + unsigned long inodeNr; + dev_t devId; + ima_entry_flags dirty; + u8 digest[20]; /* sha1 measurement hash */ + char fileName[51]; /* max first 50 characters of name + \0 */ + unsigned long fsMagic; /* file system magic (distinuish local/remote files) */ + struct super_block *superBlock; /* 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 sysfs; + atomic_t cleanInodeHits; /* times we find an inode clean when measuring */ + atomic_t cleanTableHits; /* times we find a clean htable hit */ + atomic_t dirtyTableHits; /* times we find a dirty htable hit */ + atomic_t changedFiles; /* times we realize a dirty marked entry really changed */ + unsigned int max_htable_size; + u8 terminating; + struct queue_entry *queue[MEASURE_HTABLE_SIZE]; + atomic_t queueLen[MEASURE_HTABLE_SIZE]; + spinlock_t lock; +}; + +struct sha_table { + atomic_t len; + unsigned int max_htable_size; + u8 terminating; + struct sha_entry *queue[MEASURE_HTABLE_SIZE]; + atomic_t queueLen[MEASURE_HTABLE_SIZE]; + spinlock_t lock; +}; + +/* configuration options*/ +extern int ima_test_mode; +extern int skip_boot_aggregate; +extern int ram_bypass_protection; +extern int hd_sd_bypass_protection; +extern int kmem_bypass_protection; +extern int mem_bypass_protection; + +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_IMA_RAM_BYPASS_PROTECTION + ram_bypass_protection = 1; +#else + ram_bypass_protection = 0; +#endif + +#ifdef CONFIG_IMA_HD_SD_BYPASS_PROTECTION + hd_sd_bypass_protection = 1; +#else + hd_sd_bypass_protection = 0; +#endif + +#ifdef CONFIG_IMA_KMEM_BYPASS_PROTECTION + kmem_bypass_protection = 1; +#else + kmem_bypass_protection = 0; +#endif + +#ifdef CONFIG_IMA_MEM_BYPASS_PROTECTION + mem_bypass_protection = 1; +#else + mem_bypass_protection = 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 + +/* general prototypes */ +void invalidate_pcr(char *); + +#endif diff -uprN linux-2.6.12-rc4/security/ima/ima_init.c linux-2.6.12-rc4-ima/security/ima/ima_init.c --- linux-2.6.12-rc4/security/ima/ima_init.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima_init.c 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Contributions: + * Leendert van Doorn <leendert@private> + * + * Maintained by: TBD + * + * 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" + +/* These identify the driver base version and may not be removed. */ +static const char version[] = "v2.0 05/18/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; +int ram_bypass_protection; +int hd_sd_bypass_protection; +int kmem_bypass_protection; +int mem_bypass_protection; + +void create_htable(void); +void destroy_htable(void); +void create_sha_htable(void); +void ima_proc_init(void); +void ima_sysfs_init(void); +void ima_sysfs_remove(void); +void ima_add_boot_aggregate(void); +void ima_lsm_init(void); +void tpm_extend(int index, const u8 * digest); + +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); + + +/* 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); + /* now indicate that we invalidated pcr in another pcr (not mandatory) */ + tpm_extend(CONFIG_IMA_MEASURE_INVALIDATE_INDICATION_IDX, illegal_pcr); +} + +static int __init measure_init(void) +{ + struct crypto_tfm *tfm; + 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 */ + panic("IMA: TPM/no support and IMA not in test mode!\n"); + } + } + if ((tfm = crypto_alloc_tfm("sha1", 0)) == NULL) { + if (ima_test_mode) { + printk(KERN_INFO " IMA (SHA-1/no support) aborting!\n"); + ima_enabled = 0; + return -EFAULT; + } else { + invalidate_pcr("No SHA1 support in real mode!"); + } + } else { + crypto_free_tfm(tfm); + } + /* 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_sysfs_init(); + ima_proc_init(); + return 0; +} + +static void __exit measure_exit(void) +{ + if (!ima_enabled) + return; + ima_sysfs_remove(); +} + +__initcall(measure_init); +__exitcall(measure_exit); + + +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-rc4/security/ima/ima_lsmhooks.c linux-2.6.12-rc4-ima/security/ima/ima_lsmhooks.c --- linux-2.6.12-rc4/security/ima/ima_lsmhooks.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima_lsmhooks.c 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: TBD + * + * 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" + +#define MEMORY_MAJOR 1 +#define RAMDISK_MAJOR 1 +#define HD_MAJOR 3 +#define SD_MAJOR 8 +#define MEM_MINOR 1 +#define KMEM_MINOR 2 + +/* if set, then hooks do nothing + * (controls non-lsm module hook as well) */ +unsigned char ima_terminating = 1; + +/* keeps track of calls to mmap_measure */ +atomic_t global_count_mmap_measure; + +int measure_file_exec(struct file *, const struct measure_request *); +int measure_dirty_flag_inode(struct inode *); +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) +{ + static struct measure_request mr = { + .fd = 0, .label = 0, + .flags = MMAP_MEASURE_FLAG + }; + + if (ima_terminating) + return 0; + + /* filter interesting calls that actually map files executable */ + if (!(reqprot & PROT_EXEC) || !file || !file->f_op) + 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(&global_count_mmap_measure); + measure_file_exec(file, &mr); + /* IMA is non-intrusive, so we always map */ + return 0; +} + +/* dirty flags on access with MAY_WRITE|MAY_APPEND */ +int ima_inode_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + struct ima_inode *i_security = NULL; + unsigned int major, minor; + + if (ima_terminating) + return 0; + + /* filter interesting permissions for dirty-flagging */ + if (!(mask & (MAY_WRITE | MAY_APPEND)) || !inode) + return 0; + + major = imajor(inode); + minor = iminor(inode); + /* check for write/append on /dev/kmem, major=1,minor=2 */ + if ((major == MEMORY_MAJOR) && S_ISCHR(inode->i_mode)) { + if (kmem_bypass_protection && (minor == KMEM_MINOR)) { + invalidate_pcr("/dev/kmem write violation"); + /* if needed, get cmdline as done in /fs/proc/base.c */ + } + /* X uses it all over the place ... */ + if (mem_bypass_protection && (minor == MEM_MINOR)) { + invalidate_pcr("/dev/mmem write violation"); + } + } else if (ram_bypass_protection && (major == RAMDISK_MAJOR) + && S_ISBLK(inode->i_mode)) { + invalidate_pcr("/dev/ram write violation"); + } else if (hd_sd_bypass_protection && (major == HD_MAJOR) + && S_ISBLK(inode->i_mode)) { + invalidate_pcr("/dev/hdx write violation"); + } else if (hd_sd_bypass_protection && (major == SD_MAJOR) + && S_ISBLK(inode->i_mode)) { + invalidate_pcr("/dev/sdx write violation"); + } + /* else dirty-flag file if measurement bit set in inode-extension */ + i_security = ima_get_inode_security(inode); + /* now check whether this is a file that is currently measured */ + if (i_security == NULL) + goto out; + if (i_security->magic != IMA_MAGIC) { + invalidate_pcr("IILLEGAL IMA INODE magic found.\n"); + return 0; + } + if (!i_security->file_name) { + /* no file name; don't dirty flag */ + return 0; + } + if (atomic_read(&(i_security->measure_count))) { + /* write permission on measured file was granted! + * should never occur to file_mmap-ed files but + * only to instrumented measures from user space */ + ima_error("ToMToU VIOLATION on file=%s!\n", + i_security->file_name ? + i_security->file_name : "NONAME"); + invalidate_pcr("ToMToU violation"); + } + out: + /* dirty-flag flag in inode and htable */ + measure_dirty_flag_inode(inode); + return 0; +} + +/* dirty flag files on an umounted file system */ +int ima_sb_umount(struct vfsmount *mnt, int flags) +{ + if (ima_terminating) + return 0; + + measure_dirty_flag_super(mnt->mnt_sb); + return 0; +} + +/* free security structure if applies */ +static void ima_inode_free_security(struct inode *inode) +{ + struct ima_inode *i_security; + + if (ima_terminating) + return; + + i_security = ima_get_inode_security(inode); + if (i_security) { + if (i_security->magic != IMA_MAGIC) { + ima_error("ILLEGAL IMA INODE magic=%x in ima_inode_free_security.\n", + i_security->magic); + return; + } + kfree(i_security); + ima_store_inode_security(inode, NULL); + } + return; +} + +static void ima_file_free_security(struct file *file) +{ + struct ima_file *f_security; + struct ima_inode *i_security = NULL; + + if (ima_terminating) + return; + + f_security = ima_get_file_security(file); + /* decrease measure count if file is measured */ + if (f_security == NULL) + return; + if (f_security->magic != IMA_MAGIC) { + ima_error("ILLEGAL IMA FILE magic=%x in ima_file_free_security.\n", + f_security->magic); + return; + } + i_security = ima_get_inode_security(file->f_dentry->d_inode); + if (i_security) { + if (f_security->is_measuring) { + atomic_dec(&(i_security->measure_count)); + } + } + kfree(f_security); + ima_store_file_security(file, NULL); + return; +} + +/* 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) +{ + atomic_set(&global_count_mmap_measure, 0); + + /* 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; + } + /* lsm callback and module hooks become hot now ... */ + ima_terminating = 0; +} + +void ima_remove(void) +{ + ima_terminating = 1; + /* now unregister the security module */ + unregister_security(&ima_ops); +} + diff -uprN linux-2.6.12-rc4/security/ima/ima_main.c linux-2.6.12-rc4-ima/security/ima/ima_main.c --- linux-2.6.12-rc4/security/ima/ima_main.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima_main.c 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,596 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: TBD + * + * 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/crypto.h> +#include <linux/stat.h> +#include "ima.h" + +/* name for boot aggregate entry */ +char *boot_aggregate_name = "boot_aggregate"; + +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; +void tpm_extend(int index, const u8 * digest); +void tpm_pcrread(int index, u8 * hash); + +static inline void *crypto_tfm_ctx(struct crypto_tfm *tfm) +{ + return (void *) &tfm[1]; +} + +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->inodeNr = 0; /* 0,0 are special (no files) */ + entry->devId = 0; + entry->fsMagic = 0; + entry->dirty = DIRTY; + entry->superBlock = NULL; + memset(entry->digest, 0, 20); + if ((count = strlen(boot_aggregate_name)) > ENTRY_MAXFILENAME) + count = ENTRY_MAXFILENAME; + memcpy(entry->fileName, boot_aggregate_name, count); + entry->fileName[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 0..0 entry */ + if (ima_add_measure_entry(entry) < 0) { + kfree(entry); + invalidate_pcr("error adding boot aggregate"); + } else { /* extend PCR */ + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, entry->digest); + } +} + +/* + * Returns the dirty flag setting for an inode + * (nfs, windows fs, etc. 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 can be skipped + * returns =0 if measurement must be done + */ +static int skip_measurement(struct file *file) +{ + /* what could we exclude + * - non-executable/non-library files ? + * - /proc /dev ? + */ + struct inode *inode = file->f_dentry->d_inode; + + if (!(file->f_op) || !(file->f_op->read)) + return 1; /* no file to measure */ + if ((inode->i_sb->s_magic == DEVFS_SUPER_MAGIC) || + (inode->i_sb->s_magic == PROC_SUPER_MAGIC) || + (inode->i_sb->s_magic == SYSFS_MAGIC)) { + invalidate_pcr("CANNOT measure fs type.\n"); + return 1; /*can't measure */ + } + if (S_ISLNK(inode->i_mode) || + S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { + return 1; /* don't measure */ + } + return 0; /* measure */ +} + + +/* + * measures new file and + * adds it to measurement list + */ +static struct measure_entry *measure_file(struct file *file, struct dentry *dentry, 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->inodeNr = inode->i_ino; + entry->devId = inode->i_rdev; + entry->fsMagic = inode->i_sb->s_magic; + entry->dirty = get_default_dirty_setting(inode); + entry->superBlock = inode->i_sb; + if ((count = dentry->d_name.len) > ENTRY_MAXFILENAME) + count = ENTRY_MAXFILENAME; + memcpy(entry->fileName, dentry->d_name.name, count); + entry->fileName[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 */ + if (i_security->magic != IMA_MAGIC) { + invalidate_pcr("Illegal magic in i_security structure"); + goto outm; + } else { + i_security->dirty = entry->dirty; + /* measure_count was increased in local_measure already */ + i_security->file_name = (char *) (entry->fileName); + /* increase #procs measuring; dec on file-close */ + } + } else + panic("IMA: never should we end up here! No security structure in measure!\n"); + + kfree(bufp); + return (entry); + + /* error exits */ + outf: + set_fs(oldfs); + outm: + kfree(entry); + kfree(bufp); + out: + /* invalidate TPM */ + invalidate_pcr("error measuring file"); + return (NULL); +} + +/* measure memory */ +int do_measure_memory(void *start, unsigned long len, const struct measure_request *mr, char *name) +{ + struct crypto_tfm *tfm; + u8 mem_digest[20]; + int error = 0; + struct measure_entry *entry; + + /* 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->inodeNr = 0; /* special entries, no file entries */ + entry->devId = 0; + entry->fsMagic = 0; + entry->dirty = DIRTY; + entry->superBlock = NULL; + memcpy(entry->digest, mem_digest, 20); + strncpy(entry->fileName, name, ENTRY_MAXFILENAME); /* ez-print */ + if (mr != NULL) { + memcpy(&(entry->mr), mr, + sizeof(struct measure_request)); + } else { + memset(&(entry->mr), 0, + sizeof(struct measure_request)); + } + + 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; + struct measure_request mr = {.fd = 0, .label = 0, + .flags = MODULE_MEASURE_FLAG + }; + 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); + do_measure_memory(start, len, &mr, mod->name); + return; +} + + +/* Measure files mapped as executable */ +int measure_file_exec(struct file *file, const struct measure_request *mr) +{ + struct dentry *dentry; + struct inode *inode; + int error = 0; + struct measure_entry *entry, *new_entry; + struct ima_file *f_security = NULL; + struct ima_inode *i_security = NULL; + + if (!file || !file->f_op) { + ima_error("File not found Error!\n"); + return -EACCES; + } + if (!file->f_dentry || !file->f_dentry->d_inode) { + ima_error("File dentry or inode connection broken (NULL) ERROR!\n"); + return -EACCES; + } + /* here we skip unnecessary measurements */ + if (skip_measurement(file)) { + return 0; + } + dentry = file->f_dentry; + inode = dentry->d_inode; + /* save information in file in order to dec measure_count in inode once + * file is closed ... */ + f_security = ima_get_file_security(file); + if (f_security != NULL) { + if (f_security->magic != IMA_MAGIC) { + invalidate_pcr("Internal inconsistency error (f_security with illegal magic).\n"); + return -EFAULT; + } else { + /* HERE file struct is being re-used (not freed) .. happens more and more */ + i_security = + ima_get_inode_security(file->f_dentry-> + d_inode); + if ((i_security == NULL) + || (i_security->magic != IMA_MAGIC)) { + invalidate_pcr("Internal inconsistency error (f_security not free but no i_security).\n"); + return -EFAULT; + } + } + } else { + /* file->f_security = NULL; normal case */ + f_security = kmalloc(sizeof(struct ima_file), GFP_KERNEL); + if (f_security == NULL) { + invalidate_pcr("out of memory error"); + return -EFAULT; + } else { + f_security->magic = IMA_MAGIC; + f_security->is_measuring = 1; + ima_store_file_security(file, f_security); + } + /* a) we maintain an inode copy of clean etc. to speed up clean hits */ + i_security = ima_get_inode_security(inode); + if (i_security != NULL) { + if (i_security->magic != IMA_MAGIC) { + invalidate_pcr + ("PANIC! Unexpected i_security MAGIC!"); + return -EFAULT; + } + /* only increment it once for any open file, thus here in the "f_security==null" case */ + atomic_inc(&(i_security->measure_count)); + } else { + /* create ima_inode structure */ + i_security = + kmalloc(sizeof(struct ima_inode), GFP_KERNEL); + if (i_security == NULL) {; + invalidate_pcr("out of memory error"); + return -EFAULT; + } else { + i_security->magic = IMA_MAGIC; + i_security->dirty = DIRTY; + /* is reset later after measuring file */ + atomic_set(&(i_security->measure_count), + 1); + i_security->file_name = NULL; + ima_store_inode_security(inode, + i_security); + } + } + } + /* a) catch most cases */ + i_security = ima_get_inode_security(inode); + if ((i_security) && (i_security->dirty == CLEAN)) { + atomic_inc(&htable.cleanInodeHits); + return 0; /* clean hit */ + } + /* b) if there is already a writer on this file --> error! + * only i_writecount < 0 disables writers; + * do this test AFTER setting inode->i_security->measure_count! + */ + if (atomic_read(&(inode->i_writecount)) > 0) { + invalidate_pcr("Measured file has writers."); + return -EFAULT; + } + /* c) real measure work */ + down(&h_table_mutex); + entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev); + if ((entry != NULL) && (entry->dirty == CLEAN)) { + goto out; /* release lock; nothing to measure */ + } + new_entry = measure_file(file, dentry, 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("internal error"); /* expand with illegal entry */ + error = -EFAULT; + goto out; + } + if (mr != NULL) { + memcpy(&(new_entry->mr), mr, + sizeof(struct measure_request)); + } else { + memset(&(new_entry->mr), 0, + sizeof(struct measure_request)); + } + if (entry == NULL) { /* no old entry for this inode found */ + /* add if no same-hash recorded (i.e., no copy measured yet) */ + if (!ima_lookup_sha_entry(new_entry->digest)) { + if ((error = ima_add_measure_entry(new_entry)) < 0) { + kfree(new_entry); + invalidate_pcr + ("error adding measurement entry"); + error = -EFAULT; + } else { /* extend PCR */ + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, + new_entry->digest); + } + } + } else { /* old entry exists */ + if (!memcmp(entry->digest, new_entry->digest, 20)) { + entry->dirty = get_default_dirty_setting(inode); + /* re-label with default (not to ever clean nfs etc. files) */ + kfree(new_entry); + } else { + /* dirty and look whether to add new entry */ + entry->dirty = CHANGED; + atomic_inc(&htable.changedFiles); + if (!ima_lookup_sha_entry(new_entry->digest)) { + if ((error = + ima_add_measure_entry(new_entry)) < + 0) { + kfree(new_entry); + invalidate_pcr + ("error adding measurement entry"); + error = -EFAULT; + } else { /* extend PCR */ + tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, + new_entry->digest); + } + } + } + } + out: + up(&h_table_mutex); + return (error); +} + +/* called permission for dirty-flagging + * we are moving towards inode-based flagging + * and thus avoiding table lookup for dirty flagging + */ +int measure_dirty_flag_inode(struct inode *inode) +{ + struct measure_entry *entry; + struct ima_inode *i_security = NULL; + + if (!inode) { + /* not a file to measure */ + return 0; + } + down(&h_table_mutex); + if ((entry = + ima_lookup_measure_entry(inode->i_ino, inode->i_rdev))) { + if (entry->dirty == CLEAN) + entry->dirty = DIRTY; + /* change from clean to dirty only, leave "changed" unchanged */ + /* inode dirty flag must be set, too */ + if ((i_security = ima_get_inode_security(inode)) != NULL) { + if (i_security->dirty == CLEAN) { + i_security->dirty = DIRTY; + } + } + } + up(&h_table_mutex); + return 0; +} + +/* called by mount to dirty-flag on "umount" */ +int measure_dirty_flag_super(struct super_block *super) +{ + /* here we go through the whole hash table and look + * for entries with this superblock to mark them dirty if clean + */ + struct queue_entry *qe; + int j; + + if (htable.terminating) + return 0; + + down(&h_table_mutex); + for (j = 0; j < htable.max_htable_size; j++) { + /* walk the whole hash table */ + qe = htable.queue[j]; + while (qe != NULL) { + if (qe->entry->superBlock == super) { + if (qe->entry->dirty == CLEAN) + qe->entry->dirty = DIRTY; + } + qe = qe->next; + } + } + up(&h_table_mutex); + return 0; +} + +EXPORT_SYMBOL(measure_file_exec); +EXPORT_SYMBOL(measure_kernel_module); +EXPORT_SYMBOL(measure_dirty_flag_super); +EXPORT_SYMBOL(measure_dirty_flag_inode); diff -uprN linux-2.6.12-rc4/security/ima/ima_proc.c linux-2.6.12-rc4-ima/security/ima/ima_proc.c --- linux-2.6.12-rc4/security/ima/ima_proc.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima_proc.c 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: TBD + * + * 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_proc.c + * implements proc fs for measurements; + * added static large buffer for /proc/ima/xmlmeasurements + */ +#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 <linux/string.h> +#include <linux/proc_fs.h> + +#include "ima.h" + +#define MAXPROCMEM 128*1024 +static struct proc_dir_entry *tpm_dir; +static char *xmlmem = NULL; +extern atomic_t global_count_sysfs; +extern atomic_t global_count_sysfs_measure; +extern atomic_t global_count_mmap_measure; +extern struct h_table htable; + +/* + * /proc filesystem interface + */ +static int ima_proc_read_htable(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ssize_t len = 0; + + down(&h_table_mutex); + if (count - len > 0) + len += + snprintf(page + len, count - len, + "\nTCG MEASUREMENT HASH TABLE: \n"); + if (count - len > 0) + len += + snprintf(page + len, count - len, + "len\t\tcleanIHit\tcleanHit\tdirtyHit\tchangedFiles\n"); + if (count - len > 0) { + len += + snprintf(page + len, count - len, + "%i\t\t%i\t\t%i\t\t%i\t\t%i\n\n", + atomic_read(&htable.len), + atomic_read(&htable.cleanInodeHits), + atomic_read(&htable.cleanTableHits), + atomic_read(&htable.dirtyTableHits), + atomic_read(&htable.changedFiles) + ); + } + if (count - len > 0) + len += + snprintf(page + len, count - len, + "sysfs\t\tsysfs_measure\t\tmmap_measure\t\ttermFlag\n"); + if (count - len > 0) { + len += + snprintf(page + len, count - len, + "%i\t\t%i\t\t\t%i\t\t\t%i\n\n", + atomic_read(&global_count_sysfs), + atomic_read(&global_count_sysfs_measure), + atomic_read(&global_count_mmap_measure), + htable.terminating); + } + + *eof = 1; + up(&h_table_mutex); + if (len > count) + len = count; + + return len; +} + + +static int print_measure_entry(struct measure_entry *e, char *buf, int count, int nr) +{ + return snprintf(buf, count, + "#%03d: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X [%s] %s\n", + nr, e->digest[0], e->digest[1], e->digest[2], + e->digest[3], e->digest[4], e->digest[5], + e->digest[6], e->digest[7], e->digest[8], + e->digest[9], e->digest[10], e->digest[11], + e->digest[12], e->digest[13], e->digest[14], + e->digest[15], e->digest[16], e->digest[17], + e->digest[18], e->digest[19], + (e->dirty == + DIRTY) ? "remeasure" : ((e->dirty == + CHANGED) ? "changed" : + "clean"), e->fileName); +} + + +static int ima_proc_read_measurements(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct queue_entry *qe; + static unsigned int len, i; + static unsigned int nextpos; + int ret = 0; + + down(&h_table_mutex); + /* first reset pos for new requests */ + if (!off) + nextpos = 0; + + /* ALWAYS set page here... otherwise it uses running offset */ + *start = page; + ret = 0; + *eof = 0; + + /* now overread the first "nextpos" elements */ + for (qe = first_measurement, i = 0; + qe && qe->entry && (i < nextpos); qe = qe->later, i++); + + /* make sure the next entry fits completely */ + while ((count > 500) && qe && qe->entry) { + /* now fill rest of page */ + len = + print_measure_entry(qe->entry, page + ret, count, + nextpos); + qe = qe->later; + count -= len; + ret += len; + nextpos += 1; + } + /* do we have more elements? */ + if (!qe) { + *eof = 1; + } + up(&h_table_mutex); + return ret; +} + +/* print format: 32bit-le=pcr#||char[20]=digest||filename||'\0' len(filename)<40*/ +static int print_measure_event_entry(struct measure_entry *e, char *buf, int count, int nr) +{ +#define TCG_EVENT_NAME_LEN_MAX 40 + + void *ptr = (void *) buf; + int filename_len = strlen(e->fileName); + + /* 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: filename <=40 + \'0' delimiter */ + if (filename_len > (TCG_EVENT_NAME_LEN_MAX - 1)) + filename_len = TCG_EVENT_NAME_LEN_MAX - 1; + + memcpy(ptr, e->fileName, filename_len); + ptr += filename_len; + + /* 4th: delimiter */ + *((char *) ptr) = '\0'; + ptr += 1; + + return ((u32) ptr - (u32) buf); +} + + +static int ima_proc_read_measurement_events(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct queue_entry *qe; + static unsigned int len, i; + static unsigned int nextpos; + int ret = 0; + + down(&h_table_mutex); + /* first reset pos for new requests */ + if (!off) + nextpos = 0; + + *start = page; + ret = 0; + *eof = 0; + + /* now overread the first "nextpos" elements */ + for (qe = first_measurement, i = 0; + qe && qe->entry && (i < nextpos); qe = qe->later, i++); + + /* make sure the next entry fits completely */ + while ((count > 500) && qe && qe->entry) { + /* now fill rest of page */ + len = + print_measure_event_entry(qe->entry, page + ret, count, + nextpos); + qe = qe->later; + count -= len; + ret += len; + nextpos += 1; + } + /* do we have more elements or not ? */ + if (!qe) { + *eof = 1; + } + up(&h_table_mutex); + return ret; +} + +#define mr_hook(e) \ + (((e)->mr.flags & MMAP_MEASURE_FLAG) ? \ + "mmap" : \ + ((e)->mr.flags & MODULE_MEASURE_FLAG) ? \ + "module" : \ + ((e)->mr.flags & USER_MEASURE_FLAG) ? \ + "user" : "UNKNOWN") + +static int print_extmeasure_entry(struct measure_entry *e, char *buf, int count, int nr) +{ + return snprintf(buf, count, + "#%03d: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X [%s] %s L[%u,%lu,%s]\n", + nr, e->digest[0], e->digest[1], e->digest[2], + e->digest[3], e->digest[4], e->digest[5], + e->digest[6], e->digest[7], e->digest[8], + e->digest[9], e->digest[10], e->digest[11], + e->digest[12], e->digest[13], e->digest[14], + e->digest[15], e->digest[16], e->digest[17], + e->digest[18], e->digest[19], + (e->dirty == + DIRTY) ? "remeasure" : ((e->dirty == + CHANGED) ? "changed" : + "clean"), e->fileName, + e->mr.label, e->mr.flags, mr_hook(e)); +} + + +static int ima_proc_read_extmeasurements(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct queue_entry *qe; + static unsigned int len, i; + static unsigned int nextpos; + int ret = 0; + + down(&h_table_mutex); + /* first reset pos for new requests */ + if (!off) + nextpos = 0; + + /* ALWAYS set page here... otherwise it uses running offset not return value! */ + *start = page; + ret = 0; + *eof = 0; + + /* now overread the first "nextpos" elements */ + for (qe = first_measurement, i = 0; + qe && qe->entry && (i < nextpos); qe = qe->later, i++); + + /* make sure the next entry fits completely */ + while ((count > 500) && qe && qe->entry) { + /* now fill rest of page */ + len = + print_extmeasure_entry(qe->entry, page + ret, count, + nextpos); + qe = qe->later; + count -= len; + ret += len; + nextpos += 1; + } + /* do we have more elements or not ? */ + if (!qe) { + *eof = 1; + } + up(&h_table_mutex); + return ret; +} + + +static int print_xmlmeasure_entry(struct measure_entry *e, char *buf, int count, int nr) +{ + return snprintf(buf, count, + "<NUM>%03d</NUM><SHA1>%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X</SHA1><STATUS>%s</STATUS><NAME>%s</NAME>\n", + nr, e->digest[0], e->digest[1], e->digest[2], + e->digest[3], e->digest[4], e->digest[5], + e->digest[6], e->digest[7], e->digest[8], + e->digest[9], e->digest[10], e->digest[11], + e->digest[12], e->digest[13], e->digest[14], + e->digest[15], e->digest[16], e->digest[17], + e->digest[18], e->digest[19], + (e->dirty == + DIRTY) ? "remeasure" : ((e->dirty == + CHANGED) ? "changed" : + "clean"), e->fileName); +} + + +static int ima_proc_read_xmlmeasurements(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct queue_entry *qe; + static unsigned int len, i; + static unsigned int nextpos; + int ret = 0; + + down(&h_table_mutex); + /* first reset pos for new requests */ + if (!off) + nextpos = 0; + + *start = page; + ret = 0; + *eof = 0; + + /* now overread the first "nextpos" elements */ + for (qe = first_measurement, i = 0; + qe && qe->entry && (i < nextpos); qe = qe->later, i++); + + /* make sure the next entry fits completely */ + while ((count > 500) && qe && qe->entry) { + /* now fill rest of page */ + len = + print_xmlmeasure_entry(qe->entry, page + ret, count, + nextpos); + qe = qe->later; + count -= len; + ret += len; + nextpos += 1; + } + /* do we have more elements or not ? */ + if (!qe) { + *eof = 1; + } + up(&h_table_mutex); + return ret; +} + + +void ima_proc_init(void) +{ + struct proc_dir_entry *entry; + + tpm_dir = proc_mkdir(IMA_MEASURE_PROC_NAME, NULL); + if (tpm_dir == NULL) + return; + + if ((xmlmem = kmalloc(MAXPROCMEM, GFP_KERNEL)) == NULL) { + ima_error("proc XMLmeasurements out of memory ERROR.\n"); + remove_proc_entry(IMA_MEASURE_MODULE_NAME, NULL); + return; + } + + entry = create_proc_read_entry("measurements", + 0444, tpm_dir, + ima_proc_read_measurements, NULL); + if (entry == NULL) { + remove_proc_entry(IMA_MEASURE_MODULE_NAME, NULL); + if (xmlmem != NULL) + kfree(xmlmem); + return; + } + entry->owner = THIS_MODULE; + + entry = create_proc_read_entry("xmlmeasurements", + 0444, tpm_dir, + ima_proc_read_xmlmeasurements, + NULL); + if (entry == NULL) { + remove_proc_entry(IMA_MEASURE_MODULE_NAME, NULL); + remove_proc_entry("measurements", tpm_dir); + if (xmlmem != NULL) + kfree(xmlmem); + return; + } + entry->owner = THIS_MODULE; + + entry = create_proc_read_entry("extmeasurements", + 0444, tpm_dir, + ima_proc_read_extmeasurements, + NULL); + if (entry == NULL) { + remove_proc_entry(IMA_MEASURE_MODULE_NAME, NULL); + remove_proc_entry("measurements", tpm_dir); + remove_proc_entry("xmlmeasurements", tpm_dir); + if (xmlmem != NULL) + kfree(xmlmem); + return; + } + entry->owner = THIS_MODULE; + + entry = create_proc_read_entry("measurement_events", + 0444, tpm_dir, + ima_proc_read_measurement_events, + NULL); + if (entry == NULL) { + remove_proc_entry(IMA_MEASURE_MODULE_NAME, NULL); + remove_proc_entry("measurements", tpm_dir); + remove_proc_entry("xmlmeasurements", tpm_dir); + remove_proc_entry("extmeasurements", tpm_dir); + if (xmlmem != NULL) + kfree(xmlmem); + return; + } + entry->owner = THIS_MODULE; + + entry = create_proc_read_entry("htable", + 0444, tpm_dir, ima_proc_read_htable, + NULL); + if (entry == NULL) { + remove_proc_entry("measurements", tpm_dir); + remove_proc_entry("xmlmeasurements", tpm_dir); + remove_proc_entry("extmeasurements", tpm_dir); + remove_proc_entry("measurement_events", tpm_dir); + remove_proc_entry(IMA_MEASURE_MODULE_NAME, NULL); + if (xmlmem != NULL) + kfree(xmlmem); + return; + } + entry->owner = THIS_MODULE; +} + +void ima_proc_cleanup(void) +{ + remove_proc_entry("htable", tpm_dir); + remove_proc_entry("measurements", tpm_dir); + remove_proc_entry("xmlmeasurements", tpm_dir); + remove_proc_entry("extmeasurements", tpm_dir); + remove_proc_entry("measurement_events", tpm_dir); + remove_proc_entry(IMA_MEASURE_PROC_NAME, NULL); + if (xmlmem != NULL) + kfree(xmlmem); +} diff -uprN linux-2.6.12-rc4/security/ima/ima_queue.c linux-2.6.12-rc4-ima/security/ima/ima_queue.c --- linux-2.6.12-rc4/security/ima/ima_queue.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima_queue.c 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: TBD + * + * 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; + sha_htable.terminating = 0; + for (i = 0; i < sha_htable.max_htable_size; i++) { + sha_htable.queue[i] = NULL; + atomic_set(&sha_htable.queueLen[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.cleanInodeHits, 0); + atomic_set(&htable.cleanTableHits, 0); + atomic_set(&htable.dirtyTableHits, 0); + atomic_set(&htable.changedFiles, 0); + htable.max_htable_size = MEASURE_HTABLE_SIZE; + htable.terminating = 0; + for (i = 0; i < htable.max_htable_size; i++) { + htable.queue[i] = NULL; + atomic_set(&htable.queueLen[i], 0); + } + up(&h_table_mutex); +} + +void destroy_sha_htable(void) +{ + struct sha_entry *qe; + int i; + + sha_htable.terminating = 1; + /* now release queues */ + for (i = 0; i < sha_htable.max_htable_size; i++) { + while ((qe = sha_htable.queue[i]) != NULL) { + sha_htable.queue[i] = qe->next; + kfree(qe); + } + sha_htable.queue[i] = NULL; + atomic_set(&sha_htable.queueLen[i], 0); + } + return; +} + +void destroy_htable(void) +{ + struct queue_entry *qe; + int i; + + down(&h_table_mutex); + first_measurement = NULL; + latest_measurement = NULL; + htable.terminating = 1; + /* now release queues */ + for (i = 0; i < htable.max_htable_size; i++) { + while ((qe = htable.queue[i]) != NULL) { + htable.queue[i] = qe->next; + if (qe->entry) + kfree(qe->entry); + kfree(qe); + } + htable.queue[i] = NULL; + atomic_set(&htable.queueLen[i], 0); + } + /* no up until create */ + return; +} + +/* + * also sets clean and dirty table hit marks + */ +struct measure_entry *ima_lookup_measure_entry(unsigned long inodeNumber, dev_t devNumber) +{ + struct queue_entry *qe; + struct measure_entry *me; + + if (htable.terminating) + return NULL; + + /* fill in later */ + qe = htable.queue[HASH_KEY(inodeNumber)]; + while ((qe != NULL) && ((qe->entry->inodeNr != inodeNumber) + || (qe->entry->devId != devNumber))) + qe = qe->next; + + if (qe != NULL) { + if (qe->entry->dirty != CLEAN) { + atomic_inc(&htable.dirtyTableHits); + } else { + atomic_inc(&htable.cleanTableHits); + } + 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; + + if (sha_htable.terminating) + return NULL; + + key = SHA_KEY(sha_value); + se = sha_htable.queue[key]; + while ((se != NULL) && (memcmp(se->digest, sha_value, 20))) { + /* unequal hash */ + se = se->next; + } + return se; +} + + +int ima_add_measure_entry(struct measure_entry *entry) +{ + unsigned int key; + struct queue_entry *qe; + int error = 0; + + /* new measurement -> add */ + if (htable.terminating) + return -1; + + /* calculate key */ + key = HASH_KEY(entry->inodeNr); + + /* 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.queueLen[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; + + if (sha_htable.terminating) + return -1; + + /* 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.queueLen[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-rc4/security/ima/ima_sysfs.c linux-2.6.12-rc4-ima/security/ima/ima_sysfs.c --- linux-2.6.12-rc4/security/ima/ima_sysfs.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima_sysfs.c 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: TBD + * + * 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_sysfs.c + * sysfs interface to request measurements + * through instrumented user applications + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/kmod.h> +#include <linux/kobj_map.h> +#include <linux/sysfs.h> +#include "ima.h" + +static struct subsystem security_subsys; + +atomic_t global_count_sysfs; +atomic_t global_count_sysfs_measure; + +int measure_file_exec(struct file *, const struct measure_request *); + +static ssize_t security_attr_show(struct kobject *kobj, + struct attribute *attr, char *page) +{ + /* get security attribute */ + struct subsys_attribute *security_attr = + container_of(attr, struct subsys_attribute, attr); + + if (security_attr->show) + security_attr->show(&security_subsys, page); + else + ima_error("Attr method >show< not defined.\n"); + + return (ssize_t) 0; +} + +static ssize_t security_attr_store(struct kobject *kobj,struct attribute *attr, + const char *page, size_t count) +{ + /* get security attribute */ + struct subsys_attribute *security_attr = + container_of(attr, struct subsys_attribute, attr); + if (security_attr->store) + security_attr->store(&security_subsys, page, count); + else + ima_error("Attr method >store< not defined.\n"); + + return (ssize_t) count; +} + +static struct sysfs_ops security_sysfs_ops = { + .show = &security_attr_show, + .store = &security_attr_store, +}; + +static ssize_t measurements_read(struct subsystem *sub, char *page) +{ + char *msg = "Hi There! Read is not supported :-)\n"; + strncpy(page, msg, PAGE_SIZE); + return (strlen(msg)); +} + +static ssize_t measurement_store(struct subsystem *sub, const char *page, + size_t count) +{ + struct measure_request *mr; + struct file *file; + int error = -EINVAL; + + atomic_inc(&global_count_sysfs); + if (count != sizeof(struct measure_request)) { + ima_error("illegal request size (%d, expected %d).\n", + count, sizeof(struct measure_request)); + return -EIO; + } + mr = (struct measure_request *) page; + if (mr->fd < 0) + return -EBADF; + + file = fget(mr->fd); + if (!file) + return -EACCES; + mr->flags = ((mr->flags) & (~FLAG_HOOK_MASK)) || USER_MEASURE_FLAG; + /* future: check inode->security to see if measure necessary */ + atomic_inc(&global_count_sysfs_measure); + error = measure_file_exec(file, mr); + fput(file); + if (error) + return error; + else + return (ssize_t) count; /* length of written data */ +} + +static struct subsys_attribute security_attr_measure = { + .attr = {.name = "measure",.mode = S_IRUGO | S_IWUGO}, + .show = measurements_read, + .store = measurement_store, +}; + +static struct attribute *default_security_attrs[] = { + &security_attr_measure.attr, + NULL, +}; + +static void security_object_release(struct kobject *kobj) +{ + return; +} + +static struct kobj_type ktype_security = { + .release = security_object_release, + .sysfs_ops = &security_sysfs_ops, + .default_attrs = default_security_attrs, +}; + +/* declare security_subsys. */ +static decl_subsys(security, &ktype_security, NULL); + + +int ima_sysfs_init(void) +{ + atomic_set(&global_count_sysfs, 0); + atomic_set(&global_count_sysfs_measure, 0); + subsystem_register(&security_subsys); + subsys_create_file(&security_subsys, &security_attr_measure); + return 0; +} + +int ima_sysfs_remove(void) +{ + subsys_remove_file(&security_subsys, &security_attr_measure); + subsystem_unregister(&security_subsys); + return 0; +} diff -uprN linux-2.6.12-rc4/security/ima/ima_tpm_glue.c linux-2.6.12-rc4-ima/security/ima/ima_tpm_glue.c --- linux-2.6.12-rc4/security/ima/ima_tpm_glue.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/ima_tpm_glue.c 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer <sailer@private> + * + * Maintained by: TBD + * + * 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_glue.c + * implements glue code to connect IMA to the TPM driver + * by protecting new measurements in the TPM PCR + * (glues to tpmdd on www.sourceforge.net/tpmdd) + */ +#include <linux/config.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 <linux/slab.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include "ima.h" + +#define TPM_BUFSIZE 2048 + +extern struct tpm_chip *ima_used_chip; +void tpm_extend(int index, const u8 * digest); +void tpm_pcrread(int index, u8 * hash); + +static u32 decode_u32(u8 * buf) +{ + u32 val = buf[0]; + val = (val << 8) | (u8) buf[1]; + val = (val << 8) | (u8) buf[2]; + val = (val << 8) | (u8) buf[3]; + return val; +} + +static void encode_u32(u8 * buf, u32 val) +{ + buf[0] = (u8) val >> 24; + buf[1] = (u8) val >> 16; + buf[2] = (u8) val >> 8; + buf[3] = (u8) val >> 0; +} + +static 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 */ +}; + + +void tpm_pcrread(int index, u8 * hash) +{ + u8 data[TPM_BUFSIZE]; + ssize_t len; + + if (ima_used_chip == NULL) + return; + + memcpy(data, pcrread, sizeof(pcrread)); + encode_u32(data + 10, index); + if (((len = tpm_transmit(ima_used_chip, data, sizeof(data))) >= 30) && + (decode_u32(data + 6) == 0)) + memcpy(hash, data + 10, 20); +} + + +static 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 */ +}; + +void tpm_extend(int index, const u8 * digest) +{ + u8 data[TPM_BUFSIZE]; + int len; + + if (ima_used_chip == NULL) + return; + + memcpy(data, extend, sizeof(extend)); + encode_u32(data + 10, index); + memcpy(data + 14, digest, 20); + if (((len = tpm_transmit(ima_used_chip, data, sizeof(data))) < 30) || + (decode_u32(data + 6) != 0)) { + if (!ima_test_mode) + panic("IMA: Error Communicating to TPM chip and IMA not in test mode!\n"); + } +} diff -uprN linux-2.6.12-rc4/security/ima/INSTALL linux-2.6.12-rc4-ima/security/ima/INSTALL --- linux-2.6.12-rc4/security/ima/INSTALL 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/INSTALL 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,124 @@ +File: INSTALL + +Installation File for +IBM Integrity Measurement Architecture + +Copyright IBM (c) April 30, 2005 +Author: Reiner Sailer, sailer@private + +For background information, examples, and publications + on this topic, please visit: + http://www.research.ibm.com/secure_systems_department/projects/tcglinux/ + +The following instructions work for Fedora Core3 but should be +generic (you just need a configuration file that works for the kernel) + +1. Required kernel configuration options +======================================== + a) crypto->SHA1 is (y) + [IMA needs sha before modules are loaded] + + b) security->Default Linux Capabilities (n) + [IMA cannot share LSM with the capabilities] + + c) choose (y) for "TCG run-time Integrity Measurement Architecture" + [switchtes IMA measurements on] + + d) choose (y) for "IMA test mode" + This option tells IMA to try to use a real hw TPM or bypass it + if in test mode. + Choose (y) if you don't have a TPM on your machine or if you + have a TPM on your machine but you want to test IMA and the + dependencies first. In any case, make sure you have a TPM + driver with the internal kernel interface patch posted to + LKML 05/2005. If you choose (n) and IMA can't start up for any + reason, it will panic the kernel to protect attestation. + If unsure, say (y). Say (y) only after testing. + + e) If you'd like to compile SELinux and IMA and choose between + them at boot-time then configure: + NSA SELinux boot paramter + NSA SELinux boot parameter default value to (0) + (see 3 for kernel command line options) + + +2. Compile and install the new kernel and initrd +================================================ +make all; make modules_install; make install + + +3. Change kernel command line options +===================================== +to activate the Integrity Measurement Architecture at boot-time, +add: 'ima=1', to deactivate use 'ima=0' (default) + +If you have both SELinux and IBM IMA support compiled into +the kernel, then switch at least one of: + 'ima=1 selinux=0' activates the Integrity Measurement Architecture + 'ima=0 selinux=1' activates SELinux + +You can't activate both because the kernel does not +support LSM stacking. + + +4. Trouble-shooting (restart your system to activate new kernel) +================================================================ +Use `dmesg |grep IMA` to print IMA status startup information: +You may find the following output: + + a) you are fine if you see + the following lines (if you have TPM hardware): + ---- + IBM Integrity Measurement Architecture (IBM IMA v2.0 05/18/2005). + IMA (test mode) + ---- + or the following lines (if you don't have TPM hardware): + ---- + IBM Integrity Measurement Architecture (IBM IMA v2.0 05/18/2005). + IMA (test mode) + IMA (TPM/BYPASS - no TPM chip found) + ---- + + b) you need to add the "ima=1" kernel boot paramter if you see: + --- + IBM Integrity Measurement Architecture (IBM IMA v2.0 05/18/2005). + IMA (not enabled in kernel command line) aborting! + --- + + c) you need to compile SHA1 support statically into the kernel + (see configuration requirements) if you see: + --- + IBM Integrity Measurement Architecture (IBM IMA v2.0 05/18/2005). + IMA (test mode) + IMA (SHA-1/no support) aborting! + --- + + d) you need to disable security->Default Linux Capabilities or + SELinux (see configuration requirements) if you see: + --- + IBM Integrity Measurement Architecture (IBM IMA v2.0 05/18/2005). + IMA (test mode) + IMA (LSM/not free) aborting! + --- + +5. Measurement example +====================== +# cat /proc/ima/measurements + +#000: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF [remeasure] boot_aggregate +#001: E2594BF3AA97ED8824A84F7D9ADAE054A7BAB788 [clean] nash +#002: 2A954FC4EBAC54BA26909A5AD52AEE5848425C3F [clean] udev +#003: BD145AE0CFC2021C065AEAE52355FEFEA741A0E2 [clean] insmod +#004: 6A82A9D8537E7767CC0DC980912CE2949EEFF265 [remeasure] jbd +#005: CB3A142617B950CC180E737EE0C4EC9C082A8D7C [remeasure] ext3 +#006: F3B8622110E3979FC4DE41598157C80F5F688AC6 [clean] init +#007: 5ACBD4089B3BBAD951AD13775B41BB951EAE306C [clean] ld-2.3.5.so +#008: 99554AD938DB53DDEAF1EFFBE472F05BF5F95878 [clean] libsepol.so.1 +#009: E9114FC95121F4F04EBEEE500107C80732279F87 [clean] libselinux.so.1 +#010: DCDDF67F5239F6E029CA7FA4C1332A7A109A22E2 [clean] libc-2.3.5.so +#011: F49DD0EA2ED1547B6F1B1BEC085A685404303646 [clean] modprobe +#012: 645C2BFC2D6EEF4AA807213295948AA0DB048577 [clean] bash + .... + + + diff -uprN linux-2.6.12-rc4/security/ima/Kconfig linux-2.6.12-rc4-ima/security/ima/Kconfig --- linux-2.6.12-rc4/security/ima/Kconfig 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/Kconfig 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,103 @@ +# +# 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 + 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 + 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_MEASURE_INVALIDATE_INDICATION_IDX + int "PCR for indicating invalidated PCR (8<= Index <= 15)" + depends on IMA_MEASURE + range 0 15 + default 9 + help + This determines the PCR index used to indicate that the + main measure pcr IMA_MEASURE_PCR_IDX aggregate has been + invalidated due to suspicious activity. This is just for + easily spotting invalidated IMA_MEASURE_PCR_IDX. + The main pcr was invalidated if + IMA_MEASURE_INVALIDATE_INDICATION_IDX is !=0. + If unsure, use the default pcr number 9. + +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-rc4/security/ima/Makefile linux-2.6.12-rc4-ima/security/ima/Makefile --- linux-2.6.12-rc4/security/ima/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.12-rc4-ima/security/ima/Makefile 2005-05-19 17:59:20.000000000 -0400 @@ -0,0 +1,12 @@ +# +# Makefile for the TCG run-time Measurements +# +# Author: sailer@private +# adapted to 2.6 kernel + +ifdef CONFIG_IMA_MEASURE +obj-$(CONFIG_IMA_MEASURE) += ima_init.o ima_sysfs.o ima_main.o ima_proc.o \ + ima_queue.o ima_lsmhooks.o ima_tpm_glue.o + +endif + diff -uprN linux-2.6.12-rc4/security/Kconfig linux-2.6.12-rc4-ima/security/Kconfig --- linux-2.6.12-rc4/security/Kconfig 2005-05-07 01:20:31.000000000 -0400 +++ linux-2.6.12-rc4-ima/security/Kconfig 2005-05-19 17:59:20.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-rc4/security/Makefile linux-2.6.12-rc4-ima/security/Makefile --- linux-2.6.12-rc4/security/Makefile 2005-05-07 01:20:31.000000000 -0400 +++ linux-2.6.12-rc4-ima/security/Makefile 2005-05-19 17:59:20.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 : Fri May 20 2005 - 11:48:13 PDT