1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * AppArmor security module |
4 | * |
5 | * This file contains AppArmor functions for unpacking policy loaded from |
6 | * userspace. |
7 | * |
8 | * Copyright (C) 1998-2008 Novell/SUSE |
9 | * Copyright 2009-2010 Canonical Ltd. |
10 | * |
11 | * AppArmor uses a serialized binary format for loading policy. To find |
12 | * policy format documentation see Documentation/admin-guide/LSM/apparmor.rst |
13 | * All policy is validated before it is used. |
14 | */ |
15 | |
16 | #include <asm/unaligned.h> |
17 | #include <kunit/visibility.h> |
18 | #include <linux/ctype.h> |
19 | #include <linux/errno.h> |
20 | #include <linux/zstd.h> |
21 | |
22 | #include "include/apparmor.h" |
23 | #include "include/audit.h" |
24 | #include "include/cred.h" |
25 | #include "include/crypto.h" |
26 | #include "include/file.h" |
27 | #include "include/match.h" |
28 | #include "include/path.h" |
29 | #include "include/policy.h" |
30 | #include "include/policy_unpack.h" |
31 | #include "include/policy_compat.h" |
32 | |
33 | /* audit callback for unpack fields */ |
34 | static void audit_cb(struct audit_buffer *ab, void *va) |
35 | { |
36 | struct common_audit_data *sa = va; |
37 | struct apparmor_audit_data *ad = aad(sa); |
38 | |
39 | if (ad->iface.ns) { |
40 | audit_log_format(ab, fmt: " ns=" ); |
41 | audit_log_untrustedstring(ab, string: ad->iface.ns); |
42 | } |
43 | if (ad->name) { |
44 | audit_log_format(ab, fmt: " name=" ); |
45 | audit_log_untrustedstring(ab, string: ad->name); |
46 | } |
47 | if (ad->iface.pos) |
48 | audit_log_format(ab, fmt: " offset=%ld" , ad->iface.pos); |
49 | } |
50 | |
51 | /** |
52 | * audit_iface - do audit message for policy unpacking/load/replace/remove |
53 | * @new: profile if it has been allocated (MAYBE NULL) |
54 | * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL) |
55 | * @name: name of the profile being manipulated (MAYBE NULL) |
56 | * @info: any extra info about the failure (MAYBE NULL) |
57 | * @e: buffer position info |
58 | * @error: error code |
59 | * |
60 | * Returns: %0 or error |
61 | */ |
62 | static int audit_iface(struct aa_profile *new, const char *ns_name, |
63 | const char *name, const char *info, struct aa_ext *e, |
64 | int error) |
65 | { |
66 | struct aa_profile *profile = labels_profile(aa_current_raw_label()); |
67 | DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL); |
68 | if (e) |
69 | ad.iface.pos = e->pos - e->start; |
70 | ad.iface.ns = ns_name; |
71 | if (new) |
72 | ad.name = new->base.hname; |
73 | else |
74 | ad.name = name; |
75 | ad.info = info; |
76 | ad.error = error; |
77 | |
78 | return aa_audit(type: AUDIT_APPARMOR_STATUS, profile, ad: &ad, cb: audit_cb); |
79 | } |
80 | |
81 | void __aa_loaddata_update(struct aa_loaddata *data, long revision) |
82 | { |
83 | AA_BUG(!data); |
84 | AA_BUG(!data->ns); |
85 | AA_BUG(!mutex_is_locked(&data->ns->lock)); |
86 | AA_BUG(data->revision > revision); |
87 | |
88 | data->revision = revision; |
89 | if ((data->dents[AAFS_LOADDATA_REVISION])) { |
90 | struct inode *inode; |
91 | |
92 | inode = d_inode(dentry: data->dents[AAFS_LOADDATA_DIR]); |
93 | inode_set_mtime_to_ts(inode, ts: inode_set_ctime_current(inode)); |
94 | |
95 | inode = d_inode(dentry: data->dents[AAFS_LOADDATA_REVISION]); |
96 | inode_set_mtime_to_ts(inode, ts: inode_set_ctime_current(inode)); |
97 | } |
98 | } |
99 | |
100 | bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r) |
101 | { |
102 | if (l->size != r->size) |
103 | return false; |
104 | if (l->compressed_size != r->compressed_size) |
105 | return false; |
106 | if (aa_g_hash_policy && memcmp(p: l->hash, q: r->hash, size: aa_hash_size()) != 0) |
107 | return false; |
108 | return memcmp(p: l->data, q: r->data, size: r->compressed_size ?: r->size) == 0; |
109 | } |
110 | |
111 | /* |
112 | * need to take the ns mutex lock which is NOT safe most places that |
113 | * put_loaddata is called, so we have to delay freeing it |
114 | */ |
115 | static void do_loaddata_free(struct work_struct *work) |
116 | { |
117 | struct aa_loaddata *d = container_of(work, struct aa_loaddata, work); |
118 | struct aa_ns *ns = aa_get_ns(ns: d->ns); |
119 | |
120 | if (ns) { |
121 | mutex_lock_nested(lock: &ns->lock, subclass: ns->level); |
122 | __aa_fs_remove_rawdata(rawdata: d); |
123 | mutex_unlock(lock: &ns->lock); |
124 | aa_put_ns(ns); |
125 | } |
126 | |
127 | kfree_sensitive(objp: d->hash); |
128 | kfree_sensitive(objp: d->name); |
129 | kvfree(addr: d->data); |
130 | kfree_sensitive(objp: d); |
131 | } |
132 | |
133 | void aa_loaddata_kref(struct kref *kref) |
134 | { |
135 | struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); |
136 | |
137 | if (d) { |
138 | INIT_WORK(&d->work, do_loaddata_free); |
139 | schedule_work(work: &d->work); |
140 | } |
141 | } |
142 | |
143 | struct aa_loaddata *aa_loaddata_alloc(size_t size) |
144 | { |
145 | struct aa_loaddata *d; |
146 | |
147 | d = kzalloc(size: sizeof(*d), GFP_KERNEL); |
148 | if (d == NULL) |
149 | return ERR_PTR(error: -ENOMEM); |
150 | d->data = kvzalloc(size, GFP_KERNEL); |
151 | if (!d->data) { |
152 | kfree(objp: d); |
153 | return ERR_PTR(error: -ENOMEM); |
154 | } |
155 | kref_init(kref: &d->count); |
156 | INIT_LIST_HEAD(list: &d->list); |
157 | |
158 | return d; |
159 | } |
160 | |
161 | /* test if read will be in packed data bounds */ |
162 | VISIBLE_IF_KUNIT bool aa_inbounds(struct aa_ext *e, size_t size) |
163 | { |
164 | return (size <= e->end - e->pos); |
165 | } |
166 | EXPORT_SYMBOL_IF_KUNIT(aa_inbounds); |
167 | |
168 | /** |
169 | * aa_unpack_u16_chunk - test and do bounds checking for a u16 size based chunk |
170 | * @e: serialized data read head (NOT NULL) |
171 | * @chunk: start address for chunk of data (NOT NULL) |
172 | * |
173 | * Returns: the size of chunk found with the read head at the end of the chunk. |
174 | */ |
175 | VISIBLE_IF_KUNIT size_t aa_unpack_u16_chunk(struct aa_ext *e, char **chunk) |
176 | { |
177 | size_t size = 0; |
178 | void *pos = e->pos; |
179 | |
180 | if (!aa_inbounds(e, sizeof(u16))) |
181 | goto fail; |
182 | size = le16_to_cpu(get_unaligned((__le16 *) e->pos)); |
183 | e->pos += sizeof(__le16); |
184 | if (!aa_inbounds(e, size)) |
185 | goto fail; |
186 | *chunk = e->pos; |
187 | e->pos += size; |
188 | return size; |
189 | |
190 | fail: |
191 | e->pos = pos; |
192 | return 0; |
193 | } |
194 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u16_chunk); |
195 | |
196 | /* unpack control byte */ |
197 | VISIBLE_IF_KUNIT bool aa_unpack_X(struct aa_ext *e, enum aa_code code) |
198 | { |
199 | if (!aa_inbounds(e, 1)) |
200 | return false; |
201 | if (*(u8 *) e->pos != code) |
202 | return false; |
203 | e->pos++; |
204 | return true; |
205 | } |
206 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_X); |
207 | |
208 | /** |
209 | * aa_unpack_nameX - check is the next element is of type X with a name of @name |
210 | * @e: serialized data extent information (NOT NULL) |
211 | * @code: type code |
212 | * @name: name to match to the serialized element. (MAYBE NULL) |
213 | * |
214 | * check that the next serialized data element is of type X and has a tag |
215 | * name @name. If @name is specified then there must be a matching |
216 | * name element in the stream. If @name is NULL any name element will be |
217 | * skipped and only the typecode will be tested. |
218 | * |
219 | * Returns true on success (both type code and name tests match) and the read |
220 | * head is advanced past the headers |
221 | * |
222 | * Returns: false if either match fails, the read head does not move |
223 | */ |
224 | VISIBLE_IF_KUNIT bool aa_unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) |
225 | { |
226 | /* |
227 | * May need to reset pos if name or type doesn't match |
228 | */ |
229 | void *pos = e->pos; |
230 | /* |
231 | * Check for presence of a tagname, and if present name size |
232 | * AA_NAME tag value is a u16. |
233 | */ |
234 | if (aa_unpack_X(e, AA_NAME)) { |
235 | char *tag = NULL; |
236 | size_t size = aa_unpack_u16_chunk(e, &tag); |
237 | /* if a name is specified it must match. otherwise skip tag */ |
238 | if (name && (!size || tag[size-1] != '\0' || strcmp(name, tag))) |
239 | goto fail; |
240 | } else if (name) { |
241 | /* if a name is specified and there is no name tag fail */ |
242 | goto fail; |
243 | } |
244 | |
245 | /* now check if type code matches */ |
246 | if (aa_unpack_X(e, code)) |
247 | return true; |
248 | |
249 | fail: |
250 | e->pos = pos; |
251 | return false; |
252 | } |
253 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_nameX); |
254 | |
255 | static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name) |
256 | { |
257 | void *pos = e->pos; |
258 | |
259 | if (aa_unpack_nameX(e, AA_U8, name)) { |
260 | if (!aa_inbounds(e, sizeof(u8))) |
261 | goto fail; |
262 | if (data) |
263 | *data = *((u8 *)e->pos); |
264 | e->pos += sizeof(u8); |
265 | return true; |
266 | } |
267 | |
268 | fail: |
269 | e->pos = pos; |
270 | return false; |
271 | } |
272 | |
273 | VISIBLE_IF_KUNIT bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name) |
274 | { |
275 | void *pos = e->pos; |
276 | |
277 | if (aa_unpack_nameX(e, AA_U32, name)) { |
278 | if (!aa_inbounds(e, sizeof(u32))) |
279 | goto fail; |
280 | if (data) |
281 | *data = le32_to_cpu(get_unaligned((__le32 *) e->pos)); |
282 | e->pos += sizeof(u32); |
283 | return true; |
284 | } |
285 | |
286 | fail: |
287 | e->pos = pos; |
288 | return false; |
289 | } |
290 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u32); |
291 | |
292 | VISIBLE_IF_KUNIT bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *name) |
293 | { |
294 | void *pos = e->pos; |
295 | |
296 | if (aa_unpack_nameX(e, AA_U64, name)) { |
297 | if (!aa_inbounds(e, sizeof(u64))) |
298 | goto fail; |
299 | if (data) |
300 | *data = le64_to_cpu(get_unaligned((__le64 *) e->pos)); |
301 | e->pos += sizeof(u64); |
302 | return true; |
303 | } |
304 | |
305 | fail: |
306 | e->pos = pos; |
307 | return false; |
308 | } |
309 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u64); |
310 | |
311 | static bool aa_unpack_cap_low(struct aa_ext *e, kernel_cap_t *data, const char *name) |
312 | { |
313 | u32 val; |
314 | |
315 | if (!aa_unpack_u32(e, &val, name)) |
316 | return false; |
317 | data->val = val; |
318 | return true; |
319 | } |
320 | |
321 | static bool aa_unpack_cap_high(struct aa_ext *e, kernel_cap_t *data, const char *name) |
322 | { |
323 | u32 val; |
324 | |
325 | if (!aa_unpack_u32(e, &val, name)) |
326 | return false; |
327 | data->val = (u32)data->val | ((u64)val << 32); |
328 | return true; |
329 | } |
330 | |
331 | VISIBLE_IF_KUNIT bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size) |
332 | { |
333 | void *pos = e->pos; |
334 | |
335 | if (aa_unpack_nameX(e, AA_ARRAY, name)) { |
336 | if (!aa_inbounds(e, sizeof(u16))) |
337 | goto fail; |
338 | *size = le16_to_cpu(get_unaligned((__le16 *) e->pos)); |
339 | e->pos += sizeof(u16); |
340 | return true; |
341 | } |
342 | |
343 | fail: |
344 | e->pos = pos; |
345 | return false; |
346 | } |
347 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_array); |
348 | |
349 | VISIBLE_IF_KUNIT size_t aa_unpack_blob(struct aa_ext *e, char **blob, const char *name) |
350 | { |
351 | void *pos = e->pos; |
352 | |
353 | if (aa_unpack_nameX(e, AA_BLOB, name)) { |
354 | u32 size; |
355 | if (!aa_inbounds(e, sizeof(u32))) |
356 | goto fail; |
357 | size = le32_to_cpu(get_unaligned((__le32 *) e->pos)); |
358 | e->pos += sizeof(u32); |
359 | if (aa_inbounds(e, (size_t) size)) { |
360 | *blob = e->pos; |
361 | e->pos += size; |
362 | return size; |
363 | } |
364 | } |
365 | |
366 | fail: |
367 | e->pos = pos; |
368 | return 0; |
369 | } |
370 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_blob); |
371 | |
372 | VISIBLE_IF_KUNIT int aa_unpack_str(struct aa_ext *e, const char **string, const char *name) |
373 | { |
374 | char *src_str; |
375 | size_t size = 0; |
376 | void *pos = e->pos; |
377 | *string = NULL; |
378 | if (aa_unpack_nameX(e, AA_STRING, name)) { |
379 | size = aa_unpack_u16_chunk(e, &src_str); |
380 | if (size) { |
381 | /* strings are null terminated, length is size - 1 */ |
382 | if (src_str[size - 1] != 0) |
383 | goto fail; |
384 | *string = src_str; |
385 | |
386 | return size; |
387 | } |
388 | } |
389 | |
390 | fail: |
391 | e->pos = pos; |
392 | return 0; |
393 | } |
394 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_str); |
395 | |
396 | VISIBLE_IF_KUNIT int aa_unpack_strdup(struct aa_ext *e, char **string, const char *name) |
397 | { |
398 | const char *tmp; |
399 | void *pos = e->pos; |
400 | int res = aa_unpack_str(e, &tmp, name); |
401 | *string = NULL; |
402 | |
403 | if (!res) |
404 | return 0; |
405 | |
406 | *string = kmemdup(p: tmp, size: res, GFP_KERNEL); |
407 | if (!*string) { |
408 | e->pos = pos; |
409 | return 0; |
410 | } |
411 | |
412 | return res; |
413 | } |
414 | EXPORT_SYMBOL_IF_KUNIT(aa_unpack_strdup); |
415 | |
416 | |
417 | /** |
418 | * unpack_dfa - unpack a file rule dfa |
419 | * @e: serialized data extent information (NOT NULL) |
420 | * @flags: dfa flags to check |
421 | * |
422 | * returns dfa or ERR_PTR or NULL if no dfa |
423 | */ |
424 | static struct aa_dfa *unpack_dfa(struct aa_ext *e, int flags) |
425 | { |
426 | char *blob = NULL; |
427 | size_t size; |
428 | struct aa_dfa *dfa = NULL; |
429 | |
430 | size = aa_unpack_blob(e, &blob, "aadfa" ); |
431 | if (size) { |
432 | /* |
433 | * The dfa is aligned with in the blob to 8 bytes |
434 | * from the beginning of the stream. |
435 | * alignment adjust needed by dfa unpack |
436 | */ |
437 | size_t sz = blob - (char *) e->start - |
438 | ((e->pos - e->start) & 7); |
439 | size_t pad = ALIGN(sz, 8) - sz; |
440 | if (aa_g_paranoid_load) |
441 | flags |= DFA_FLAG_VERIFY_STATES; |
442 | dfa = aa_dfa_unpack(blob: blob + pad, size: size - pad, flags); |
443 | |
444 | if (IS_ERR(ptr: dfa)) |
445 | return dfa; |
446 | |
447 | } |
448 | |
449 | return dfa; |
450 | } |
451 | |
452 | /** |
453 | * unpack_trans_table - unpack a profile transition table |
454 | * @e: serialized data extent information (NOT NULL) |
455 | * @strs: str table to unpack to (NOT NULL) |
456 | * |
457 | * Returns: true if table successfully unpacked or not present |
458 | */ |
459 | static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) |
460 | { |
461 | void *saved_pos = e->pos; |
462 | char **table = NULL; |
463 | |
464 | /* exec table is optional */ |
465 | if (aa_unpack_nameX(e, AA_STRUCT, "xtable" )) { |
466 | u16 size; |
467 | int i; |
468 | |
469 | if (!aa_unpack_array(e, NULL, &size)) |
470 | /* |
471 | * Note: index into trans table array is a max |
472 | * of 2^24, but unpack array can only unpack |
473 | * an array of 2^16 in size atm so no need |
474 | * for size check here |
475 | */ |
476 | goto fail; |
477 | table = kcalloc(n: size, size: sizeof(char *), GFP_KERNEL); |
478 | if (!table) |
479 | goto fail; |
480 | |
481 | strs->table = table; |
482 | strs->size = size; |
483 | for (i = 0; i < size; i++) { |
484 | char *str; |
485 | int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL); |
486 | /* aa_unpack_strdup verifies that the last character is |
487 | * null termination byte. |
488 | */ |
489 | if (!size2) |
490 | goto fail; |
491 | table[i] = str; |
492 | /* verify that name doesn't start with space */ |
493 | if (isspace(*str)) |
494 | goto fail; |
495 | |
496 | /* count internal # of internal \0 */ |
497 | for (c = j = 0; j < size2 - 1; j++) { |
498 | if (!str[j]) { |
499 | pos = j; |
500 | c++; |
501 | } |
502 | } |
503 | if (*str == ':') { |
504 | /* first character after : must be valid */ |
505 | if (!str[1]) |
506 | goto fail; |
507 | /* beginning with : requires an embedded \0, |
508 | * verify that exactly 1 internal \0 exists |
509 | * trailing \0 already verified by aa_unpack_strdup |
510 | * |
511 | * convert \0 back to : for label_parse |
512 | */ |
513 | if (c == 1) |
514 | str[pos] = ':'; |
515 | else if (c > 1) |
516 | goto fail; |
517 | } else if (c) |
518 | /* fail - all other cases with embedded \0 */ |
519 | goto fail; |
520 | } |
521 | if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) |
522 | goto fail; |
523 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
524 | goto fail; |
525 | } |
526 | return true; |
527 | |
528 | fail: |
529 | aa_free_str_table(table: strs); |
530 | e->pos = saved_pos; |
531 | return false; |
532 | } |
533 | |
534 | static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) |
535 | { |
536 | void *pos = e->pos; |
537 | |
538 | if (aa_unpack_nameX(e, AA_STRUCT, "xattrs" )) { |
539 | u16 size; |
540 | int i; |
541 | |
542 | if (!aa_unpack_array(e, NULL, &size)) |
543 | goto fail; |
544 | profile->attach.xattr_count = size; |
545 | profile->attach.xattrs = kcalloc(n: size, size: sizeof(char *), GFP_KERNEL); |
546 | if (!profile->attach.xattrs) |
547 | goto fail; |
548 | for (i = 0; i < size; i++) { |
549 | if (!aa_unpack_strdup(e, &profile->attach.xattrs[i], NULL)) |
550 | goto fail; |
551 | } |
552 | if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) |
553 | goto fail; |
554 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
555 | goto fail; |
556 | } |
557 | |
558 | return true; |
559 | |
560 | fail: |
561 | e->pos = pos; |
562 | return false; |
563 | } |
564 | |
565 | static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules) |
566 | { |
567 | void *pos = e->pos; |
568 | u16 size; |
569 | int i; |
570 | |
571 | if (aa_unpack_nameX(e, AA_STRUCT, "secmark" )) { |
572 | if (!aa_unpack_array(e, NULL, &size)) |
573 | goto fail; |
574 | |
575 | rules->secmark = kcalloc(n: size, size: sizeof(struct aa_secmark), |
576 | GFP_KERNEL); |
577 | if (!rules->secmark) |
578 | goto fail; |
579 | |
580 | rules->secmark_count = size; |
581 | |
582 | for (i = 0; i < size; i++) { |
583 | if (!unpack_u8(e, data: &rules->secmark[i].audit, NULL)) |
584 | goto fail; |
585 | if (!unpack_u8(e, data: &rules->secmark[i].deny, NULL)) |
586 | goto fail; |
587 | if (!aa_unpack_strdup(e, &rules->secmark[i].label, NULL)) |
588 | goto fail; |
589 | } |
590 | if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) |
591 | goto fail; |
592 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
593 | goto fail; |
594 | } |
595 | |
596 | return true; |
597 | |
598 | fail: |
599 | if (rules->secmark) { |
600 | for (i = 0; i < size; i++) |
601 | kfree(objp: rules->secmark[i].label); |
602 | kfree(objp: rules->secmark); |
603 | rules->secmark_count = 0; |
604 | rules->secmark = NULL; |
605 | } |
606 | |
607 | e->pos = pos; |
608 | return false; |
609 | } |
610 | |
611 | static bool unpack_rlimits(struct aa_ext *e, struct aa_ruleset *rules) |
612 | { |
613 | void *pos = e->pos; |
614 | |
615 | /* rlimits are optional */ |
616 | if (aa_unpack_nameX(e, AA_STRUCT, "rlimits" )) { |
617 | u16 size; |
618 | int i; |
619 | u32 tmp = 0; |
620 | if (!aa_unpack_u32(e, &tmp, NULL)) |
621 | goto fail; |
622 | rules->rlimits.mask = tmp; |
623 | |
624 | if (!aa_unpack_array(e, NULL, &size) || |
625 | size > RLIM_NLIMITS) |
626 | goto fail; |
627 | for (i = 0; i < size; i++) { |
628 | u64 tmp2 = 0; |
629 | int a = aa_map_resource(resource: i); |
630 | if (!aa_unpack_u64(e, &tmp2, NULL)) |
631 | goto fail; |
632 | rules->rlimits.limits[a].rlim_max = tmp2; |
633 | } |
634 | if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) |
635 | goto fail; |
636 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
637 | goto fail; |
638 | } |
639 | return true; |
640 | |
641 | fail: |
642 | e->pos = pos; |
643 | return false; |
644 | } |
645 | |
646 | static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) |
647 | { |
648 | if (version != 1) |
649 | return false; |
650 | |
651 | return aa_unpack_u32(e, &perm->allow, NULL) && |
652 | aa_unpack_u32(e, &perm->allow, NULL) && |
653 | aa_unpack_u32(e, &perm->deny, NULL) && |
654 | aa_unpack_u32(e, &perm->subtree, NULL) && |
655 | aa_unpack_u32(e, &perm->cond, NULL) && |
656 | aa_unpack_u32(e, &perm->kill, NULL) && |
657 | aa_unpack_u32(e, &perm->complain, NULL) && |
658 | aa_unpack_u32(e, &perm->prompt, NULL) && |
659 | aa_unpack_u32(e, &perm->audit, NULL) && |
660 | aa_unpack_u32(e, &perm->quiet, NULL) && |
661 | aa_unpack_u32(e, &perm->hide, NULL) && |
662 | aa_unpack_u32(e, &perm->xindex, NULL) && |
663 | aa_unpack_u32(e, &perm->tag, NULL) && |
664 | aa_unpack_u32(e, &perm->label, NULL); |
665 | } |
666 | |
667 | static ssize_t unpack_perms_table(struct aa_ext *e, struct aa_perms **perms) |
668 | { |
669 | void *pos = e->pos; |
670 | u16 size = 0; |
671 | |
672 | AA_BUG(!perms); |
673 | /* |
674 | * policy perms are optional, in which case perms are embedded |
675 | * in the dfa accept table |
676 | */ |
677 | if (aa_unpack_nameX(e, AA_STRUCT, "perms" )) { |
678 | int i; |
679 | u32 version; |
680 | |
681 | if (!aa_unpack_u32(e, &version, "version" )) |
682 | goto fail_reset; |
683 | if (!aa_unpack_array(e, NULL, &size)) |
684 | goto fail_reset; |
685 | *perms = kcalloc(n: size, size: sizeof(struct aa_perms), GFP_KERNEL); |
686 | if (!*perms) |
687 | goto fail_reset; |
688 | for (i = 0; i < size; i++) { |
689 | if (!unpack_perm(e, version, perm: &(*perms)[i])) |
690 | goto fail; |
691 | } |
692 | if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) |
693 | goto fail; |
694 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
695 | goto fail; |
696 | } else |
697 | *perms = NULL; |
698 | |
699 | return size; |
700 | |
701 | fail: |
702 | kfree(objp: *perms); |
703 | fail_reset: |
704 | e->pos = pos; |
705 | return -EPROTO; |
706 | } |
707 | |
708 | static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, |
709 | bool required_dfa, bool required_trans, |
710 | const char **info) |
711 | { |
712 | struct aa_policydb *pdb; |
713 | void *pos = e->pos; |
714 | int i, flags, error = -EPROTO; |
715 | ssize_t size; |
716 | |
717 | pdb = aa_alloc_pdb(GFP_KERNEL); |
718 | if (!pdb) |
719 | return -ENOMEM; |
720 | |
721 | size = unpack_perms_table(e, perms: &pdb->perms); |
722 | if (size < 0) { |
723 | error = size; |
724 | pdb->perms = NULL; |
725 | *info = "failed to unpack - perms" ; |
726 | goto fail; |
727 | } |
728 | pdb->size = size; |
729 | |
730 | if (pdb->perms) { |
731 | /* perms table present accept is index */ |
732 | flags = TO_ACCEPT1_FLAG(YYTD_DATA32); |
733 | } else { |
734 | /* packed perms in accept1 and accept2 */ |
735 | flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | |
736 | TO_ACCEPT2_FLAG(YYTD_DATA32); |
737 | } |
738 | |
739 | pdb->dfa = unpack_dfa(e, flags); |
740 | if (IS_ERR(ptr: pdb->dfa)) { |
741 | error = PTR_ERR(ptr: pdb->dfa); |
742 | pdb->dfa = NULL; |
743 | *info = "failed to unpack - dfa" ; |
744 | goto fail; |
745 | } else if (!pdb->dfa) { |
746 | if (required_dfa) { |
747 | *info = "missing required dfa" ; |
748 | goto fail; |
749 | } |
750 | goto out; |
751 | } |
752 | |
753 | /* |
754 | * only unpack the following if a dfa is present |
755 | * |
756 | * sadly start was given different names for file and policydb |
757 | * but since it is optional we can try both |
758 | */ |
759 | if (!aa_unpack_u32(e, &pdb->start[0], "start" )) |
760 | /* default start state */ |
761 | pdb->start[0] = DFA_START; |
762 | if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start" )) { |
763 | /* default start state for xmatch and file dfa */ |
764 | pdb->start[AA_CLASS_FILE] = DFA_START; |
765 | } /* setup class index */ |
766 | for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { |
767 | pdb->start[i] = aa_dfa_next(dfa: pdb->dfa, state: pdb->start[0], |
768 | c: i); |
769 | } |
770 | if (!unpack_trans_table(e, strs: &pdb->trans) && required_trans) { |
771 | *info = "failed to unpack profile transition table" ; |
772 | goto fail; |
773 | } |
774 | |
775 | /* TODO: move compat mapping here, requires dfa merging first */ |
776 | /* TODO: move verify here, it has to be done after compat mappings */ |
777 | out: |
778 | *policy = pdb; |
779 | return 0; |
780 | |
781 | fail: |
782 | aa_put_pdb(pdb); |
783 | e->pos = pos; |
784 | return error; |
785 | } |
786 | |
787 | static u32 strhash(const void *data, u32 len, u32 seed) |
788 | { |
789 | const char * const *key = data; |
790 | |
791 | return jhash(key: *key, strlen(*key), initval: seed); |
792 | } |
793 | |
794 | static int datacmp(struct rhashtable_compare_arg *arg, const void *obj) |
795 | { |
796 | const struct aa_data *data = obj; |
797 | const char * const *key = arg->key; |
798 | |
799 | return strcmp(data->key, *key); |
800 | } |
801 | |
802 | /** |
803 | * unpack_profile - unpack a serialized profile |
804 | * @e: serialized data extent information (NOT NULL) |
805 | * @ns_name: pointer of newly allocated copy of %NULL in case of error |
806 | * |
807 | * NOTE: unpack profile sets audit struct if there is a failure |
808 | */ |
809 | static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) |
810 | { |
811 | struct aa_ruleset *rules; |
812 | struct aa_profile *profile = NULL; |
813 | const char *tmpname, *tmpns = NULL, *name = NULL; |
814 | const char *info = "failed to unpack profile" ; |
815 | size_t ns_len; |
816 | struct rhashtable_params params = { 0 }; |
817 | char *key = NULL, *disconnected = NULL; |
818 | struct aa_data *data; |
819 | int error = -EPROTO; |
820 | kernel_cap_t tmpcap; |
821 | u32 tmp; |
822 | |
823 | *ns_name = NULL; |
824 | |
825 | /* check that we have the right struct being passed */ |
826 | if (!aa_unpack_nameX(e, AA_STRUCT, "profile" )) |
827 | goto fail; |
828 | if (!aa_unpack_str(e, &name, NULL)) |
829 | goto fail; |
830 | if (*name == '\0') |
831 | goto fail; |
832 | |
833 | tmpname = aa_splitn_fqname(fqname: name, strlen(name), ns_name: &tmpns, ns_len: &ns_len); |
834 | if (tmpns) { |
835 | if (!tmpname) { |
836 | info = "empty profile name" ; |
837 | goto fail; |
838 | } |
839 | *ns_name = kstrndup(s: tmpns, len: ns_len, GFP_KERNEL); |
840 | if (!*ns_name) { |
841 | info = "out of memory" ; |
842 | error = -ENOMEM; |
843 | goto fail; |
844 | } |
845 | name = tmpname; |
846 | } |
847 | |
848 | profile = aa_alloc_profile(name, NULL, GFP_KERNEL); |
849 | if (!profile) { |
850 | info = "out of memory" ; |
851 | error = -ENOMEM; |
852 | goto fail; |
853 | } |
854 | rules = list_first_entry(&profile->rules, typeof(*rules), list); |
855 | |
856 | /* profile renaming is optional */ |
857 | (void) aa_unpack_str(e, &profile->rename, "rename" ); |
858 | |
859 | /* attachment string is optional */ |
860 | (void) aa_unpack_str(e, &profile->attach.xmatch_str, "attach" ); |
861 | |
862 | /* xmatch is optional and may be NULL */ |
863 | error = unpack_pdb(e, policy: &profile->attach.xmatch, required_dfa: false, required_trans: false, info: &info); |
864 | if (error) { |
865 | info = "bad xmatch" ; |
866 | goto fail; |
867 | } |
868 | |
869 | /* neither xmatch_len not xmatch_perms are optional if xmatch is set */ |
870 | if (profile->attach.xmatch->dfa) { |
871 | if (!aa_unpack_u32(e, &tmp, NULL)) { |
872 | info = "missing xmatch len" ; |
873 | goto fail; |
874 | } |
875 | profile->attach.xmatch_len = tmp; |
876 | profile->attach.xmatch->start[AA_CLASS_XMATCH] = DFA_START; |
877 | if (!profile->attach.xmatch->perms) { |
878 | error = aa_compat_map_xmatch(policy: profile->attach.xmatch); |
879 | if (error) { |
880 | info = "failed to convert xmatch permission table" ; |
881 | goto fail; |
882 | } |
883 | } |
884 | } |
885 | |
886 | /* disconnected attachment string is optional */ |
887 | (void) aa_unpack_strdup(e, &disconnected, "disconnected" ); |
888 | profile->disconnected = disconnected; |
889 | |
890 | /* per profile debug flags (complain, audit) */ |
891 | if (!aa_unpack_nameX(e, AA_STRUCT, "flags" )) { |
892 | info = "profile missing flags" ; |
893 | goto fail; |
894 | } |
895 | info = "failed to unpack profile flags" ; |
896 | if (!aa_unpack_u32(e, &tmp, NULL)) |
897 | goto fail; |
898 | if (tmp & PACKED_FLAG_HAT) |
899 | profile->label.flags |= FLAG_HAT; |
900 | if (tmp & PACKED_FLAG_DEBUG1) |
901 | profile->label.flags |= FLAG_DEBUG1; |
902 | if (tmp & PACKED_FLAG_DEBUG2) |
903 | profile->label.flags |= FLAG_DEBUG2; |
904 | if (!aa_unpack_u32(e, &tmp, NULL)) |
905 | goto fail; |
906 | if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) { |
907 | profile->mode = APPARMOR_COMPLAIN; |
908 | } else if (tmp == PACKED_MODE_ENFORCE) { |
909 | profile->mode = APPARMOR_ENFORCE; |
910 | } else if (tmp == PACKED_MODE_KILL) { |
911 | profile->mode = APPARMOR_KILL; |
912 | } else if (tmp == PACKED_MODE_UNCONFINED) { |
913 | profile->mode = APPARMOR_UNCONFINED; |
914 | profile->label.flags |= FLAG_UNCONFINED; |
915 | } else if (tmp == PACKED_MODE_USER) { |
916 | profile->mode = APPARMOR_USER; |
917 | } else { |
918 | goto fail; |
919 | } |
920 | if (!aa_unpack_u32(e, &tmp, NULL)) |
921 | goto fail; |
922 | if (tmp) |
923 | profile->audit = AUDIT_ALL; |
924 | |
925 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
926 | goto fail; |
927 | |
928 | /* path_flags is optional */ |
929 | if (aa_unpack_u32(e, &profile->path_flags, "path_flags" )) |
930 | profile->path_flags |= profile->label.flags & |
931 | PATH_MEDIATE_DELETED; |
932 | else |
933 | /* set a default value if path_flags field is not present */ |
934 | profile->path_flags = PATH_MEDIATE_DELETED; |
935 | |
936 | info = "failed to unpack profile capabilities" ; |
937 | if (!aa_unpack_cap_low(e, data: &rules->caps.allow, NULL)) |
938 | goto fail; |
939 | if (!aa_unpack_cap_low(e, data: &rules->caps.audit, NULL)) |
940 | goto fail; |
941 | if (!aa_unpack_cap_low(e, data: &rules->caps.quiet, NULL)) |
942 | goto fail; |
943 | if (!aa_unpack_cap_low(e, data: &tmpcap, NULL)) |
944 | goto fail; |
945 | |
946 | info = "failed to unpack upper profile capabilities" ; |
947 | if (aa_unpack_nameX(e, AA_STRUCT, "caps64" )) { |
948 | /* optional upper half of 64 bit caps */ |
949 | if (!aa_unpack_cap_high(e, data: &rules->caps.allow, NULL)) |
950 | goto fail; |
951 | if (!aa_unpack_cap_high(e, data: &rules->caps.audit, NULL)) |
952 | goto fail; |
953 | if (!aa_unpack_cap_high(e, data: &rules->caps.quiet, NULL)) |
954 | goto fail; |
955 | if (!aa_unpack_cap_high(e, data: &tmpcap, NULL)) |
956 | goto fail; |
957 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
958 | goto fail; |
959 | } |
960 | |
961 | info = "failed to unpack extended profile capabilities" ; |
962 | if (aa_unpack_nameX(e, AA_STRUCT, "capsx" )) { |
963 | /* optional extended caps mediation mask */ |
964 | if (!aa_unpack_cap_low(e, data: &rules->caps.extended, NULL)) |
965 | goto fail; |
966 | if (!aa_unpack_cap_high(e, data: &rules->caps.extended, NULL)) |
967 | goto fail; |
968 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
969 | goto fail; |
970 | } |
971 | |
972 | if (!unpack_xattrs(e, profile)) { |
973 | info = "failed to unpack profile xattrs" ; |
974 | goto fail; |
975 | } |
976 | |
977 | if (!unpack_rlimits(e, rules)) { |
978 | info = "failed to unpack profile rlimits" ; |
979 | goto fail; |
980 | } |
981 | |
982 | if (!unpack_secmark(e, rules)) { |
983 | info = "failed to unpack profile secmark rules" ; |
984 | goto fail; |
985 | } |
986 | |
987 | if (aa_unpack_nameX(e, AA_STRUCT, "policydb" )) { |
988 | /* generic policy dfa - optional and may be NULL */ |
989 | info = "failed to unpack policydb" ; |
990 | error = unpack_pdb(e, policy: &rules->policy, required_dfa: true, required_trans: false, |
991 | info: &info); |
992 | if (error) |
993 | goto fail; |
994 | /* Fixup: drop when we get rid of start array */ |
995 | if (aa_dfa_next(dfa: rules->policy->dfa, state: rules->policy->start[0], |
996 | AA_CLASS_FILE)) |
997 | rules->policy->start[AA_CLASS_FILE] = |
998 | aa_dfa_next(dfa: rules->policy->dfa, |
999 | state: rules->policy->start[0], |
1000 | AA_CLASS_FILE); |
1001 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) |
1002 | goto fail; |
1003 | if (!rules->policy->perms) { |
1004 | error = aa_compat_map_policy(policy: rules->policy, |
1005 | version: e->version); |
1006 | if (error) { |
1007 | info = "failed to remap policydb permission table" ; |
1008 | goto fail; |
1009 | } |
1010 | } |
1011 | } else { |
1012 | rules->policy = aa_get_pdb(pdb: nullpdb); |
1013 | } |
1014 | /* get file rules */ |
1015 | error = unpack_pdb(e, policy: &rules->file, required_dfa: false, required_trans: true, info: &info); |
1016 | if (error) { |
1017 | goto fail; |
1018 | } else if (rules->file->dfa) { |
1019 | if (!rules->file->perms) { |
1020 | error = aa_compat_map_file(policy: rules->file); |
1021 | if (error) { |
1022 | info = "failed to remap file permission table" ; |
1023 | goto fail; |
1024 | } |
1025 | } |
1026 | } else if (rules->policy->dfa && |
1027 | rules->policy->start[AA_CLASS_FILE]) { |
1028 | aa_put_pdb(pdb: rules->file); |
1029 | rules->file = aa_get_pdb(pdb: rules->policy); |
1030 | } else { |
1031 | aa_put_pdb(pdb: rules->file); |
1032 | rules->file = aa_get_pdb(pdb: nullpdb); |
1033 | } |
1034 | error = -EPROTO; |
1035 | if (aa_unpack_nameX(e, AA_STRUCT, "data" )) { |
1036 | info = "out of memory" ; |
1037 | profile->data = kzalloc(size: sizeof(*profile->data), GFP_KERNEL); |
1038 | if (!profile->data) { |
1039 | error = -ENOMEM; |
1040 | goto fail; |
1041 | } |
1042 | params.nelem_hint = 3; |
1043 | params.key_len = sizeof(void *); |
1044 | params.key_offset = offsetof(struct aa_data, key); |
1045 | params.head_offset = offsetof(struct aa_data, head); |
1046 | params.hashfn = strhash; |
1047 | params.obj_cmpfn = datacmp; |
1048 | |
1049 | if (rhashtable_init(ht: profile->data, params: ¶ms)) { |
1050 | info = "failed to init key, value hash table" ; |
1051 | goto fail; |
1052 | } |
1053 | |
1054 | while (aa_unpack_strdup(e, &key, NULL)) { |
1055 | data = kzalloc(size: sizeof(*data), GFP_KERNEL); |
1056 | if (!data) { |
1057 | kfree_sensitive(objp: key); |
1058 | error = -ENOMEM; |
1059 | goto fail; |
1060 | } |
1061 | |
1062 | data->key = key; |
1063 | data->size = aa_unpack_blob(e, &data->data, NULL); |
1064 | data->data = kvmemdup(src: data->data, len: data->size, GFP_KERNEL); |
1065 | if (data->size && !data->data) { |
1066 | kfree_sensitive(objp: data->key); |
1067 | kfree_sensitive(objp: data); |
1068 | error = -ENOMEM; |
1069 | goto fail; |
1070 | } |
1071 | |
1072 | if (rhashtable_insert_fast(ht: profile->data, obj: &data->head, |
1073 | params: profile->data->p)) { |
1074 | kfree_sensitive(objp: data->key); |
1075 | kfree_sensitive(objp: data); |
1076 | info = "failed to insert data to table" ; |
1077 | goto fail; |
1078 | } |
1079 | } |
1080 | |
1081 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) { |
1082 | info = "failed to unpack end of key, value data table" ; |
1083 | goto fail; |
1084 | } |
1085 | } |
1086 | |
1087 | if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) { |
1088 | info = "failed to unpack end of profile" ; |
1089 | goto fail; |
1090 | } |
1091 | |
1092 | return profile; |
1093 | |
1094 | fail: |
1095 | if (error == 0) |
1096 | /* default error covers most cases */ |
1097 | error = -EPROTO; |
1098 | if (*ns_name) { |
1099 | kfree(objp: *ns_name); |
1100 | *ns_name = NULL; |
1101 | } |
1102 | if (profile) |
1103 | name = NULL; |
1104 | else if (!name) |
1105 | name = "unknown" ; |
1106 | audit_iface(new: profile, NULL, name, info, e, error); |
1107 | aa_free_profile(profile); |
1108 | |
1109 | return ERR_PTR(error); |
1110 | } |
1111 | |
1112 | /** |
1113 | * verify_header - unpack serialized stream header |
1114 | * @e: serialized data read head (NOT NULL) |
1115 | * @required: whether the header is required or optional |
1116 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) |
1117 | * |
1118 | * Returns: error or 0 if header is good |
1119 | */ |
1120 | static int (struct aa_ext *e, int required, const char **ns) |
1121 | { |
1122 | int error = -EPROTONOSUPPORT; |
1123 | const char *name = NULL; |
1124 | *ns = NULL; |
1125 | |
1126 | /* get the interface version */ |
1127 | if (!aa_unpack_u32(e, &e->version, "version" )) { |
1128 | if (required) { |
1129 | audit_iface(NULL, NULL, NULL, info: "invalid profile format" , |
1130 | e, error); |
1131 | return error; |
1132 | } |
1133 | } |
1134 | |
1135 | /* Check that the interface version is currently supported. |
1136 | * if not specified use previous version |
1137 | * Mask off everything that is not kernel abi version |
1138 | */ |
1139 | if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v9)) { |
1140 | audit_iface(NULL, NULL, NULL, info: "unsupported interface version" , |
1141 | e, error); |
1142 | return error; |
1143 | } |
1144 | |
1145 | /* read the namespace if present */ |
1146 | if (aa_unpack_str(e, &name, "namespace" )) { |
1147 | if (*name == '\0') { |
1148 | audit_iface(NULL, NULL, NULL, info: "invalid namespace name" , |
1149 | e, error); |
1150 | return error; |
1151 | } |
1152 | if (*ns && strcmp(*ns, name)) { |
1153 | audit_iface(NULL, NULL, NULL, info: "invalid ns change" , e, |
1154 | error); |
1155 | } else if (!*ns) { |
1156 | *ns = kstrdup(s: name, GFP_KERNEL); |
1157 | if (!*ns) |
1158 | return -ENOMEM; |
1159 | } |
1160 | } |
1161 | |
1162 | return 0; |
1163 | } |
1164 | |
1165 | /** |
1166 | * verify_dfa_accept_index - verify accept indexes are in range of perms table |
1167 | * @dfa: the dfa to check accept indexes are in range |
1168 | * @table_size: the permission table size the indexes should be within |
1169 | */ |
1170 | static bool verify_dfa_accept_index(struct aa_dfa *dfa, int table_size) |
1171 | { |
1172 | int i; |
1173 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { |
1174 | if (ACCEPT_TABLE(dfa)[i] >= table_size) |
1175 | return false; |
1176 | } |
1177 | return true; |
1178 | } |
1179 | |
1180 | static bool verify_perm(struct aa_perms *perm) |
1181 | { |
1182 | /* TODO: allow option to just force the perms into a valid state */ |
1183 | if (perm->allow & perm->deny) |
1184 | return false; |
1185 | if (perm->subtree & ~perm->allow) |
1186 | return false; |
1187 | if (perm->cond & (perm->allow | perm->deny)) |
1188 | return false; |
1189 | if (perm->kill & perm->allow) |
1190 | return false; |
1191 | if (perm->complain & (perm->allow | perm->deny)) |
1192 | return false; |
1193 | if (perm->prompt & (perm->allow | perm->deny)) |
1194 | return false; |
1195 | if (perm->complain & perm->prompt) |
1196 | return false; |
1197 | if (perm->hide & perm->allow) |
1198 | return false; |
1199 | |
1200 | return true; |
1201 | } |
1202 | |
1203 | static bool verify_perms(struct aa_policydb *pdb) |
1204 | { |
1205 | int i; |
1206 | |
1207 | for (i = 0; i < pdb->size; i++) { |
1208 | if (!verify_perm(perm: &pdb->perms[i])) |
1209 | return false; |
1210 | /* verify indexes into str table */ |
1211 | if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE && |
1212 | (pdb->perms[i].xindex & AA_X_INDEX_MASK) >= pdb->trans.size) |
1213 | return false; |
1214 | if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size) |
1215 | return false; |
1216 | if (pdb->perms[i].label && |
1217 | pdb->perms[i].label >= pdb->trans.size) |
1218 | return false; |
1219 | } |
1220 | |
1221 | return true; |
1222 | } |
1223 | |
1224 | /** |
1225 | * verify_profile - Do post unpack analysis to verify profile consistency |
1226 | * @profile: profile to verify (NOT NULL) |
1227 | * |
1228 | * Returns: 0 if passes verification else error |
1229 | * |
1230 | * This verification is post any unpack mapping or changes |
1231 | */ |
1232 | static int verify_profile(struct aa_profile *profile) |
1233 | { |
1234 | struct aa_ruleset *rules = list_first_entry(&profile->rules, |
1235 | typeof(*rules), list); |
1236 | if (!rules) |
1237 | return 0; |
1238 | |
1239 | if (rules->file->dfa && !verify_dfa_accept_index(dfa: rules->file->dfa, |
1240 | table_size: rules->file->size)) { |
1241 | audit_iface(new: profile, NULL, NULL, |
1242 | info: "Unpack: file Invalid named transition" , NULL, |
1243 | error: -EPROTO); |
1244 | return -EPROTO; |
1245 | } |
1246 | if (rules->policy->dfa && |
1247 | !verify_dfa_accept_index(dfa: rules->policy->dfa, table_size: rules->policy->size)) { |
1248 | audit_iface(new: profile, NULL, NULL, |
1249 | info: "Unpack: policy Invalid named transition" , NULL, |
1250 | error: -EPROTO); |
1251 | return -EPROTO; |
1252 | } |
1253 | |
1254 | if (!verify_perms(pdb: rules->file)) { |
1255 | audit_iface(new: profile, NULL, NULL, |
1256 | info: "Unpack: Invalid perm index" , NULL, error: -EPROTO); |
1257 | return -EPROTO; |
1258 | } |
1259 | if (!verify_perms(pdb: rules->policy)) { |
1260 | audit_iface(new: profile, NULL, NULL, |
1261 | info: "Unpack: Invalid perm index" , NULL, error: -EPROTO); |
1262 | return -EPROTO; |
1263 | } |
1264 | if (!verify_perms(pdb: profile->attach.xmatch)) { |
1265 | audit_iface(new: profile, NULL, NULL, |
1266 | info: "Unpack: Invalid perm index" , NULL, error: -EPROTO); |
1267 | return -EPROTO; |
1268 | } |
1269 | |
1270 | return 0; |
1271 | } |
1272 | |
1273 | void aa_load_ent_free(struct aa_load_ent *ent) |
1274 | { |
1275 | if (ent) { |
1276 | aa_put_profile(p: ent->rename); |
1277 | aa_put_profile(p: ent->old); |
1278 | aa_put_profile(p: ent->new); |
1279 | kfree(objp: ent->ns_name); |
1280 | kfree_sensitive(objp: ent); |
1281 | } |
1282 | } |
1283 | |
1284 | struct aa_load_ent *aa_load_ent_alloc(void) |
1285 | { |
1286 | struct aa_load_ent *ent = kzalloc(size: sizeof(*ent), GFP_KERNEL); |
1287 | if (ent) |
1288 | INIT_LIST_HEAD(list: &ent->list); |
1289 | return ent; |
1290 | } |
1291 | |
1292 | static int compress_zstd(const char *src, size_t slen, char **dst, size_t *dlen) |
1293 | { |
1294 | #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY |
1295 | const zstd_parameters params = |
1296 | zstd_get_params(level: aa_g_rawdata_compression_level, estimated_src_size: slen); |
1297 | const size_t wksp_len = zstd_cctx_workspace_bound(parameters: ¶ms.cParams); |
1298 | void *wksp = NULL; |
1299 | zstd_cctx *ctx = NULL; |
1300 | size_t out_len = zstd_compress_bound(src_size: slen); |
1301 | void *out = NULL; |
1302 | int ret = 0; |
1303 | |
1304 | out = kvzalloc(size: out_len, GFP_KERNEL); |
1305 | if (!out) { |
1306 | ret = -ENOMEM; |
1307 | goto cleanup; |
1308 | } |
1309 | |
1310 | wksp = kvzalloc(size: wksp_len, GFP_KERNEL); |
1311 | if (!wksp) { |
1312 | ret = -ENOMEM; |
1313 | goto cleanup; |
1314 | } |
1315 | |
1316 | ctx = zstd_init_cctx(workspace: wksp, workspace_size: wksp_len); |
1317 | if (!ctx) { |
1318 | ret = -EINVAL; |
1319 | goto cleanup; |
1320 | } |
1321 | |
1322 | out_len = zstd_compress_cctx(cctx: ctx, dst: out, dst_capacity: out_len, src, src_size: slen, parameters: ¶ms); |
1323 | if (zstd_is_error(code: out_len) || out_len >= slen) { |
1324 | ret = -EINVAL; |
1325 | goto cleanup; |
1326 | } |
1327 | |
1328 | if (is_vmalloc_addr(x: out)) { |
1329 | *dst = kvzalloc(size: out_len, GFP_KERNEL); |
1330 | if (*dst) { |
1331 | memcpy(*dst, out, out_len); |
1332 | kvfree(addr: out); |
1333 | out = NULL; |
1334 | } |
1335 | } else { |
1336 | /* |
1337 | * If the staging buffer was kmalloc'd, then using krealloc is |
1338 | * probably going to be faster. The destination buffer will |
1339 | * always be smaller, so it's just shrunk, avoiding a memcpy |
1340 | */ |
1341 | *dst = krealloc(objp: out, new_size: out_len, GFP_KERNEL); |
1342 | } |
1343 | |
1344 | if (!*dst) { |
1345 | ret = -ENOMEM; |
1346 | goto cleanup; |
1347 | } |
1348 | |
1349 | *dlen = out_len; |
1350 | |
1351 | cleanup: |
1352 | if (ret) { |
1353 | kvfree(addr: out); |
1354 | *dst = NULL; |
1355 | } |
1356 | |
1357 | kvfree(addr: wksp); |
1358 | return ret; |
1359 | #else |
1360 | *dlen = slen; |
1361 | return 0; |
1362 | #endif |
1363 | } |
1364 | |
1365 | static int compress_loaddata(struct aa_loaddata *data) |
1366 | { |
1367 | AA_BUG(data->compressed_size > 0); |
1368 | |
1369 | /* |
1370 | * Shortcut the no compression case, else we increase the amount of |
1371 | * storage required by a small amount |
1372 | */ |
1373 | if (aa_g_rawdata_compression_level != 0) { |
1374 | void *udata = data->data; |
1375 | int error = compress_zstd(src: udata, slen: data->size, dst: &data->data, |
1376 | dlen: &data->compressed_size); |
1377 | if (error) { |
1378 | data->compressed_size = data->size; |
1379 | return error; |
1380 | } |
1381 | if (udata != data->data) |
1382 | kvfree(addr: udata); |
1383 | } else |
1384 | data->compressed_size = data->size; |
1385 | |
1386 | return 0; |
1387 | } |
1388 | |
1389 | /** |
1390 | * aa_unpack - unpack packed binary profile(s) data loaded from user space |
1391 | * @udata: user data copied to kmem (NOT NULL) |
1392 | * @lh: list to place unpacked profiles in a aa_repl_ws |
1393 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) |
1394 | * |
1395 | * Unpack user data and return refcounted allocated profile(s) stored in |
1396 | * @lh in order of discovery, with the list chain stored in base.list |
1397 | * or error |
1398 | * |
1399 | * Returns: profile(s) on @lh else error pointer if fails to unpack |
1400 | */ |
1401 | int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, |
1402 | const char **ns) |
1403 | { |
1404 | struct aa_load_ent *tmp, *ent; |
1405 | struct aa_profile *profile = NULL; |
1406 | char *ns_name = NULL; |
1407 | int error; |
1408 | struct aa_ext e = { |
1409 | .start = udata->data, |
1410 | .end = udata->data + udata->size, |
1411 | .pos = udata->data, |
1412 | }; |
1413 | |
1414 | *ns = NULL; |
1415 | while (e.pos < e.end) { |
1416 | void *start; |
1417 | error = verify_header(e: &e, required: e.pos == e.start, ns); |
1418 | if (error) |
1419 | goto fail; |
1420 | |
1421 | start = e.pos; |
1422 | profile = unpack_profile(e: &e, ns_name: &ns_name); |
1423 | if (IS_ERR(ptr: profile)) { |
1424 | error = PTR_ERR(ptr: profile); |
1425 | goto fail; |
1426 | } |
1427 | |
1428 | error = verify_profile(profile); |
1429 | if (error) |
1430 | goto fail_profile; |
1431 | |
1432 | if (aa_g_hash_policy) |
1433 | error = aa_calc_profile_hash(profile, version: e.version, start, |
1434 | len: e.pos - start); |
1435 | if (error) |
1436 | goto fail_profile; |
1437 | |
1438 | ent = aa_load_ent_alloc(); |
1439 | if (!ent) { |
1440 | error = -ENOMEM; |
1441 | goto fail_profile; |
1442 | } |
1443 | |
1444 | ent->new = profile; |
1445 | ent->ns_name = ns_name; |
1446 | ns_name = NULL; |
1447 | list_add_tail(new: &ent->list, head: lh); |
1448 | } |
1449 | udata->abi = e.version & K_ABI_MASK; |
1450 | if (aa_g_hash_policy) { |
1451 | udata->hash = aa_calc_hash(data: udata->data, len: udata->size); |
1452 | if (IS_ERR(ptr: udata->hash)) { |
1453 | error = PTR_ERR(ptr: udata->hash); |
1454 | udata->hash = NULL; |
1455 | goto fail; |
1456 | } |
1457 | } |
1458 | |
1459 | if (aa_g_export_binary) { |
1460 | error = compress_loaddata(data: udata); |
1461 | if (error) |
1462 | goto fail; |
1463 | } |
1464 | return 0; |
1465 | |
1466 | fail_profile: |
1467 | kfree(objp: ns_name); |
1468 | aa_put_profile(p: profile); |
1469 | |
1470 | fail: |
1471 | list_for_each_entry_safe(ent, tmp, lh, list) { |
1472 | list_del_init(entry: &ent->list); |
1473 | aa_load_ent_free(ent); |
1474 | } |
1475 | |
1476 | return error; |
1477 | } |
1478 | |