aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2013-07-11 00:05:43 -0400
committerJohn Johansen <john.johansen@canonical.com>2013-08-14 14:42:06 -0400
commitdd51c84857630e77c139afe4d9bba65fc051dc3f (patch)
tree2dbfb9435feadac6123600aef75004ee2197f6af
parent9d910a3bc01008d432b3bb79a69e7e3cdb4821b2 (diff)
apparmor: provide base for multiple profiles to be replaced at once
previously profiles had to be loaded one at a time, which could result in cases where a replacement of a set would partially succeed, and then fail resulting in inconsistent policy. Allow multiple profiles to replaced "atomically" so that the replacement either succeeds or fails for the entire set of profiles. Signed-off-by: John Johansen <john.johansen@canonical.com>
-rw-r--r--security/apparmor/apparmorfs.c1
-rw-r--r--security/apparmor/include/policy_unpack.h14
-rw-r--r--security/apparmor/policy.c300
-rw-r--r--security/apparmor/policy_unpack.c114
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
201static struct aa_fs_entry aa_fs_entry_policy[] = { 201static 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
18struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); 18#include <linux/list.h>
19
20struct 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
27void aa_load_ent_free(struct aa_load_ent *ent);
28struct aa_load_ent *aa_load_ent_alloc(void);
29
30int 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 */
487static 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
514static void __profile_list_release(struct list_head *head); 475static 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 */
963static 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
964static 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 */
999static 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 */
1051static 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 */
1034ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) 1079ssize_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)
1099audit: 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
1115out: 1175out:
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
1182fail_lock:
1183 write_unlock(&ns->lock);
1124fail: 1184fail:
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 */
629static int verify_header(struct aa_ext *e, const char **ns) 632static 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
710void 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
720struct 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 */
706struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) 741int 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
783fail:
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}