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 | } |
