[CRIU] [PATCH v3 3/7] lsm: add support for rewriting apparmor profiles

Tycho Andersen tycho.andersen at canonical.com
Wed Oct 26 10:01:33 PDT 2016


--lsm-profile is used to rewrite some apparmor profiles which might contain
the name or runtime path of the container engine, and thus might be
different on restore. Let's extend its support to apparmor profiles.

Note that if there is nesting beyond what is specified by --lsm-profile.
The assumption here is that it is only necessary to rewrite some prefix of
the full path, and that stuff below that in the tree was set up by whatever
was in the container and should be left alone.

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 criu/apparmor.c         | 120 ++++++++++++++++++++++++++++++++++++++++++++++--
 criu/cr-restore.c       |   6 ---
 criu/include/apparmor.h |   2 +
 criu/include/lsm.h      |   9 +---
 criu/lsm.c              |  37 ++++++---------
 5 files changed, 134 insertions(+), 40 deletions(-)

diff --git a/criu/apparmor.c b/criu/apparmor.c
index b23186f..2c8d060 100644
--- a/criu/apparmor.c
+++ b/criu/apparmor.c
@@ -405,7 +405,24 @@ bool check_aa_ns_dumping(void)
 	return major >= 1 && minor >= 2;
 }
 
-static int restore_aa_namespace(AaNamespace *ns, char *path, int offset)
+#define NEXT_AA_TOKEN(pos)					\
+	while (*pos) {						\
+		if (*pos == '/' &&				\
+				*(pos+1) && *(pos+1) == '/' &&	\
+				*(pos+2) && *(pos+2) == '&') {	\
+			pos += 3;				\
+			break;					\
+		}						\
+		if (*pos == ':' &&				\
+				*(pos+1) && *(pos+1) == '/' &&	\
+				*(pos+2) && *(pos+2) == '/') {	\
+			pos += 3;				\
+			break;					\
+		}						\
+		pos++;						\
+	}
+
+static int restore_aa_namespace(AaNamespace *ns, char *path, int offset, char *rewrite)
 {
 	pid_t pid;
 	int status;
@@ -418,9 +435,31 @@ static int restore_aa_namespace(AaNamespace *ns, char *path, int offset)
 
 	if (!pid) {
 		int i, my_offset, ret, fd;
-		char buf[1024];
+		char buf[PATH_MAX], *rewrite_pos = rewrite, namespace[PATH_MAX];
+
+		if (!rewrite) {
+			strncpy(namespace, ns->name, sizeof(namespace));
+		} else {
+			NEXT_AA_TOKEN(rewrite_pos);
+			switch(*rewrite_pos) {
+			case ':':
+				*(rewrite_pos-3) = 0;
+				strncpy(namespace, rewrite_pos+1, sizeof(namespace));
+				namespace[strlen(namespace)-1] = 0;
+				*(rewrite_pos-3) = ':';
+				break;
+			default:
+				strncpy(namespace, ns->name, sizeof(namespace));
+				*(rewrite_pos-3) = 0;
+				for (i = 0; i < ns->n_policies; i++) {
+					if (strcmp(ns->policies[i]->name, rewrite_pos))
+						pr_warn("binary rewriting of apparmor policies not supported right now, not renaming %s to %s\n", ns->policies[i]->name, rewrite_pos);
+				}
+				*(rewrite_pos-3) = '/';
+			}
+		}
 
-		ret = snprintf(buf, sizeof(buf), "changeprofile :%s:", ns->name);
+		ret = snprintf(buf, sizeof(buf), "changeprofile :%s:", namespace);
 		if (ret < 0 || ret >= sizeof(buf)) {
 			pr_err("profile %s too big\n", ns->name);
 			exit(1);
@@ -456,7 +495,7 @@ static int restore_aa_namespace(AaNamespace *ns, char *path, int offset)
 		}
 
 		for (i = 0; i < ns->n_namespaces; i++) {
-			if (restore_aa_namespace(ns, path, offset + my_offset) < 0)
+			if (restore_aa_namespace(ns, path, offset + my_offset, rewrite_pos) < 0)
 				goto fail;
 		}
 
@@ -519,7 +558,7 @@ int prepare_apparmor_namespaces(void)
 	for (i = 0; i < ae->n_namespaces; i++) {
 		char path[PATH_MAX] = AA_SECURITYFS_PATH "/policy";
 
-		if (restore_aa_namespace(ae->namespaces[i], path, strlen(path)) < 0) {
+		if (restore_aa_namespace(ae->namespaces[i], path, strlen(path), opts.lsm_profile) < 0) {
 			ret = -1;
 			goto out;
 		}
@@ -530,3 +569,74 @@ out:
 	apparmor_entry__free_unpacked(ae, NULL);
 	return ret;
 }
+
+int render_aa_profile(char **out, const char *cur)
+{
+	const char *pos;
+	int n_namespaces = 0, n_profiles = 0;
+	bool last_namespace = false;
+
+	/* no rewriting necessary */
+	if (!opts.lsm_supplied) {
+		*out = xsprintf("changeprofile %s", cur);
+		if (!*out)
+			return -1;
+
+		return 0;
+	}
+
+	/* user asked to re-write to an unconfined profile */
+	if (!opts.lsm_profile) {
+		*out = NULL;
+		return 0;
+	}
+
+	pos = opts.lsm_profile;
+	while (*pos) {
+		switch(*pos) {
+		case ':':
+			n_namespaces++;
+			break;
+		default:
+			n_profiles++;
+		}
+
+		NEXT_AA_TOKEN(pos);
+	}
+
+	/* special case: there is no namespacing or stacking; we can just
+	 * changeprofile to the rewritten string
+	 */
+	if (n_profiles == 1 && n_namespaces == 0) {
+		*out = xsprintf("changeprofile %s", opts.lsm_profile);
+		if (!*out)
+			return -1;
+
+		pr_info("rewrote apparmor profile from %s to %s\n", cur, *out);
+		return 0;
+	}
+
+	pos = cur;
+	while (*pos) {
+		switch(*pos) {
+		case ':':
+			n_namespaces--;
+			last_namespace = true;
+			break;
+		default:
+			n_profiles--;
+		}
+
+		NEXT_AA_TOKEN(pos);
+
+		if (n_profiles == 0 && n_namespaces == 0)
+			break;
+	}
+
+	*out = xsprintf("changeprofile %s//%s%s", opts.lsm_profile, last_namespace ? "" : "&", pos);
+	if (!*out)
+		return -1;
+
+	pr_info("rewrote apparmor profile from %s to %s\n", cur, *out);
+	return 0;
+}
diff --git a/criu/cr-restore.c b/criu/cr-restore.c
index f81de07..ea01dca 100644
--- a/criu/cr-restore.c
+++ b/criu/cr-restore.c
@@ -2708,12 +2708,6 @@ rst_prep_creds_args(CredsEntry *ce, unsigned long *prev_pos)
 		char *rendered = NULL, *profile;
 
 		profile = ce->lsm_profile;
-		if (opts.lsm_supplied)
-			profile = opts.lsm_profile;
-
-		if (validate_lsm(profile) < 0)
-			return ERR_PTR(-EINVAL);
-
 		if (profile && render_lsm_profile(profile, &rendered)) {
 			return ERR_PTR(-EINVAL);
 		}
diff --git a/criu/include/apparmor.h b/criu/include/apparmor.h
index 447e015..223827e 100644
--- a/criu/include/apparmor.h
+++ b/criu/include/apparmor.h
@@ -9,4 +9,6 @@ bool check_aa_ns_dumping(void);
 
 int prepare_apparmor_namespaces(void);
 
+int render_aa_profile(char **out, const char *cur);
+
 #endif /* __CR_APPARMOR_H__ */
diff --git a/criu/include/lsm.h b/criu/include/lsm.h
index b97195e..554d150 100644
--- a/criu/include/lsm.h
+++ b/criu/include/lsm.h
@@ -22,14 +22,9 @@ extern void kerndat_lsm(void);
 extern int collect_lsm_profile(pid_t, CredsEntry *);
 
 /*
- * Validate that the LSM profiles can be correctly applied (must happen after
- * pstree is set up).
- */
-int validate_lsm(char *profile);
-
-/*
  * Render the profile name in the way that the LSM wants it written to
- * /proc/<pid>/attr/current.
+ * /proc/<pid>/attr/current, according to whatever is in the images and
+ * specified by --lsm-profile.
  */
 int render_lsm_profile(char *profile, char **val);
 
diff --git a/criu/lsm.c b/criu/lsm.c
index cd3ef6d..39f13a3 100644
--- a/criu/lsm.c
+++ b/criu/lsm.c
@@ -174,37 +174,30 @@ int collect_lsm_profile(pid_t pid, CredsEntry *ce)
 // in inventory.c
 extern Lsmtype image_lsm;
 
-int validate_lsm(char *lsm_profile)
+int render_lsm_profile(char *profile, char **val)
 {
-	if (image_lsm == LSMTYPE__NO_LSM || image_lsm == lsmtype)
-		return 0;
+	*val = NULL;
 
-	/*
-	 * This is really only a problem if the processes have actually
-	 * specified an LSM profile. If not, we won't restore anything anyway,
-	 * so it's fine.
-	 */
-	if (lsm_profile) {
-		pr_err("mismatched lsm types and lsm profile specified\n");
+	if (image_lsm != lsmtype) {
+		pr_err("image lsm doesn't match current kernel's lsm\n");
 		return -1;
 	}
 
-	return 0;
-}
-
-int render_lsm_profile(char *profile, char **val)
-{
-	*val = NULL;
+	/* nothing to do */
+	if (!profile)
+		return 0;
 
 	switch (lsmtype) {
 	case LSMTYPE__APPARMOR:
-		if (strcmp(profile, "unconfined") != 0 && asprintf(val, "changeprofile %s", profile) < 0) {
-			pr_err("allocating lsm profile failed");
-			*val = NULL;
-			return -1;
-		}
-		break;
+		return render_aa_profile(val, profile);
 	case LSMTYPE__SELINUX:
+		if (opts.lsm_supplied) {
+			if (!opts.lsm_profile)
+				return 0;
+
+			profile = opts.lsm_profile;
+		}
+
 		if (asprintf(val, "%s", profile) < 0) {
 			*val = NULL;
 			return -1;
-- 
2.9.3



More information about the CRIU mailing list