diff options
author | Paul Moore <pmoore@redhat.com> | 2014-01-28 14:44:16 -0500 |
---|---|---|
committer | Paul Moore <pmoore@redhat.com> | 2014-02-05 10:39:48 -0500 |
commit | 825e587af2e90e9b953849f3347a01d8f383d577 (patch) | |
tree | e48942a05882da47544e179c6a0c920e00137a6a /security/integrity/ima/ima_template_lib.c | |
parent | 8ed814602876bec9bad2649ca17f34b499357a1c (diff) | |
parent | d8ec26d7f8287f5788a494f56e8814210f0e64be (diff) |
Merge tag 'v3.13' into stable-3.14
Linux 3.13
Conflicts:
security/selinux/hooks.c
Trivial merge issue in selinux_inet_conn_request() likely due to me
including patches that I sent to the stable folks in my next tree
resulting in the patch hitting twice (I think). Thankfully it was an
easy fix this time, but regardless, lesson learned, I will not do that
again.
Diffstat (limited to 'security/integrity/ima/ima_template_lib.c')
-rw-r--r-- | security/integrity/ima/ima_template_lib.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c new file mode 100644 index 000000000000..c38adcc910fb --- /dev/null +++ b/security/integrity/ima/ima_template_lib.c | |||
@@ -0,0 +1,351 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Politecnico di Torino, Italy | ||
3 | * TORSEC group -- http://security.polito.it | ||
4 | * | ||
5 | * Author: Roberto Sassu <roberto.sassu@polito.it> | ||
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_template_lib.c | ||
13 | * Library of supported template fields. | ||
14 | */ | ||
15 | #include <crypto/hash_info.h> | ||
16 | |||
17 | #include "ima_template_lib.h" | ||
18 | |||
19 | static bool ima_template_hash_algo_allowed(u8 algo) | ||
20 | { | ||
21 | if (algo == HASH_ALGO_SHA1 || algo == HASH_ALGO_MD5) | ||
22 | return true; | ||
23 | |||
24 | return false; | ||
25 | } | ||
26 | |||
27 | enum data_formats { | ||
28 | DATA_FMT_DIGEST = 0, | ||
29 | DATA_FMT_DIGEST_WITH_ALGO, | ||
30 | DATA_FMT_EVENT_NAME, | ||
31 | DATA_FMT_STRING, | ||
32 | DATA_FMT_HEX | ||
33 | }; | ||
34 | |||
35 | static int ima_write_template_field_data(const void *data, const u32 datalen, | ||
36 | enum data_formats datafmt, | ||
37 | struct ima_field_data *field_data) | ||
38 | { | ||
39 | u8 *buf, *buf_ptr; | ||
40 | u32 buflen; | ||
41 | |||
42 | switch (datafmt) { | ||
43 | case DATA_FMT_EVENT_NAME: | ||
44 | buflen = IMA_EVENT_NAME_LEN_MAX + 1; | ||
45 | break; | ||
46 | case DATA_FMT_STRING: | ||
47 | buflen = datalen + 1; | ||
48 | break; | ||
49 | default: | ||
50 | buflen = datalen; | ||
51 | } | ||
52 | |||
53 | buf = kzalloc(buflen, GFP_KERNEL); | ||
54 | if (!buf) | ||
55 | return -ENOMEM; | ||
56 | |||
57 | memcpy(buf, data, datalen); | ||
58 | |||
59 | /* | ||
60 | * Replace all space characters with underscore for event names and | ||
61 | * strings. This avoid that, during the parsing of a measurements list, | ||
62 | * filenames with spaces or that end with the suffix ' (deleted)' are | ||
63 | * split into multiple template fields (the space is the delimitator | ||
64 | * character for measurements lists in ASCII format). | ||
65 | */ | ||
66 | if (datafmt == DATA_FMT_EVENT_NAME || datafmt == DATA_FMT_STRING) { | ||
67 | for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++) | ||
68 | if (*buf_ptr == ' ') | ||
69 | *buf_ptr = '_'; | ||
70 | } | ||
71 | |||
72 | field_data->data = buf; | ||
73 | field_data->len = buflen; | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static void ima_show_template_data_ascii(struct seq_file *m, | ||
78 | enum ima_show_type show, | ||
79 | enum data_formats datafmt, | ||
80 | struct ima_field_data *field_data) | ||
81 | { | ||
82 | u8 *buf_ptr = field_data->data, buflen = field_data->len; | ||
83 | |||
84 | switch (datafmt) { | ||
85 | case DATA_FMT_DIGEST_WITH_ALGO: | ||
86 | buf_ptr = strnchr(field_data->data, buflen, ':'); | ||
87 | if (buf_ptr != field_data->data) | ||
88 | seq_printf(m, "%s", field_data->data); | ||
89 | |||
90 | /* skip ':' and '\0' */ | ||
91 | buf_ptr += 2; | ||
92 | buflen -= buf_ptr - field_data->data; | ||
93 | case DATA_FMT_DIGEST: | ||
94 | case DATA_FMT_HEX: | ||
95 | if (!buflen) | ||
96 | break; | ||
97 | ima_print_digest(m, buf_ptr, buflen); | ||
98 | break; | ||
99 | case DATA_FMT_STRING: | ||
100 | seq_printf(m, "%s", buf_ptr); | ||
101 | break; | ||
102 | default: | ||
103 | break; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static void ima_show_template_data_binary(struct seq_file *m, | ||
108 | enum ima_show_type show, | ||
109 | enum data_formats datafmt, | ||
110 | struct ima_field_data *field_data) | ||
111 | { | ||
112 | if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) | ||
113 | ima_putc(m, &field_data->len, sizeof(u32)); | ||
114 | |||
115 | if (!field_data->len) | ||
116 | return; | ||
117 | |||
118 | ima_putc(m, field_data->data, field_data->len); | ||
119 | } | ||
120 | |||
121 | static void ima_show_template_field_data(struct seq_file *m, | ||
122 | enum ima_show_type show, | ||
123 | enum data_formats datafmt, | ||
124 | struct ima_field_data *field_data) | ||
125 | { | ||
126 | switch (show) { | ||
127 | case IMA_SHOW_ASCII: | ||
128 | ima_show_template_data_ascii(m, show, datafmt, field_data); | ||
129 | break; | ||
130 | case IMA_SHOW_BINARY: | ||
131 | case IMA_SHOW_BINARY_NO_FIELD_LEN: | ||
132 | ima_show_template_data_binary(m, show, datafmt, field_data); | ||
133 | break; | ||
134 | default: | ||
135 | break; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | void ima_show_template_digest(struct seq_file *m, enum ima_show_type show, | ||
140 | struct ima_field_data *field_data) | ||
141 | { | ||
142 | ima_show_template_field_data(m, show, DATA_FMT_DIGEST, field_data); | ||
143 | } | ||
144 | |||
145 | void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show, | ||
146 | struct ima_field_data *field_data) | ||
147 | { | ||
148 | ima_show_template_field_data(m, show, DATA_FMT_DIGEST_WITH_ALGO, | ||
149 | field_data); | ||
150 | } | ||
151 | |||
152 | void ima_show_template_string(struct seq_file *m, enum ima_show_type show, | ||
153 | struct ima_field_data *field_data) | ||
154 | { | ||
155 | ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data); | ||
156 | } | ||
157 | |||
158 | void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, | ||
159 | struct ima_field_data *field_data) | ||
160 | { | ||
161 | ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data); | ||
162 | } | ||
163 | |||
164 | static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, | ||
165 | struct ima_field_data *field_data, | ||
166 | bool size_limit) | ||
167 | { | ||
168 | /* | ||
169 | * digest formats: | ||
170 | * - DATA_FMT_DIGEST: digest | ||
171 | * - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest, | ||
172 | * where <hash algo> is provided if the hash algoritm is not | ||
173 | * SHA1 or MD5 | ||
174 | */ | ||
175 | u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 }; | ||
176 | enum data_formats fmt = DATA_FMT_DIGEST; | ||
177 | u32 offset = 0; | ||
178 | |||
179 | if (!size_limit) { | ||
180 | fmt = DATA_FMT_DIGEST_WITH_ALGO; | ||
181 | if (hash_algo < HASH_ALGO__LAST) | ||
182 | offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, | ||
183 | "%s", hash_algo_name[hash_algo]); | ||
184 | buffer[offset] = ':'; | ||
185 | offset += 2; | ||
186 | } | ||
187 | |||
188 | if (digest) | ||
189 | memcpy(buffer + offset, digest, digestsize); | ||
190 | else | ||
191 | /* | ||
192 | * If digest is NULL, the event being recorded is a violation. | ||
193 | * Make room for the digest by increasing the offset of | ||
194 | * IMA_DIGEST_SIZE. | ||
195 | */ | ||
196 | offset += IMA_DIGEST_SIZE; | ||
197 | |||
198 | return ima_write_template_field_data(buffer, offset + digestsize, | ||
199 | fmt, field_data); | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * This function writes the digest of an event (with size limit). | ||
204 | */ | ||
205 | int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, | ||
206 | const unsigned char *filename, | ||
207 | struct evm_ima_xattr_data *xattr_value, int xattr_len, | ||
208 | struct ima_field_data *field_data) | ||
209 | { | ||
210 | struct { | ||
211 | struct ima_digest_data hdr; | ||
212 | char digest[IMA_MAX_DIGEST_SIZE]; | ||
213 | } hash; | ||
214 | u8 *cur_digest = NULL; | ||
215 | u32 cur_digestsize = 0; | ||
216 | struct inode *inode; | ||
217 | int result; | ||
218 | |||
219 | memset(&hash, 0, sizeof(hash)); | ||
220 | |||
221 | if (!iint) /* recording a violation. */ | ||
222 | goto out; | ||
223 | |||
224 | if (ima_template_hash_algo_allowed(iint->ima_hash->algo)) { | ||
225 | cur_digest = iint->ima_hash->digest; | ||
226 | cur_digestsize = iint->ima_hash->length; | ||
227 | goto out; | ||
228 | } | ||
229 | |||
230 | if (!file) /* missing info to re-calculate the digest */ | ||
231 | return -EINVAL; | ||
232 | |||
233 | inode = file_inode(file); | ||
234 | hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? | ||
235 | ima_hash_algo : HASH_ALGO_SHA1; | ||
236 | result = ima_calc_file_hash(file, &hash.hdr); | ||
237 | if (result) { | ||
238 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, | ||
239 | filename, "collect_data", | ||
240 | "failed", result, 0); | ||
241 | return result; | ||
242 | } | ||
243 | cur_digest = hash.hdr.digest; | ||
244 | cur_digestsize = hash.hdr.length; | ||
245 | out: | ||
246 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, -1, | ||
247 | field_data, true); | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * This function writes the digest of an event (without size limit). | ||
252 | */ | ||
253 | int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, | ||
254 | struct file *file, const unsigned char *filename, | ||
255 | struct evm_ima_xattr_data *xattr_value, | ||
256 | int xattr_len, struct ima_field_data *field_data) | ||
257 | { | ||
258 | u8 *cur_digest = NULL, hash_algo = HASH_ALGO__LAST; | ||
259 | u32 cur_digestsize = 0; | ||
260 | |||
261 | /* If iint is NULL, we are recording a violation. */ | ||
262 | if (!iint) | ||
263 | goto out; | ||
264 | |||
265 | cur_digest = iint->ima_hash->digest; | ||
266 | cur_digestsize = iint->ima_hash->length; | ||
267 | |||
268 | hash_algo = iint->ima_hash->algo; | ||
269 | out: | ||
270 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, | ||
271 | hash_algo, field_data, false); | ||
272 | } | ||
273 | |||
274 | static int ima_eventname_init_common(struct integrity_iint_cache *iint, | ||
275 | struct file *file, | ||
276 | const unsigned char *filename, | ||
277 | struct ima_field_data *field_data, | ||
278 | bool size_limit) | ||
279 | { | ||
280 | const char *cur_filename = NULL; | ||
281 | u32 cur_filename_len = 0; | ||
282 | enum data_formats fmt = size_limit ? | ||
283 | DATA_FMT_EVENT_NAME : DATA_FMT_STRING; | ||
284 | |||
285 | BUG_ON(filename == NULL && file == NULL); | ||
286 | |||
287 | if (filename) { | ||
288 | cur_filename = filename; | ||
289 | cur_filename_len = strlen(filename); | ||
290 | |||
291 | if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX) | ||
292 | goto out; | ||
293 | } | ||
294 | |||
295 | if (file) { | ||
296 | cur_filename = file->f_dentry->d_name.name; | ||
297 | cur_filename_len = strlen(cur_filename); | ||
298 | } else | ||
299 | /* | ||
300 | * Truncate filename if the latter is too long and | ||
301 | * the file descriptor is not available. | ||
302 | */ | ||
303 | cur_filename_len = IMA_EVENT_NAME_LEN_MAX; | ||
304 | out: | ||
305 | return ima_write_template_field_data(cur_filename, cur_filename_len, | ||
306 | fmt, field_data); | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * This function writes the name of an event (with size limit). | ||
311 | */ | ||
312 | int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, | ||
313 | const unsigned char *filename, | ||
314 | struct evm_ima_xattr_data *xattr_value, int xattr_len, | ||
315 | struct ima_field_data *field_data) | ||
316 | { | ||
317 | return ima_eventname_init_common(iint, file, filename, | ||
318 | field_data, true); | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * This function writes the name of an event (without size limit). | ||
323 | */ | ||
324 | int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file, | ||
325 | const unsigned char *filename, | ||
326 | struct evm_ima_xattr_data *xattr_value, int xattr_len, | ||
327 | struct ima_field_data *field_data) | ||
328 | { | ||
329 | return ima_eventname_init_common(iint, file, filename, | ||
330 | field_data, false); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * ima_eventsig_init - include the file signature as part of the template data | ||
335 | */ | ||
336 | int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file, | ||
337 | const unsigned char *filename, | ||
338 | struct evm_ima_xattr_data *xattr_value, int xattr_len, | ||
339 | struct ima_field_data *field_data) | ||
340 | { | ||
341 | enum data_formats fmt = DATA_FMT_HEX; | ||
342 | int rc = 0; | ||
343 | |||
344 | if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) | ||
345 | goto out; | ||
346 | |||
347 | rc = ima_write_template_field_data(xattr_value, xattr_len, fmt, | ||
348 | field_data); | ||
349 | out: | ||
350 | return rc; | ||
351 | } | ||