diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/Kconfig | 5 | ||||
-rw-r--r-- | security/Makefile | 4 | ||||
-rw-r--r-- | security/integrity/ima/Kconfig | 49 | ||||
-rw-r--r-- | security/integrity/ima/Makefile | 9 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 135 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 190 | ||||
-rw-r--r-- | security/integrity/ima/ima_audit.c | 78 | ||||
-rw-r--r-- | security/integrity/ima/ima_crypto.c | 140 | ||||
-rw-r--r-- | security/integrity/ima/ima_iint.c | 185 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 90 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 280 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 126 | ||||
-rw-r--r-- | security/integrity/ima/ima_queue.c | 140 |
13 files changed, 1430 insertions, 1 deletions
diff --git a/security/Kconfig b/security/Kconfig index d9f47ce7e207..a79b23f73d03 100644 --- a/security/Kconfig +++ b/security/Kconfig | |||
@@ -55,7 +55,8 @@ config SECURITYFS | |||
55 | bool "Enable the securityfs filesystem" | 55 | bool "Enable the securityfs filesystem" |
56 | help | 56 | help |
57 | This will build the securityfs filesystem. It is currently used by | 57 | This will build the securityfs filesystem. It is currently used by |
58 | the TPM bios character driver. It is not used by SELinux or SMACK. | 58 | the TPM bios character driver and IMA, an integrity provider. It is |
59 | not used by SELinux or SMACK. | ||
59 | 60 | ||
60 | If you are unsure how to answer this question, answer N. | 61 | If you are unsure how to answer this question, answer N. |
61 | 62 | ||
@@ -126,5 +127,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR | |||
126 | source security/selinux/Kconfig | 127 | source security/selinux/Kconfig |
127 | source security/smack/Kconfig | 128 | source security/smack/Kconfig |
128 | 129 | ||
130 | source security/integrity/ima/Kconfig | ||
131 | |||
129 | endmenu | 132 | endmenu |
130 | 133 | ||
diff --git a/security/Makefile b/security/Makefile index c05c127fff9a..595536cbffb2 100644 --- a/security/Makefile +++ b/security/Makefile | |||
@@ -17,3 +17,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o | |||
17 | obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o | 17 | obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o |
18 | obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o | 18 | obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o |
19 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o | 19 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o |
20 | |||
21 | # Object integrity file lists | ||
22 | subdir-$(CONFIG_IMA) += integrity/ima | ||
23 | obj-$(CONFIG_IMA) += integrity/ima/built-in.o | ||
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig new file mode 100644 index 000000000000..2a761c8ac996 --- /dev/null +++ b/security/integrity/ima/Kconfig | |||
@@ -0,0 +1,49 @@ | |||
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 | |||
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile new file mode 100644 index 000000000000..9d6bf973b9be --- /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_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..bfa72ed41b9b --- /dev/null +++ b/security/integrity/ima/ima.h | |||
@@ -0,0 +1,135 @@ | |||
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 | 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 | int ima_add_template_entry(struct ima_template_entry *entry, int violation, | ||
71 | const char *op, struct inode *inode); | ||
72 | int ima_calc_hash(struct file *file, char *digest); | ||
73 | int ima_calc_template_hash(int template_len, void *template, char *digest); | ||
74 | int ima_calc_boot_aggregate(char *digest); | ||
75 | void ima_add_violation(struct inode *inode, const unsigned char *filename, | ||
76 | const char *op, const char *cause); | ||
77 | |||
78 | /* | ||
79 | * used to protect h_table and sha_table | ||
80 | */ | ||
81 | extern spinlock_t ima_queue_lock; | ||
82 | |||
83 | struct ima_h_table { | ||
84 | atomic_long_t len; /* number of stored measurements in the list */ | ||
85 | atomic_long_t violations; | ||
86 | struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; | ||
87 | }; | ||
88 | extern struct ima_h_table ima_htable; | ||
89 | |||
90 | static inline unsigned long ima_hash_key(u8 *digest) | ||
91 | { | ||
92 | return hash_long(*digest, IMA_HASH_BITS); | ||
93 | } | ||
94 | |||
95 | /* iint cache flags */ | ||
96 | #define IMA_MEASURED 1 | ||
97 | |||
98 | /* integrity data associated with an inode */ | ||
99 | struct ima_iint_cache { | ||
100 | u64 version; /* track inode changes */ | ||
101 | unsigned long flags; | ||
102 | u8 digest[IMA_DIGEST_SIZE]; | ||
103 | struct mutex mutex; /* protects: version, flags, digest */ | ||
104 | long readcount; /* measured files readcount */ | ||
105 | long writecount; /* measured files writecount */ | ||
106 | struct kref refcount; /* ima_iint_cache reference count */ | ||
107 | struct rcu_head rcu; | ||
108 | }; | ||
109 | |||
110 | /* LIM API function definitions */ | ||
111 | int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode, | ||
112 | int mask, int function); | ||
113 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file); | ||
114 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, | ||
115 | const unsigned char *filename); | ||
116 | int ima_store_template(struct ima_template_entry *entry, int violation, | ||
117 | struct inode *inode); | ||
118 | |||
119 | /* radix tree calls to lookup, insert, delete | ||
120 | * integrity data associated with an inode. | ||
121 | */ | ||
122 | struct ima_iint_cache *ima_iint_insert(struct inode *inode); | ||
123 | struct ima_iint_cache *ima_iint_find_get(struct inode *inode); | ||
124 | struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode); | ||
125 | void ima_iint_delete(struct inode *inode); | ||
126 | void iint_free(struct kref *kref); | ||
127 | void iint_rcu_free(struct rcu_head *rcu); | ||
128 | |||
129 | /* IMA policy related functions */ | ||
130 | enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK }; | ||
131 | |||
132 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask); | ||
133 | void ima_init_policy(void); | ||
134 | void ima_update_policy(void); | ||
135 | #endif | ||
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c new file mode 100644 index 000000000000..a148a25804f6 --- /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 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..8a0f1e23ccf1 --- /dev/null +++ b/security/integrity/ima/ima_audit.c | |||
@@ -0,0 +1,78 @@ | |||
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; | ||
26 | char *op; | ||
27 | |||
28 | rc = strict_strtoul(str, 0, &audit); | ||
29 | if (rc || audit > 1) | ||
30 | printk(KERN_INFO "ima: invalid ima_audit value\n"); | ||
31 | else | ||
32 | ima_audit = audit; | ||
33 | op = ima_audit ? "ima_audit_enabled" : "ima_audit_not_enabled"; | ||
34 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, NULL, op, 0, 0); | ||
35 | return 1; | ||
36 | } | ||
37 | __setup("ima_audit=", ima_audit_setup); | ||
38 | #endif | ||
39 | |||
40 | void integrity_audit_msg(int audit_msgno, struct inode *inode, | ||
41 | const unsigned char *fname, const char *op, | ||
42 | const char *cause, int result, int audit_info) | ||
43 | { | ||
44 | struct audit_buffer *ab; | ||
45 | |||
46 | if (!ima_audit && audit_info == 1) /* Skip informational messages */ | ||
47 | return; | ||
48 | |||
49 | ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); | ||
50 | audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u", | ||
51 | current->pid, current->cred->uid, | ||
52 | audit_get_loginuid(current)); | ||
53 | audit_log_task_context(ab); | ||
54 | switch (audit_msgno) { | ||
55 | case AUDIT_INTEGRITY_DATA: | ||
56 | case AUDIT_INTEGRITY_METADATA: | ||
57 | case AUDIT_INTEGRITY_PCR: | ||
58 | audit_log_format(ab, " op=%s cause=%s", op, cause); | ||
59 | break; | ||
60 | case AUDIT_INTEGRITY_HASH: | ||
61 | audit_log_format(ab, " op=%s hash=%s", op, cause); | ||
62 | break; | ||
63 | case AUDIT_INTEGRITY_STATUS: | ||
64 | default: | ||
65 | audit_log_format(ab, " op=%s", op); | ||
66 | } | ||
67 | audit_log_format(ab, " comm="); | ||
68 | audit_log_untrustedstring(ab, current->comm); | ||
69 | if (fname) { | ||
70 | audit_log_format(ab, " name="); | ||
71 | audit_log_untrustedstring(ab, fname); | ||
72 | } | ||
73 | if (inode) | ||
74 | audit_log_format(ab, " dev=%s ino=%lu", | ||
75 | inode->i_sb->s_id, inode->i_ino); | ||
76 | audit_log_format(ab, " res=%d", result); | ||
77 | audit_log_end(ab); | ||
78 | } | ||
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c new file mode 100644 index 000000000000..c2a46e40999d --- /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_set_buf(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_set_buf(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_iint.c b/security/integrity/ima/ima_iint.c new file mode 100644 index 000000000000..750db3c993a7 --- /dev/null +++ b/security/integrity/ima/ima_iint.c | |||
@@ -0,0 +1,185 @@ | |||
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 | iint = radix_tree_lookup(&ima_iint_store, | ||
77 | (unsigned long)inode); | ||
78 | } else | ||
79 | iint = NULL; | ||
80 | } | ||
81 | radix_tree_preload_end(); | ||
82 | return iint; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * ima_inode_alloc - allocate an iint associated with an inode | ||
87 | * @inode: pointer to the inode | ||
88 | * | ||
89 | * Return 0 on success, 1 on failure. | ||
90 | */ | ||
91 | int ima_inode_alloc(struct inode *inode) | ||
92 | { | ||
93 | struct ima_iint_cache *iint; | ||
94 | |||
95 | if (!ima_initialized) | ||
96 | return 0; | ||
97 | |||
98 | iint = ima_iint_insert(inode); | ||
99 | if (!iint) | ||
100 | return 1; | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | /* ima_iint_find_insert_get - get the iint associated with an inode | ||
105 | * | ||
106 | * Most insertions are done at inode_alloc, except those allocated | ||
107 | * before late_initcall. When the iint does not exist, allocate it, | ||
108 | * initialize and insert it, and increment the iint refcount. | ||
109 | * | ||
110 | * (Can't initialize at security_initcall before any inodes are | ||
111 | * allocated, got to wait at least until proc_init.) | ||
112 | * | ||
113 | * Return the iint. | ||
114 | */ | ||
115 | struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode) | ||
116 | { | ||
117 | struct ima_iint_cache *iint = NULL; | ||
118 | |||
119 | iint = ima_iint_find_get(inode); | ||
120 | if (iint) | ||
121 | return iint; | ||
122 | |||
123 | iint = ima_iint_insert(inode); | ||
124 | if (iint) | ||
125 | kref_get(&iint->refcount); | ||
126 | |||
127 | return iint; | ||
128 | } | ||
129 | |||
130 | /* iint_free - called when the iint refcount goes to zero */ | ||
131 | void iint_free(struct kref *kref) | ||
132 | { | ||
133 | struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache, | ||
134 | refcount); | ||
135 | iint->version = 0; | ||
136 | iint->flags = 0UL; | ||
137 | kref_set(&iint->refcount, 1); | ||
138 | kmem_cache_free(iint_cache, iint); | ||
139 | } | ||
140 | |||
141 | void iint_rcu_free(struct rcu_head *rcu_head) | ||
142 | { | ||
143 | struct ima_iint_cache *iint = container_of(rcu_head, | ||
144 | struct ima_iint_cache, rcu); | ||
145 | kref_put(&iint->refcount, iint_free); | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * ima_iint_delete - called on integrity_inode_free | ||
150 | * @inode: pointer to the inode | ||
151 | * | ||
152 | * Free the integrity information(iint) associated with an inode. | ||
153 | */ | ||
154 | void ima_iint_delete(struct inode *inode) | ||
155 | { | ||
156 | struct ima_iint_cache *iint; | ||
157 | |||
158 | if (!ima_initialized) | ||
159 | return; | ||
160 | spin_lock(&ima_iint_lock); | ||
161 | iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode); | ||
162 | spin_unlock(&ima_iint_lock); | ||
163 | if (iint) | ||
164 | call_rcu(&iint->rcu, iint_rcu_free); | ||
165 | } | ||
166 | |||
167 | static void init_once(void *foo) | ||
168 | { | ||
169 | struct ima_iint_cache *iint = foo; | ||
170 | |||
171 | memset(iint, 0, sizeof *iint); | ||
172 | iint->version = 0; | ||
173 | iint->flags = 0UL; | ||
174 | mutex_init(&iint->mutex); | ||
175 | iint->readcount = 0; | ||
176 | iint->writecount = 0; | ||
177 | kref_set(&iint->refcount, 1); | ||
178 | } | ||
179 | |||
180 | void ima_iintcache_init(void) | ||
181 | { | ||
182 | iint_cache = | ||
183 | kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0, | ||
184 | SLAB_PANIC, init_once); | ||
185 | } | ||
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c new file mode 100644 index 000000000000..e0f02e328d77 --- /dev/null +++ b/security/integrity/ima/ima_init.c | |||
@@ -0,0 +1,90 @@ | |||
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 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 | return 0; | ||
90 | } | ||
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c new file mode 100644 index 000000000000..53cee4c512ce --- /dev/null +++ b/security/integrity/ima/ima_main.c | |||
@@ -0,0 +1,280 @@ | |||
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 ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
70 | iint->readcount--; | ||
71 | |||
72 | if (file->f_mode & FMODE_WRITE) { | ||
73 | iint->writecount--; | ||
74 | if (iint->writecount == 0) { | ||
75 | if (iint->version != inode->i_version) | ||
76 | iint->flags &= ~IMA_MEASURED; | ||
77 | } | ||
78 | } | ||
79 | mutex_unlock(&iint->mutex); | ||
80 | kref_put(&iint->refcount, iint_free); | ||
81 | } | ||
82 | |||
83 | /* ima_read_write_check - reflect possible reading/writing errors in the PCR. | ||
84 | * | ||
85 | * When opening a file for read, if the file is already open for write, | ||
86 | * the file could change, resulting in a file measurement error. | ||
87 | * | ||
88 | * Opening a file for write, if the file is already open for read, results | ||
89 | * in a time of measure, time of use (ToMToU) error. | ||
90 | * | ||
91 | * In either case invalidate the PCR. | ||
92 | */ | ||
93 | enum iint_pcr_error { TOMTOU, OPEN_WRITERS }; | ||
94 | static void ima_read_write_check(enum iint_pcr_error error, | ||
95 | struct ima_iint_cache *iint, | ||
96 | struct inode *inode, | ||
97 | const unsigned char *filename) | ||
98 | { | ||
99 | switch (error) { | ||
100 | case TOMTOU: | ||
101 | if (iint->readcount > 0) | ||
102 | ima_add_violation(inode, filename, "invalid_pcr", | ||
103 | "ToMToU"); | ||
104 | break; | ||
105 | case OPEN_WRITERS: | ||
106 | if (iint->writecount > 0) | ||
107 | ima_add_violation(inode, filename, "invalid_pcr", | ||
108 | "open_writers"); | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static int get_path_measurement(struct ima_iint_cache *iint, struct file *file, | ||
114 | const unsigned char *filename) | ||
115 | { | ||
116 | int rc = 0; | ||
117 | |||
118 | if (IS_ERR(file)) { | ||
119 | pr_info("%s dentry_open failed\n", filename); | ||
120 | return rc; | ||
121 | } | ||
122 | iint->readcount++; | ||
123 | |||
124 | rc = ima_collect_measurement(iint, file); | ||
125 | if (!rc) | ||
126 | ima_store_measurement(iint, file, filename); | ||
127 | return rc; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * ima_path_check - based on policy, collect/store measurement. | ||
132 | * @path: contains a pointer to the path to be measured | ||
133 | * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE | ||
134 | * | ||
135 | * Measure the file being open for readonly, based on the | ||
136 | * ima_must_measure() policy decision. | ||
137 | * | ||
138 | * Keep read/write counters for all files, but only | ||
139 | * invalidate the PCR for measured files: | ||
140 | * - Opening a file for write when already open for read, | ||
141 | * results in a time of measure, time of use (ToMToU) error. | ||
142 | * - Opening a file for read when already open for write, | ||
143 | * could result in a file measurement error. | ||
144 | * | ||
145 | * Return 0 on success, an error code on failure. | ||
146 | * (Based on the results of appraise_measurement().) | ||
147 | */ | ||
148 | int ima_path_check(struct path *path, int mask) | ||
149 | { | ||
150 | struct inode *inode = path->dentry->d_inode; | ||
151 | struct ima_iint_cache *iint; | ||
152 | struct file *file = NULL; | ||
153 | int rc; | ||
154 | |||
155 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
156 | return 0; | ||
157 | iint = ima_iint_find_insert_get(inode); | ||
158 | if (!iint) | ||
159 | return 0; | ||
160 | |||
161 | mutex_lock(&iint->mutex); | ||
162 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
163 | iint->writecount++; | ||
164 | else if (mask & (MAY_READ | MAY_EXEC)) | ||
165 | iint->readcount++; | ||
166 | |||
167 | rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK); | ||
168 | if (rc < 0) | ||
169 | goto out; | ||
170 | |||
171 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
172 | ima_read_write_check(TOMTOU, iint, inode, | ||
173 | path->dentry->d_name.name); | ||
174 | |||
175 | if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ) | ||
176 | goto out; | ||
177 | |||
178 | ima_read_write_check(OPEN_WRITERS, iint, inode, | ||
179 | path->dentry->d_name.name); | ||
180 | if (!(iint->flags & IMA_MEASURED)) { | ||
181 | struct dentry *dentry = dget(path->dentry); | ||
182 | struct vfsmount *mnt = mntget(path->mnt); | ||
183 | |||
184 | file = dentry_open(dentry, mnt, O_RDONLY, current->cred); | ||
185 | rc = get_path_measurement(iint, file, dentry->d_name.name); | ||
186 | } | ||
187 | out: | ||
188 | mutex_unlock(&iint->mutex); | ||
189 | if (file) | ||
190 | fput(file); | ||
191 | kref_put(&iint->refcount, iint_free); | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int process_measurement(struct file *file, const unsigned char *filename, | ||
196 | int mask, int function) | ||
197 | { | ||
198 | struct inode *inode = file->f_dentry->d_inode; | ||
199 | struct ima_iint_cache *iint; | ||
200 | int rc; | ||
201 | |||
202 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
203 | return 0; | ||
204 | iint = ima_iint_find_insert_get(inode); | ||
205 | if (!iint) | ||
206 | return -ENOMEM; | ||
207 | |||
208 | mutex_lock(&iint->mutex); | ||
209 | rc = ima_must_measure(iint, inode, mask, function); | ||
210 | if (rc != 0) | ||
211 | goto out; | ||
212 | |||
213 | rc = ima_collect_measurement(iint, file); | ||
214 | if (!rc) | ||
215 | ima_store_measurement(iint, file, filename); | ||
216 | out: | ||
217 | mutex_unlock(&iint->mutex); | ||
218 | kref_put(&iint->refcount, iint_free); | ||
219 | return rc; | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * ima_file_mmap - based on policy, collect/store measurement. | ||
224 | * @file: pointer to the file to be measured (May be NULL) | ||
225 | * @prot: contains the protection that will be applied by the kernel. | ||
226 | * | ||
227 | * Measure files being mmapped executable based on the ima_must_measure() | ||
228 | * policy decision. | ||
229 | * | ||
230 | * Return 0 on success, an error code on failure. | ||
231 | * (Based on the results of appraise_measurement().) | ||
232 | */ | ||
233 | int ima_file_mmap(struct file *file, unsigned long prot) | ||
234 | { | ||
235 | int rc; | ||
236 | |||
237 | if (!file) | ||
238 | return 0; | ||
239 | if (prot & PROT_EXEC) | ||
240 | rc = process_measurement(file, file->f_dentry->d_name.name, | ||
241 | MAY_EXEC, FILE_MMAP); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * ima_bprm_check - based on policy, collect/store measurement. | ||
247 | * @bprm: contains the linux_binprm structure | ||
248 | * | ||
249 | * The OS protects against an executable file, already open for write, | ||
250 | * from being executed in deny_write_access() and an executable file, | ||
251 | * already open for execute, from being modified in get_write_access(). | ||
252 | * So we can be certain that what we verify and measure here is actually | ||
253 | * what is being executed. | ||
254 | * | ||
255 | * Return 0 on success, an error code on failure. | ||
256 | * (Based on the results of appraise_measurement().) | ||
257 | */ | ||
258 | int ima_bprm_check(struct linux_binprm *bprm) | ||
259 | { | ||
260 | int rc; | ||
261 | |||
262 | rc = process_measurement(bprm->file, bprm->filename, | ||
263 | MAY_EXEC, BPRM_CHECK); | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int __init init_ima(void) | ||
268 | { | ||
269 | int error; | ||
270 | |||
271 | ima_iintcache_init(); | ||
272 | error = ima_init(); | ||
273 | ima_initialized = 1; | ||
274 | return error; | ||
275 | } | ||
276 | |||
277 | late_initcall(init_ima); /* Start IMA after the TPM is available */ | ||
278 | |||
279 | MODULE_DESCRIPTION("Integrity Measurement Architecture"); | ||
280 | 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..7c3d1ffb1472 --- /dev/null +++ b/security/integrity/ima/ima_policy.c | |||
@@ -0,0 +1,126 @@ | |||
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/audit.h> | ||
16 | #include <linux/security.h> | ||
17 | #include <linux/magic.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 { DONT_MEASURE, MEASURE }; | ||
28 | |||
29 | struct ima_measure_rule_entry { | ||
30 | struct list_head list; | ||
31 | enum ima_action action; | ||
32 | unsigned int flags; | ||
33 | enum ima_hooks func; | ||
34 | int mask; | ||
35 | unsigned long fsmagic; | ||
36 | uid_t uid; | ||
37 | }; | ||
38 | |||
39 | static struct ima_measure_rule_entry default_rules[] = { | ||
40 | {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC, | ||
41 | .flags = IMA_FSMAGIC}, | ||
42 | {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
43 | {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
44 | {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
45 | {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC, | ||
46 | .flags = IMA_FSMAGIC}, | ||
47 | {.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC}, | ||
48 | {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC, | ||
49 | .flags = IMA_FUNC | IMA_MASK}, | ||
50 | {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, | ||
51 | .flags = IMA_FUNC | IMA_MASK}, | ||
52 | {.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0, | ||
53 | .flags = IMA_FUNC | IMA_MASK | IMA_UID} | ||
54 | }; | ||
55 | |||
56 | static LIST_HEAD(measure_default_rules); | ||
57 | static struct list_head *ima_measure; | ||
58 | |||
59 | /** | ||
60 | * ima_match_rules - determine whether an inode matches the measure rule. | ||
61 | * @rule: a pointer to a rule | ||
62 | * @inode: a pointer to an inode | ||
63 | * @func: LIM hook identifier | ||
64 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | ||
65 | * | ||
66 | * Returns true on rule match, false on failure. | ||
67 | */ | ||
68 | static bool ima_match_rules(struct ima_measure_rule_entry *rule, | ||
69 | struct inode *inode, enum ima_hooks func, int mask) | ||
70 | { | ||
71 | struct task_struct *tsk = current; | ||
72 | |||
73 | if ((rule->flags & IMA_FUNC) && rule->func != func) | ||
74 | return false; | ||
75 | if ((rule->flags & IMA_MASK) && rule->mask != mask) | ||
76 | return false; | ||
77 | if ((rule->flags & IMA_FSMAGIC) | ||
78 | && rule->fsmagic != inode->i_sb->s_magic) | ||
79 | return false; | ||
80 | if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid) | ||
81 | return false; | ||
82 | return true; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * ima_match_policy - decision based on LSM and other conditions | ||
87 | * @inode: pointer to an inode for which the policy decision is being made | ||
88 | * @func: IMA hook identifier | ||
89 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | ||
90 | * | ||
91 | * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) | ||
92 | * conditions. | ||
93 | * | ||
94 | * (There is no need for locking when walking the policy list, | ||
95 | * as elements in the list are never deleted, nor does the list | ||
96 | * change.) | ||
97 | */ | ||
98 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) | ||
99 | { | ||
100 | struct ima_measure_rule_entry *entry; | ||
101 | |||
102 | list_for_each_entry(entry, ima_measure, list) { | ||
103 | bool rc; | ||
104 | |||
105 | rc = ima_match_rules(entry, inode, func, mask); | ||
106 | if (rc) | ||
107 | return entry->action; | ||
108 | } | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * ima_init_policy - initialize the default measure rules. | ||
114 | * | ||
115 | * (Could use the default_rules directly, but in policy patch | ||
116 | * ima_measure points to either the measure_default_rules or the | ||
117 | * the new measure_policy_rules.) | ||
118 | */ | ||
119 | void ima_init_policy(void) | ||
120 | { | ||
121 | int i; | ||
122 | |||
123 | for (i = 0; i < ARRAY_SIZE(default_rules); i++) | ||
124 | list_add_tail(&default_rules[i].list, &measure_default_rules); | ||
125 | ima_measure = &measure_default_rules; | ||
126 | } | ||
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 | } | ||