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, | |
30 | DATA_FMT_EVENT_NAME, | |
bcbc9b0c MZ |
31 | DATA_FMT_STRING, |
32 | DATA_FMT_HEX | |
4d7aeee7 RS |
33 | }; |
34 | ||
3ce1217d RS |
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 | { | |
4d7aeee7 RS |
82 | u8 *buf_ptr = field_data->data, buflen = field_data->len; |
83 | ||
3ce1217d | 84 | switch (datafmt) { |
4d7aeee7 RS |
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; | |
3ce1217d | 93 | case DATA_FMT_DIGEST: |
bcbc9b0c MZ |
94 | case DATA_FMT_HEX: |
95 | if (!buflen) | |
96 | break; | |
4d7aeee7 | 97 | ima_print_digest(m, buf_ptr, buflen); |
3ce1217d RS |
98 | break; |
99 | case DATA_FMT_STRING: | |
4d7aeee7 | 100 | seq_printf(m, "%s", buf_ptr); |
3ce1217d RS |
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 | { | |
c019e307 RS |
112 | u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ? |
113 | strlen(field_data->data) : field_data->len; | |
114 | ||
3e8e5503 | 115 | if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) |
c019e307 | 116 | ima_putc(m, &len, sizeof(len)); |
3e8e5503 | 117 | |
c019e307 | 118 | if (!len) |
3ce1217d | 119 | return; |
3e8e5503 | 120 | |
c019e307 | 121 | ima_putc(m, field_data->data, len); |
3ce1217d RS |
122 | } |
123 | ||
124 | static void ima_show_template_field_data(struct seq_file *m, | |
125 | enum ima_show_type show, | |
126 | enum data_formats datafmt, | |
127 | struct ima_field_data *field_data) | |
128 | { | |
129 | switch (show) { | |
130 | case IMA_SHOW_ASCII: | |
131 | ima_show_template_data_ascii(m, show, datafmt, field_data); | |
132 | break; | |
133 | case IMA_SHOW_BINARY: | |
3e8e5503 | 134 | case IMA_SHOW_BINARY_NO_FIELD_LEN: |
c019e307 | 135 | case IMA_SHOW_BINARY_OLD_STRING_FMT: |
3ce1217d RS |
136 | ima_show_template_data_binary(m, show, datafmt, field_data); |
137 | break; | |
138 | default: | |
139 | break; | |
140 | } | |
141 | } | |
142 | ||
143 | void ima_show_template_digest(struct seq_file *m, enum ima_show_type show, | |
144 | struct ima_field_data *field_data) | |
145 | { | |
146 | ima_show_template_field_data(m, show, DATA_FMT_DIGEST, field_data); | |
147 | } | |
148 | ||
4d7aeee7 RS |
149 | void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show, |
150 | struct ima_field_data *field_data) | |
151 | { | |
152 | ima_show_template_field_data(m, show, DATA_FMT_DIGEST_WITH_ALGO, | |
153 | field_data); | |
154 | } | |
155 | ||
3ce1217d RS |
156 | void ima_show_template_string(struct seq_file *m, enum ima_show_type show, |
157 | struct ima_field_data *field_data) | |
158 | { | |
159 | ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data); | |
160 | } | |
161 | ||
bcbc9b0c MZ |
162 | void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, |
163 | struct ima_field_data *field_data) | |
164 | { | |
165 | ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data); | |
166 | } | |
167 | ||
4d7aeee7 | 168 | static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, |
dcf4e392 | 169 | struct ima_field_data *field_data) |
4d7aeee7 RS |
170 | { |
171 | /* | |
172 | * digest formats: | |
173 | * - DATA_FMT_DIGEST: digest | |
174 | * - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest, | |
175 | * where <hash algo> is provided if the hash algoritm is not | |
176 | * SHA1 or MD5 | |
177 | */ | |
178 | u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 }; | |
179 | enum data_formats fmt = DATA_FMT_DIGEST; | |
180 | u32 offset = 0; | |
181 | ||
dcf4e392 | 182 | if (hash_algo < HASH_ALGO__LAST) { |
4d7aeee7 | 183 | fmt = DATA_FMT_DIGEST_WITH_ALGO; |
dcf4e392 RS |
184 | offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s", |
185 | hash_algo_name[hash_algo]); | |
4d7aeee7 RS |
186 | buffer[offset] = ':'; |
187 | offset += 2; | |
188 | } | |
189 | ||
190 | if (digest) | |
191 | memcpy(buffer + offset, digest, digestsize); | |
192 | else | |
193 | /* | |
194 | * If digest is NULL, the event being recorded is a violation. | |
195 | * Make room for the digest by increasing the offset of | |
196 | * IMA_DIGEST_SIZE. | |
197 | */ | |
198 | offset += IMA_DIGEST_SIZE; | |
199 | ||
200 | return ima_write_template_field_data(buffer, offset + digestsize, | |
201 | fmt, field_data); | |
202 | } | |
203 | ||
3ce1217d | 204 | /* |
4d7aeee7 | 205 | * This function writes the digest of an event (with size limit). |
3ce1217d RS |
206 | */ |
207 | int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, | |
208 | const unsigned char *filename, | |
bcbc9b0c | 209 | struct evm_ima_xattr_data *xattr_value, int xattr_len, |
3ce1217d RS |
210 | struct ima_field_data *field_data) |
211 | { | |
212 | struct { | |
213 | struct ima_digest_data hdr; | |
214 | char digest[IMA_MAX_DIGEST_SIZE]; | |
215 | } hash; | |
4d7aeee7 RS |
216 | u8 *cur_digest = NULL; |
217 | u32 cur_digestsize = 0; | |
3ce1217d RS |
218 | struct inode *inode; |
219 | int result; | |
220 | ||
221 | memset(&hash, 0, sizeof(hash)); | |
222 | ||
223 | if (!iint) /* recording a violation. */ | |
224 | goto out; | |
225 | ||
4d7aeee7 | 226 | if (ima_template_hash_algo_allowed(iint->ima_hash->algo)) { |
3ce1217d RS |
227 | cur_digest = iint->ima_hash->digest; |
228 | cur_digestsize = iint->ima_hash->length; | |
229 | goto out; | |
230 | } | |
231 | ||
232 | if (!file) /* missing info to re-calculate the digest */ | |
233 | return -EINVAL; | |
234 | ||
235 | inode = file_inode(file); | |
4d7aeee7 RS |
236 | hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? |
237 | ima_hash_algo : HASH_ALGO_SHA1; | |
3ce1217d RS |
238 | result = ima_calc_file_hash(file, &hash.hdr); |
239 | if (result) { | |
240 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, | |
241 | filename, "collect_data", | |
242 | "failed", result, 0); | |
243 | return result; | |
244 | } | |
4d7aeee7 RS |
245 | cur_digest = hash.hdr.digest; |
246 | cur_digestsize = hash.hdr.length; | |
3ce1217d | 247 | out: |
712a49bd | 248 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, |
dcf4e392 | 249 | HASH_ALGO__LAST, field_data); |
3ce1217d RS |
250 | } |
251 | ||
252 | /* | |
4d7aeee7 | 253 | * This function writes the digest of an event (without size limit). |
3ce1217d | 254 | */ |
4d7aeee7 RS |
255 | int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, |
256 | struct file *file, const unsigned char *filename, | |
bcbc9b0c MZ |
257 | struct evm_ima_xattr_data *xattr_value, |
258 | int xattr_len, struct ima_field_data *field_data) | |
4d7aeee7 | 259 | { |
c502c78b | 260 | u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1; |
4d7aeee7 RS |
261 | u32 cur_digestsize = 0; |
262 | ||
263 | /* If iint is NULL, we are recording a violation. */ | |
264 | if (!iint) | |
265 | goto out; | |
266 | ||
267 | cur_digest = iint->ima_hash->digest; | |
268 | cur_digestsize = iint->ima_hash->length; | |
269 | ||
270 | hash_algo = iint->ima_hash->algo; | |
271 | out: | |
272 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, | |
dcf4e392 | 273 | hash_algo, field_data); |
4d7aeee7 RS |
274 | } |
275 | ||
276 | static int ima_eventname_init_common(struct integrity_iint_cache *iint, | |
277 | struct file *file, | |
278 | const unsigned char *filename, | |
279 | struct ima_field_data *field_data, | |
280 | bool size_limit) | |
3ce1217d RS |
281 | { |
282 | const char *cur_filename = NULL; | |
283 | u32 cur_filename_len = 0; | |
4d7aeee7 RS |
284 | enum data_formats fmt = size_limit ? |
285 | DATA_FMT_EVENT_NAME : DATA_FMT_STRING; | |
3ce1217d RS |
286 | |
287 | BUG_ON(filename == NULL && file == NULL); | |
288 | ||
289 | if (filename) { | |
290 | cur_filename = filename; | |
291 | cur_filename_len = strlen(filename); | |
292 | ||
4d7aeee7 | 293 | if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX) |
3ce1217d RS |
294 | goto out; |
295 | } | |
296 | ||
297 | if (file) { | |
298 | cur_filename = file->f_dentry->d_name.name; | |
299 | cur_filename_len = strlen(cur_filename); | |
300 | } else | |
301 | /* | |
302 | * Truncate filename if the latter is too long and | |
303 | * the file descriptor is not available. | |
304 | */ | |
305 | cur_filename_len = IMA_EVENT_NAME_LEN_MAX; | |
306 | out: | |
307 | return ima_write_template_field_data(cur_filename, cur_filename_len, | |
4d7aeee7 RS |
308 | fmt, field_data); |
309 | } | |
310 | ||
311 | /* | |
312 | * This function writes the name of an event (with size limit). | |
313 | */ | |
314 | int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, | |
315 | const unsigned char *filename, | |
bcbc9b0c | 316 | struct evm_ima_xattr_data *xattr_value, int xattr_len, |
4d7aeee7 RS |
317 | struct ima_field_data *field_data) |
318 | { | |
319 | return ima_eventname_init_common(iint, file, filename, | |
320 | field_data, true); | |
321 | } | |
322 | ||
323 | /* | |
324 | * This function writes the name of an event (without size limit). | |
325 | */ | |
326 | int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file, | |
327 | const unsigned char *filename, | |
bcbc9b0c | 328 | struct evm_ima_xattr_data *xattr_value, int xattr_len, |
4d7aeee7 RS |
329 | struct ima_field_data *field_data) |
330 | { | |
331 | return ima_eventname_init_common(iint, file, filename, | |
332 | field_data, false); | |
3ce1217d | 333 | } |
bcbc9b0c MZ |
334 | |
335 | /* | |
336 | * ima_eventsig_init - include the file signature as part of the template data | |
337 | */ | |
338 | int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file, | |
339 | const unsigned char *filename, | |
340 | struct evm_ima_xattr_data *xattr_value, int xattr_len, | |
341 | struct ima_field_data *field_data) | |
342 | { | |
343 | enum data_formats fmt = DATA_FMT_HEX; | |
344 | int rc = 0; | |
345 | ||
346 | if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) | |
347 | goto out; | |
348 | ||
349 | rc = ima_write_template_field_data(xattr_value, xattr_len, fmt, | |
350 | field_data); | |
351 | out: | |
352 | return rc; | |
353 | } |