diff options
-rw-r--r-- | security/apparmor/apparmorfs.c | 1 | ||||
-rw-r--r-- | security/apparmor/include/policy_unpack.h | 14 | ||||
-rw-r--r-- | security/apparmor/policy.c | 300 | ||||
-rw-r--r-- | security/apparmor/policy_unpack.c | 114 |
4 files changed, 283 insertions, 146 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ad6c74892b5f..3ed56e21a9fd 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
@@ -199,6 +199,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { | |||
199 | }; | 199 | }; |
200 | 200 | ||
201 | static struct aa_fs_entry aa_fs_entry_policy[] = { | 201 | static struct aa_fs_entry aa_fs_entry_policy[] = { |
202 | AA_FS_FILE_BOOLEAN("set_load", 1), | ||
202 | {} | 203 | {} |
203 | }; | 204 | }; |
204 | 205 | ||
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index a2dcccac45aa..0d7ad722b8ff 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h | |||
@@ -15,6 +15,18 @@ | |||
15 | #ifndef __POLICY_INTERFACE_H | 15 | #ifndef __POLICY_INTERFACE_H |
16 | #define __POLICY_INTERFACE_H | 16 | #define __POLICY_INTERFACE_H |
17 | 17 | ||
18 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); | 18 | #include <linux/list.h> |
19 | |||
20 | struct aa_load_ent { | ||
21 | struct list_head list; | ||
22 | struct aa_profile *new; | ||
23 | struct aa_profile *old; | ||
24 | struct aa_profile *rename; | ||
25 | }; | ||
26 | |||
27 | void aa_load_ent_free(struct aa_load_ent *ent); | ||
28 | struct aa_load_ent *aa_load_ent_alloc(void); | ||
29 | |||
30 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); | ||
19 | 31 | ||
20 | #endif /* __POLICY_INTERFACE_H */ | 32 | #endif /* __POLICY_INTERFACE_H */ |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0f345c4dee5f..407b442c0a2c 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
@@ -472,45 +472,6 @@ static void __list_remove_profile(struct aa_profile *profile) | |||
472 | aa_put_profile(profile); | 472 | aa_put_profile(profile); |
473 | } | 473 | } |
474 | 474 | ||
475 | /** | ||
476 | * __replace_profile - replace @old with @new on a list | ||
477 | * @old: profile to be replaced (NOT NULL) | ||
478 | * @new: profile to replace @old with (NOT NULL) | ||
479 | * | ||
480 | * Will duplicate and refcount elements that @new inherits from @old | ||
481 | * and will inherit @old children. | ||
482 | * | ||
483 | * refcount @new for list, put @old list refcount | ||
484 | * | ||
485 | * Requires: namespace list lock be held, or list not be shared | ||
486 | */ | ||
487 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | ||
488 | { | ||
489 | struct aa_policy *policy; | ||
490 | struct aa_profile *child, *tmp; | ||
491 | |||
492 | if (old->parent) | ||
493 | policy = &old->parent->base; | ||
494 | else | ||
495 | policy = &old->ns->base; | ||
496 | |||
497 | /* released when @new is freed */ | ||
498 | new->parent = aa_get_profile(old->parent); | ||
499 | new->ns = aa_get_namespace(old->ns); | ||
500 | __list_add_profile(&policy->profiles, new); | ||
501 | /* inherit children */ | ||
502 | list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { | ||
503 | aa_put_profile(child->parent); | ||
504 | child->parent = aa_get_profile(new); | ||
505 | /* list refcount transferred to @new*/ | ||
506 | list_move(&child->base.list, &new->base.profiles); | ||
507 | } | ||
508 | |||
509 | /* released by free_profile */ | ||
510 | old->replacedby = aa_get_profile(new); | ||
511 | __list_remove_profile(old); | ||
512 | } | ||
513 | |||
514 | static void __profile_list_release(struct list_head *head); | 475 | static void __profile_list_release(struct list_head *head); |
515 | 476 | ||
516 | /** | 477 | /** |
@@ -953,25 +914,6 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, | |||
953 | } | 914 | } |
954 | 915 | ||
955 | /** | 916 | /** |
956 | * __add_new_profile - simple wrapper around __list_add_profile | ||
957 | * @ns: namespace that profile is being added to (NOT NULL) | ||
958 | * @policy: the policy container to add the profile to (NOT NULL) | ||
959 | * @profile: profile to add (NOT NULL) | ||
960 | * | ||
961 | * add a profile to a list and do other required basic allocations | ||
962 | */ | ||
963 | static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, | ||
964 | struct aa_profile *profile) | ||
965 | { | ||
966 | if (policy != &ns->base) | ||
967 | /* released on profile replacement or free_profile */ | ||
968 | profile->parent = aa_get_profile((struct aa_profile *) policy); | ||
969 | __list_add_profile(&policy->profiles, profile); | ||
970 | /* released on free_profile */ | ||
971 | profile->ns = aa_get_namespace(ns); | ||
972 | } | ||
973 | |||
974 | /** | ||
975 | * aa_audit_policy - Do auditing of policy changes | 917 | * aa_audit_policy - Do auditing of policy changes |
976 | * @op: policy operation being performed | 918 | * @op: policy operation being performed |
977 | * @gfp: memory allocation flags | 919 | * @gfp: memory allocation flags |
@@ -1019,6 +961,109 @@ bool aa_may_manage_policy(int op) | |||
1019 | return 1; | 961 | return 1; |
1020 | } | 962 | } |
1021 | 963 | ||
964 | static struct aa_profile *__list_lookup_parent(struct list_head *lh, | ||
965 | struct aa_profile *profile) | ||
966 | { | ||
967 | const char *base = hname_tail(profile->base.hname); | ||
968 | long len = base - profile->base.hname; | ||
969 | struct aa_load_ent *ent; | ||
970 | |||
971 | /* parent won't have trailing // so remove from len */ | ||
972 | if (len <= 2) | ||
973 | return NULL; | ||
974 | len -= 2; | ||
975 | |||
976 | list_for_each_entry(ent, lh, list) { | ||
977 | if (ent->new == profile) | ||
978 | continue; | ||
979 | if (strncmp(ent->new->base.hname, profile->base.hname, len) == | ||
980 | 0 && ent->new->base.hname[len] == 0) | ||
981 | return ent->new; | ||
982 | } | ||
983 | |||
984 | return NULL; | ||
985 | } | ||
986 | |||
987 | /** | ||
988 | * __replace_profile - replace @old with @new on a list | ||
989 | * @old: profile to be replaced (NOT NULL) | ||
990 | * @new: profile to replace @old with (NOT NULL) | ||
991 | * | ||
992 | * Will duplicate and refcount elements that @new inherits from @old | ||
993 | * and will inherit @old children. | ||
994 | * | ||
995 | * refcount @new for list, put @old list refcount | ||
996 | * | ||
997 | * Requires: namespace list lock be held, or list not be shared | ||
998 | */ | ||
999 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | ||
1000 | { | ||
1001 | struct aa_profile *child, *tmp; | ||
1002 | |||
1003 | if (!list_empty(&old->base.profiles)) { | ||
1004 | LIST_HEAD(lh); | ||
1005 | list_splice_init(&old->base.profiles, &lh); | ||
1006 | |||
1007 | list_for_each_entry_safe(child, tmp, &lh, base.list) { | ||
1008 | struct aa_profile *p; | ||
1009 | |||
1010 | list_del_init(&child->base.list); | ||
1011 | p = __find_child(&new->base.profiles, child->base.name); | ||
1012 | if (p) { | ||
1013 | /* @p replaces @child */ | ||
1014 | __replace_profile(child, p); | ||
1015 | continue; | ||
1016 | } | ||
1017 | |||
1018 | /* inherit @child and its children */ | ||
1019 | /* TODO: update hname of inherited children */ | ||
1020 | /* list refcount transferred to @new */ | ||
1021 | list_add(&child->base.list, &new->base.profiles); | ||
1022 | aa_put_profile(child->parent); | ||
1023 | child->parent = aa_get_profile(new); | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | if (!new->parent) | ||
1028 | new->parent = aa_get_profile(old->parent); | ||
1029 | /* released by free_profile */ | ||
1030 | old->replacedby = aa_get_profile(new); | ||
1031 | |||
1032 | if (list_empty(&new->base.list)) { | ||
1033 | /* new is not on a list already */ | ||
1034 | list_replace_init(&old->base.list, &new->base.list); | ||
1035 | aa_get_profile(new); | ||
1036 | aa_put_profile(old); | ||
1037 | } else | ||
1038 | __list_remove_profile(old); | ||
1039 | } | ||
1040 | |||
1041 | /** | ||
1042 | * __lookup_replace - lookup replacement information for a profile | ||
1043 | * @ns - namespace the lookup occurs in | ||
1044 | * @hname - name of profile to lookup | ||
1045 | * @noreplace - true if not replacing an existing profile | ||
1046 | * @p - Returns: profile to be replaced | ||
1047 | * @info - Returns: info string on why lookup failed | ||
1048 | * | ||
1049 | * Returns: profile to replace (no ref) on success else ptr error | ||
1050 | */ | ||
1051 | static int __lookup_replace(struct aa_namespace *ns, const char *hname, | ||
1052 | bool noreplace, struct aa_profile **p, | ||
1053 | const char **info) | ||
1054 | { | ||
1055 | *p = aa_get_profile(__lookup_profile(&ns->base, hname)); | ||
1056 | if (*p) { | ||
1057 | int error = replacement_allowed(*p, noreplace, info); | ||
1058 | if (error) { | ||
1059 | *info = "profile can not be replaced"; | ||
1060 | return error; | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1064 | return 0; | ||
1065 | } | ||
1066 | |||
1022 | /** | 1067 | /** |
1023 | * aa_replace_profiles - replace profile(s) on the profile list | 1068 | * aa_replace_profiles - replace profile(s) on the profile list |
1024 | * @udata: serialized data stream (NOT NULL) | 1069 | * @udata: serialized data stream (NOT NULL) |
@@ -1033,21 +1078,17 @@ bool aa_may_manage_policy(int op) | |||
1033 | */ | 1078 | */ |
1034 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | 1079 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) |
1035 | { | 1080 | { |
1036 | struct aa_policy *policy; | ||
1037 | struct aa_profile *old_profile = NULL, *new_profile = NULL; | ||
1038 | struct aa_profile *rename_profile = NULL; | ||
1039 | struct aa_namespace *ns = NULL; | ||
1040 | const char *ns_name, *name = NULL, *info = NULL; | 1081 | const char *ns_name, *name = NULL, *info = NULL; |
1082 | struct aa_namespace *ns = NULL; | ||
1083 | struct aa_load_ent *ent, *tmp; | ||
1041 | int op = OP_PROF_REPL; | 1084 | int op = OP_PROF_REPL; |
1042 | ssize_t error; | 1085 | ssize_t error; |
1086 | LIST_HEAD(lh); | ||
1043 | 1087 | ||
1044 | /* released below */ | 1088 | /* released below */ |
1045 | new_profile = aa_unpack(udata, size, &ns_name); | 1089 | error = aa_unpack(udata, size, &lh, &ns_name); |
1046 | if (IS_ERR(new_profile)) { | 1090 | if (error) |
1047 | error = PTR_ERR(new_profile); | 1091 | goto out; |
1048 | new_profile = NULL; | ||
1049 | goto fail; | ||
1050 | } | ||
1051 | 1092 | ||
1052 | /* released below */ | 1093 | /* released below */ |
1053 | ns = aa_prepare_namespace(ns_name); | 1094 | ns = aa_prepare_namespace(ns_name); |
@@ -1058,71 +1099,96 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
1058 | goto fail; | 1099 | goto fail; |
1059 | } | 1100 | } |
1060 | 1101 | ||
1061 | name = new_profile->base.hname; | ||
1062 | |||
1063 | write_lock(&ns->lock); | 1102 | write_lock(&ns->lock); |
1064 | /* no ref on policy only use inside lock */ | 1103 | /* setup parent and ns info */ |
1065 | policy = __lookup_parent(ns, new_profile->base.hname); | 1104 | list_for_each_entry(ent, &lh, list) { |
1066 | 1105 | struct aa_policy *policy; | |
1067 | if (!policy) { | 1106 | |
1068 | info = "parent does not exist"; | 1107 | name = ent->new->base.hname; |
1069 | error = -ENOENT; | 1108 | error = __lookup_replace(ns, ent->new->base.hname, noreplace, |
1070 | goto audit; | 1109 | &ent->old, &info); |
1071 | } | 1110 | if (error) |
1072 | 1111 | goto fail_lock; | |
1073 | old_profile = __find_child(&policy->profiles, new_profile->base.name); | 1112 | |
1074 | /* released below */ | 1113 | if (ent->new->rename) { |
1075 | aa_get_profile(old_profile); | 1114 | error = __lookup_replace(ns, ent->new->rename, |
1076 | 1115 | noreplace, &ent->rename, | |
1077 | if (new_profile->rename) { | 1116 | &info); |
1078 | rename_profile = __lookup_profile(&ns->base, | 1117 | if (error) |
1079 | new_profile->rename); | 1118 | goto fail_lock; |
1080 | /* released below */ | ||
1081 | aa_get_profile(rename_profile); | ||
1082 | |||
1083 | if (!rename_profile) { | ||
1084 | info = "profile to rename does not exist"; | ||
1085 | name = new_profile->rename; | ||
1086 | error = -ENOENT; | ||
1087 | goto audit; | ||
1088 | } | 1119 | } |
1089 | } | ||
1090 | |||
1091 | error = replacement_allowed(old_profile, noreplace, &info); | ||
1092 | if (error) | ||
1093 | goto audit; | ||
1094 | 1120 | ||
1095 | error = replacement_allowed(rename_profile, noreplace, &info); | 1121 | /* released when @new is freed */ |
1096 | if (error) | 1122 | ent->new->ns = aa_get_namespace(ns); |
1097 | goto audit; | 1123 | |
1098 | 1124 | if (ent->old || ent->rename) | |
1099 | audit: | 1125 | continue; |
1100 | if (!old_profile && !rename_profile) | 1126 | |
1101 | op = OP_PROF_LOAD; | 1127 | /* no ref on policy only use inside lock */ |
1128 | policy = __lookup_parent(ns, ent->new->base.hname); | ||
1129 | if (!policy) { | ||
1130 | struct aa_profile *p; | ||
1131 | p = __list_lookup_parent(&lh, ent->new); | ||
1132 | if (!p) { | ||
1133 | error = -ENOENT; | ||
1134 | info = "parent does not exist"; | ||
1135 | name = ent->new->base.hname; | ||
1136 | goto fail_lock; | ||
1137 | } | ||
1138 | ent->new->parent = aa_get_profile(p); | ||
1139 | } else if (policy != &ns->base) | ||
1140 | /* released on profile replacement or free_profile */ | ||
1141 | ent->new->parent = aa_get_profile((struct aa_profile *) | ||
1142 | policy); | ||
1143 | } | ||
1102 | 1144 | ||
1103 | error = audit_policy(op, GFP_ATOMIC, name, info, error); | 1145 | /* do actual replacement */ |
1146 | list_for_each_entry_safe(ent, tmp, &lh, list) { | ||
1147 | list_del_init(&ent->list); | ||
1148 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; | ||
1149 | |||
1150 | audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); | ||
1151 | |||
1152 | if (ent->old) { | ||
1153 | __replace_profile(ent->old, ent->new); | ||
1154 | if (ent->rename) | ||
1155 | __replace_profile(ent->rename, ent->new); | ||
1156 | } else if (ent->rename) { | ||
1157 | __replace_profile(ent->rename, ent->new); | ||
1158 | } else if (ent->new->parent) { | ||
1159 | struct aa_profile *parent; | ||
1160 | parent = aa_newest_version(ent->new->parent); | ||
1161 | /* parent replaced in this atomic set? */ | ||
1162 | if (parent != ent->new->parent) { | ||
1163 | aa_get_profile(parent); | ||
1164 | aa_put_profile(ent->new->parent); | ||
1165 | ent->new->parent = parent; | ||
1166 | } | ||
1167 | __list_add_profile(&parent->base.profiles, ent->new); | ||
1168 | } else | ||
1169 | __list_add_profile(&ns->base.profiles, ent->new); | ||
1104 | 1170 | ||
1105 | if (!error) { | 1171 | aa_load_ent_free(ent); |
1106 | if (rename_profile) | ||
1107 | __replace_profile(rename_profile, new_profile); | ||
1108 | if (old_profile) | ||
1109 | __replace_profile(old_profile, new_profile); | ||
1110 | if (!(old_profile || rename_profile)) | ||
1111 | __add_new_profile(ns, policy, new_profile); | ||
1112 | } | 1172 | } |
1113 | write_unlock(&ns->lock); | 1173 | write_unlock(&ns->lock); |
1114 | 1174 | ||
1115 | out: | 1175 | out: |
1116 | aa_put_namespace(ns); | 1176 | aa_put_namespace(ns); |
1117 | aa_put_profile(rename_profile); | 1177 | |
1118 | aa_put_profile(old_profile); | ||
1119 | aa_put_profile(new_profile); | ||
1120 | if (error) | 1178 | if (error) |
1121 | return error; | 1179 | return error; |
1122 | return size; | 1180 | return size; |
1123 | 1181 | ||
1182 | fail_lock: | ||
1183 | write_unlock(&ns->lock); | ||
1124 | fail: | 1184 | fail: |
1125 | error = audit_policy(op, GFP_KERNEL, name, info, error); | 1185 | error = audit_policy(op, GFP_KERNEL, name, info, error); |
1186 | |||
1187 | list_for_each_entry_safe(ent, tmp, &lh, list) { | ||
1188 | list_del_init(&ent->list); | ||
1189 | aa_load_ent_free(ent); | ||
1190 | } | ||
1191 | |||
1126 | goto out; | 1192 | goto out; |
1127 | } | 1193 | } |
1128 | 1194 | ||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 6dac7d77cb4d..080a26b11f01 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c | |||
@@ -333,8 +333,10 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) | |||
333 | /* | 333 | /* |
334 | * The dfa is aligned with in the blob to 8 bytes | 334 | * The dfa is aligned with in the blob to 8 bytes |
335 | * from the beginning of the stream. | 335 | * from the beginning of the stream. |
336 | * alignment adjust needed by dfa unpack | ||
336 | */ | 337 | */ |
337 | size_t sz = blob - (char *)e->start; | 338 | size_t sz = blob - (char *) e->start - |
339 | ((e->pos - e->start) & 7); | ||
338 | size_t pad = ALIGN(sz, 8) - sz; | 340 | size_t pad = ALIGN(sz, 8) - sz; |
339 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | 341 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | |
340 | TO_ACCEPT2_FLAG(YYTD_DATA32); | 342 | TO_ACCEPT2_FLAG(YYTD_DATA32); |
@@ -622,29 +624,41 @@ fail: | |||
622 | /** | 624 | /** |
623 | * verify_head - unpack serialized stream header | 625 | * verify_head - unpack serialized stream header |
624 | * @e: serialized data read head (NOT NULL) | 626 | * @e: serialized data read head (NOT NULL) |
627 | * @required: whether the header is required or optional | ||
625 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) | 628 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) |
626 | * | 629 | * |
627 | * Returns: error or 0 if header is good | 630 | * Returns: error or 0 if header is good |
628 | */ | 631 | */ |
629 | static int verify_header(struct aa_ext *e, const char **ns) | 632 | static int verify_header(struct aa_ext *e, int required, const char **ns) |
630 | { | 633 | { |
631 | int error = -EPROTONOSUPPORT; | 634 | int error = -EPROTONOSUPPORT; |
635 | const char *name = NULL; | ||
636 | *ns = NULL; | ||
637 | |||
632 | /* get the interface version */ | 638 | /* get the interface version */ |
633 | if (!unpack_u32(e, &e->version, "version")) { | 639 | if (!unpack_u32(e, &e->version, "version")) { |
634 | audit_iface(NULL, NULL, "invalid profile format", e, error); | 640 | if (required) { |
635 | return error; | 641 | audit_iface(NULL, NULL, "invalid profile format", e, |
636 | } | 642 | error); |
643 | return error; | ||
644 | } | ||
637 | 645 | ||
638 | /* check that the interface version is currently supported */ | 646 | /* check that the interface version is currently supported */ |
639 | if (e->version != 5) { | 647 | if (e->version != 5) { |
640 | audit_iface(NULL, NULL, "unsupported interface version", e, | 648 | audit_iface(NULL, NULL, "unsupported interface version", |
641 | error); | 649 | e, error); |
642 | return error; | 650 | return error; |
651 | } | ||
643 | } | 652 | } |
644 | 653 | ||
654 | |||
645 | /* read the namespace if present */ | 655 | /* read the namespace if present */ |
646 | if (!unpack_str(e, ns, "namespace")) | 656 | if (unpack_str(e, &name, "namespace")) { |
647 | *ns = NULL; | 657 | if (*ns && strcmp(*ns, name)) |
658 | audit_iface(NULL, NULL, "invalid ns change", e, error); | ||
659 | else if (!*ns) | ||
660 | *ns = name; | ||
661 | } | ||
648 | 662 | ||
649 | return 0; | 663 | return 0; |
650 | } | 664 | } |
@@ -693,18 +707,40 @@ static int verify_profile(struct aa_profile *profile) | |||
693 | return 0; | 707 | return 0; |
694 | } | 708 | } |
695 | 709 | ||
710 | void aa_load_ent_free(struct aa_load_ent *ent) | ||
711 | { | ||
712 | if (ent) { | ||
713 | aa_put_profile(ent->rename); | ||
714 | aa_put_profile(ent->old); | ||
715 | aa_put_profile(ent->new); | ||
716 | kzfree(ent); | ||
717 | } | ||
718 | } | ||
719 | |||
720 | struct aa_load_ent *aa_load_ent_alloc(void) | ||
721 | { | ||
722 | struct aa_load_ent *ent = kzalloc(sizeof(*ent), GFP_KERNEL); | ||
723 | if (ent) | ||
724 | INIT_LIST_HEAD(&ent->list); | ||
725 | return ent; | ||
726 | } | ||
727 | |||
696 | /** | 728 | /** |
697 | * aa_unpack - unpack packed binary profile data loaded from user space | 729 | * aa_unpack - unpack packed binary profile(s) data loaded from user space |
698 | * @udata: user data copied to kmem (NOT NULL) | 730 | * @udata: user data copied to kmem (NOT NULL) |
699 | * @size: the size of the user data | 731 | * @size: the size of the user data |
732 | * @lh: list to place unpacked profiles in a aa_repl_ws | ||
700 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) | 733 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) |
701 | * | 734 | * |
702 | * Unpack user data and return refcounted allocated profile or ERR_PTR | 735 | * Unpack user data and return refcounted allocated profile(s) stored in |
736 | * @lh in order of discovery, with the list chain stored in base.list | ||
737 | * or error | ||
703 | * | 738 | * |
704 | * Returns: profile else error pointer if fails to unpack | 739 | * Returns: profile(s) on @lh else error pointer if fails to unpack |
705 | */ | 740 | */ |
706 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | 741 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) |
707 | { | 742 | { |
743 | struct aa_load_ent *tmp, *ent; | ||
708 | struct aa_profile *profile = NULL; | 744 | struct aa_profile *profile = NULL; |
709 | int error; | 745 | int error; |
710 | struct aa_ext e = { | 746 | struct aa_ext e = { |
@@ -713,20 +749,42 @@ struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | |||
713 | .pos = udata, | 749 | .pos = udata, |
714 | }; | 750 | }; |
715 | 751 | ||
716 | error = verify_header(&e, ns); | 752 | *ns = NULL; |
717 | if (error) | 753 | while (e.pos < e.end) { |
718 | return ERR_PTR(error); | 754 | error = verify_header(&e, e.pos == e.start, ns); |
755 | if (error) | ||
756 | goto fail; | ||
719 | 757 | ||
720 | profile = unpack_profile(&e); | 758 | profile = unpack_profile(&e); |
721 | if (IS_ERR(profile)) | 759 | if (IS_ERR(profile)) { |
722 | return profile; | 760 | error = PTR_ERR(profile); |
761 | goto fail; | ||
762 | } | ||
763 | |||
764 | error = verify_profile(profile); | ||
765 | if (error) { | ||
766 | aa_put_profile(profile); | ||
767 | goto fail; | ||
768 | } | ||
769 | |||
770 | ent = aa_load_ent_alloc(); | ||
771 | if (!ent) { | ||
772 | error = -ENOMEM; | ||
773 | aa_put_profile(profile); | ||
774 | goto fail; | ||
775 | } | ||
723 | 776 | ||
724 | error = verify_profile(profile); | 777 | ent->new = profile; |
725 | if (error) { | 778 | list_add_tail(&ent->list, lh); |
726 | aa_put_profile(profile); | ||
727 | profile = ERR_PTR(error); | ||
728 | } | 779 | } |
729 | 780 | ||
730 | /* return refcount */ | 781 | return 0; |
731 | return profile; | 782 | |
783 | fail: | ||
784 | list_for_each_entry_safe(ent, tmp, lh, list) { | ||
785 | list_del_init(&ent->list); | ||
786 | aa_load_ent_free(ent); | ||
787 | } | ||
788 | |||
789 | return error; | ||
732 | } | 790 | } |