diff options
-rw-r--r-- | security/integrity/ima/Makefile | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 5 | ||||
-rw-r--r-- | security/integrity/ima/ima_fs.c | 296 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 8 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 5 |
5 files changed, 314 insertions, 2 deletions
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 9d6bf973b9be..787c4cb916cd 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile | |||
@@ -5,5 +5,5 @@ | |||
5 | 5 | ||
6 | obj-$(CONFIG_IMA) += ima.o | 6 | obj-$(CONFIG_IMA) += ima.o |
7 | 7 | ||
8 | ima-y := ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ | 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 | 9 | ima_policy.o ima_iint.o ima_audit.o |
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index bfa72ed41b9b..9c280cc73004 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
@@ -67,6 +67,9 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, | |||
67 | /* Internal IMA function definitions */ | 67 | /* Internal IMA function definitions */ |
68 | void ima_iintcache_init(void); | 68 | void ima_iintcache_init(void); |
69 | int ima_init(void); | 69 | int ima_init(void); |
70 | void ima_cleanup(void); | ||
71 | int ima_fs_init(void); | ||
72 | void ima_fs_cleanup(void); | ||
70 | int ima_add_template_entry(struct ima_template_entry *entry, int violation, | 73 | int ima_add_template_entry(struct ima_template_entry *entry, int violation, |
71 | const char *op, struct inode *inode); | 74 | const char *op, struct inode *inode); |
72 | int ima_calc_hash(struct file *file, char *digest); | 75 | int ima_calc_hash(struct file *file, char *digest); |
@@ -115,6 +118,8 @@ void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, | |||
115 | const unsigned char *filename); | 118 | const unsigned char *filename); |
116 | int ima_store_template(struct ima_template_entry *entry, int violation, | 119 | int ima_store_template(struct ima_template_entry *entry, int violation, |
117 | struct inode *inode); | 120 | struct inode *inode); |
121 | void ima_template_show(struct seq_file *m, void *e, | ||
122 | enum ima_show_type show); | ||
118 | 123 | ||
119 | /* radix tree calls to lookup, insert, delete | 124 | /* radix tree calls to lookup, insert, delete |
120 | * integrity data associated with an inode. | 125 | * integrity data associated with an inode. |
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c new file mode 100644 index 000000000000..4f25be768b50 --- /dev/null +++ b/security/integrity/ima/ima_fs.c | |||
@@ -0,0 +1,296 @@ | |||
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 | |||
23 | #include "ima.h" | ||
24 | |||
25 | #define TMPBUFLEN 12 | ||
26 | static ssize_t ima_show_htable_value(char __user *buf, size_t count, | ||
27 | loff_t *ppos, atomic_long_t *val) | ||
28 | { | ||
29 | char tmpbuf[TMPBUFLEN]; | ||
30 | ssize_t len; | ||
31 | |||
32 | len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val)); | ||
33 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); | ||
34 | } | ||
35 | |||
36 | static ssize_t ima_show_htable_violations(struct file *filp, | ||
37 | char __user *buf, | ||
38 | size_t count, loff_t *ppos) | ||
39 | { | ||
40 | return ima_show_htable_value(buf, count, ppos, &ima_htable.violations); | ||
41 | } | ||
42 | |||
43 | static struct file_operations ima_htable_violations_ops = { | ||
44 | .read = ima_show_htable_violations | ||
45 | }; | ||
46 | |||
47 | static ssize_t ima_show_measurements_count(struct file *filp, | ||
48 | char __user *buf, | ||
49 | size_t count, loff_t *ppos) | ||
50 | { | ||
51 | return ima_show_htable_value(buf, count, ppos, &ima_htable.len); | ||
52 | |||
53 | } | ||
54 | |||
55 | static struct file_operations ima_measurements_count_ops = { | ||
56 | .read = ima_show_measurements_count | ||
57 | }; | ||
58 | |||
59 | /* returns pointer to hlist_node */ | ||
60 | static void *ima_measurements_start(struct seq_file *m, loff_t *pos) | ||
61 | { | ||
62 | loff_t l = *pos; | ||
63 | struct ima_queue_entry *qe; | ||
64 | |||
65 | /* we need a lock since pos could point beyond last element */ | ||
66 | rcu_read_lock(); | ||
67 | list_for_each_entry_rcu(qe, &ima_measurements, later) { | ||
68 | if (!l--) { | ||
69 | rcu_read_unlock(); | ||
70 | return qe; | ||
71 | } | ||
72 | } | ||
73 | rcu_read_unlock(); | ||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) | ||
78 | { | ||
79 | struct ima_queue_entry *qe = v; | ||
80 | |||
81 | /* lock protects when reading beyond last element | ||
82 | * against concurrent list-extension | ||
83 | */ | ||
84 | rcu_read_lock(); | ||
85 | qe = list_entry(rcu_dereference(qe->later.next), | ||
86 | struct ima_queue_entry, later); | ||
87 | rcu_read_unlock(); | ||
88 | (*pos)++; | ||
89 | |||
90 | return (&qe->later == &ima_measurements) ? NULL : qe; | ||
91 | } | ||
92 | |||
93 | static void ima_measurements_stop(struct seq_file *m, void *v) | ||
94 | { | ||
95 | } | ||
96 | |||
97 | static void ima_putc(struct seq_file *m, void *data, int datalen) | ||
98 | { | ||
99 | while (datalen--) | ||
100 | seq_putc(m, *(char *)data++); | ||
101 | } | ||
102 | |||
103 | /* print format: | ||
104 | * 32bit-le=pcr# | ||
105 | * char[20]=template digest | ||
106 | * 32bit-le=template name size | ||
107 | * char[n]=template name | ||
108 | * eventdata[n]=template specific data | ||
109 | */ | ||
110 | static int ima_measurements_show(struct seq_file *m, void *v) | ||
111 | { | ||
112 | /* the list never shrinks, so we don't need a lock here */ | ||
113 | struct ima_queue_entry *qe = v; | ||
114 | struct ima_template_entry *e; | ||
115 | int namelen; | ||
116 | u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; | ||
117 | |||
118 | /* get entry */ | ||
119 | e = qe->entry; | ||
120 | if (e == NULL) | ||
121 | return -1; | ||
122 | |||
123 | /* | ||
124 | * 1st: PCRIndex | ||
125 | * PCR used is always the same (config option) in | ||
126 | * little-endian format | ||
127 | */ | ||
128 | ima_putc(m, &pcr, sizeof pcr); | ||
129 | |||
130 | /* 2nd: template digest */ | ||
131 | ima_putc(m, e->digest, IMA_DIGEST_SIZE); | ||
132 | |||
133 | /* 3rd: template name size */ | ||
134 | namelen = strlen(e->template_name); | ||
135 | ima_putc(m, &namelen, sizeof namelen); | ||
136 | |||
137 | /* 4th: template name */ | ||
138 | ima_putc(m, e->template_name, namelen); | ||
139 | |||
140 | /* 5th: template specific data */ | ||
141 | ima_template_show(m, (struct ima_template_data *)&e->template, | ||
142 | IMA_SHOW_BINARY); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static struct seq_operations ima_measurments_seqops = { | ||
147 | .start = ima_measurements_start, | ||
148 | .next = ima_measurements_next, | ||
149 | .stop = ima_measurements_stop, | ||
150 | .show = ima_measurements_show | ||
151 | }; | ||
152 | |||
153 | static int ima_measurements_open(struct inode *inode, struct file *file) | ||
154 | { | ||
155 | return seq_open(file, &ima_measurments_seqops); | ||
156 | } | ||
157 | |||
158 | static struct file_operations ima_measurements_ops = { | ||
159 | .open = ima_measurements_open, | ||
160 | .read = seq_read, | ||
161 | .llseek = seq_lseek, | ||
162 | .release = seq_release, | ||
163 | }; | ||
164 | |||
165 | static void ima_print_digest(struct seq_file *m, u8 *digest) | ||
166 | { | ||
167 | int i; | ||
168 | |||
169 | for (i = 0; i < IMA_DIGEST_SIZE; i++) | ||
170 | seq_printf(m, "%02x", *(digest + i)); | ||
171 | } | ||
172 | |||
173 | void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show) | ||
174 | { | ||
175 | struct ima_template_data *entry = e; | ||
176 | int namelen; | ||
177 | |||
178 | switch (show) { | ||
179 | case IMA_SHOW_ASCII: | ||
180 | ima_print_digest(m, entry->digest); | ||
181 | seq_printf(m, " %s\n", entry->file_name); | ||
182 | break; | ||
183 | case IMA_SHOW_BINARY: | ||
184 | ima_putc(m, entry->digest, IMA_DIGEST_SIZE); | ||
185 | |||
186 | namelen = strlen(entry->file_name); | ||
187 | ima_putc(m, &namelen, sizeof namelen); | ||
188 | ima_putc(m, entry->file_name, namelen); | ||
189 | default: | ||
190 | break; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /* print in ascii */ | ||
195 | static int ima_ascii_measurements_show(struct seq_file *m, void *v) | ||
196 | { | ||
197 | /* the list never shrinks, so we don't need a lock here */ | ||
198 | struct ima_queue_entry *qe = v; | ||
199 | struct ima_template_entry *e; | ||
200 | |||
201 | /* get entry */ | ||
202 | e = qe->entry; | ||
203 | if (e == NULL) | ||
204 | return -1; | ||
205 | |||
206 | /* 1st: PCR used (config option) */ | ||
207 | seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX); | ||
208 | |||
209 | /* 2nd: SHA1 template hash */ | ||
210 | ima_print_digest(m, e->digest); | ||
211 | |||
212 | /* 3th: template name */ | ||
213 | seq_printf(m, " %s ", e->template_name); | ||
214 | |||
215 | /* 4th: template specific data */ | ||
216 | ima_template_show(m, (struct ima_template_data *)&e->template, | ||
217 | IMA_SHOW_ASCII); | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static struct seq_operations ima_ascii_measurements_seqops = { | ||
222 | .start = ima_measurements_start, | ||
223 | .next = ima_measurements_next, | ||
224 | .stop = ima_measurements_stop, | ||
225 | .show = ima_ascii_measurements_show | ||
226 | }; | ||
227 | |||
228 | static int ima_ascii_measurements_open(struct inode *inode, struct file *file) | ||
229 | { | ||
230 | return seq_open(file, &ima_ascii_measurements_seqops); | ||
231 | } | ||
232 | |||
233 | static struct file_operations ima_ascii_measurements_ops = { | ||
234 | .open = ima_ascii_measurements_open, | ||
235 | .read = seq_read, | ||
236 | .llseek = seq_lseek, | ||
237 | .release = seq_release, | ||
238 | }; | ||
239 | |||
240 | static struct dentry *ima_dir; | ||
241 | static struct dentry *binary_runtime_measurements; | ||
242 | static struct dentry *ascii_runtime_measurements; | ||
243 | static struct dentry *runtime_measurements_count; | ||
244 | static struct dentry *violations; | ||
245 | |||
246 | int ima_fs_init(void) | ||
247 | { | ||
248 | ima_dir = securityfs_create_dir("ima", NULL); | ||
249 | if (IS_ERR(ima_dir)) | ||
250 | return -1; | ||
251 | |||
252 | binary_runtime_measurements = | ||
253 | securityfs_create_file("binary_runtime_measurements", | ||
254 | S_IRUSR | S_IRGRP, ima_dir, NULL, | ||
255 | &ima_measurements_ops); | ||
256 | if (IS_ERR(binary_runtime_measurements)) | ||
257 | goto out; | ||
258 | |||
259 | ascii_runtime_measurements = | ||
260 | securityfs_create_file("ascii_runtime_measurements", | ||
261 | S_IRUSR | S_IRGRP, ima_dir, NULL, | ||
262 | &ima_ascii_measurements_ops); | ||
263 | if (IS_ERR(ascii_runtime_measurements)) | ||
264 | goto out; | ||
265 | |||
266 | runtime_measurements_count = | ||
267 | securityfs_create_file("runtime_measurements_count", | ||
268 | S_IRUSR | S_IRGRP, ima_dir, NULL, | ||
269 | &ima_measurements_count_ops); | ||
270 | if (IS_ERR(runtime_measurements_count)) | ||
271 | goto out; | ||
272 | |||
273 | violations = | ||
274 | securityfs_create_file("violations", S_IRUSR | S_IRGRP, | ||
275 | ima_dir, NULL, &ima_htable_violations_ops); | ||
276 | if (IS_ERR(violations)) | ||
277 | goto out; | ||
278 | |||
279 | return 0; | ||
280 | |||
281 | out: | ||
282 | securityfs_remove(runtime_measurements_count); | ||
283 | securityfs_remove(ascii_runtime_measurements); | ||
284 | securityfs_remove(binary_runtime_measurements); | ||
285 | securityfs_remove(ima_dir); | ||
286 | return -1; | ||
287 | } | ||
288 | |||
289 | void __exit ima_fs_cleanup(void) | ||
290 | { | ||
291 | securityfs_remove(violations); | ||
292 | securityfs_remove(runtime_measurements_count); | ||
293 | securityfs_remove(ascii_runtime_measurements); | ||
294 | securityfs_remove(binary_runtime_measurements); | ||
295 | securityfs_remove(ima_dir); | ||
296 | } | ||
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index e0f02e328d77..cf227dbfac2c 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c | |||
@@ -86,5 +86,11 @@ int ima_init(void) | |||
86 | 86 | ||
87 | ima_add_boot_aggregate(); /* boot aggregate must be first entry */ | 87 | ima_add_boot_aggregate(); /* boot aggregate must be first entry */ |
88 | ima_init_policy(); | 88 | ima_init_policy(); |
89 | return 0; | 89 | |
90 | return ima_fs_init(); | ||
91 | } | ||
92 | |||
93 | void __exit ima_cleanup(void) | ||
94 | { | ||
95 | ima_fs_cleanup(); | ||
90 | } | 96 | } |
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 53cee4c512ce..871e356e8d6c 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
@@ -274,6 +274,11 @@ static int __init init_ima(void) | |||
274 | return error; | 274 | return error; |
275 | } | 275 | } |
276 | 276 | ||
277 | static void __exit cleanup_ima(void) | ||
278 | { | ||
279 | ima_cleanup(); | ||
280 | } | ||
281 | |||
277 | late_initcall(init_ima); /* Start IMA after the TPM is available */ | 282 | late_initcall(init_ima); /* Start IMA after the TPM is available */ |
278 | 283 | ||
279 | MODULE_DESCRIPTION("Integrity Measurement Architecture"); | 284 | MODULE_DESCRIPTION("Integrity Measurement Architecture"); |