lkml.org 
[lkml]   [2005]   [Jun]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH] 3 of 5 IMA: LSM-based measurement code
    From
    Date
    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@watson.ibm.com>
    ---

    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@us.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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@watson.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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@watson.ibm.com>
    + *
    + * Contributions:
    + * Leendert van Doorn <leendert@watson.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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@watson.ibm.com>");
    +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@watson.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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@watson.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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@watson.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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@watson.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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@watson.ibm.com>
    + *
    + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
    + *
    + * 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



    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2005-06-15 16:50    [W:0.125 / U:1.472 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site