.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * AppArmor security module |
---|
3 | 4 | * |
---|
.. | .. |
---|
7 | 8 | * Copyright (C) 1998-2008 Novell/SUSE |
---|
8 | 9 | * Copyright 2009-2010 Canonical Ltd. |
---|
9 | 10 | * |
---|
10 | | - * This program is free software; you can redistribute it and/or |
---|
11 | | - * modify it under the terms of the GNU General Public License as |
---|
12 | | - * published by the Free Software Foundation, version 2 of the |
---|
13 | | - * License. |
---|
14 | | - * |
---|
15 | 11 | * AppArmor uses a serialized binary format for loading policy. To find |
---|
16 | 12 | * policy format documentation see Documentation/admin-guide/LSM/apparmor.rst |
---|
17 | 13 | * All policy is validated before it is used. |
---|
.. | .. |
---|
20 | 16 | #include <asm/unaligned.h> |
---|
21 | 17 | #include <linux/ctype.h> |
---|
22 | 18 | #include <linux/errno.h> |
---|
| 19 | +#include <linux/zlib.h> |
---|
23 | 20 | |
---|
24 | 21 | #include "include/apparmor.h" |
---|
25 | 22 | #include "include/audit.h" |
---|
.. | .. |
---|
143 | 140 | { |
---|
144 | 141 | if (l->size != r->size) |
---|
145 | 142 | return false; |
---|
| 143 | + if (l->compressed_size != r->compressed_size) |
---|
| 144 | + return false; |
---|
146 | 145 | if (aa_g_hash_policy && memcmp(l->hash, r->hash, aa_hash_size()) != 0) |
---|
147 | 146 | return false; |
---|
148 | | - return memcmp(l->data, r->data, r->size) == 0; |
---|
| 147 | + return memcmp(l->data, r->data, r->compressed_size ?: r->size) == 0; |
---|
149 | 148 | } |
---|
150 | 149 | |
---|
151 | 150 | /* |
---|
.. | .. |
---|
164 | 163 | aa_put_ns(ns); |
---|
165 | 164 | } |
---|
166 | 165 | |
---|
167 | | - kzfree(d->hash); |
---|
168 | | - kzfree(d->name); |
---|
| 166 | + kfree_sensitive(d->hash); |
---|
| 167 | + kfree_sensitive(d->name); |
---|
169 | 168 | kvfree(d->data); |
---|
170 | | - kzfree(d); |
---|
| 169 | + kfree_sensitive(d); |
---|
171 | 170 | } |
---|
172 | 171 | |
---|
173 | 172 | void aa_loaddata_kref(struct kref *kref) |
---|
.. | .. |
---|
244 | 243 | static bool unpack_X(struct aa_ext *e, enum aa_code code) |
---|
245 | 244 | { |
---|
246 | 245 | if (!inbounds(e, 1)) |
---|
247 | | - return 0; |
---|
| 246 | + return false; |
---|
248 | 247 | if (*(u8 *) e->pos != code) |
---|
249 | | - return 0; |
---|
| 248 | + return false; |
---|
250 | 249 | e->pos++; |
---|
251 | | - return 1; |
---|
| 250 | + return true; |
---|
252 | 251 | } |
---|
253 | 252 | |
---|
254 | 253 | /** |
---|
.. | .. |
---|
262 | 261 | * name element in the stream. If @name is NULL any name element will be |
---|
263 | 262 | * skipped and only the typecode will be tested. |
---|
264 | 263 | * |
---|
265 | | - * Returns 1 on success (both type code and name tests match) and the read |
---|
| 264 | + * Returns true on success (both type code and name tests match) and the read |
---|
266 | 265 | * head is advanced past the headers |
---|
267 | 266 | * |
---|
268 | | - * Returns: 0 if either match fails, the read head does not move |
---|
| 267 | + * Returns: false if either match fails, the read head does not move |
---|
269 | 268 | */ |
---|
270 | 269 | static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) |
---|
271 | 270 | { |
---|
.. | .. |
---|
290 | 289 | |
---|
291 | 290 | /* now check if type code matches */ |
---|
292 | 291 | if (unpack_X(e, code)) |
---|
293 | | - return 1; |
---|
| 292 | + return true; |
---|
294 | 293 | |
---|
295 | 294 | fail: |
---|
296 | 295 | e->pos = pos; |
---|
297 | | - return 0; |
---|
| 296 | + return false; |
---|
| 297 | +} |
---|
| 298 | + |
---|
| 299 | +static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name) |
---|
| 300 | +{ |
---|
| 301 | + void *pos = e->pos; |
---|
| 302 | + |
---|
| 303 | + if (unpack_nameX(e, AA_U8, name)) { |
---|
| 304 | + if (!inbounds(e, sizeof(u8))) |
---|
| 305 | + goto fail; |
---|
| 306 | + if (data) |
---|
| 307 | + *data = get_unaligned((u8 *)e->pos); |
---|
| 308 | + e->pos += sizeof(u8); |
---|
| 309 | + return true; |
---|
| 310 | + } |
---|
| 311 | + |
---|
| 312 | +fail: |
---|
| 313 | + e->pos = pos; |
---|
| 314 | + return false; |
---|
298 | 315 | } |
---|
299 | 316 | |
---|
300 | 317 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) |
---|
.. | .. |
---|
307 | 324 | if (data) |
---|
308 | 325 | *data = le32_to_cpu(get_unaligned((__le32 *) e->pos)); |
---|
309 | 326 | e->pos += sizeof(u32); |
---|
310 | | - return 1; |
---|
| 327 | + return true; |
---|
311 | 328 | } |
---|
312 | 329 | |
---|
313 | 330 | fail: |
---|
314 | 331 | e->pos = pos; |
---|
315 | | - return 0; |
---|
| 332 | + return false; |
---|
316 | 333 | } |
---|
317 | 334 | |
---|
318 | 335 | static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) |
---|
.. | .. |
---|
325 | 342 | if (data) |
---|
326 | 343 | *data = le64_to_cpu(get_unaligned((__le64 *) e->pos)); |
---|
327 | 344 | e->pos += sizeof(u64); |
---|
328 | | - return 1; |
---|
| 345 | + return true; |
---|
329 | 346 | } |
---|
330 | 347 | |
---|
331 | 348 | fail: |
---|
332 | 349 | e->pos = pos; |
---|
333 | | - return 0; |
---|
| 350 | + return false; |
---|
334 | 351 | } |
---|
335 | 352 | |
---|
336 | 353 | static size_t unpack_array(struct aa_ext *e, const char *name) |
---|
.. | .. |
---|
455 | 472 | * @e: serialized data extent information (NOT NULL) |
---|
456 | 473 | * @profile: profile to add the accept table to (NOT NULL) |
---|
457 | 474 | * |
---|
458 | | - * Returns: 1 if table successfully unpacked |
---|
| 475 | + * Returns: true if table successfully unpacked |
---|
459 | 476 | */ |
---|
460 | 477 | static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) |
---|
461 | 478 | { |
---|
.. | .. |
---|
518 | 535 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
---|
519 | 536 | goto fail; |
---|
520 | 537 | } |
---|
521 | | - return 1; |
---|
| 538 | + return true; |
---|
522 | 539 | |
---|
523 | 540 | fail: |
---|
524 | 541 | aa_free_domain_entries(&profile->file.trans); |
---|
525 | 542 | e->pos = saved_pos; |
---|
526 | | - return 0; |
---|
| 543 | + return false; |
---|
527 | 544 | } |
---|
528 | 545 | |
---|
529 | 546 | static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) |
---|
.. | .. |
---|
548 | 565 | goto fail; |
---|
549 | 566 | } |
---|
550 | 567 | |
---|
551 | | - return 1; |
---|
| 568 | + return true; |
---|
552 | 569 | |
---|
553 | 570 | fail: |
---|
554 | 571 | e->pos = pos; |
---|
555 | | - return 0; |
---|
| 572 | + return false; |
---|
| 573 | +} |
---|
| 574 | + |
---|
| 575 | +static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile) |
---|
| 576 | +{ |
---|
| 577 | + void *pos = e->pos; |
---|
| 578 | + int i, size; |
---|
| 579 | + |
---|
| 580 | + if (unpack_nameX(e, AA_STRUCT, "secmark")) { |
---|
| 581 | + size = unpack_array(e, NULL); |
---|
| 582 | + |
---|
| 583 | + profile->secmark = kcalloc(size, sizeof(struct aa_secmark), |
---|
| 584 | + GFP_KERNEL); |
---|
| 585 | + if (!profile->secmark) |
---|
| 586 | + goto fail; |
---|
| 587 | + |
---|
| 588 | + profile->secmark_count = size; |
---|
| 589 | + |
---|
| 590 | + for (i = 0; i < size; i++) { |
---|
| 591 | + if (!unpack_u8(e, &profile->secmark[i].audit, NULL)) |
---|
| 592 | + goto fail; |
---|
| 593 | + if (!unpack_u8(e, &profile->secmark[i].deny, NULL)) |
---|
| 594 | + goto fail; |
---|
| 595 | + if (!unpack_strdup(e, &profile->secmark[i].label, NULL)) |
---|
| 596 | + goto fail; |
---|
| 597 | + } |
---|
| 598 | + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
---|
| 599 | + goto fail; |
---|
| 600 | + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
---|
| 601 | + goto fail; |
---|
| 602 | + } |
---|
| 603 | + |
---|
| 604 | + return true; |
---|
| 605 | + |
---|
| 606 | +fail: |
---|
| 607 | + if (profile->secmark) { |
---|
| 608 | + for (i = 0; i < size; i++) |
---|
| 609 | + kfree(profile->secmark[i].label); |
---|
| 610 | + kfree(profile->secmark); |
---|
| 611 | + profile->secmark_count = 0; |
---|
| 612 | + profile->secmark = NULL; |
---|
| 613 | + } |
---|
| 614 | + |
---|
| 615 | + e->pos = pos; |
---|
| 616 | + return false; |
---|
556 | 617 | } |
---|
557 | 618 | |
---|
558 | 619 | static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) |
---|
.. | .. |
---|
582 | 643 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
---|
583 | 644 | goto fail; |
---|
584 | 645 | } |
---|
585 | | - return 1; |
---|
| 646 | + return true; |
---|
586 | 647 | |
---|
587 | 648 | fail: |
---|
588 | 649 | e->pos = pos; |
---|
589 | | - return 0; |
---|
| 650 | + return false; |
---|
590 | 651 | } |
---|
591 | 652 | |
---|
592 | 653 | static u32 strhash(const void *data, u32 len, u32 seed) |
---|
.. | .. |
---|
685 | 746 | profile->label.flags |= FLAG_HAT; |
---|
686 | 747 | if (!unpack_u32(e, &tmp, NULL)) |
---|
687 | 748 | goto fail; |
---|
688 | | - if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) |
---|
| 749 | + if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) { |
---|
689 | 750 | profile->mode = APPARMOR_COMPLAIN; |
---|
690 | | - else if (tmp == PACKED_MODE_KILL) |
---|
| 751 | + } else if (tmp == PACKED_MODE_ENFORCE) { |
---|
| 752 | + profile->mode = APPARMOR_ENFORCE; |
---|
| 753 | + } else if (tmp == PACKED_MODE_KILL) { |
---|
691 | 754 | profile->mode = APPARMOR_KILL; |
---|
692 | | - else if (tmp == PACKED_MODE_UNCONFINED) |
---|
| 755 | + } else if (tmp == PACKED_MODE_UNCONFINED) { |
---|
693 | 756 | profile->mode = APPARMOR_UNCONFINED; |
---|
| 757 | + profile->label.flags |= FLAG_UNCONFINED; |
---|
| 758 | + } else { |
---|
| 759 | + goto fail; |
---|
| 760 | + } |
---|
694 | 761 | if (!unpack_u32(e, &tmp, NULL)) |
---|
695 | 762 | goto fail; |
---|
696 | 763 | if (tmp) |
---|
.. | .. |
---|
750 | 817 | |
---|
751 | 818 | if (!unpack_rlimits(e, profile)) { |
---|
752 | 819 | info = "failed to unpack profile rlimits"; |
---|
| 820 | + goto fail; |
---|
| 821 | + } |
---|
| 822 | + |
---|
| 823 | + if (!unpack_secmark(e, profile)) { |
---|
| 824 | + info = "failed to unpack profile secmark rules"; |
---|
753 | 825 | goto fail; |
---|
754 | 826 | } |
---|
755 | 827 | |
---|
.. | .. |
---|
824 | 896 | while (unpack_strdup(e, &key, NULL)) { |
---|
825 | 897 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
---|
826 | 898 | if (!data) { |
---|
827 | | - kzfree(key); |
---|
| 899 | + kfree_sensitive(key); |
---|
828 | 900 | goto fail; |
---|
829 | 901 | } |
---|
830 | 902 | |
---|
.. | .. |
---|
832 | 904 | data->size = unpack_blob(e, &data->data, NULL); |
---|
833 | 905 | data->data = kvmemdup(data->data, data->size); |
---|
834 | 906 | if (data->size && !data->data) { |
---|
835 | | - kzfree(data->key); |
---|
836 | | - kzfree(data); |
---|
| 907 | + kfree_sensitive(data->key); |
---|
| 908 | + kfree_sensitive(data); |
---|
837 | 909 | goto fail; |
---|
838 | 910 | } |
---|
839 | 911 | |
---|
840 | | - rhashtable_insert_fast(profile->data, &data->head, |
---|
841 | | - profile->data->p); |
---|
| 912 | + if (rhashtable_insert_fast(profile->data, &data->head, |
---|
| 913 | + profile->data->p)) { |
---|
| 914 | + kfree_sensitive(data->key); |
---|
| 915 | + kfree_sensitive(data); |
---|
| 916 | + info = "failed to insert data to table"; |
---|
| 917 | + goto fail; |
---|
| 918 | + } |
---|
842 | 919 | } |
---|
843 | 920 | |
---|
844 | 921 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { |
---|
.. | .. |
---|
892 | 969 | * if not specified use previous version |
---|
893 | 970 | * Mask off everything that is not kernel abi version |
---|
894 | 971 | */ |
---|
895 | | - if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) { |
---|
| 972 | + if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v8)) { |
---|
896 | 973 | audit_iface(NULL, NULL, NULL, "unsupported interface version", |
---|
897 | 974 | e, error); |
---|
898 | 975 | return error; |
---|
.. | .. |
---|
905 | 982 | e, error); |
---|
906 | 983 | return error; |
---|
907 | 984 | } |
---|
908 | | - if (*ns && strcmp(*ns, name)) |
---|
| 985 | + if (*ns && strcmp(*ns, name)) { |
---|
909 | 986 | audit_iface(NULL, NULL, NULL, "invalid ns change", e, |
---|
910 | 987 | error); |
---|
911 | | - else if (!*ns) |
---|
912 | | - *ns = name; |
---|
| 988 | + } else if (!*ns) { |
---|
| 989 | + *ns = kstrdup(name, GFP_KERNEL); |
---|
| 990 | + if (!*ns) |
---|
| 991 | + return -ENOMEM; |
---|
| 992 | + } |
---|
913 | 993 | } |
---|
914 | 994 | |
---|
915 | 995 | return 0; |
---|
.. | .. |
---|
921 | 1001 | xtype = xindex & AA_X_TYPE_MASK; |
---|
922 | 1002 | index = xindex & AA_X_INDEX_MASK; |
---|
923 | 1003 | if (xtype == AA_X_TABLE && index >= table_size) |
---|
924 | | - return 0; |
---|
925 | | - return 1; |
---|
| 1004 | + return false; |
---|
| 1005 | + return true; |
---|
926 | 1006 | } |
---|
927 | 1007 | |
---|
928 | 1008 | /* verify dfa xindexes are in range of transition tables */ |
---|
.. | .. |
---|
931 | 1011 | int i; |
---|
932 | 1012 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { |
---|
933 | 1013 | if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) |
---|
934 | | - return 0; |
---|
| 1014 | + return false; |
---|
935 | 1015 | if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) |
---|
936 | | - return 0; |
---|
| 1016 | + return false; |
---|
937 | 1017 | } |
---|
938 | | - return 1; |
---|
| 1018 | + return true; |
---|
939 | 1019 | } |
---|
940 | 1020 | |
---|
941 | 1021 | /** |
---|
.. | .. |
---|
964 | 1044 | aa_put_profile(ent->old); |
---|
965 | 1045 | aa_put_profile(ent->new); |
---|
966 | 1046 | kfree(ent->ns_name); |
---|
967 | | - kzfree(ent); |
---|
| 1047 | + kfree_sensitive(ent); |
---|
968 | 1048 | } |
---|
969 | 1049 | } |
---|
970 | 1050 | |
---|
.. | .. |
---|
974 | 1054 | if (ent) |
---|
975 | 1055 | INIT_LIST_HEAD(&ent->list); |
---|
976 | 1056 | return ent; |
---|
| 1057 | +} |
---|
| 1058 | + |
---|
| 1059 | +static int deflate_compress(const char *src, size_t slen, char **dst, |
---|
| 1060 | + size_t *dlen) |
---|
| 1061 | +{ |
---|
| 1062 | + int error; |
---|
| 1063 | + struct z_stream_s strm; |
---|
| 1064 | + void *stgbuf, *dstbuf; |
---|
| 1065 | + size_t stglen = deflateBound(slen); |
---|
| 1066 | + |
---|
| 1067 | + memset(&strm, 0, sizeof(strm)); |
---|
| 1068 | + |
---|
| 1069 | + if (stglen < slen) |
---|
| 1070 | + return -EFBIG; |
---|
| 1071 | + |
---|
| 1072 | + strm.workspace = kvzalloc(zlib_deflate_workspacesize(MAX_WBITS, |
---|
| 1073 | + MAX_MEM_LEVEL), |
---|
| 1074 | + GFP_KERNEL); |
---|
| 1075 | + if (!strm.workspace) |
---|
| 1076 | + return -ENOMEM; |
---|
| 1077 | + |
---|
| 1078 | + error = zlib_deflateInit(&strm, aa_g_rawdata_compression_level); |
---|
| 1079 | + if (error != Z_OK) { |
---|
| 1080 | + error = -ENOMEM; |
---|
| 1081 | + goto fail_deflate_init; |
---|
| 1082 | + } |
---|
| 1083 | + |
---|
| 1084 | + stgbuf = kvzalloc(stglen, GFP_KERNEL); |
---|
| 1085 | + if (!stgbuf) { |
---|
| 1086 | + error = -ENOMEM; |
---|
| 1087 | + goto fail_stg_alloc; |
---|
| 1088 | + } |
---|
| 1089 | + |
---|
| 1090 | + strm.next_in = src; |
---|
| 1091 | + strm.avail_in = slen; |
---|
| 1092 | + strm.next_out = stgbuf; |
---|
| 1093 | + strm.avail_out = stglen; |
---|
| 1094 | + |
---|
| 1095 | + error = zlib_deflate(&strm, Z_FINISH); |
---|
| 1096 | + if (error != Z_STREAM_END) { |
---|
| 1097 | + error = -EINVAL; |
---|
| 1098 | + goto fail_deflate; |
---|
| 1099 | + } |
---|
| 1100 | + error = 0; |
---|
| 1101 | + |
---|
| 1102 | + if (is_vmalloc_addr(stgbuf)) { |
---|
| 1103 | + dstbuf = kvzalloc(strm.total_out, GFP_KERNEL); |
---|
| 1104 | + if (dstbuf) { |
---|
| 1105 | + memcpy(dstbuf, stgbuf, strm.total_out); |
---|
| 1106 | + kvfree(stgbuf); |
---|
| 1107 | + } |
---|
| 1108 | + } else |
---|
| 1109 | + /* |
---|
| 1110 | + * If the staging buffer was kmalloc'd, then using krealloc is |
---|
| 1111 | + * probably going to be faster. The destination buffer will |
---|
| 1112 | + * always be smaller, so it's just shrunk, avoiding a memcpy |
---|
| 1113 | + */ |
---|
| 1114 | + dstbuf = krealloc(stgbuf, strm.total_out, GFP_KERNEL); |
---|
| 1115 | + |
---|
| 1116 | + if (!dstbuf) { |
---|
| 1117 | + error = -ENOMEM; |
---|
| 1118 | + goto fail_deflate; |
---|
| 1119 | + } |
---|
| 1120 | + |
---|
| 1121 | + *dst = dstbuf; |
---|
| 1122 | + *dlen = strm.total_out; |
---|
| 1123 | + |
---|
| 1124 | +fail_stg_alloc: |
---|
| 1125 | + zlib_deflateEnd(&strm); |
---|
| 1126 | +fail_deflate_init: |
---|
| 1127 | + kvfree(strm.workspace); |
---|
| 1128 | + return error; |
---|
| 1129 | + |
---|
| 1130 | +fail_deflate: |
---|
| 1131 | + kvfree(stgbuf); |
---|
| 1132 | + goto fail_stg_alloc; |
---|
| 1133 | +} |
---|
| 1134 | + |
---|
| 1135 | +static int compress_loaddata(struct aa_loaddata *data) |
---|
| 1136 | +{ |
---|
| 1137 | + |
---|
| 1138 | + AA_BUG(data->compressed_size > 0); |
---|
| 1139 | + |
---|
| 1140 | + /* |
---|
| 1141 | + * Shortcut the no compression case, else we increase the amount of |
---|
| 1142 | + * storage required by a small amount |
---|
| 1143 | + */ |
---|
| 1144 | + if (aa_g_rawdata_compression_level != 0) { |
---|
| 1145 | + void *udata = data->data; |
---|
| 1146 | + int error = deflate_compress(udata, data->size, &data->data, |
---|
| 1147 | + &data->compressed_size); |
---|
| 1148 | + if (error) |
---|
| 1149 | + return error; |
---|
| 1150 | + |
---|
| 1151 | + kvfree(udata); |
---|
| 1152 | + } else |
---|
| 1153 | + data->compressed_size = data->size; |
---|
| 1154 | + |
---|
| 1155 | + return 0; |
---|
977 | 1156 | } |
---|
978 | 1157 | |
---|
979 | 1158 | /** |
---|
.. | .. |
---|
1044 | 1223 | goto fail; |
---|
1045 | 1224 | } |
---|
1046 | 1225 | } |
---|
| 1226 | + error = compress_loaddata(udata); |
---|
| 1227 | + if (error) |
---|
| 1228 | + goto fail; |
---|
1047 | 1229 | return 0; |
---|
1048 | 1230 | |
---|
1049 | 1231 | fail_profile: |
---|
.. | .. |
---|
1057 | 1239 | |
---|
1058 | 1240 | return error; |
---|
1059 | 1241 | } |
---|
| 1242 | + |
---|
| 1243 | +#ifdef CONFIG_SECURITY_APPARMOR_KUNIT_TEST |
---|
| 1244 | +#include "policy_unpack_test.c" |
---|
| 1245 | +#endif /* CONFIG_SECURITY_APPARMOR_KUNIT_TEST */ |
---|