Commit | Line | Data |
---|---|---|
3ce1217d RS |
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 | */ | |
4d7aeee7 RS |
15 | #include <crypto/hash_info.h> |
16 | ||
3ce1217d RS |
17 | #include "ima_template_lib.h" |
18 | ||
4d7aeee7 RS |
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, | |
bcbc9b0c MZ |
30 | DATA_FMT_STRING, |
31 | DATA_FMT_HEX | |
4d7aeee7 RS |
32 | }; |
33 | ||
3ce1217d RS |
34 | static int ima_write_template_field_data(const void *data, const u32 datalen, |
35 | enum data_formats datafmt, | |
36 | struct ima_field_data *field_data) | |
37 | { | |
38 | u8 *buf, *buf_ptr; | |
e3b64c26 | 39 | u32 buflen = datalen; |
3ce1217d | 40 | |
e3b64c26 | 41 | if (datafmt == DATA_FMT_STRING) |
3ce1217d | 42 | buflen = datalen + 1; |
3ce1217d RS |
43 | |
44 | buf = kzalloc(buflen, GFP_KERNEL); | |
45 | if (!buf) | |
46 | return -ENOMEM; | |
47 | ||
48 | memcpy(buf, data, datalen); | |
49 | ||
50 | /* | |
51 | * Replace all space characters with underscore for event names and | |
52 | * strings. This avoid that, during the parsing of a measurements list, | |
53 | * filenames with spaces or that end with the suffix ' (deleted)' are | |
54 | * split into multiple template fields (the space is the delimitator | |
55 | * character for measurements lists in ASCII format). | |
56 | */ | |
e3b64c26 | 57 | if (datafmt == DATA_FMT_STRING) { |
3ce1217d RS |
58 | for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++) |
59 | if (*buf_ptr == ' ') | |
60 | *buf_ptr = '_'; | |
61 | } | |
62 | ||
63 | field_data->data = buf; | |
64 | field_data->len = buflen; | |
65 | return 0; | |
66 | } | |
67 | ||
68 | static void ima_show_template_data_ascii(struct seq_file *m, | |
69 | enum ima_show_type show, | |
70 | enum data_formats datafmt, | |
71 | struct ima_field_data *field_data) | |
72 | { | |
4d7aeee7 RS |
73 | u8 *buf_ptr = field_data->data, buflen = field_data->len; |
74 | ||
3ce1217d | 75 | switch (datafmt) { |
4d7aeee7 RS |
76 | case DATA_FMT_DIGEST_WITH_ALGO: |
77 | buf_ptr = strnchr(field_data->data, buflen, ':'); | |
78 | if (buf_ptr != field_data->data) | |
79 | seq_printf(m, "%s", field_data->data); | |
80 | ||
81 | /* skip ':' and '\0' */ | |
82 | buf_ptr += 2; | |
83 | buflen -= buf_ptr - field_data->data; | |
3ce1217d | 84 | case DATA_FMT_DIGEST: |
bcbc9b0c MZ |
85 | case DATA_FMT_HEX: |
86 | if (!buflen) | |
87 | break; | |
4d7aeee7 | 88 | ima_print_digest(m, buf_ptr, buflen); |
3ce1217d RS |
89 | break; |
90 | case DATA_FMT_STRING: | |
4d7aeee7 | 91 | seq_printf(m, "%s", buf_ptr); |
3ce1217d RS |
92 | break; |
93 | default: | |
94 | break; | |
95 | } | |
96 | } | |
97 | ||
98 | static void ima_show_template_data_binary(struct seq_file *m, | |
99 | enum ima_show_type show, | |
100 | enum data_formats datafmt, | |
101 | struct ima_field_data *field_data) | |
102 | { | |
c019e307 RS |
103 | u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ? |
104 | strlen(field_data->data) : field_data->len; | |
105 | ||
3e8e5503 | 106 | if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) |
c019e307 | 107 | ima_putc(m, &len, sizeof(len)); |
3e8e5503 | 108 | |
c019e307 | 109 | if (!len) |
3ce1217d | 110 | return; |
3e8e5503 | 111 | |
c019e307 | 112 | ima_putc(m, field_data->data, len); |
3ce1217d RS |
113 | } |
114 | ||
115 | static void ima_show_template_field_data(struct seq_file *m, | |
116 | enum ima_show_type show, | |
117 | enum data_formats datafmt, | |
118 | struct ima_field_data *field_data) | |
119 | { | |
120 | switch (show) { | |
121 | case IMA_SHOW_ASCII: | |
122 | ima_show_template_data_ascii(m, show, datafmt, field_data); | |
123 | break; | |
124 | case IMA_SHOW_BINARY: | |
3e8e5503 | 125 | case IMA_SHOW_BINARY_NO_FIELD_LEN: |
c019e307 | 126 | case IMA_SHOW_BINARY_OLD_STRING_FMT: |
3ce1217d RS |
127 | ima_show_template_data_binary(m, show, datafmt, field_data); |
128 | break; | |
129 | default: | |
130 | break; | |
131 | } | |
132 | } | |
133 | ||
134 | void ima_show_template_digest(struct seq_file *m, enum ima_show_type show, | |
135 | struct ima_field_data *field_data) | |
136 | { | |
137 | ima_show_template_field_data(m, show, DATA_FMT_DIGEST, field_data); | |
138 | } | |
139 | ||
4d7aeee7 RS |
140 | void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show, |
141 | struct ima_field_data *field_data) | |
142 | { | |
143 | ima_show_template_field_data(m, show, DATA_FMT_DIGEST_WITH_ALGO, | |
144 | field_data); | |
145 | } | |
146 | ||
3ce1217d RS |
147 | void ima_show_template_string(struct seq_file *m, enum ima_show_type show, |
148 | struct ima_field_data *field_data) | |
149 | { | |
150 | ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data); | |
151 | } | |
152 | ||
bcbc9b0c MZ |
153 | void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, |
154 | struct ima_field_data *field_data) | |
155 | { | |
156 | ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data); | |
157 | } | |
158 | ||
4d7aeee7 | 159 | static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, |
dcf4e392 | 160 | struct ima_field_data *field_data) |
4d7aeee7 RS |
161 | { |
162 | /* | |
163 | * digest formats: | |
164 | * - DATA_FMT_DIGEST: digest | |
165 | * - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest, | |
166 | * where <hash algo> is provided if the hash algoritm is not | |
167 | * SHA1 or MD5 | |
168 | */ | |
169 | u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 }; | |
170 | enum data_formats fmt = DATA_FMT_DIGEST; | |
171 | u32 offset = 0; | |
172 | ||
dcf4e392 | 173 | if (hash_algo < HASH_ALGO__LAST) { |
4d7aeee7 | 174 | fmt = DATA_FMT_DIGEST_WITH_ALGO; |
dcf4e392 RS |
175 | offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s", |
176 | hash_algo_name[hash_algo]); | |
4d7aeee7 RS |
177 | buffer[offset] = ':'; |
178 | offset += 2; | |
179 | } | |
180 | ||
181 | if (digest) | |
182 | memcpy(buffer + offset, digest, digestsize); | |
183 | else | |
184 | /* | |
185 | * If digest is NULL, the event being recorded is a violation. | |
186 | * Make room for the digest by increasing the offset of | |
187 | * IMA_DIGEST_SIZE. | |
188 | */ | |
189 | offset += IMA_DIGEST_SIZE; | |
190 | ||
191 | return ima_write_template_field_data(buffer, offset + digestsize, | |
192 | fmt, field_data); | |
193 | } | |
194 | ||
3ce1217d | 195 | /* |
4d7aeee7 | 196 | * This function writes the digest of an event (with size limit). |
3ce1217d RS |
197 | */ |
198 | int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, | |
199 | const unsigned char *filename, | |
bcbc9b0c | 200 | struct evm_ima_xattr_data *xattr_value, int xattr_len, |
3ce1217d RS |
201 | struct ima_field_data *field_data) |
202 | { | |
203 | struct { | |
204 | struct ima_digest_data hdr; | |
205 | char digest[IMA_MAX_DIGEST_SIZE]; | |
206 | } hash; | |
4d7aeee7 RS |
207 | u8 *cur_digest = NULL; |
208 | u32 cur_digestsize = 0; | |
3ce1217d RS |
209 | struct inode *inode; |
210 | int result; | |
211 | ||
212 | memset(&hash, 0, sizeof(hash)); | |
213 | ||
214 | if (!iint) /* recording a violation. */ | |
215 | goto out; | |
216 | ||
4d7aeee7 | 217 | if (ima_template_hash_algo_allowed(iint->ima_hash->algo)) { |
3ce1217d RS |
218 | cur_digest = iint->ima_hash->digest; |
219 | cur_digestsize = iint->ima_hash->length; | |
220 | goto out; | |
221 | } | |
222 | ||
223 | if (!file) /* missing info to re-calculate the digest */ | |
224 | return -EINVAL; | |
225 | ||
226 | inode = file_inode(file); | |
4d7aeee7 RS |
227 | hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? |
228 | ima_hash_algo : HASH_ALGO_SHA1; | |
3ce1217d RS |
229 | result = ima_calc_file_hash(file, &hash.hdr); |
230 | if (result) { | |
231 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, | |
232 | filename, "collect_data", | |
233 | "failed", result, 0); | |
234 | return result; | |
235 | } | |
4d7aeee7 RS |
236 | cur_digest = hash.hdr.digest; |
237 | cur_digestsize = hash.hdr.length; | |
3ce1217d | 238 | out: |
712a49bd | 239 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, |
dcf4e392 | 240 | HASH_ALGO__LAST, field_data); |
3ce1217d RS |
241 | } |
242 | ||
243 | /* | |
4d7aeee7 | 244 | * This function writes the digest of an event (without size limit). |
3ce1217d | 245 | */ |
4d7aeee7 RS |
246 | int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, |
247 | struct file *file, const unsigned char *filename, | |
bcbc9b0c MZ |
248 | struct evm_ima_xattr_data *xattr_value, |
249 | int xattr_len, struct ima_field_data *field_data) | |
4d7aeee7 | 250 | { |
c502c78b | 251 | u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1; |
4d7aeee7 RS |
252 | u32 cur_digestsize = 0; |
253 | ||
254 | /* If iint is NULL, we are recording a violation. */ | |
255 | if (!iint) | |
256 | goto out; | |
257 | ||
258 | cur_digest = iint->ima_hash->digest; | |
259 | cur_digestsize = iint->ima_hash->length; | |
260 | ||
261 | hash_algo = iint->ima_hash->algo; | |
262 | out: | |
263 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, | |
dcf4e392 | 264 | hash_algo, field_data); |
4d7aeee7 RS |
265 | } |
266 | ||
267 | static int ima_eventname_init_common(struct integrity_iint_cache *iint, | |
268 | struct file *file, | |
269 | const unsigned char *filename, | |
270 | struct ima_field_data *field_data, | |
271 | bool size_limit) | |
3ce1217d RS |
272 | { |
273 | const char *cur_filename = NULL; | |
274 | u32 cur_filename_len = 0; | |
275 | ||
276 | BUG_ON(filename == NULL && file == NULL); | |
277 | ||
278 | if (filename) { | |
279 | cur_filename = filename; | |
280 | cur_filename_len = strlen(filename); | |
281 | ||
4d7aeee7 | 282 | if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX) |
3ce1217d RS |
283 | goto out; |
284 | } | |
285 | ||
286 | if (file) { | |
b583043e | 287 | cur_filename = file->f_path.dentry->d_name.name; |
3ce1217d RS |
288 | cur_filename_len = strlen(cur_filename); |
289 | } else | |
290 | /* | |
291 | * Truncate filename if the latter is too long and | |
292 | * the file descriptor is not available. | |
293 | */ | |
294 | cur_filename_len = IMA_EVENT_NAME_LEN_MAX; | |
295 | out: | |
296 | return ima_write_template_field_data(cur_filename, cur_filename_len, | |
e3b64c26 | 297 | DATA_FMT_STRING, field_data); |
4d7aeee7 RS |
298 | } |
299 | ||
300 | /* | |
301 | * This function writes the name of an event (with size limit). | |
302 | */ | |
303 | int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, | |
304 | const unsigned char *filename, | |
bcbc9b0c | 305 | struct evm_ima_xattr_data *xattr_value, int xattr_len, |
4d7aeee7 RS |
306 | struct ima_field_data *field_data) |
307 | { | |
308 | return ima_eventname_init_common(iint, file, filename, | |
309 | field_data, true); | |
310 | } | |
311 | ||
312 | /* | |
313 | * This function writes the name of an event (without size limit). | |
314 | */ | |
315 | int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file, | |
316 | const unsigned char *filename, | |
bcbc9b0c | 317 | struct evm_ima_xattr_data *xattr_value, int xattr_len, |
4d7aeee7 RS |
318 | struct ima_field_data *field_data) |
319 | { | |
320 | return ima_eventname_init_common(iint, file, filename, | |
321 | field_data, false); | |
3ce1217d | 322 | } |
bcbc9b0c MZ |
323 | |
324 | /* | |
325 | * ima_eventsig_init - include the file signature as part of the template data | |
326 | */ | |
327 | int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file, | |
328 | const unsigned char *filename, | |
329 | struct evm_ima_xattr_data *xattr_value, int xattr_len, | |
330 | struct ima_field_data *field_data) | |
331 | { | |
332 | enum data_formats fmt = DATA_FMT_HEX; | |
333 | int rc = 0; | |
334 | ||
335 | if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) | |
336 | goto out; | |
337 | ||
338 | rc = ima_write_template_field_data(xattr_value, xattr_len, fmt, | |
339 | field_data); | |
340 | out: | |
341 | return rc; | |
342 | } |