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 | 15 | |
3ce1217d RS |
16 | #include "ima_template_lib.h" |
17 | ||
4d7aeee7 RS |
18 | static bool ima_template_hash_algo_allowed(u8 algo) |
19 | { | |
20 | if (algo == HASH_ALGO_SHA1 || algo == HASH_ALGO_MD5) | |
21 | return true; | |
22 | ||
23 | return false; | |
24 | } | |
25 | ||
26 | enum data_formats { | |
27 | DATA_FMT_DIGEST = 0, | |
28 | DATA_FMT_DIGEST_WITH_ALGO, | |
bcbc9b0c MZ |
29 | DATA_FMT_STRING, |
30 | DATA_FMT_HEX | |
4d7aeee7 RS |
31 | }; |
32 | ||
3ce1217d RS |
33 | static int ima_write_template_field_data(const void *data, const u32 datalen, |
34 | enum data_formats datafmt, | |
35 | struct ima_field_data *field_data) | |
36 | { | |
37 | u8 *buf, *buf_ptr; | |
e3b64c26 | 38 | u32 buflen = datalen; |
3ce1217d | 39 | |
e3b64c26 | 40 | if (datafmt == DATA_FMT_STRING) |
3ce1217d | 41 | buflen = datalen + 1; |
3ce1217d RS |
42 | |
43 | buf = kzalloc(buflen, GFP_KERNEL); | |
44 | if (!buf) | |
45 | return -ENOMEM; | |
46 | ||
47 | memcpy(buf, data, datalen); | |
48 | ||
49 | /* | |
50 | * Replace all space characters with underscore for event names and | |
51 | * strings. This avoid that, during the parsing of a measurements list, | |
52 | * filenames with spaces or that end with the suffix ' (deleted)' are | |
53 | * split into multiple template fields (the space is the delimitator | |
54 | * character for measurements lists in ASCII format). | |
55 | */ | |
e3b64c26 | 56 | if (datafmt == DATA_FMT_STRING) { |
3ce1217d RS |
57 | for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++) |
58 | if (*buf_ptr == ' ') | |
59 | *buf_ptr = '_'; | |
60 | } | |
61 | ||
62 | field_data->data = buf; | |
63 | field_data->len = buflen; | |
64 | return 0; | |
65 | } | |
66 | ||
67 | static void ima_show_template_data_ascii(struct seq_file *m, | |
68 | enum ima_show_type show, | |
69 | enum data_formats datafmt, | |
70 | struct ima_field_data *field_data) | |
71 | { | |
45b26133 MZ |
72 | u8 *buf_ptr = field_data->data; |
73 | u32 buflen = field_data->len; | |
4d7aeee7 | 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 | 197 | */ |
23b57419 | 198 | int ima_eventdigest_init(struct ima_event_data *event_data, |
3ce1217d RS |
199 | struct ima_field_data *field_data) |
200 | { | |
201 | struct { | |
202 | struct ima_digest_data hdr; | |
203 | char digest[IMA_MAX_DIGEST_SIZE]; | |
204 | } hash; | |
4d7aeee7 RS |
205 | u8 *cur_digest = NULL; |
206 | u32 cur_digestsize = 0; | |
3ce1217d RS |
207 | struct inode *inode; |
208 | int result; | |
209 | ||
210 | memset(&hash, 0, sizeof(hash)); | |
211 | ||
8d94eb9b | 212 | if (event_data->violation) /* recording a violation. */ |
3ce1217d RS |
213 | goto out; |
214 | ||
23b57419 RS |
215 | if (ima_template_hash_algo_allowed(event_data->iint->ima_hash->algo)) { |
216 | cur_digest = event_data->iint->ima_hash->digest; | |
217 | cur_digestsize = event_data->iint->ima_hash->length; | |
3ce1217d RS |
218 | goto out; |
219 | } | |
220 | ||
23b57419 | 221 | if (!event_data->file) /* missing info to re-calculate the digest */ |
3ce1217d RS |
222 | return -EINVAL; |
223 | ||
23b57419 | 224 | inode = file_inode(event_data->file); |
4d7aeee7 RS |
225 | hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? |
226 | ima_hash_algo : HASH_ALGO_SHA1; | |
23b57419 | 227 | result = ima_calc_file_hash(event_data->file, &hash.hdr); |
3ce1217d RS |
228 | if (result) { |
229 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, | |
23b57419 | 230 | event_data->filename, "collect_data", |
3ce1217d RS |
231 | "failed", result, 0); |
232 | return result; | |
233 | } | |
4d7aeee7 RS |
234 | cur_digest = hash.hdr.digest; |
235 | cur_digestsize = hash.hdr.length; | |
3ce1217d | 236 | out: |
712a49bd | 237 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, |
dcf4e392 | 238 | HASH_ALGO__LAST, field_data); |
3ce1217d RS |
239 | } |
240 | ||
241 | /* | |
4d7aeee7 | 242 | * This function writes the digest of an event (without size limit). |
3ce1217d | 243 | */ |
23b57419 RS |
244 | int ima_eventdigest_ng_init(struct ima_event_data *event_data, |
245 | struct ima_field_data *field_data) | |
4d7aeee7 | 246 | { |
c502c78b | 247 | u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1; |
4d7aeee7 RS |
248 | u32 cur_digestsize = 0; |
249 | ||
8d94eb9b | 250 | if (event_data->violation) /* recording a violation. */ |
4d7aeee7 RS |
251 | goto out; |
252 | ||
23b57419 RS |
253 | cur_digest = event_data->iint->ima_hash->digest; |
254 | cur_digestsize = event_data->iint->ima_hash->length; | |
4d7aeee7 | 255 | |
23b57419 | 256 | hash_algo = event_data->iint->ima_hash->algo; |
4d7aeee7 RS |
257 | out: |
258 | return ima_eventdigest_init_common(cur_digest, cur_digestsize, | |
dcf4e392 | 259 | hash_algo, field_data); |
4d7aeee7 RS |
260 | } |
261 | ||
23b57419 | 262 | static int ima_eventname_init_common(struct ima_event_data *event_data, |
4d7aeee7 RS |
263 | struct ima_field_data *field_data, |
264 | bool size_limit) | |
3ce1217d RS |
265 | { |
266 | const char *cur_filename = NULL; | |
267 | u32 cur_filename_len = 0; | |
268 | ||
23b57419 | 269 | BUG_ON(event_data->filename == NULL && event_data->file == NULL); |
3ce1217d | 270 | |
23b57419 RS |
271 | if (event_data->filename) { |
272 | cur_filename = event_data->filename; | |
273 | cur_filename_len = strlen(event_data->filename); | |
3ce1217d | 274 | |
4d7aeee7 | 275 | if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX) |
3ce1217d RS |
276 | goto out; |
277 | } | |
278 | ||
23b57419 RS |
279 | if (event_data->file) { |
280 | cur_filename = event_data->file->f_path.dentry->d_name.name; | |
3ce1217d RS |
281 | cur_filename_len = strlen(cur_filename); |
282 | } else | |
283 | /* | |
284 | * Truncate filename if the latter is too long and | |
285 | * the file descriptor is not available. | |
286 | */ | |
287 | cur_filename_len = IMA_EVENT_NAME_LEN_MAX; | |
288 | out: | |
289 | return ima_write_template_field_data(cur_filename, cur_filename_len, | |
e3b64c26 | 290 | DATA_FMT_STRING, field_data); |
4d7aeee7 RS |
291 | } |
292 | ||
293 | /* | |
294 | * This function writes the name of an event (with size limit). | |
295 | */ | |
23b57419 | 296 | int ima_eventname_init(struct ima_event_data *event_data, |
4d7aeee7 RS |
297 | struct ima_field_data *field_data) |
298 | { | |
23b57419 | 299 | return ima_eventname_init_common(event_data, field_data, true); |
4d7aeee7 RS |
300 | } |
301 | ||
302 | /* | |
303 | * This function writes the name of an event (without size limit). | |
304 | */ | |
23b57419 | 305 | int ima_eventname_ng_init(struct ima_event_data *event_data, |
4d7aeee7 RS |
306 | struct ima_field_data *field_data) |
307 | { | |
23b57419 | 308 | return ima_eventname_init_common(event_data, field_data, false); |
3ce1217d | 309 | } |
bcbc9b0c MZ |
310 | |
311 | /* | |
312 | * ima_eventsig_init - include the file signature as part of the template data | |
313 | */ | |
23b57419 | 314 | int ima_eventsig_init(struct ima_event_data *event_data, |
bcbc9b0c MZ |
315 | struct ima_field_data *field_data) |
316 | { | |
317 | enum data_formats fmt = DATA_FMT_HEX; | |
23b57419 RS |
318 | struct evm_ima_xattr_data *xattr_value = event_data->xattr_value; |
319 | int xattr_len = event_data->xattr_len; | |
bcbc9b0c MZ |
320 | int rc = 0; |
321 | ||
322 | if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) | |
323 | goto out; | |
324 | ||
325 | rc = ima_write_template_field_data(xattr_value, xattr_len, fmt, | |
326 | field_data); | |
327 | out: | |
328 | return rc; | |
329 | } |