[1/5] UBUNTU: SAUCE: apparmor: fix label parse for stacked labels

Message ID 20170331120508.16255-2-john.johansen@canonical.com
State New
Headers show

Commit Message

John Johansen March 31, 2017, 12:05 p.m.
Stacked labels with two ns profile components specified and the ns only
specified on one of them are not being correctly parsed.  That is

  P//&:ns://A//&unconfined

is being parsed as if P and unconfined are in the same ns while A
is in :ns: ie. it is being parsed as the equiv of

  P//&unconfined//&:ns://A

The apparmor label spec requires that profiles are ordered by ns so
:ns://A//&unconfined should be parsed as if it is

  P//&:ns://A//&:ns://unconfined

Note: the above with a ns spec for each profile component is currently
parsed correctly and the ns spec is always relative to the label view.

BugLink: http://bugs.launchpad.net/bugs/1677959
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
 security/apparmor/include/lib.h |  1 +
 security/apparmor/label.c       | 29 ++++++++++++++++++++++++++---
 security/apparmor/lib.c         |  2 +-
 3 files changed, 28 insertions(+), 4 deletions(-)

Patch

diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index 954e22a..f44561b 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -59,6 +59,7 @@ 
 extern int apparmor_initialized __initdata;
 
 /* fn's in lib */
+const char *skipn_spaces(const char *str, size_t n);
 char *aa_split_fqname(char *args, char **ns_name);
 const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
 			     size_t *ns_len);
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index 69a600b4..db0eaba 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -1850,6 +1850,23 @@  static int label_count_str_entries(const char *str)
 	return count;
 }
 
+/*
+ * ensure stacks with components like
+ *   :ns://A//&B
+ * have :ns: applied to both 'A' and 'B' by making the lookup relative
+ * to the base if the lookup specifies an ns, else making the stacked lookup
+ * relative to the last embedded ns in the string.
+ */
+static struct aa_profile *fqlookupn_profile(struct aa_label *base,
+					    struct aa_label *currentbase,
+					    const char *str, size_t n)
+{
+	const char *first = skipn_spaces(str, n);
+	if (first && *first == ':')
+		return aa_fqlookupn_profile(base, str, n);
+	return aa_fqlookupn_profile(currentbase, str, n);
+}
+
 /**
  * aa_label_parse - parse, validate and convert a text string to a label
  * @base: base label to use for lookups (NOT NULL)
@@ -1865,7 +1882,7 @@  struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
 				gfp_t gfp, bool create, bool force_stack)
 {
 	DEFINE_VEC(profile, vec);
-	struct aa_label *label;
+	struct aa_label *label, *currbase = base;
 	int i, len, stack = 0, error;
 	char *split;
 
@@ -1889,15 +1906,21 @@  struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
 		vec[i] = aa_get_profile(base->vec[i]);
 
 	for (split = strstr(str, "//&"), i = stack; split && i < len; i++) {
-		vec[i] = aa_fqlookupn_profile(base, str, split - str);
+		vec[i] = fqlookupn_profile(base, currbase, str, split - str);
 		if (!vec[i])
 			goto fail;
+		/*
+		 * if component specified a new ns it becomes the new base
+		 * so that subsequent lookups are relative to it
+		 */
+		if (vec[i]->ns != labels_ns(currbase))
+			currbase = &vec[i]->label;
 		str = split + 3;
 		split = strstr(str, "//&");
 	}
 	/* last element doesn't have a split so this should be the case but just to be safe */
 	if (i < len) {
-		vec[i] = aa_fqlookupn_profile(base, str, strlen(str));
+		vec[i] = fqlookupn_profile(base, currbase, str, strlen(str));
 		if (!vec[i])
 			goto fail;
 	}
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index 52d4efc..783d498 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -76,7 +76,7 @@  char *aa_split_fqname(char *fqname, char **ns_name)
  * if all whitespace will return NULL
  */
 
-static const char *skipn_spaces(const char *str, size_t n)
+const char *skipn_spaces(const char *str, size_t n)
 {
 	for (;n && isspace(*str); --n)
 		++str;