diff options
Diffstat (limited to 'security/apparmor/policy.c')
-rw-r--r-- | security/apparmor/policy.c | 300 |
1 files changed, 183 insertions, 117 deletions
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 | ||