diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-04-05 19:41:22 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-04-05 19:41:22 -0400 |
| commit | 9efe21cb82b5dbe3b0b2ae4de4eccc64ecb94e95 (patch) | |
| tree | 7ff8833745d2f268f897f6fa4a27263b4a572245 /security/integrity/ima | |
| parent | de18836e447c2dc30120c0919b8db8ddc0401cc4 (diff) | |
| parent | 0221c81b1b8eb0cbb6b30a0ced52ead32d2b4e4c (diff) | |
Merge branch 'linus' into irq/threaded
Conflicts:
include/linux/irq.h
kernel/irq/handle.c
Diffstat (limited to 'security/integrity/ima')
| -rw-r--r-- | security/integrity/ima/Kconfig | 55 | ||||
| -rw-r--r-- | security/integrity/ima/Makefile | 9 | ||||
| -rw-r--r-- | security/integrity/ima/ima.h | 166 | ||||
| -rw-r--r-- | security/integrity/ima/ima_api.c | 190 | ||||
| -rw-r--r-- | security/integrity/ima/ima_audit.c | 81 | ||||
| -rw-r--r-- | security/integrity/ima/ima_crypto.c | 140 | ||||
| -rw-r--r-- | security/integrity/ima/ima_fs.c | 376 | ||||
| -rw-r--r-- | security/integrity/ima/ima_iint.c | 204 | ||||
| -rw-r--r-- | security/integrity/ima/ima_init.c | 96 | ||||
| -rw-r--r-- | security/integrity/ima/ima_main.c | 327 | ||||
| -rw-r--r-- | security/integrity/ima/ima_policy.c | 414 | ||||
| -rw-r--r-- | security/integrity/ima/ima_queue.c | 140 |
12 files changed, 2198 insertions, 0 deletions
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig new file mode 100644 index 000000000000..53d9764e8f09 --- /dev/null +++ b/security/integrity/ima/Kconfig | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | # IBM Integrity Measurement Architecture | ||
| 2 | # | ||
| 3 | config IMA | ||
| 4 | bool "Integrity Measurement Architecture(IMA)" | ||
| 5 | depends on ACPI | ||
| 6 | select SECURITYFS | ||
| 7 | select CRYPTO | ||
| 8 | select CRYPTO_HMAC | ||
| 9 | select CRYPTO_MD5 | ||
| 10 | select CRYPTO_SHA1 | ||
| 11 | select TCG_TPM | ||
| 12 | select TCG_TIS | ||
| 13 | help | ||
| 14 | The Trusted Computing Group(TCG) runtime Integrity | ||
| 15 | Measurement Architecture(IMA) maintains a list of hash | ||
| 16 | values of executables and other sensitive system files, | ||
| 17 | as they are read or executed. If an attacker manages | ||
| 18 | to change the contents of an important system file | ||
| 19 | being measured, we can tell. | ||
| 20 | |||
| 21 | If your system has a TPM chip, then IMA also maintains | ||
| 22 | an aggregate integrity value over this list inside the | ||
| 23 | TPM hardware, so that the TPM can prove to a third party | ||
| 24 | whether or not critical system files have been modified. | ||
| 25 | Read <http://www.usenix.org/events/sec04/tech/sailer.html> | ||
| 26 | to learn more about IMA. | ||
| 27 | If unsure, say N. | ||
| 28 | |||
| 29 | config IMA_MEASURE_PCR_IDX | ||
| 30 | int | ||
| 31 | depends on IMA | ||
| 32 | range 8 14 | ||
| 33 | default 10 | ||
| 34 | help | ||
| 35 | IMA_MEASURE_PCR_IDX determines the TPM PCR register index | ||
| 36 | that IMA uses to maintain the integrity aggregate of the | ||
| 37 | measurement list. If unsure, use the default 10. | ||
| 38 | |||
| 39 | config IMA_AUDIT | ||
| 40 | bool | ||
| 41 | depends on IMA | ||
| 42 | default y | ||
| 43 | help | ||
| 44 | This option adds a kernel parameter 'ima_audit', which | ||
| 45 | allows informational auditing messages to be enabled | ||
| 46 | at boot. If this option is selected, informational integrity | ||
| 47 | auditing messages can be enabled with 'ima_audit=1' on | ||
| 48 | the kernel command line. | ||
| 49 | |||
| 50 | config IMA_LSM_RULES | ||
| 51 | bool | ||
| 52 | depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK) | ||
| 53 | default y | ||
| 54 | help | ||
| 55 | Disabling this option will disregard LSM based policy rules. | ||
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile new file mode 100644 index 000000000000..787c4cb916cd --- /dev/null +++ b/security/integrity/ima/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # | ||
| 2 | # Makefile for building Trusted Computing Group's(TCG) runtime Integrity | ||
| 3 | # Measurement Architecture(IMA). | ||
| 4 | # | ||
| 5 | |||
| 6 | obj-$(CONFIG_IMA) += ima.o | ||
| 7 | |||
| 8 | ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ | ||
| 9 | ima_policy.o ima_iint.o ima_audit.o | ||
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h new file mode 100644 index 000000000000..165eb5397ea5 --- /dev/null +++ b/security/integrity/ima/ima.h | |||
| @@ -0,0 +1,166 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005,2006,2007,2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Reiner Sailer <sailer@watson.ibm.com> | ||
| 6 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License as | ||
| 10 | * published by the Free Software Foundation, version 2 of the | ||
| 11 | * License. | ||
| 12 | * | ||
| 13 | * File: ima.h | ||
| 14 | * internal Integrity Measurement Architecture (IMA) definitions | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef __LINUX_IMA_H | ||
| 18 | #define __LINUX_IMA_H | ||
| 19 | |||
| 20 | #include <linux/types.h> | ||
| 21 | #include <linux/crypto.h> | ||
| 22 | #include <linux/security.h> | ||
| 23 | #include <linux/hash.h> | ||
| 24 | #include <linux/tpm.h> | ||
| 25 | #include <linux/audit.h> | ||
| 26 | |||
| 27 | enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII }; | ||
| 28 | enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; | ||
| 29 | |||
| 30 | /* digest size for IMA, fits SHA1 or MD5 */ | ||
| 31 | #define IMA_DIGEST_SIZE 20 | ||
| 32 | #define IMA_EVENT_NAME_LEN_MAX 255 | ||
| 33 | |||
| 34 | #define IMA_HASH_BITS 9 | ||
| 35 | #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) | ||
| 36 | |||
| 37 | /* set during initialization */ | ||
| 38 | extern int ima_initialized; | ||
| 39 | extern int ima_used_chip; | ||
| 40 | extern char *ima_hash; | ||
| 41 | |||
| 42 | /* IMA inode template definition */ | ||
| 43 | struct ima_template_data { | ||
| 44 | u8 digest[IMA_DIGEST_SIZE]; /* sha1/md5 measurement hash */ | ||
| 45 | char file_name[IMA_EVENT_NAME_LEN_MAX + 1]; /* name + \0 */ | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct ima_template_entry { | ||
| 49 | u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ | ||
| 50 | const char *template_name; | ||
| 51 | int template_len; | ||
| 52 | struct ima_template_data template; | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct ima_queue_entry { | ||
| 56 | struct hlist_node hnext; /* place in hash collision list */ | ||
| 57 | struct list_head later; /* place in ima_measurements list */ | ||
| 58 | struct ima_template_entry *entry; | ||
| 59 | }; | ||
| 60 | extern struct list_head ima_measurements; /* list of all measurements */ | ||
| 61 | |||
| 62 | /* declarations */ | ||
| 63 | void integrity_audit_msg(int audit_msgno, struct inode *inode, | ||
| 64 | const unsigned char *fname, const char *op, | ||
| 65 | const char *cause, int result, int info); | ||
| 66 | |||
| 67 | /* Internal IMA function definitions */ | ||
| 68 | void ima_iintcache_init(void); | ||
| 69 | int ima_init(void); | ||
| 70 | void ima_cleanup(void); | ||
| 71 | int ima_fs_init(void); | ||
| 72 | void ima_fs_cleanup(void); | ||
| 73 | int ima_add_template_entry(struct ima_template_entry *entry, int violation, | ||
| 74 | const char *op, struct inode *inode); | ||
| 75 | int ima_calc_hash(struct file *file, char *digest); | ||
| 76 | int ima_calc_template_hash(int template_len, void *template, char *digest); | ||
| 77 | int ima_calc_boot_aggregate(char *digest); | ||
| 78 | void ima_add_violation(struct inode *inode, const unsigned char *filename, | ||
| 79 | const char *op, const char *cause); | ||
| 80 | |||
| 81 | /* | ||
| 82 | * used to protect h_table and sha_table | ||
| 83 | */ | ||
| 84 | extern spinlock_t ima_queue_lock; | ||
| 85 | |||
| 86 | struct ima_h_table { | ||
| 87 | atomic_long_t len; /* number of stored measurements in the list */ | ||
| 88 | atomic_long_t violations; | ||
| 89 | struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; | ||
| 90 | }; | ||
| 91 | extern struct ima_h_table ima_htable; | ||
| 92 | |||
| 93 | static inline unsigned long ima_hash_key(u8 *digest) | ||
| 94 | { | ||
| 95 | return hash_long(*digest, IMA_HASH_BITS); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* iint cache flags */ | ||
| 99 | #define IMA_MEASURED 1 | ||
| 100 | #define IMA_IINT_DUMP_STACK 512 | ||
| 101 | |||
| 102 | /* integrity data associated with an inode */ | ||
| 103 | struct ima_iint_cache { | ||
| 104 | u64 version; /* track inode changes */ | ||
| 105 | unsigned long flags; | ||
| 106 | u8 digest[IMA_DIGEST_SIZE]; | ||
| 107 | struct mutex mutex; /* protects: version, flags, digest */ | ||
| 108 | long readcount; /* measured files readcount */ | ||
| 109 | long writecount; /* measured files writecount */ | ||
| 110 | long opencount; /* opens reference count */ | ||
| 111 | struct kref refcount; /* ima_iint_cache reference count */ | ||
| 112 | struct rcu_head rcu; | ||
| 113 | }; | ||
| 114 | |||
| 115 | /* LIM API function definitions */ | ||
| 116 | int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode, | ||
| 117 | int mask, int function); | ||
| 118 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file); | ||
| 119 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, | ||
| 120 | const unsigned char *filename); | ||
| 121 | int ima_store_template(struct ima_template_entry *entry, int violation, | ||
| 122 | struct inode *inode); | ||
| 123 | void ima_template_show(struct seq_file *m, void *e, | ||
| 124 | enum ima_show_type show); | ||
| 125 | |||
| 126 | /* radix tree calls to lookup, insert, delete | ||
| 127 | * integrity data associated with an inode. | ||
| 128 | */ | ||
| 129 | struct ima_iint_cache *ima_iint_insert(struct inode *inode); | ||
| 130 | struct ima_iint_cache *ima_iint_find_get(struct inode *inode); | ||
| 131 | struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode); | ||
| 132 | void ima_iint_delete(struct inode *inode); | ||
| 133 | void iint_free(struct kref *kref); | ||
| 134 | void iint_rcu_free(struct rcu_head *rcu); | ||
| 135 | |||
| 136 | /* IMA policy related functions */ | ||
| 137 | enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK }; | ||
| 138 | |||
| 139 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask); | ||
| 140 | void ima_init_policy(void); | ||
| 141 | void ima_update_policy(void); | ||
| 142 | int ima_parse_add_rule(char *); | ||
| 143 | void ima_delete_rules(void); | ||
| 144 | |||
| 145 | /* LSM based policy rules require audit */ | ||
| 146 | #ifdef CONFIG_IMA_LSM_RULES | ||
| 147 | |||
| 148 | #define security_filter_rule_init security_audit_rule_init | ||
| 149 | #define security_filter_rule_match security_audit_rule_match | ||
| 150 | |||
| 151 | #else | ||
| 152 | |||
| 153 | static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr, | ||
| 154 | void **lsmrule) | ||
| 155 | { | ||
| 156 | return -EINVAL; | ||
| 157 | } | ||
| 158 | |||
| 159 | static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, | ||
| 160 | void *lsmrule, | ||
| 161 | struct audit_context *actx) | ||
| 162 | { | ||
| 163 | return -EINVAL; | ||
| 164 | } | ||
| 165 | #endif /* CONFIG_IMA_LSM_RULES */ | ||
| 166 | #endif | ||
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c new file mode 100644 index 000000000000..3cd58b60afd2 --- /dev/null +++ b/security/integrity/ima/ima_api.c | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Author: Mimi Zohar <zohar@us.ibm.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License as | ||
| 8 | * published by the Free Software Foundation, version 2 of the | ||
| 9 | * License. | ||
| 10 | * | ||
| 11 | * File: ima_api.c | ||
| 12 | * Implements must_measure, collect_measurement, store_measurement, | ||
| 13 | * and store_template. | ||
| 14 | */ | ||
| 15 | #include <linux/module.h> | ||
| 16 | |||
| 17 | #include "ima.h" | ||
| 18 | static const char *IMA_TEMPLATE_NAME = "ima"; | ||
| 19 | |||
| 20 | /* | ||
| 21 | * ima_store_template - store ima template measurements | ||
| 22 | * | ||
| 23 | * Calculate the hash of a template entry, add the template entry | ||
| 24 | * to an ordered list of measurement entries maintained inside the kernel, | ||
| 25 | * and also update the aggregate integrity value (maintained inside the | ||
| 26 | * configured TPM PCR) over the hashes of the current list of measurement | ||
| 27 | * entries. | ||
| 28 | * | ||
| 29 | * Applications retrieve the current kernel-held measurement list through | ||
| 30 | * the securityfs entries in /sys/kernel/security/ima. The signed aggregate | ||
| 31 | * TPM PCR (called quote) can be retrieved using a TPM user space library | ||
| 32 | * and is used to validate the measurement list. | ||
| 33 | * | ||
| 34 | * Returns 0 on success, error code otherwise | ||
| 35 | */ | ||
| 36 | int ima_store_template(struct ima_template_entry *entry, | ||
| 37 | int violation, struct inode *inode) | ||
| 38 | { | ||
| 39 | const char *op = "add_template_measure"; | ||
| 40 | const char *audit_cause = "hashing_error"; | ||
| 41 | int result; | ||
| 42 | |||
| 43 | memset(entry->digest, 0, sizeof(entry->digest)); | ||
| 44 | entry->template_name = IMA_TEMPLATE_NAME; | ||
| 45 | entry->template_len = sizeof(entry->template); | ||
| 46 | |||
| 47 | if (!violation) { | ||
| 48 | result = ima_calc_template_hash(entry->template_len, | ||
| 49 | &entry->template, | ||
| 50 | entry->digest); | ||
| 51 | if (result < 0) { | ||
| 52 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, | ||
| 53 | entry->template_name, op, | ||
| 54 | audit_cause, result, 0); | ||
| 55 | return result; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | result = ima_add_template_entry(entry, violation, op, inode); | ||
| 59 | return result; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* | ||
| 63 | * ima_add_violation - add violation to measurement list. | ||
| 64 | * | ||
| 65 | * Violations are flagged in the measurement list with zero hash values. | ||
| 66 | * By extending the PCR with 0xFF's instead of with zeroes, the PCR | ||
| 67 | * value is invalidated. | ||
| 68 | */ | ||
| 69 | void ima_add_violation(struct inode *inode, const unsigned char *filename, | ||
| 70 | const char *op, const char *cause) | ||
| 71 | { | ||
| 72 | struct ima_template_entry *entry; | ||
| 73 | int violation = 1; | ||
| 74 | int result; | ||
| 75 | |||
| 76 | /* can overflow, only indicator */ | ||
| 77 | atomic_long_inc(&ima_htable.violations); | ||
| 78 | |||
| 79 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 80 | if (!entry) { | ||
| 81 | result = -ENOMEM; | ||
| 82 | goto err_out; | ||
| 83 | } | ||
| 84 | memset(&entry->template, 0, sizeof(entry->template)); | ||
| 85 | strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); | ||
| 86 | result = ima_store_template(entry, violation, inode); | ||
| 87 | if (result < 0) | ||
| 88 | kfree(entry); | ||
| 89 | err_out: | ||
| 90 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, | ||
| 91 | op, cause, result, 0); | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * ima_must_measure - measure decision based on policy. | ||
| 96 | * @inode: pointer to inode to measure | ||
| 97 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) | ||
| 98 | * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP) | ||
| 99 | * | ||
| 100 | * The policy is defined in terms of keypairs: | ||
| 101 | * subj=, obj=, type=, func=, mask=, fsmagic= | ||
| 102 | * subj,obj, and type: are LSM specific. | ||
| 103 | * func: PATH_CHECK | BPRM_CHECK | FILE_MMAP | ||
| 104 | * mask: contains the permission mask | ||
| 105 | * fsmagic: hex value | ||
| 106 | * | ||
| 107 | * Must be called with iint->mutex held. | ||
| 108 | * | ||
| 109 | * Return 0 to measure. Return 1 if already measured. | ||
| 110 | * For matching a DONT_MEASURE policy, no policy, or other | ||
| 111 | * error, return an error code. | ||
| 112 | */ | ||
| 113 | int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode, | ||
| 114 | int mask, int function) | ||
| 115 | { | ||
| 116 | int must_measure; | ||
| 117 | |||
| 118 | if (iint->flags & IMA_MEASURED) | ||
| 119 | return 1; | ||
| 120 | |||
| 121 | must_measure = ima_match_policy(inode, function, mask); | ||
| 122 | return must_measure ? 0 : -EACCES; | ||
| 123 | } | ||
| 124 | |||
| 125 | /* | ||
| 126 | * ima_collect_measurement - collect file measurement | ||
| 127 | * | ||
| 128 | * Calculate the file hash, if it doesn't already exist, | ||
| 129 | * storing the measurement and i_version in the iint. | ||
| 130 | * | ||
| 131 | * Must be called with iint->mutex held. | ||
| 132 | * | ||
| 133 | * Return 0 on success, error code otherwise | ||
| 134 | */ | ||
| 135 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file) | ||
| 136 | { | ||
| 137 | int result = -EEXIST; | ||
| 138 | |||
| 139 | if (!(iint->flags & IMA_MEASURED)) { | ||
| 140 | u64 i_version = file->f_dentry->d_inode->i_version; | ||
| 141 | |||
| 142 | memset(iint->digest, 0, IMA_DIGEST_SIZE); | ||
| 143 | result = ima_calc_hash(file, iint->digest); | ||
| 144 | if (!result) | ||
| 145 | iint->version = i_version; | ||
| 146 | } | ||
| 147 | return result; | ||
| 148 | } | ||
| 149 | |||
| 150 | /* | ||
| 151 | * ima_store_measurement - store file measurement | ||
| 152 | * | ||
| 153 | * Create an "ima" template and then store the template by calling | ||
| 154 | * ima_store_template. | ||
| 155 | * | ||
| 156 | * We only get here if the inode has not already been measured, | ||
| 157 | * but the measurement could already exist: | ||
| 158 | * - multiple copies of the same file on either the same or | ||
| 159 | * different filesystems. | ||
| 160 | * - the inode was previously flushed as well as the iint info, | ||
| 161 | * containing the hashing info. | ||
| 162 | * | ||
| 163 | * Must be called with iint->mutex held. | ||
| 164 | */ | ||
| 165 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, | ||
| 166 | const unsigned char *filename) | ||
| 167 | { | ||
| 168 | const char *op = "add_template_measure"; | ||
| 169 | const char *audit_cause = "ENOMEM"; | ||
| 170 | int result = -ENOMEM; | ||
| 171 | struct inode *inode = file->f_dentry->d_inode; | ||
| 172 | struct ima_template_entry *entry; | ||
| 173 | int violation = 0; | ||
| 174 | |||
| 175 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 176 | if (!entry) { | ||
| 177 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, | ||
| 178 | op, audit_cause, result, 0); | ||
| 179 | return; | ||
| 180 | } | ||
| 181 | memset(&entry->template, 0, sizeof(entry->template)); | ||
| 182 | memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE); | ||
| 183 | strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); | ||
| 184 | |||
| 185 | result = ima_store_template(entry, violation, inode); | ||
| 186 | if (!result) | ||
| 187 | iint->flags |= IMA_MEASURED; | ||
| 188 | else | ||
| 189 | kfree(entry); | ||
| 190 | } | ||
diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c new file mode 100644 index 000000000000..1e082bb987be --- /dev/null +++ b/security/integrity/ima/ima_audit.c | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2008 IBM Corporation | ||
| 3 | * Author: Mimi Zohar <zohar@us.ibm.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, version 2 of the License. | ||
| 8 | * | ||
| 9 | * File: integrity_audit.c | ||
| 10 | * Audit calls for the integrity subsystem | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/fs.h> | ||
| 14 | #include <linux/audit.h> | ||
| 15 | #include "ima.h" | ||
| 16 | |||
| 17 | static int ima_audit; | ||
| 18 | |||
| 19 | #ifdef CONFIG_IMA_AUDIT | ||
| 20 | |||
| 21 | /* ima_audit_setup - enable informational auditing messages */ | ||
| 22 | static int __init ima_audit_setup(char *str) | ||
| 23 | { | ||
| 24 | unsigned long audit; | ||
| 25 | int rc, result = 0; | ||
| 26 | char *op = "ima_audit"; | ||
| 27 | char *cause; | ||
| 28 | |||
| 29 | rc = strict_strtoul(str, 0, &audit); | ||
| 30 | if (rc || audit > 1) | ||
| 31 | result = 1; | ||
| 32 | else | ||
| 33 | ima_audit = audit; | ||
| 34 | cause = ima_audit ? "enabled" : "not_enabled"; | ||
| 35 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, | ||
| 36 | op, cause, result, 0); | ||
| 37 | return 1; | ||
| 38 | } | ||
| 39 | __setup("ima_audit=", ima_audit_setup); | ||
| 40 | #endif | ||
| 41 | |||
| 42 | void integrity_audit_msg(int audit_msgno, struct inode *inode, | ||
| 43 | const unsigned char *fname, const char *op, | ||
| 44 | const char *cause, int result, int audit_info) | ||
| 45 | { | ||
| 46 | struct audit_buffer *ab; | ||
| 47 | |||
| 48 | if (!ima_audit && audit_info == 1) /* Skip informational messages */ | ||
| 49 | return; | ||
| 50 | |||
| 51 | ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); | ||
| 52 | audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u ses=%u", | ||
| 53 | current->pid, current->cred->uid, | ||
| 54 | audit_get_loginuid(current), | ||
| 55 | audit_get_sessionid(current)); | ||
| 56 | audit_log_task_context(ab); | ||
| 57 | switch (audit_msgno) { | ||
| 58 | case AUDIT_INTEGRITY_DATA: | ||
| 59 | case AUDIT_INTEGRITY_METADATA: | ||
| 60 | case AUDIT_INTEGRITY_PCR: | ||
| 61 | case AUDIT_INTEGRITY_STATUS: | ||
| 62 | audit_log_format(ab, " op=%s cause=%s", op, cause); | ||
| 63 | break; | ||
| 64 | case AUDIT_INTEGRITY_HASH: | ||
| 65 | audit_log_format(ab, " op=%s hash=%s", op, cause); | ||
| 66 | break; | ||
| 67 | default: | ||
| 68 | audit_log_format(ab, " op=%s", op); | ||
| 69 | } | ||
| 70 | audit_log_format(ab, " comm="); | ||
| 71 | audit_log_untrustedstring(ab, current->comm); | ||
| 72 | if (fname) { | ||
| 73 | audit_log_format(ab, " name="); | ||
| 74 | audit_log_untrustedstring(ab, fname); | ||
| 75 | } | ||
| 76 | if (inode) | ||
| 77 | audit_log_format(ab, " dev=%s ino=%lu", | ||
| 78 | inode->i_sb->s_id, inode->i_ino); | ||
| 79 | audit_log_format(ab, " res=%d", !result ? 0 : 1); | ||
| 80 | audit_log_end(ab); | ||
| 81 | } | ||
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c new file mode 100644 index 000000000000..50d572b74caf --- /dev/null +++ b/security/integrity/ima/ima_crypto.c | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005,2006,2007,2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * Kylene Hall <kjhall@us.ibm.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation, version 2 of the License. | ||
| 11 | * | ||
| 12 | * File: ima_crypto.c | ||
| 13 | * Calculates md5/sha1 file hash, template hash, boot-aggreate hash | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/file.h> | ||
| 18 | #include <linux/crypto.h> | ||
| 19 | #include <linux/scatterlist.h> | ||
| 20 | #include <linux/err.h> | ||
| 21 | #include "ima.h" | ||
| 22 | |||
| 23 | static int init_desc(struct hash_desc *desc) | ||
| 24 | { | ||
| 25 | int rc; | ||
| 26 | |||
| 27 | desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC); | ||
| 28 | if (IS_ERR(desc->tfm)) { | ||
| 29 | pr_info("failed to load %s transform: %ld\n", | ||
| 30 | ima_hash, PTR_ERR(desc->tfm)); | ||
| 31 | rc = PTR_ERR(desc->tfm); | ||
| 32 | return rc; | ||
| 33 | } | ||
| 34 | desc->flags = 0; | ||
| 35 | rc = crypto_hash_init(desc); | ||
| 36 | if (rc) | ||
| 37 | crypto_free_hash(desc->tfm); | ||
| 38 | return rc; | ||
| 39 | } | ||
| 40 | |||
| 41 | /* | ||
| 42 | * Calculate the MD5/SHA1 file digest | ||
| 43 | */ | ||
| 44 | int ima_calc_hash(struct file *file, char *digest) | ||
| 45 | { | ||
| 46 | struct hash_desc desc; | ||
| 47 | struct scatterlist sg[1]; | ||
| 48 | loff_t i_size; | ||
| 49 | char *rbuf; | ||
| 50 | int rc, offset = 0; | ||
| 51 | |||
| 52 | rc = init_desc(&desc); | ||
| 53 | if (rc != 0) | ||
| 54 | return rc; | ||
| 55 | |||
| 56 | rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 57 | if (!rbuf) { | ||
| 58 | rc = -ENOMEM; | ||
| 59 | goto out; | ||
| 60 | } | ||
| 61 | i_size = i_size_read(file->f_dentry->d_inode); | ||
| 62 | while (offset < i_size) { | ||
| 63 | int rbuf_len; | ||
| 64 | |||
| 65 | rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE); | ||
| 66 | if (rbuf_len < 0) { | ||
| 67 | rc = rbuf_len; | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | offset += rbuf_len; | ||
| 71 | sg_init_one(sg, rbuf, rbuf_len); | ||
| 72 | |||
| 73 | rc = crypto_hash_update(&desc, sg, rbuf_len); | ||
| 74 | if (rc) | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | kfree(rbuf); | ||
| 78 | if (!rc) | ||
| 79 | rc = crypto_hash_final(&desc, digest); | ||
| 80 | out: | ||
| 81 | crypto_free_hash(desc.tfm); | ||
| 82 | return rc; | ||
| 83 | } | ||
| 84 | |||
| 85 | /* | ||
| 86 | * Calculate the hash of a given template | ||
| 87 | */ | ||
| 88 | int ima_calc_template_hash(int template_len, void *template, char *digest) | ||
| 89 | { | ||
| 90 | struct hash_desc desc; | ||
| 91 | struct scatterlist sg[1]; | ||
| 92 | int rc; | ||
| 93 | |||
| 94 | rc = init_desc(&desc); | ||
| 95 | if (rc != 0) | ||
| 96 | return rc; | ||
| 97 | |||
| 98 | sg_init_one(sg, template, template_len); | ||
| 99 | rc = crypto_hash_update(&desc, sg, template_len); | ||
| 100 | if (!rc) | ||
| 101 | rc = crypto_hash_final(&desc, digest); | ||
| 102 | crypto_free_hash(desc.tfm); | ||
| 103 | return rc; | ||
| 104 | } | ||
| 105 | |||
| 106 | static void ima_pcrread(int idx, u8 *pcr) | ||
| 107 | { | ||
| 108 | if (!ima_used_chip) | ||
| 109 | return; | ||
| 110 | |||
| 111 | if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0) | ||
| 112 | pr_err("Error Communicating to TPM chip\n"); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* | ||
| 116 | * Calculate the boot aggregate hash | ||
| 117 | */ | ||
| 118 | int ima_calc_boot_aggregate(char *digest) | ||
| 119 | { | ||
| 120 | struct hash_desc desc; | ||
| 121 | struct scatterlist sg; | ||
| 122 | u8 pcr_i[IMA_DIGEST_SIZE]; | ||
| 123 | int rc, i; | ||
| 124 | |||
| 125 | rc = init_desc(&desc); | ||
| 126 | if (rc != 0) | ||
| 127 | return rc; | ||
| 128 | |||
| 129 | /* cumulative sha1 over tpm registers 0-7 */ | ||
| 130 | for (i = TPM_PCR0; i < TPM_PCR8; i++) { | ||
| 131 | ima_pcrread(i, pcr_i); | ||
| 132 | /* now accumulate with current aggregate */ | ||
| 133 | sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE); | ||
| 134 | rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE); | ||
| 135 | } | ||
| 136 | if (!rc) | ||
| 137 | crypto_hash_final(&desc, digest); | ||
| 138 | crypto_free_hash(desc.tfm); | ||
| 139 | return rc; | ||
| 140 | } | ||
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c new file mode 100644 index 000000000000..ffbe259700b1 --- /dev/null +++ b/security/integrity/ima/ima_fs.c | |||
| @@ -0,0 +1,376 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005,2006,2007,2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Kylene Hall <kjhall@us.ibm.com> | ||
| 6 | * Reiner Sailer <sailer@us.ibm.com> | ||
| 7 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | * | ||
| 14 | * File: ima_fs.c | ||
| 15 | * implemenents security file system for reporting | ||
| 16 | * current measurement list and IMA statistics | ||
| 17 | */ | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/seq_file.h> | ||
| 20 | #include <linux/rculist.h> | ||
| 21 | #include <linux/rcupdate.h> | ||
| 22 | #include <linux/parser.h> | ||
| 23 | |||
| 24 | #include "ima.h" | ||
| 25 | |||
| 26 | static int valid_policy = 1; | ||
| 27 | #define TMPBUFLEN 12 | ||
| 28 | static ssize_t ima_show_htable_value(char __user *buf, size_t count, | ||
| 29 | loff_t *ppos, atomic_long_t *val) | ||
| 30 | { | ||
| 31 | char tmpbuf[TMPBUFLEN]; | ||
| 32 | ssize_t len; | ||
| 33 | |||
| 34 | len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val)); | ||
| 35 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); | ||
| 36 | } | ||
| 37 | |||
| 38 | static ssize_t ima_show_htable_violations(struct file *filp, | ||
| 39 | char __user *buf, | ||
| 40 | size_t count, loff_t *ppos) | ||
| 41 | { | ||
| 42 | return ima_show_htable_value(buf, count, ppos, &ima_htable.violations); | ||
| 43 | } | ||
| 44 | |||
| 45 | static struct file_operations ima_htable_violations_ops = { | ||
| 46 | .read = ima_show_htable_violations | ||
| 47 | }; | ||
| 48 | |||
| 49 | static ssize_t ima_show_measurements_count(struct file *filp, | ||
| 50 | char __user *buf, | ||
| 51 | size_t count, loff_t *ppos) | ||
| 52 | { | ||
| 53 | return ima_show_htable_value(buf, count, ppos, &ima_htable.len); | ||
| 54 | |||
| 55 | } | ||
| 56 | |||
| 57 | static struct file_operations ima_measurements_count_ops = { | ||
| 58 | .read = ima_show_measurements_count | ||
| 59 | }; | ||
| 60 | |||
| 61 | /* returns pointer to hlist_node */ | ||
| 62 | static void *ima_measurements_start(struct seq_file *m, loff_t *pos) | ||
| 63 | { | ||
| 64 | loff_t l = *pos; | ||
| 65 | struct ima_queue_entry *qe; | ||
| 66 | |||
| 67 | /* we need a lock since pos could point beyond last element */ | ||
| 68 | rcu_read_lock(); | ||
| 69 | list_for_each_entry_rcu(qe, &ima_measurements, later) { | ||
| 70 | if (!l--) { | ||
| 71 | rcu_read_unlock(); | ||
| 72 | return qe; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | rcu_read_unlock(); | ||
| 76 | return NULL; | ||
| 77 | } | ||
| 78 | |||
| 79 | static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) | ||
| 80 | { | ||
| 81 | struct ima_queue_entry *qe = v; | ||
| 82 | |||
| 83 | /* lock protects when reading beyond last element | ||
| 84 | * against concurrent list-extension | ||
| 85 | */ | ||
| 86 | rcu_read_lock(); | ||
| 87 | qe = list_entry(rcu_dereference(qe->later.next), | ||
| 88 | struct ima_queue_entry, later); | ||
| 89 | rcu_read_unlock(); | ||
| 90 | (*pos)++; | ||
| 91 | |||
| 92 | return (&qe->later == &ima_measurements) ? NULL : qe; | ||
| 93 | } | ||
| 94 | |||
| 95 | static void ima_measurements_stop(struct seq_file *m, void *v) | ||
| 96 | { | ||
| 97 | } | ||
| 98 | |||
| 99 | static void ima_putc(struct seq_file *m, void *data, int datalen) | ||
| 100 | { | ||
| 101 | while (datalen--) | ||
| 102 | seq_putc(m, *(char *)data++); | ||
| 103 | } | ||
| 104 | |||
| 105 | /* print format: | ||
| 106 | * 32bit-le=pcr# | ||
| 107 | * char[20]=template digest | ||
| 108 | * 32bit-le=template name size | ||
| 109 | * char[n]=template name | ||
| 110 | * eventdata[n]=template specific data | ||
| 111 | */ | ||
| 112 | static int ima_measurements_show(struct seq_file *m, void *v) | ||
| 113 | { | ||
| 114 | /* the list never shrinks, so we don't need a lock here */ | ||
| 115 | struct ima_queue_entry *qe = v; | ||
| 116 | struct ima_template_entry *e; | ||
| 117 | int namelen; | ||
| 118 | u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; | ||
| 119 | |||
| 120 | /* get entry */ | ||
| 121 | e = qe->entry; | ||
| 122 | if (e == NULL) | ||
| 123 | return -1; | ||
| 124 | |||
| 125 | /* | ||
| 126 | * 1st: PCRIndex | ||
| 127 | * PCR used is always the same (config option) in | ||
| 128 | * little-endian format | ||
| 129 | */ | ||
| 130 | ima_putc(m, &pcr, sizeof pcr); | ||
| 131 | |||
| 132 | /* 2nd: template digest */ | ||
| 133 | ima_putc(m, e->digest, IMA_DIGEST_SIZE); | ||
| 134 | |||
| 135 | /* 3rd: template name size */ | ||
| 136 | namelen = strlen(e->template_name); | ||
| 137 | ima_putc(m, &namelen, sizeof namelen); | ||
| 138 | |||
| 139 | /* 4th: template name */ | ||
| 140 | ima_putc(m, (void *)e->template_name, namelen); | ||
| 141 | |||
| 142 | /* 5th: template specific data */ | ||
| 143 | ima_template_show(m, (struct ima_template_data *)&e->template, | ||
| 144 | IMA_SHOW_BINARY); | ||
| 145 | return 0; | ||
| 146 | } | ||
| 147 | |||
| 148 | static struct seq_operations ima_measurments_seqops = { | ||
| 149 | .start = ima_measurements_start, | ||
| 150 | .next = ima_measurements_next, | ||
| 151 | .stop = ima_measurements_stop, | ||
| 152 | .show = ima_measurements_show | ||
| 153 | }; | ||
| 154 | |||
| 155 | static int ima_measurements_open(struct inode *inode, struct file *file) | ||
| 156 | { | ||
| 157 | return seq_open(file, &ima_measurments_seqops); | ||
| 158 | } | ||
| 159 | |||
| 160 | static struct file_operations ima_measurements_ops = { | ||
| 161 | .open = ima_measurements_open, | ||
| 162 | .read = seq_read, | ||
| 163 | .llseek = seq_lseek, | ||
| 164 | .release = seq_release, | ||
| 165 | }; | ||
| 166 | |||
| 167 | static void ima_print_digest(struct seq_file *m, u8 *digest) | ||
| 168 | { | ||
| 169 | int i; | ||
| 170 | |||
| 171 | for (i = 0; i < IMA_DIGEST_SIZE; i++) | ||
| 172 | seq_printf(m, "%02x", *(digest + i)); | ||
| 173 | } | ||
| 174 | |||
| 175 | void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show) | ||
| 176 | { | ||
| 177 | struct ima_template_data *entry = e; | ||
| 178 | int namelen; | ||
| 179 | |||
| 180 | switch (show) { | ||
| 181 | case IMA_SHOW_ASCII: | ||
| 182 | ima_print_digest(m, entry->digest); | ||
| 183 | seq_printf(m, " %s\n", entry->file_name); | ||
| 184 | break; | ||
| 185 | case IMA_SHOW_BINARY: | ||
| 186 | ima_putc(m, entry->digest, IMA_DIGEST_SIZE); | ||
| 187 | |||
| 188 | namelen = strlen(entry->file_name); | ||
| 189 | ima_putc(m, &namelen, sizeof namelen); | ||
| 190 | ima_putc(m, entry->file_name, namelen); | ||
| 191 | default: | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | /* print in ascii */ | ||
| 197 | static int ima_ascii_measurements_show(struct seq_file *m, void *v) | ||
| 198 | { | ||
| 199 | /* the list never shrinks, so we don't need a lock here */ | ||
| 200 | struct ima_queue_entry *qe = v; | ||
| 201 | struct ima_template_entry *e; | ||
| 202 | |||
| 203 | /* get entry */ | ||
| 204 | e = qe->entry; | ||
| 205 | if (e == NULL) | ||
| 206 | return -1; | ||
| 207 | |||
| 208 | /* 1st: PCR used (config option) */ | ||
| 209 | seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX); | ||
| 210 | |||
| 211 | /* 2nd: SHA1 template hash */ | ||
| 212 | ima_print_digest(m, e->digest); | ||
| 213 | |||
| 214 | /* 3th: template name */ | ||
| 215 | seq_printf(m, " %s ", e->template_name); | ||
| 216 | |||
| 217 | /* 4th: template specific data */ | ||
| 218 | ima_template_show(m, (struct ima_template_data *)&e->template, | ||
| 219 | IMA_SHOW_ASCII); | ||
| 220 | return 0; | ||
| 221 | } | ||
| 222 | |||
| 223 | static struct seq_operations ima_ascii_measurements_seqops = { | ||
| 224 | .start = ima_measurements_start, | ||
| 225 | .next = ima_measurements_next, | ||
| 226 | .stop = ima_measurements_stop, | ||
| 227 | .show = ima_ascii_measurements_show | ||
| 228 | }; | ||
| 229 | |||
| 230 | static int ima_ascii_measurements_open(struct inode *inode, struct file *file) | ||
| 231 | { | ||
| 232 | return seq_open(file, &ima_ascii_measurements_seqops); | ||
| 233 | } | ||
| 234 | |||
| 235 | static struct file_operations ima_ascii_measurements_ops = { | ||
| 236 | .open = ima_ascii_measurements_open, | ||
| 237 | .read = seq_read, | ||
| 238 | .llseek = seq_lseek, | ||
| 239 | .release = seq_release, | ||
| 240 | }; | ||
| 241 | |||
| 242 | static ssize_t ima_write_policy(struct file *file, const char __user *buf, | ||
| 243 | size_t datalen, loff_t *ppos) | ||
| 244 | { | ||
| 245 | char *data; | ||
| 246 | int rc; | ||
| 247 | |||
| 248 | if (datalen >= PAGE_SIZE) | ||
| 249 | return -ENOMEM; | ||
| 250 | if (*ppos != 0) { | ||
| 251 | /* No partial writes. */ | ||
| 252 | return -EINVAL; | ||
| 253 | } | ||
| 254 | data = kmalloc(datalen + 1, GFP_KERNEL); | ||
| 255 | if (!data) | ||
| 256 | return -ENOMEM; | ||
| 257 | |||
| 258 | if (copy_from_user(data, buf, datalen)) { | ||
| 259 | kfree(data); | ||
| 260 | return -EFAULT; | ||
| 261 | } | ||
| 262 | *(data + datalen) = '\0'; | ||
| 263 | rc = ima_parse_add_rule(data); | ||
| 264 | if (rc < 0) { | ||
| 265 | datalen = -EINVAL; | ||
| 266 | valid_policy = 0; | ||
| 267 | } | ||
| 268 | |||
| 269 | kfree(data); | ||
| 270 | return datalen; | ||
| 271 | } | ||
| 272 | |||
| 273 | static struct dentry *ima_dir; | ||
| 274 | static struct dentry *binary_runtime_measurements; | ||
| 275 | static struct dentry *ascii_runtime_measurements; | ||
| 276 | static struct dentry *runtime_measurements_count; | ||
| 277 | static struct dentry *violations; | ||
| 278 | static struct dentry *ima_policy; | ||
| 279 | |||
| 280 | static atomic_t policy_opencount = ATOMIC_INIT(1); | ||
| 281 | /* | ||
| 282 | * ima_open_policy: sequentialize access to the policy file | ||
| 283 | */ | ||
| 284 | int ima_open_policy(struct inode * inode, struct file * filp) | ||
| 285 | { | ||
| 286 | if (atomic_dec_and_test(&policy_opencount)) | ||
| 287 | return 0; | ||
| 288 | return -EBUSY; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* | ||
| 292 | * ima_release_policy - start using the new measure policy rules. | ||
| 293 | * | ||
| 294 | * Initially, ima_measure points to the default policy rules, now | ||
| 295 | * point to the new policy rules, and remove the securityfs policy file, | ||
| 296 | * assuming a valid policy. | ||
| 297 | */ | ||
| 298 | static int ima_release_policy(struct inode *inode, struct file *file) | ||
| 299 | { | ||
| 300 | if (!valid_policy) { | ||
| 301 | ima_delete_rules(); | ||
| 302 | valid_policy = 1; | ||
| 303 | atomic_set(&policy_opencount, 1); | ||
| 304 | return 0; | ||
| 305 | } | ||
| 306 | ima_update_policy(); | ||
| 307 | securityfs_remove(ima_policy); | ||
| 308 | ima_policy = NULL; | ||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | static struct file_operations ima_measure_policy_ops = { | ||
| 313 | .open = ima_open_policy, | ||
| 314 | .write = ima_write_policy, | ||
| 315 | .release = ima_release_policy | ||
| 316 | }; | ||
| 317 | |||
| 318 | int ima_fs_init(void) | ||
| 319 | { | ||
| 320 | ima_dir = securityfs_create_dir("ima", NULL); | ||
| 321 | if (IS_ERR(ima_dir)) | ||
| 322 | return -1; | ||
| 323 | |||
| 324 | binary_runtime_measurements = | ||
| 325 | securityfs_create_file("binary_runtime_measurements", | ||
| 326 | S_IRUSR | S_IRGRP, ima_dir, NULL, | ||
| 327 | &ima_measurements_ops); | ||
| 328 | if (IS_ERR(binary_runtime_measurements)) | ||
| 329 | goto out; | ||
| 330 | |||
| 331 | ascii_runtime_measurements = | ||
| 332 | securityfs_create_file("ascii_runtime_measurements", | ||
| 333 | S_IRUSR | S_IRGRP, ima_dir, NULL, | ||
| 334 | &ima_ascii_measurements_ops); | ||
| 335 | if (IS_ERR(ascii_runtime_measurements)) | ||
| 336 | goto out; | ||
| 337 | |||
| 338 | runtime_measurements_count = | ||
| 339 | securityfs_create_file("runtime_measurements_count", | ||
| 340 | S_IRUSR | S_IRGRP, ima_dir, NULL, | ||
| 341 | &ima_measurements_count_ops); | ||
| 342 | if (IS_ERR(runtime_measurements_count)) | ||
| 343 | goto out; | ||
| 344 | |||
| 345 | violations = | ||
| 346 | securityfs_create_file("violations", S_IRUSR | S_IRGRP, | ||
| 347 | ima_dir, NULL, &ima_htable_violations_ops); | ||
| 348 | if (IS_ERR(violations)) | ||
| 349 | goto out; | ||
| 350 | |||
| 351 | ima_policy = securityfs_create_file("policy", | ||
| 352 | S_IRUSR | S_IRGRP | S_IWUSR, | ||
| 353 | ima_dir, NULL, | ||
| 354 | &ima_measure_policy_ops); | ||
| 355 | if (IS_ERR(ima_policy)) | ||
| 356 | goto out; | ||
| 357 | |||
| 358 | return 0; | ||
| 359 | out: | ||
| 360 | securityfs_remove(runtime_measurements_count); | ||
| 361 | securityfs_remove(ascii_runtime_measurements); | ||
| 362 | securityfs_remove(binary_runtime_measurements); | ||
| 363 | securityfs_remove(ima_dir); | ||
| 364 | securityfs_remove(ima_policy); | ||
| 365 | return -1; | ||
| 366 | } | ||
| 367 | |||
| 368 | void __exit ima_fs_cleanup(void) | ||
| 369 | { | ||
| 370 | securityfs_remove(violations); | ||
| 371 | securityfs_remove(runtime_measurements_count); | ||
| 372 | securityfs_remove(ascii_runtime_measurements); | ||
| 373 | securityfs_remove(binary_runtime_measurements); | ||
| 374 | securityfs_remove(ima_dir); | ||
| 375 | securityfs_remove(ima_policy); | ||
| 376 | } | ||
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c new file mode 100644 index 000000000000..ec79f1ee992c --- /dev/null +++ b/security/integrity/ima/ima_iint.c | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License as | ||
| 9 | * published by the Free Software Foundation, version 2 of the | ||
| 10 | * License. | ||
| 11 | * | ||
| 12 | * File: ima_iint.c | ||
| 13 | * - implements the IMA hooks: ima_inode_alloc, ima_inode_free | ||
| 14 | * - cache integrity information associated with an inode | ||
| 15 | * using a radix tree. | ||
| 16 | */ | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/spinlock.h> | ||
| 19 | #include <linux/radix-tree.h> | ||
| 20 | #include "ima.h" | ||
| 21 | |||
| 22 | #define ima_iint_delete ima_inode_free | ||
| 23 | |||
| 24 | RADIX_TREE(ima_iint_store, GFP_ATOMIC); | ||
| 25 | DEFINE_SPINLOCK(ima_iint_lock); | ||
| 26 | |||
| 27 | static struct kmem_cache *iint_cache __read_mostly; | ||
| 28 | |||
| 29 | /* ima_iint_find_get - return the iint associated with an inode | ||
| 30 | * | ||
| 31 | * ima_iint_find_get gets a reference to the iint. Caller must | ||
| 32 | * remember to put the iint reference. | ||
| 33 | */ | ||
| 34 | struct ima_iint_cache *ima_iint_find_get(struct inode *inode) | ||
| 35 | { | ||
| 36 | struct ima_iint_cache *iint; | ||
| 37 | |||
| 38 | rcu_read_lock(); | ||
| 39 | iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode); | ||
| 40 | if (!iint) | ||
| 41 | goto out; | ||
| 42 | kref_get(&iint->refcount); | ||
| 43 | out: | ||
| 44 | rcu_read_unlock(); | ||
| 45 | return iint; | ||
| 46 | } | ||
| 47 | |||
| 48 | /* Allocate memory for the iint associated with the inode | ||
| 49 | * from the iint_cache slab, initialize the iint, and | ||
| 50 | * insert it into the radix tree. | ||
| 51 | * | ||
| 52 | * On success return a pointer to the iint; on failure return NULL. | ||
| 53 | */ | ||
| 54 | struct ima_iint_cache *ima_iint_insert(struct inode *inode) | ||
| 55 | { | ||
| 56 | struct ima_iint_cache *iint = NULL; | ||
| 57 | int rc = 0; | ||
| 58 | |||
| 59 | if (!ima_initialized) | ||
| 60 | return iint; | ||
| 61 | iint = kmem_cache_alloc(iint_cache, GFP_KERNEL); | ||
| 62 | if (!iint) | ||
| 63 | return iint; | ||
| 64 | |||
| 65 | rc = radix_tree_preload(GFP_KERNEL); | ||
| 66 | if (rc < 0) | ||
| 67 | goto out; | ||
| 68 | |||
| 69 | spin_lock(&ima_iint_lock); | ||
| 70 | rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint); | ||
| 71 | spin_unlock(&ima_iint_lock); | ||
| 72 | out: | ||
| 73 | if (rc < 0) { | ||
| 74 | kmem_cache_free(iint_cache, iint); | ||
| 75 | if (rc == -EEXIST) { | ||
| 76 | spin_lock(&ima_iint_lock); | ||
| 77 | iint = radix_tree_lookup(&ima_iint_store, | ||
| 78 | (unsigned long)inode); | ||
| 79 | spin_unlock(&ima_iint_lock); | ||
| 80 | } else | ||
| 81 | iint = NULL; | ||
| 82 | } | ||
| 83 | radix_tree_preload_end(); | ||
| 84 | return iint; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * ima_inode_alloc - allocate an iint associated with an inode | ||
| 89 | * @inode: pointer to the inode | ||
| 90 | * | ||
| 91 | * Return 0 on success, 1 on failure. | ||
| 92 | */ | ||
| 93 | int ima_inode_alloc(struct inode *inode) | ||
| 94 | { | ||
| 95 | struct ima_iint_cache *iint; | ||
| 96 | |||
| 97 | if (!ima_initialized) | ||
| 98 | return 0; | ||
| 99 | |||
| 100 | iint = ima_iint_insert(inode); | ||
| 101 | if (!iint) | ||
| 102 | return 1; | ||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | /* ima_iint_find_insert_get - get the iint associated with an inode | ||
| 107 | * | ||
| 108 | * Most insertions are done at inode_alloc, except those allocated | ||
| 109 | * before late_initcall. When the iint does not exist, allocate it, | ||
| 110 | * initialize and insert it, and increment the iint refcount. | ||
| 111 | * | ||
| 112 | * (Can't initialize at security_initcall before any inodes are | ||
| 113 | * allocated, got to wait at least until proc_init.) | ||
| 114 | * | ||
| 115 | * Return the iint. | ||
| 116 | */ | ||
| 117 | struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode) | ||
| 118 | { | ||
| 119 | struct ima_iint_cache *iint = NULL; | ||
| 120 | |||
| 121 | iint = ima_iint_find_get(inode); | ||
| 122 | if (iint) | ||
| 123 | return iint; | ||
| 124 | |||
| 125 | iint = ima_iint_insert(inode); | ||
| 126 | if (iint) | ||
| 127 | kref_get(&iint->refcount); | ||
| 128 | |||
| 129 | return iint; | ||
| 130 | } | ||
| 131 | EXPORT_SYMBOL_GPL(ima_iint_find_insert_get); | ||
| 132 | |||
| 133 | /* iint_free - called when the iint refcount goes to zero */ | ||
| 134 | void iint_free(struct kref *kref) | ||
| 135 | { | ||
| 136 | struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache, | ||
| 137 | refcount); | ||
| 138 | iint->version = 0; | ||
| 139 | iint->flags = 0UL; | ||
| 140 | if (iint->readcount != 0) { | ||
| 141 | printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__, | ||
| 142 | iint->readcount); | ||
| 143 | iint->readcount = 0; | ||
| 144 | } | ||
| 145 | if (iint->writecount != 0) { | ||
| 146 | printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__, | ||
| 147 | iint->writecount); | ||
| 148 | iint->writecount = 0; | ||
| 149 | } | ||
| 150 | if (iint->opencount != 0) { | ||
| 151 | printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__, | ||
| 152 | iint->opencount); | ||
| 153 | iint->opencount = 0; | ||
| 154 | } | ||
| 155 | kref_set(&iint->refcount, 1); | ||
| 156 | kmem_cache_free(iint_cache, iint); | ||
| 157 | } | ||
| 158 | |||
| 159 | void iint_rcu_free(struct rcu_head *rcu_head) | ||
| 160 | { | ||
| 161 | struct ima_iint_cache *iint = container_of(rcu_head, | ||
| 162 | struct ima_iint_cache, rcu); | ||
| 163 | kref_put(&iint->refcount, iint_free); | ||
| 164 | } | ||
| 165 | |||
| 166 | /** | ||
| 167 | * ima_iint_delete - called on integrity_inode_free | ||
| 168 | * @inode: pointer to the inode | ||
| 169 | * | ||
| 170 | * Free the integrity information(iint) associated with an inode. | ||
| 171 | */ | ||
| 172 | void ima_iint_delete(struct inode *inode) | ||
| 173 | { | ||
| 174 | struct ima_iint_cache *iint; | ||
| 175 | |||
| 176 | if (!ima_initialized) | ||
| 177 | return; | ||
| 178 | spin_lock(&ima_iint_lock); | ||
| 179 | iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode); | ||
| 180 | spin_unlock(&ima_iint_lock); | ||
| 181 | if (iint) | ||
| 182 | call_rcu(&iint->rcu, iint_rcu_free); | ||
| 183 | } | ||
| 184 | |||
| 185 | static void init_once(void *foo) | ||
| 186 | { | ||
| 187 | struct ima_iint_cache *iint = foo; | ||
| 188 | |||
| 189 | memset(iint, 0, sizeof *iint); | ||
| 190 | iint->version = 0; | ||
| 191 | iint->flags = 0UL; | ||
| 192 | mutex_init(&iint->mutex); | ||
| 193 | iint->readcount = 0; | ||
| 194 | iint->writecount = 0; | ||
| 195 | iint->opencount = 0; | ||
| 196 | kref_set(&iint->refcount, 1); | ||
| 197 | } | ||
| 198 | |||
| 199 | void ima_iintcache_init(void) | ||
| 200 | { | ||
| 201 | iint_cache = | ||
| 202 | kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0, | ||
| 203 | SLAB_PANIC, init_once); | ||
| 204 | } | ||
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c new file mode 100644 index 000000000000..0b0bb8c978cc --- /dev/null +++ b/security/integrity/ima/ima_init.c | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005,2006,2007,2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Reiner Sailer <sailer@watson.ibm.com> | ||
| 6 | * Leendert van Doorn <leendert@watson.ibm.com> | ||
| 7 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | * | ||
| 14 | * File: ima_init.c | ||
| 15 | * initialization and cleanup functions | ||
| 16 | */ | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/scatterlist.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include "ima.h" | ||
| 21 | |||
| 22 | /* name for boot aggregate entry */ | ||
| 23 | static const char *boot_aggregate_name = "boot_aggregate"; | ||
| 24 | int ima_used_chip; | ||
| 25 | |||
| 26 | /* Add the boot aggregate to the IMA measurement list and extend | ||
| 27 | * the PCR register. | ||
| 28 | * | ||
| 29 | * Calculate the boot aggregate, a SHA1 over tpm registers 0-7, | ||
| 30 | * assuming a TPM chip exists, and zeroes if the TPM chip does not | ||
| 31 | * exist. Add the boot aggregate measurement to the measurement | ||
| 32 | * list and extend the PCR register. | ||
| 33 | * | ||
| 34 | * If a tpm chip does not exist, indicate the core root of trust is | ||
| 35 | * not hardware based by invalidating the aggregate PCR value. | ||
| 36 | * (The aggregate PCR value is invalidated by adding one value to | ||
| 37 | * the measurement list and extending the aggregate PCR value with | ||
| 38 | * a different value.) Violations add a zero entry to the measurement | ||
| 39 | * list and extend the aggregate PCR value with ff...ff's. | ||
| 40 | */ | ||
| 41 | static void ima_add_boot_aggregate(void) | ||
| 42 | { | ||
| 43 | struct ima_template_entry *entry; | ||
| 44 | const char *op = "add_boot_aggregate"; | ||
| 45 | const char *audit_cause = "ENOMEM"; | ||
| 46 | int result = -ENOMEM; | ||
| 47 | int violation = 1; | ||
| 48 | |||
| 49 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 50 | if (!entry) | ||
| 51 | goto err_out; | ||
| 52 | |||
| 53 | memset(&entry->template, 0, sizeof(entry->template)); | ||
| 54 | strncpy(entry->template.file_name, boot_aggregate_name, | ||
| 55 | IMA_EVENT_NAME_LEN_MAX); | ||
| 56 | if (ima_used_chip) { | ||
| 57 | violation = 0; | ||
| 58 | result = ima_calc_boot_aggregate(entry->template.digest); | ||
| 59 | if (result < 0) { | ||
| 60 | audit_cause = "hashing_error"; | ||
| 61 | kfree(entry); | ||
| 62 | goto err_out; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | result = ima_store_template(entry, violation, NULL); | ||
| 66 | if (result < 0) | ||
| 67 | kfree(entry); | ||
| 68 | return; | ||
| 69 | err_out: | ||
| 70 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op, | ||
| 71 | audit_cause, result, 0); | ||
| 72 | } | ||
| 73 | |||
| 74 | int ima_init(void) | ||
| 75 | { | ||
| 76 | u8 pcr_i[IMA_DIGEST_SIZE]; | ||
| 77 | int rc; | ||
| 78 | |||
| 79 | ima_used_chip = 0; | ||
| 80 | rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i); | ||
| 81 | if (rc == 0) | ||
| 82 | ima_used_chip = 1; | ||
| 83 | |||
| 84 | if (!ima_used_chip) | ||
| 85 | pr_info("No TPM chip found, activating TPM-bypass!\n"); | ||
| 86 | |||
| 87 | ima_add_boot_aggregate(); /* boot aggregate must be first entry */ | ||
| 88 | ima_init_policy(); | ||
| 89 | |||
| 90 | return ima_fs_init(); | ||
| 91 | } | ||
| 92 | |||
| 93 | void __exit ima_cleanup(void) | ||
| 94 | { | ||
| 95 | ima_fs_cleanup(); | ||
| 96 | } | ||
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c new file mode 100644 index 000000000000..f4e7266f5aee --- /dev/null +++ b/security/integrity/ima/ima_main.c | |||
| @@ -0,0 +1,327 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005,2006,2007,2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Reiner Sailer <sailer@watson.ibm.com> | ||
| 6 | * Serge Hallyn <serue@us.ibm.com> | ||
| 7 | * Kylene Hall <kylene@us.ibm.com> | ||
| 8 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or | ||
| 11 | * modify it under the terms of the GNU General Public License as | ||
| 12 | * published by the Free Software Foundation, version 2 of the | ||
| 13 | * License. | ||
| 14 | * | ||
| 15 | * File: ima_main.c | ||
| 16 | * implements the IMA hooks: ima_bprm_check, ima_file_mmap, | ||
| 17 | * and ima_path_check. | ||
| 18 | */ | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/file.h> | ||
| 21 | #include <linux/binfmts.h> | ||
| 22 | #include <linux/mount.h> | ||
| 23 | #include <linux/mman.h> | ||
| 24 | |||
| 25 | #include "ima.h" | ||
| 26 | |||
| 27 | int ima_initialized; | ||
| 28 | |||
| 29 | char *ima_hash = "sha1"; | ||
| 30 | static int __init hash_setup(char *str) | ||
| 31 | { | ||
| 32 | const char *op = "hash_setup"; | ||
| 33 | const char *hash = "sha1"; | ||
| 34 | int result = 0; | ||
| 35 | int audit_info = 0; | ||
| 36 | |||
| 37 | if (strncmp(str, "md5", 3) == 0) { | ||
| 38 | hash = "md5"; | ||
| 39 | ima_hash = str; | ||
| 40 | } else if (strncmp(str, "sha1", 4) != 0) { | ||
| 41 | hash = "invalid_hash_type"; | ||
| 42 | result = 1; | ||
| 43 | } | ||
| 44 | integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash, | ||
| 45 | result, audit_info); | ||
| 46 | return 1; | ||
| 47 | } | ||
| 48 | __setup("ima_hash=", hash_setup); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * ima_file_free - called on __fput() | ||
| 52 | * @file: pointer to file structure being freed | ||
| 53 | * | ||
| 54 | * Flag files that changed, based on i_version; | ||
| 55 | * and decrement the iint readcount/writecount. | ||
| 56 | */ | ||
| 57 | void ima_file_free(struct file *file) | ||
| 58 | { | ||
| 59 | struct inode *inode = file->f_dentry->d_inode; | ||
| 60 | struct ima_iint_cache *iint; | ||
| 61 | |||
| 62 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
| 63 | return; | ||
| 64 | iint = ima_iint_find_get(inode); | ||
| 65 | if (!iint) | ||
| 66 | return; | ||
| 67 | |||
| 68 | mutex_lock(&iint->mutex); | ||
| 69 | if (iint->opencount <= 0) { | ||
| 70 | printk(KERN_INFO | ||
| 71 | "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n", | ||
| 72 | __FUNCTION__, file->f_dentry->d_name.name, | ||
| 73 | iint->readcount, iint->writecount, | ||
| 74 | iint->opencount, atomic_long_read(&file->f_count)); | ||
| 75 | if (!(iint->flags & IMA_IINT_DUMP_STACK)) { | ||
| 76 | dump_stack(); | ||
| 77 | iint->flags |= IMA_IINT_DUMP_STACK; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | iint->opencount--; | ||
| 81 | |||
| 82 | if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
| 83 | iint->readcount--; | ||
| 84 | |||
| 85 | if (file->f_mode & FMODE_WRITE) { | ||
| 86 | iint->writecount--; | ||
| 87 | if (iint->writecount == 0) { | ||
| 88 | if (iint->version != inode->i_version) | ||
| 89 | iint->flags &= ~IMA_MEASURED; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | mutex_unlock(&iint->mutex); | ||
| 93 | kref_put(&iint->refcount, iint_free); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* ima_read_write_check - reflect possible reading/writing errors in the PCR. | ||
| 97 | * | ||
| 98 | * When opening a file for read, if the file is already open for write, | ||
| 99 | * the file could change, resulting in a file measurement error. | ||
| 100 | * | ||
| 101 | * Opening a file for write, if the file is already open for read, results | ||
| 102 | * in a time of measure, time of use (ToMToU) error. | ||
| 103 | * | ||
| 104 | * In either case invalidate the PCR. | ||
| 105 | */ | ||
| 106 | enum iint_pcr_error { TOMTOU, OPEN_WRITERS }; | ||
| 107 | static void ima_read_write_check(enum iint_pcr_error error, | ||
| 108 | struct ima_iint_cache *iint, | ||
| 109 | struct inode *inode, | ||
| 110 | const unsigned char *filename) | ||
| 111 | { | ||
| 112 | switch (error) { | ||
| 113 | case TOMTOU: | ||
| 114 | if (iint->readcount > 0) | ||
| 115 | ima_add_violation(inode, filename, "invalid_pcr", | ||
| 116 | "ToMToU"); | ||
| 117 | break; | ||
| 118 | case OPEN_WRITERS: | ||
| 119 | if (iint->writecount > 0) | ||
| 120 | ima_add_violation(inode, filename, "invalid_pcr", | ||
| 121 | "open_writers"); | ||
| 122 | break; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | static int get_path_measurement(struct ima_iint_cache *iint, struct file *file, | ||
| 127 | const unsigned char *filename) | ||
| 128 | { | ||
| 129 | int rc = 0; | ||
| 130 | |||
| 131 | if (IS_ERR(file)) { | ||
| 132 | pr_info("%s dentry_open failed\n", filename); | ||
| 133 | return rc; | ||
| 134 | } | ||
| 135 | iint->opencount++; | ||
| 136 | iint->readcount++; | ||
| 137 | |||
| 138 | rc = ima_collect_measurement(iint, file); | ||
| 139 | if (!rc) | ||
| 140 | ima_store_measurement(iint, file, filename); | ||
| 141 | return rc; | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * ima_path_check - based on policy, collect/store measurement. | ||
| 146 | * @path: contains a pointer to the path to be measured | ||
| 147 | * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE | ||
| 148 | * | ||
| 149 | * Measure the file being open for readonly, based on the | ||
| 150 | * ima_must_measure() policy decision. | ||
| 151 | * | ||
| 152 | * Keep read/write counters for all files, but only | ||
| 153 | * invalidate the PCR for measured files: | ||
| 154 | * - Opening a file for write when already open for read, | ||
| 155 | * results in a time of measure, time of use (ToMToU) error. | ||
| 156 | * - Opening a file for read when already open for write, | ||
| 157 | * could result in a file measurement error. | ||
| 158 | * | ||
| 159 | * Return 0 on success, an error code on failure. | ||
| 160 | * (Based on the results of appraise_measurement().) | ||
| 161 | */ | ||
| 162 | int ima_path_check(struct path *path, int mask) | ||
| 163 | { | ||
| 164 | struct inode *inode = path->dentry->d_inode; | ||
| 165 | struct ima_iint_cache *iint; | ||
| 166 | struct file *file = NULL; | ||
| 167 | int rc; | ||
| 168 | |||
| 169 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
| 170 | return 0; | ||
| 171 | iint = ima_iint_find_insert_get(inode); | ||
| 172 | if (!iint) | ||
| 173 | return 0; | ||
| 174 | |||
| 175 | mutex_lock(&iint->mutex); | ||
| 176 | iint->opencount++; | ||
| 177 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
| 178 | iint->writecount++; | ||
| 179 | else if (mask & (MAY_READ | MAY_EXEC)) | ||
| 180 | iint->readcount++; | ||
| 181 | |||
| 182 | rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK); | ||
| 183 | if (rc < 0) | ||
| 184 | goto out; | ||
| 185 | |||
| 186 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
| 187 | ima_read_write_check(TOMTOU, iint, inode, | ||
| 188 | path->dentry->d_name.name); | ||
| 189 | |||
| 190 | if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ) | ||
| 191 | goto out; | ||
| 192 | |||
| 193 | ima_read_write_check(OPEN_WRITERS, iint, inode, | ||
| 194 | path->dentry->d_name.name); | ||
| 195 | if (!(iint->flags & IMA_MEASURED)) { | ||
| 196 | struct dentry *dentry = dget(path->dentry); | ||
| 197 | struct vfsmount *mnt = mntget(path->mnt); | ||
| 198 | |||
| 199 | file = dentry_open(dentry, mnt, O_RDONLY, current->cred); | ||
| 200 | rc = get_path_measurement(iint, file, dentry->d_name.name); | ||
| 201 | } | ||
| 202 | out: | ||
| 203 | mutex_unlock(&iint->mutex); | ||
| 204 | if (file) | ||
| 205 | fput(file); | ||
| 206 | kref_put(&iint->refcount, iint_free); | ||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | static int process_measurement(struct file *file, const unsigned char *filename, | ||
| 211 | int mask, int function) | ||
| 212 | { | ||
| 213 | struct inode *inode = file->f_dentry->d_inode; | ||
| 214 | struct ima_iint_cache *iint; | ||
| 215 | int rc; | ||
| 216 | |||
| 217 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
| 218 | return 0; | ||
| 219 | iint = ima_iint_find_insert_get(inode); | ||
| 220 | if (!iint) | ||
| 221 | return -ENOMEM; | ||
| 222 | |||
| 223 | mutex_lock(&iint->mutex); | ||
| 224 | rc = ima_must_measure(iint, inode, mask, function); | ||
| 225 | if (rc != 0) | ||
| 226 | goto out; | ||
| 227 | |||
| 228 | rc = ima_collect_measurement(iint, file); | ||
| 229 | if (!rc) | ||
| 230 | ima_store_measurement(iint, file, filename); | ||
| 231 | out: | ||
| 232 | mutex_unlock(&iint->mutex); | ||
| 233 | kref_put(&iint->refcount, iint_free); | ||
| 234 | return rc; | ||
| 235 | } | ||
| 236 | |||
| 237 | static void opencount_get(struct file *file) | ||
| 238 | { | ||
| 239 | struct inode *inode = file->f_dentry->d_inode; | ||
| 240 | struct ima_iint_cache *iint; | ||
| 241 | |||
| 242 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
| 243 | return; | ||
| 244 | iint = ima_iint_find_insert_get(inode); | ||
| 245 | if (!iint) | ||
| 246 | return; | ||
| 247 | mutex_lock(&iint->mutex); | ||
| 248 | iint->opencount++; | ||
| 249 | mutex_unlock(&iint->mutex); | ||
| 250 | } | ||
| 251 | |||
| 252 | /** | ||
| 253 | * ima_file_mmap - based on policy, collect/store measurement. | ||
| 254 | * @file: pointer to the file to be measured (May be NULL) | ||
| 255 | * @prot: contains the protection that will be applied by the kernel. | ||
| 256 | * | ||
| 257 | * Measure files being mmapped executable based on the ima_must_measure() | ||
| 258 | * policy decision. | ||
| 259 | * | ||
| 260 | * Return 0 on success, an error code on failure. | ||
| 261 | * (Based on the results of appraise_measurement().) | ||
| 262 | */ | ||
| 263 | int ima_file_mmap(struct file *file, unsigned long prot) | ||
| 264 | { | ||
| 265 | int rc; | ||
| 266 | |||
| 267 | if (!file) | ||
| 268 | return 0; | ||
| 269 | if (prot & PROT_EXEC) | ||
| 270 | rc = process_measurement(file, file->f_dentry->d_name.name, | ||
| 271 | MAY_EXEC, FILE_MMAP); | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | /* | ||
| 276 | * ima_shm_check - IPC shm and shmat create/fput a file | ||
| 277 | * | ||
| 278 | * Maintain the opencount for these files to prevent unnecessary | ||
| 279 | * imbalance messages. | ||
| 280 | */ | ||
| 281 | void ima_shm_check(struct file *file) | ||
| 282 | { | ||
| 283 | opencount_get(file); | ||
| 284 | return; | ||
| 285 | } | ||
| 286 | |||
| 287 | /** | ||
| 288 | * ima_bprm_check - based on policy, collect/store measurement. | ||
| 289 | * @bprm: contains the linux_binprm structure | ||
| 290 | * | ||
| 291 | * The OS protects against an executable file, already open for write, | ||
| 292 | * from being executed in deny_write_access() and an executable file, | ||
| 293 | * already open for execute, from being modified in get_write_access(). | ||
| 294 | * So we can be certain that what we verify and measure here is actually | ||
| 295 | * what is being executed. | ||
| 296 | * | ||
| 297 | * Return 0 on success, an error code on failure. | ||
| 298 | * (Based on the results of appraise_measurement().) | ||
| 299 | */ | ||
| 300 | int ima_bprm_check(struct linux_binprm *bprm) | ||
| 301 | { | ||
| 302 | int rc; | ||
| 303 | |||
| 304 | rc = process_measurement(bprm->file, bprm->filename, | ||
| 305 | MAY_EXEC, BPRM_CHECK); | ||
| 306 | return 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | static int __init init_ima(void) | ||
| 310 | { | ||
| 311 | int error; | ||
| 312 | |||
| 313 | ima_iintcache_init(); | ||
| 314 | error = ima_init(); | ||
| 315 | ima_initialized = 1; | ||
| 316 | return error; | ||
| 317 | } | ||
| 318 | |||
| 319 | static void __exit cleanup_ima(void) | ||
| 320 | { | ||
| 321 | ima_cleanup(); | ||
| 322 | } | ||
| 323 | |||
| 324 | late_initcall(init_ima); /* Start IMA after the TPM is available */ | ||
| 325 | |||
| 326 | MODULE_DESCRIPTION("Integrity Measurement Architecture"); | ||
| 327 | MODULE_LICENSE("GPL"); | ||
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c new file mode 100644 index 000000000000..b5291ad5ef56 --- /dev/null +++ b/security/integrity/ima/ima_policy.c | |||
| @@ -0,0 +1,414 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2008 IBM Corporation | ||
| 3 | * Author: Mimi Zohar <zohar@us.ibm.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, version 2 of the License. | ||
| 8 | * | ||
| 9 | * ima_policy.c | ||
| 10 | * - initialize default measure policy rules | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/list.h> | ||
| 15 | #include <linux/security.h> | ||
| 16 | #include <linux/magic.h> | ||
| 17 | #include <linux/parser.h> | ||
| 18 | |||
| 19 | #include "ima.h" | ||
| 20 | |||
| 21 | /* flags definitions */ | ||
| 22 | #define IMA_FUNC 0x0001 | ||
| 23 | #define IMA_MASK 0x0002 | ||
| 24 | #define IMA_FSMAGIC 0x0004 | ||
| 25 | #define IMA_UID 0x0008 | ||
| 26 | |||
| 27 | enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE }; | ||
| 28 | |||
| 29 | #define MAX_LSM_RULES 6 | ||
| 30 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, | ||
| 31 | LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct ima_measure_rule_entry { | ||
| 35 | struct list_head list; | ||
| 36 | enum ima_action action; | ||
| 37 | unsigned int flags; | ||
| 38 | enum ima_hooks func; | ||
| 39 | int mask; | ||
| 40 | unsigned long fsmagic; | ||
| 41 | uid_t uid; | ||
| 42 | struct { | ||
| 43 | void *rule; /* LSM file metadata specific */ | ||
| 44 | int type; /* audit type */ | ||
| 45 | } lsm[MAX_LSM_RULES]; | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* Without LSM specific knowledge, the default policy can only be | ||
| 49 | * written in terms of .action, .func, .mask, .fsmagic, and .uid | ||
| 50 | */ | ||
| 51 | static struct ima_measure_rule_entry default_rules[] = { | ||
| 52 | {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC, | ||
| 53 | .flags = IMA_FSMAGIC}, | ||
| 54 | {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 55 | {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 56 | {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 57 | {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC, | ||
| 58 | .flags = IMA_FSMAGIC}, | ||
| 59 | {.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC}, | ||
| 60 | {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC, | ||
| 61 | .flags = IMA_FUNC | IMA_MASK}, | ||
| 62 | {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, | ||
| 63 | .flags = IMA_FUNC | IMA_MASK}, | ||
| 64 | {.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0, | ||
| 65 | .flags = IMA_FUNC | IMA_MASK | IMA_UID} | ||
| 66 | }; | ||
| 67 | |||
| 68 | static LIST_HEAD(measure_default_rules); | ||
| 69 | static LIST_HEAD(measure_policy_rules); | ||
| 70 | static struct list_head *ima_measure; | ||
| 71 | |||
| 72 | static DEFINE_MUTEX(ima_measure_mutex); | ||
| 73 | |||
| 74 | /** | ||
| 75 | * ima_match_rules - determine whether an inode matches the measure rule. | ||
| 76 | * @rule: a pointer to a rule | ||
| 77 | * @inode: a pointer to an inode | ||
| 78 | * @func: LIM hook identifier | ||
| 79 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | ||
| 80 | * | ||
| 81 | * Returns true on rule match, false on failure. | ||
| 82 | */ | ||
| 83 | static bool ima_match_rules(struct ima_measure_rule_entry *rule, | ||
| 84 | struct inode *inode, enum ima_hooks func, int mask) | ||
| 85 | { | ||
| 86 | struct task_struct *tsk = current; | ||
| 87 | int i; | ||
| 88 | |||
| 89 | if ((rule->flags & IMA_FUNC) && rule->func != func) | ||
| 90 | return false; | ||
| 91 | if ((rule->flags & IMA_MASK) && rule->mask != mask) | ||
| 92 | return false; | ||
| 93 | if ((rule->flags & IMA_FSMAGIC) | ||
| 94 | && rule->fsmagic != inode->i_sb->s_magic) | ||
| 95 | return false; | ||
| 96 | if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid) | ||
| 97 | return false; | ||
| 98 | for (i = 0; i < MAX_LSM_RULES; i++) { | ||
| 99 | int rc; | ||
| 100 | u32 osid, sid; | ||
| 101 | |||
| 102 | if (!rule->lsm[i].rule) | ||
| 103 | continue; | ||
| 104 | |||
| 105 | switch (i) { | ||
| 106 | case LSM_OBJ_USER: | ||
| 107 | case LSM_OBJ_ROLE: | ||
| 108 | case LSM_OBJ_TYPE: | ||
| 109 | security_inode_getsecid(inode, &osid); | ||
| 110 | rc = security_filter_rule_match(osid, | ||
| 111 | rule->lsm[i].type, | ||
| 112 | AUDIT_EQUAL, | ||
| 113 | rule->lsm[i].rule, | ||
| 114 | NULL); | ||
| 115 | break; | ||
| 116 | case LSM_SUBJ_USER: | ||
| 117 | case LSM_SUBJ_ROLE: | ||
| 118 | case LSM_SUBJ_TYPE: | ||
| 119 | security_task_getsecid(tsk, &sid); | ||
| 120 | rc = security_filter_rule_match(sid, | ||
| 121 | rule->lsm[i].type, | ||
| 122 | AUDIT_EQUAL, | ||
| 123 | rule->lsm[i].rule, | ||
| 124 | NULL); | ||
| 125 | default: | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | if (!rc) | ||
| 129 | return false; | ||
| 130 | } | ||
| 131 | return true; | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * ima_match_policy - decision based on LSM and other conditions | ||
| 136 | * @inode: pointer to an inode for which the policy decision is being made | ||
| 137 | * @func: IMA hook identifier | ||
| 138 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | ||
| 139 | * | ||
| 140 | * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) | ||
| 141 | * conditions. | ||
| 142 | * | ||
| 143 | * (There is no need for locking when walking the policy list, | ||
| 144 | * as elements in the list are never deleted, nor does the list | ||
| 145 | * change.) | ||
| 146 | */ | ||
| 147 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) | ||
| 148 | { | ||
| 149 | struct ima_measure_rule_entry *entry; | ||
| 150 | |||
| 151 | list_for_each_entry(entry, ima_measure, list) { | ||
| 152 | bool rc; | ||
| 153 | |||
| 154 | rc = ima_match_rules(entry, inode, func, mask); | ||
| 155 | if (rc) | ||
| 156 | return entry->action; | ||
| 157 | } | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * ima_init_policy - initialize the default measure rules. | ||
| 163 | * | ||
| 164 | * ima_measure points to either the measure_default_rules or the | ||
| 165 | * the new measure_policy_rules. | ||
| 166 | */ | ||
| 167 | void ima_init_policy(void) | ||
| 168 | { | ||
| 169 | int i; | ||
| 170 | |||
| 171 | for (i = 0; i < ARRAY_SIZE(default_rules); i++) | ||
| 172 | list_add_tail(&default_rules[i].list, &measure_default_rules); | ||
| 173 | ima_measure = &measure_default_rules; | ||
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * ima_update_policy - update default_rules with new measure rules | ||
| 178 | * | ||
| 179 | * Called on file .release to update the default rules with a complete new | ||
| 180 | * policy. Once updated, the policy is locked, no additional rules can be | ||
| 181 | * added to the policy. | ||
| 182 | */ | ||
| 183 | void ima_update_policy(void) | ||
| 184 | { | ||
| 185 | const char *op = "policy_update"; | ||
| 186 | const char *cause = "already exists"; | ||
| 187 | int result = 1; | ||
| 188 | int audit_info = 0; | ||
| 189 | |||
| 190 | if (ima_measure == &measure_default_rules) { | ||
| 191 | ima_measure = &measure_policy_rules; | ||
| 192 | cause = "complete"; | ||
| 193 | result = 0; | ||
| 194 | } | ||
| 195 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||
| 196 | NULL, op, cause, result, audit_info); | ||
| 197 | } | ||
| 198 | |||
| 199 | enum { | ||
| 200 | Opt_err = -1, | ||
| 201 | Opt_measure = 1, Opt_dont_measure, | ||
| 202 | Opt_obj_user, Opt_obj_role, Opt_obj_type, | ||
| 203 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | ||
| 204 | Opt_func, Opt_mask, Opt_fsmagic, Opt_uid | ||
| 205 | }; | ||
| 206 | |||
| 207 | static match_table_t policy_tokens = { | ||
| 208 | {Opt_measure, "measure"}, | ||
| 209 | {Opt_dont_measure, "dont_measure"}, | ||
| 210 | {Opt_obj_user, "obj_user=%s"}, | ||
| 211 | {Opt_obj_role, "obj_role=%s"}, | ||
| 212 | {Opt_obj_type, "obj_type=%s"}, | ||
| 213 | {Opt_subj_user, "subj_user=%s"}, | ||
| 214 | {Opt_subj_role, "subj_role=%s"}, | ||
| 215 | {Opt_subj_type, "subj_type=%s"}, | ||
| 216 | {Opt_func, "func=%s"}, | ||
| 217 | {Opt_mask, "mask=%s"}, | ||
| 218 | {Opt_fsmagic, "fsmagic=%s"}, | ||
| 219 | {Opt_uid, "uid=%s"}, | ||
| 220 | {Opt_err, NULL} | ||
| 221 | }; | ||
| 222 | |||
| 223 | static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, | ||
| 224 | char *args, int lsm_rule, int audit_type) | ||
| 225 | { | ||
| 226 | int result; | ||
| 227 | |||
| 228 | entry->lsm[lsm_rule].type = audit_type; | ||
| 229 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, | ||
| 230 | AUDIT_EQUAL, args, | ||
| 231 | &entry->lsm[lsm_rule].rule); | ||
| 232 | return result; | ||
| 233 | } | ||
| 234 | |||
| 235 | static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | ||
| 236 | { | ||
| 237 | struct audit_buffer *ab; | ||
| 238 | char *p; | ||
| 239 | int result = 0; | ||
| 240 | |||
| 241 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); | ||
| 242 | |||
| 243 | entry->action = -1; | ||
| 244 | while ((p = strsep(&rule, " \n")) != NULL) { | ||
| 245 | substring_t args[MAX_OPT_ARGS]; | ||
| 246 | int token; | ||
| 247 | unsigned long lnum; | ||
| 248 | |||
| 249 | if (result < 0) | ||
| 250 | break; | ||
| 251 | if (!*p) | ||
| 252 | continue; | ||
| 253 | token = match_token(p, policy_tokens, args); | ||
| 254 | switch (token) { | ||
| 255 | case Opt_measure: | ||
| 256 | audit_log_format(ab, "%s ", "measure"); | ||
| 257 | entry->action = MEASURE; | ||
| 258 | break; | ||
| 259 | case Opt_dont_measure: | ||
| 260 | audit_log_format(ab, "%s ", "dont_measure"); | ||
| 261 | entry->action = DONT_MEASURE; | ||
| 262 | break; | ||
| 263 | case Opt_func: | ||
| 264 | audit_log_format(ab, "func=%s ", args[0].from); | ||
| 265 | if (strcmp(args[0].from, "PATH_CHECK") == 0) | ||
| 266 | entry->func = PATH_CHECK; | ||
| 267 | else if (strcmp(args[0].from, "FILE_MMAP") == 0) | ||
| 268 | entry->func = FILE_MMAP; | ||
| 269 | else if (strcmp(args[0].from, "BPRM_CHECK") == 0) | ||
| 270 | entry->func = BPRM_CHECK; | ||
| 271 | else | ||
| 272 | result = -EINVAL; | ||
| 273 | if (!result) | ||
| 274 | entry->flags |= IMA_FUNC; | ||
| 275 | break; | ||
| 276 | case Opt_mask: | ||
| 277 | audit_log_format(ab, "mask=%s ", args[0].from); | ||
| 278 | if ((strcmp(args[0].from, "MAY_EXEC")) == 0) | ||
| 279 | entry->mask = MAY_EXEC; | ||
| 280 | else if (strcmp(args[0].from, "MAY_WRITE") == 0) | ||
| 281 | entry->mask = MAY_WRITE; | ||
| 282 | else if (strcmp(args[0].from, "MAY_READ") == 0) | ||
| 283 | entry->mask = MAY_READ; | ||
| 284 | else if (strcmp(args[0].from, "MAY_APPEND") == 0) | ||
| 285 | entry->mask = MAY_APPEND; | ||
| 286 | else | ||
| 287 | result = -EINVAL; | ||
| 288 | if (!result) | ||
| 289 | entry->flags |= IMA_MASK; | ||
| 290 | break; | ||
| 291 | case Opt_fsmagic: | ||
| 292 | audit_log_format(ab, "fsmagic=%s ", args[0].from); | ||
| 293 | result = strict_strtoul(args[0].from, 16, | ||
| 294 | &entry->fsmagic); | ||
| 295 | if (!result) | ||
| 296 | entry->flags |= IMA_FSMAGIC; | ||
| 297 | break; | ||
| 298 | case Opt_uid: | ||
| 299 | audit_log_format(ab, "uid=%s ", args[0].from); | ||
| 300 | result = strict_strtoul(args[0].from, 10, &lnum); | ||
| 301 | if (!result) { | ||
| 302 | entry->uid = (uid_t) lnum; | ||
| 303 | if (entry->uid != lnum) | ||
| 304 | result = -EINVAL; | ||
| 305 | else | ||
| 306 | entry->flags |= IMA_UID; | ||
| 307 | } | ||
| 308 | break; | ||
| 309 | case Opt_obj_user: | ||
| 310 | audit_log_format(ab, "obj_user=%s ", args[0].from); | ||
| 311 | result = ima_lsm_rule_init(entry, args[0].from, | ||
| 312 | LSM_OBJ_USER, | ||
| 313 | AUDIT_OBJ_USER); | ||
| 314 | break; | ||
| 315 | case Opt_obj_role: | ||
| 316 | audit_log_format(ab, "obj_role=%s ", args[0].from); | ||
| 317 | result = ima_lsm_rule_init(entry, args[0].from, | ||
| 318 | LSM_OBJ_ROLE, | ||
| 319 | AUDIT_OBJ_ROLE); | ||
| 320 | break; | ||
| 321 | case Opt_obj_type: | ||
| 322 | audit_log_format(ab, "obj_type=%s ", args[0].from); | ||
| 323 | result = ima_lsm_rule_init(entry, args[0].from, | ||
| 324 | LSM_OBJ_TYPE, | ||
| 325 | AUDIT_OBJ_TYPE); | ||
| 326 | break; | ||
| 327 | case Opt_subj_user: | ||
| 328 | audit_log_format(ab, "subj_user=%s ", args[0].from); | ||
| 329 | result = ima_lsm_rule_init(entry, args[0].from, | ||
| 330 | LSM_SUBJ_USER, | ||
| 331 | AUDIT_SUBJ_USER); | ||
| 332 | break; | ||
| 333 | case Opt_subj_role: | ||
| 334 | audit_log_format(ab, "subj_role=%s ", args[0].from); | ||
| 335 | result = ima_lsm_rule_init(entry, args[0].from, | ||
| 336 | LSM_SUBJ_ROLE, | ||
| 337 | AUDIT_SUBJ_ROLE); | ||
| 338 | break; | ||
| 339 | case Opt_subj_type: | ||
| 340 | audit_log_format(ab, "subj_type=%s ", args[0].from); | ||
| 341 | result = ima_lsm_rule_init(entry, args[0].from, | ||
| 342 | LSM_SUBJ_TYPE, | ||
| 343 | AUDIT_SUBJ_TYPE); | ||
| 344 | break; | ||
| 345 | case Opt_err: | ||
| 346 | audit_log_format(ab, "UNKNOWN=%s ", p); | ||
| 347 | break; | ||
| 348 | } | ||
| 349 | } | ||
| 350 | if (entry->action == UNKNOWN) | ||
| 351 | result = -EINVAL; | ||
| 352 | |||
| 353 | audit_log_format(ab, "res=%d", !result ? 0 : 1); | ||
| 354 | audit_log_end(ab); | ||
| 355 | return result; | ||
| 356 | } | ||
| 357 | |||
| 358 | /** | ||
| 359 | * ima_parse_add_rule - add a rule to measure_policy_rules | ||
| 360 | * @rule - ima measurement policy rule | ||
| 361 | * | ||
| 362 | * Uses a mutex to protect the policy list from multiple concurrent writers. | ||
| 363 | * Returns 0 on success, an error code on failure. | ||
| 364 | */ | ||
| 365 | int ima_parse_add_rule(char *rule) | ||
| 366 | { | ||
| 367 | const char *op = "update_policy"; | ||
| 368 | struct ima_measure_rule_entry *entry; | ||
| 369 | int result = 0; | ||
| 370 | int audit_info = 0; | ||
| 371 | |||
| 372 | /* Prevent installed policy from changing */ | ||
| 373 | if (ima_measure != &measure_default_rules) { | ||
| 374 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||
| 375 | NULL, op, "already exists", | ||
| 376 | -EACCES, audit_info); | ||
| 377 | return -EACCES; | ||
| 378 | } | ||
| 379 | |||
| 380 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
| 381 | if (!entry) { | ||
| 382 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||
| 383 | NULL, op, "-ENOMEM", -ENOMEM, audit_info); | ||
| 384 | return -ENOMEM; | ||
| 385 | } | ||
| 386 | |||
| 387 | INIT_LIST_HEAD(&entry->list); | ||
| 388 | |||
| 389 | result = ima_parse_rule(rule, entry); | ||
| 390 | if (!result) { | ||
| 391 | mutex_lock(&ima_measure_mutex); | ||
| 392 | list_add_tail(&entry->list, &measure_policy_rules); | ||
| 393 | mutex_unlock(&ima_measure_mutex); | ||
| 394 | } else { | ||
| 395 | kfree(entry); | ||
| 396 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||
| 397 | NULL, op, "invalid policy", result, | ||
| 398 | audit_info); | ||
| 399 | } | ||
| 400 | return result; | ||
| 401 | } | ||
| 402 | |||
| 403 | /* ima_delete_rules called to cleanup invalid policy */ | ||
| 404 | void ima_delete_rules(void) | ||
| 405 | { | ||
| 406 | struct ima_measure_rule_entry *entry, *tmp; | ||
| 407 | |||
| 408 | mutex_lock(&ima_measure_mutex); | ||
| 409 | list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) { | ||
| 410 | list_del(&entry->list); | ||
| 411 | kfree(entry); | ||
| 412 | } | ||
| 413 | mutex_unlock(&ima_measure_mutex); | ||
| 414 | } | ||
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c new file mode 100644 index 000000000000..7ec94314ac0c --- /dev/null +++ b/security/integrity/ima/ima_queue.c | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005,2006,2007,2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Serge Hallyn <serue@us.ibm.com> | ||
| 6 | * Reiner Sailer <sailer@watson.ibm.com> | ||
| 7 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | * | ||
| 14 | * File: ima_queue.c | ||
| 15 | * Implements queues that store template measurements and | ||
| 16 | * maintains aggregate over the stored measurements | ||
| 17 | * in the pre-configured TPM PCR (if available). | ||
| 18 | * The measurement list is append-only. No entry is | ||
| 19 | * ever removed or changed during the boot-cycle. | ||
| 20 | */ | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/rculist.h> | ||
| 23 | #include "ima.h" | ||
| 24 | |||
| 25 | LIST_HEAD(ima_measurements); /* list of all measurements */ | ||
| 26 | |||
| 27 | /* key: inode (before secure-hashing a file) */ | ||
| 28 | struct ima_h_table ima_htable = { | ||
| 29 | .len = ATOMIC_LONG_INIT(0), | ||
| 30 | .violations = ATOMIC_LONG_INIT(0), | ||
| 31 | .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT | ||
| 32 | }; | ||
| 33 | |||
| 34 | /* mutex protects atomicity of extending measurement list | ||
| 35 | * and extending the TPM PCR aggregate. Since tpm_extend can take | ||
| 36 | * long (and the tpm driver uses a mutex), we can't use the spinlock. | ||
| 37 | */ | ||
| 38 | static DEFINE_MUTEX(ima_extend_list_mutex); | ||
| 39 | |||
| 40 | /* lookup up the digest value in the hash table, and return the entry */ | ||
| 41 | static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value) | ||
| 42 | { | ||
| 43 | struct ima_queue_entry *qe, *ret = NULL; | ||
| 44 | unsigned int key; | ||
| 45 | struct hlist_node *pos; | ||
| 46 | int rc; | ||
| 47 | |||
| 48 | key = ima_hash_key(digest_value); | ||
| 49 | rcu_read_lock(); | ||
| 50 | hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) { | ||
| 51 | rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE); | ||
| 52 | if (rc == 0) { | ||
| 53 | ret = qe; | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | rcu_read_unlock(); | ||
| 58 | return ret; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* ima_add_template_entry helper function: | ||
| 62 | * - Add template entry to measurement list and hash table. | ||
| 63 | * | ||
| 64 | * (Called with ima_extend_list_mutex held.) | ||
| 65 | */ | ||
| 66 | static int ima_add_digest_entry(struct ima_template_entry *entry) | ||
| 67 | { | ||
| 68 | struct ima_queue_entry *qe; | ||
| 69 | unsigned int key; | ||
| 70 | |||
| 71 | qe = kmalloc(sizeof(*qe), GFP_KERNEL); | ||
| 72 | if (qe == NULL) { | ||
| 73 | pr_err("OUT OF MEMORY ERROR creating queue entry.\n"); | ||
| 74 | return -ENOMEM; | ||
| 75 | } | ||
| 76 | qe->entry = entry; | ||
| 77 | |||
| 78 | INIT_LIST_HEAD(&qe->later); | ||
| 79 | list_add_tail_rcu(&qe->later, &ima_measurements); | ||
| 80 | |||
| 81 | atomic_long_inc(&ima_htable.len); | ||
| 82 | key = ima_hash_key(entry->digest); | ||
| 83 | hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); | ||
| 84 | return 0; | ||
| 85 | } | ||
| 86 | |||
| 87 | static int ima_pcr_extend(const u8 *hash) | ||
| 88 | { | ||
| 89 | int result = 0; | ||
| 90 | |||
| 91 | if (!ima_used_chip) | ||
| 92 | return result; | ||
| 93 | |||
| 94 | result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash); | ||
| 95 | if (result != 0) | ||
| 96 | pr_err("Error Communicating to TPM chip\n"); | ||
| 97 | return result; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Add template entry to the measurement list and hash table, | ||
| 101 | * and extend the pcr. | ||
| 102 | */ | ||
| 103 | int ima_add_template_entry(struct ima_template_entry *entry, int violation, | ||
| 104 | const char *op, struct inode *inode) | ||
| 105 | { | ||
| 106 | u8 digest[IMA_DIGEST_SIZE]; | ||
| 107 | const char *audit_cause = "hash_added"; | ||
| 108 | int audit_info = 1; | ||
| 109 | int result = 0; | ||
| 110 | |||
| 111 | mutex_lock(&ima_extend_list_mutex); | ||
| 112 | if (!violation) { | ||
| 113 | memcpy(digest, entry->digest, sizeof digest); | ||
| 114 | if (ima_lookup_digest_entry(digest)) { | ||
| 115 | audit_cause = "hash_exists"; | ||
| 116 | goto out; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | result = ima_add_digest_entry(entry); | ||
| 121 | if (result < 0) { | ||
| 122 | audit_cause = "ENOMEM"; | ||
| 123 | audit_info = 0; | ||
| 124 | goto out; | ||
| 125 | } | ||
| 126 | |||
| 127 | if (violation) /* invalidate pcr */ | ||
| 128 | memset(digest, 0xff, sizeof digest); | ||
| 129 | |||
| 130 | result = ima_pcr_extend(digest); | ||
| 131 | if (result != 0) { | ||
| 132 | audit_cause = "TPM error"; | ||
| 133 | audit_info = 0; | ||
| 134 | } | ||
| 135 | out: | ||
| 136 | mutex_unlock(&ima_extend_list_mutex); | ||
| 137 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name, | ||
| 138 | op, audit_cause, result, audit_info); | ||
| 139 | return result; | ||
| 140 | } | ||
