[CRIU] [PATCH v2 3/9] lsm: support checkpoint/restore of stacked apparmor profiles

Tycho Andersen tycho.andersen at canonical.com
Tue Oct 25 09:16:09 PDT 2016


On Tue, Oct 25, 2016 at 03:45:59PM +0300, Pavel Emelyanov wrote:
> On 10/24/2016 06:51 PM, Tycho Andersen wrote:
> > Support for apparmor namespaces and stacking is coming to Ubuntu kernels in
> > 16.10, and should hopefully be upstreamed Soon (TM) :).
> 
> Is there any POC patch on lkml about it?

Unfortunately not. One of the TODO items for the AppArmor maintainer
is to split it all out into stuff that's upstreamable, but nothing
like that exists right now. The best we have is this patch, which is
big and includes a bunch of unrelated stuff:

http://kernel.ubuntu.com/git/jj/linux-apparmor-backports/commit/?h=aa3.6-backport-to-v4.8&id=b9a3e4011dd843ff312c75c303a61e0c23bf258d

Tycho

> > The basic idea is similar to how cgroups are done: we can restore the
> > apparmor namespace and profile blobs independently of the tasks, and then
> > at the end we can just set the task's label appropriately. This means the
> > code that moves tasks under a label stays the same, and the only new code
> > is the stuff that dumps and restores the policy blobs that are in the
> > namespace that were loaded by the container.
> > 
> > v2: dump profiles in timestamp order instead of name order; the timestamp
> >     indicates what order profiles are installed in, and users can
> >     potentially reload a profile that changes others, so we should insert
> >     the blobs in timestamp order
> > 
> > Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
> > ---
> >  criu/Makefile.crtools        |   1 +
> >  criu/apparmor.c              | 532 +++++++++++++++++++++++++++++++++++++++++++
> >  criu/cr-dump.c               |   7 +-
> >  criu/cr-restore.c            |   4 +
> >  criu/image-desc.c            |   1 +
> >  criu/include/apparmor.h      |  12 +
> >  criu/include/image-desc.h    |   1 +
> >  criu/include/magic.h         |   1 +
> >  criu/include/protobuf-desc.h |   1 +
> >  criu/lsm.c                   |   7 +
> >  criu/protobuf-desc.c         |   1 +
> >  images/Makefile              |   1 +
> >  images/apparmor.proto        |  16 ++
> >  images/creds.proto           |   3 +-
> >  lib/py/images/images.py      |   1 +
> >  15 files changed, 587 insertions(+), 2 deletions(-)
> >  create mode 100644 criu/apparmor.c
> >  create mode 100644 criu/include/apparmor.h
> >  create mode 100644 images/apparmor.proto
> > 
> > diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools
> > index c435748..a10b605 100644
> > --- a/criu/Makefile.crtools
> > +++ b/criu/Makefile.crtools
> > @@ -6,6 +6,7 @@ ccflags-y		+= -iquote compel/arch/$(ARCH)/plugins/std
> >  obj-y			+= action-scripts.o
> >  obj-y			+= external.o
> >  obj-y			+= aio.o
> > +obj-y			+= apparmor.o
> >  obj-y			+= bfd.o
> >  obj-y			+= bitmap.o
> >  obj-y			+= cgroup.o
> > diff --git a/criu/apparmor.c b/criu/apparmor.c
> > new file mode 100644
> > index 0000000..b23186f
> > --- /dev/null
> > +++ b/criu/apparmor.c
> > @@ -0,0 +1,532 @@
> > +#include <errno.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <fcntl.h>
> > +#include <sys/types.h>
> > +#include <sys/wait.h>
> > +#include <unistd.h>
> > +#include <ftw.h>
> > +
> > +#include "config.h"
> > +#include "imgset.h"
> > +#include "pstree.h"
> > +#include "util.h"
> > +#include "lsm.h"
> > +#include "cr_options.h"
> > +
> > +#include "protobuf.h"
> > +#include "images/inventory.pb-c.h"
> > +#include "images/apparmor.pb-c.h"
> > +
> > +/*
> > + * Apparmor stacked profile checkpoint restore. Previously, we just saved the
> > + * profile that was in use by the task, and we expected it to be present on the
> > + * target host. Now with stacking, containers are able to load their own
> > + * profiles, so we can't rely on this.
> > + *
> > + * The basic idea here is that there is some (collection) of (potentially
> > + * nested) namespaces that a container uses. We don't collect everything on the
> > + * host level, but we *do* collect everything inside the namespace; a container
> > + * could have loaded a profile but not yet used it when we start to checkpoint.
> > + *
> > + * Thus, the old code that saves and restores AA profiles is still relevant, we
> > + * just need to add the new code in this file to walk the namespace and dump
> > + * any blobs in that AA namespace, and then restore these blobs on restore so
> > + * that the profiles the old code tries to use are actualy present.
> > + */
> > +
> > +static AaNamespace **namespaces = NULL;
> > +static int n_namespaces = 0;
> > +
> > +bool ns_dumping_enabled = false;
> > +
> > +static AaNamespace *new_namespace(char *name, AaNamespace *parent)
> > +{
> > +	void *m;
> > +	AaNamespace *ret;
> > +
> > +	ret = xmalloc(sizeof(*ret));
> > +	if (!ret)
> > +		return NULL;
> > +	aa_namespace__init(ret);
> > +
> > +	ret->name = xstrdup(name);
> > +	if (!ret->name) {
> > +		xfree(ret);
> > +		return NULL;
> > +	}
> > +
> > +	if (parent) {
> > +		m = xrealloc(parent->namespaces, sizeof(*parent->namespaces) * (parent->n_namespaces + 1));
> > +		if (!m) {
> > +			xfree(ret->name);
> > +			xfree(ret);
> > +			return NULL;
> > +		}
> > +
> > +		parent->namespaces = m;
> > +		parent->namespaces[parent->n_namespaces++] = ret;
> > +	}
> > +
> > +	m = xrealloc(namespaces, sizeof(*namespaces) * (n_namespaces + 1));
> > +	if (!m) {
> > +		if (parent)
> > +			parent->n_namespaces--;
> > +
> > +		xfree(ret->name);
> > +		xfree(ret);
> > +		return NULL;
> > +	}
> > +
> > +	namespaces = m;
> > +	namespaces[n_namespaces++] = ret;
> > +
> > +	return ret;
> > +}
> > +
> > +static int collect_profile(char *path, char *name, AaNamespace *ns)
> > +{
> > +	AaPolicy *cur;
> > +	char *c;
> > +	int fd;
> > +	struct stat sb;
> > +	ssize_t n;
> > +	void *m;
> > +
> > +	strcat(path, name);
> > +	strcat(path, "/raw_data");
> > +
> > +	/* the apparmor kernel stuff puts an extra .N on the profile name that
> > +	 * is the directory name which is annoying. We strip it off here. */
> > +	c = strrchr(name, '.');
> > +	if (!c) {
> > +		pr_err("malformed apparmor profile name %s\n", name);
> > +		return -1;
> > +	}
> > +	*c = 0;
> > +
> > +	pr_info("dumping profile %s\n", path);
> > +
> > +	cur = xmalloc(sizeof(*cur));
> > +	if (!cur)
> > +		return -1;
> > +	aa_policy__init(cur);
> > +
> > +	cur->name = xstrdup(name);
> > +	if (!cur->name) {
> > +		xfree(cur);
> > +		return -1;
> > +	}
> > +
> > +	fd = open(path, O_RDONLY);
> > +	if (fd < 0) {
> > +		pr_perror("failed to open aa policy %s", path);
> > +		goto err;
> > +	}
> > +
> > +	if (fstat(fd, &sb) < 0) {
> > +		pr_perror("failed to stat %s", path);
> > +		goto close;
> > +	}
> > +
> > +	cur->blob.len = sb.st_size;
> > +	cur->blob.data = xmalloc(sb.st_size);
> > +	if (!cur->blob.data)
> > +		goto close;
> > +
> > +	n = read(fd, cur->blob.data, sb.st_size);
> > +	if (n < 0) {
> > +		pr_perror("failed to read %s", path);
> > +		goto close;
> > +	}
> > +
> > +	if (n != sb.st_size) {
> > +		pr_err("didn't read all of %s\n", path);
> > +		goto close;
> > +	}
> > +
> > +	close(fd);
> > +
> > +	m = xrealloc(ns->policies, sizeof(*ns->policies) * (ns->n_policies + 1));
> > +	if (!m)
> > +		goto err;
> > +	ns->policies = m;
> > +	ns->policies[ns->n_policies++] = cur;
> > +
> > +	return 0;
> > +
> > +close:
> > +	close(fd);
> > +
> > +err:
> > +	xfree(cur->name);
> > +	xfree(cur);
> > +	return -1;
> > +}
> > +
> > +char *ns_path;
> > +int sort_err;
> > +
> > +static int no_dirdots(const struct dirent *de)
> > +{
> > +	return !dir_dots(de);
> > +}
> > +
> > +static int by_time(const struct dirent **de1, const struct dirent **de2)
> > +{
> > +	char path[PATH_MAX];
> > +	struct stat sb1, sb2;
> > +
> > +	snprintf(path, sizeof(path), "%s/%s", ns_path, (*de1)->d_name);
> > +	if (stat(path, &sb1) < 0) {
> > +		pr_perror("couldn't stat %s\n", path);
> > +		sort_err = errno;
> > +		return 0;
> > +	}
> > +
> > +	snprintf(path, sizeof(path), "%s/%s", ns_path, (*de2)->d_name);
> > +	if (stat(path, &sb2) < 0) {
> > +		pr_perror("couldn't state %s\n", path);
> > +		sort_err = errno;
> > +		return 0;
> > +	}
> > +
> > +	if (sb1.st_mtim.tv_sec == sb2.st_mtim.tv_sec) {
> > +		if (sb1.st_mtim.tv_nsec < sb2.st_mtim.tv_nsec)
> > +			return -1;
> > +		if (sb1.st_mtim.tv_nsec == sb2.st_mtim.tv_nsec)
> > +			return 0;
> > +		return 1;
> > +	} else {
> > +		if (sb1.st_mtim.tv_sec < sb2.st_mtim.tv_sec)
> > +			return -1;
> > +		if (sb1.st_mtim.tv_sec == sb2.st_mtim.tv_sec)
> > +			return 0;
> > +		return 1;
> > +	}
> > +}
> > +
> > +static int walk_namespace(char *path, size_t offset, AaNamespace *ns)
> > +{
> > +	DIR *dir = NULL;
> > +	struct dirent *de, **namelist = NULL;
> > +	int ret = -1, n_names = 0, i;
> > +	size_t my_offset;
> > +
> > +	/* collect all the child namespaces */
> > +	strcat(path, "/namespaces/");
> > +	my_offset = offset + 12;
> > +
> > +	dir = opendir(path);
> > +	if (!dir)
> > +		goto out;
> > +
> > +	while((de = readdir(dir))) {
> > +		AaNamespace *cur;
> > +
> > +		if (dir_dots(de))
> > +			continue;
> > +
> > +		path[my_offset] = '\0';
> > +		strcat(path, de->d_name);
> > +
> > +		cur = new_namespace(de->d_name, ns);
> > +		if (!cur)
> > +			goto out;
> > +
> > +		if (walk_namespace(path, my_offset + strlen(de->d_name), cur) < 0) {
> > +			aa_namespace__free_unpacked(cur, NULL);
> > +			ns->n_namespaces--;
> > +			goto out;
> > +		}
> > +	}
> > +
> > +	closedir(dir);
> > +	dir = NULL;
> > +
> > +	/* now collect the profiles for this namespace */
> > +	path[offset] = '\0';
> > +	strcat(path, "/profiles/");
> > +	my_offset = offset + 10;
> > +
> > +	sort_err = 0;
> > +	ns_path = path;
> > +	n_names = scandir(path, &namelist, no_dirdots, by_time);
> > +	if (n_names < 0 || sort_err != 0) {
> > +		pr_perror("scandir failed");
> > +		goto out;
> > +	}
> > +
> > +	for (i = 0; i < n_names; i++) {
> > +		de = namelist[i];
> > +
> > +		path[my_offset] = 0;
> > +		if (collect_profile(path, de->d_name, ns) < 0)
> > +			goto out;
> > +	}
> > +
> > +	ret = 0;
> > +out:
> > +	if (dir)
> > +		closedir(dir);
> > +
> > +	if (namelist) {
> > +		for (i = 0; i < n_names; i++)
> > +			xfree(namelist[i]);
> > +		xfree(namelist);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +int collect_aa_namespace(char *profile)
> > +{
> > +	char path[PATH_MAX], *namespace, *end;
> > +	int ret, i;
> > +	AaNamespace *ns;
> > +
> > +	if (!profile)
> > +		return 0;
> > +
> > +	namespace = strchr(profile, ':');
> > +	if (!namespace)
> > +		return 0;  /* no namespace to dump */
> > +	namespace++;
> > +
> > +	if (!ns_dumping_enabled) {
> > +		pr_warn("Apparmor namespace present but dumping not enabled\n");
> > +		return 0;
> > +	}
> > +
> > +	/* XXX: this is not strictly correct; if something is using namespace
> > +	 * views, extra //s can indicate a namespace separation. However, I
> > +	 * think only the apparmor developers use this feature :)
> > +	 */
> > +	end = strchr(namespace, ':');
> > +	if (!end) {
> > +		pr_err("couldn't find AA namespace end in: %s", namespace);
> > +		return -1;
> > +	}
> > +
> > +	*end = '\0';
> > +
> > +	for (i = 0; i < n_namespaces; i++) {
> > +		/* did we already dump this namespace? */
> > +		if (!strcmp(namespaces[i]->name, namespace)) {
> > +			*end = ':';
> > +			return 0;
> > +		}
> > +	}
> > +
> > +	pr_info("dumping AA namespace %s\n", namespace);
> > +
> > +	ns = new_namespace(namespace, NULL);
> > +	*end = ':';
> > +	if (!ns)
> > +		return -1;
> > +
> > +	ret = snprintf(path, sizeof(path), AA_SECURITYFS_PATH "/policy/namespaces/%s", ns->name);
> > +	if (ret < 0 || ret >= sizeof(path)) {
> > +		pr_err("snprintf failed?\n");
> > +		goto err;
> > +	}
> > +
> > +	if (walk_namespace(path, ret, ns) < 0) {
> > +		pr_err("walking AA namespace %s failed\n", ns->name);
> > +		goto err;
> > +	}
> > +
> > +	return 0;
> > +
> > +err:
> > +	aa_namespace__free_unpacked(ns, NULL);
> > +	n_namespaces--;
> > +	return -1;
> > +}
> > +
> > +int dump_aa_namespaces(void)
> > +{
> > +	ApparmorEntry *ae = NULL;
> > +	int ret;
> > +
> > +	if (n_namespaces == 0)
> > +		return 0;
> > +
> > +	ae = xmalloc(sizeof(*ae));
> > +	if (!ae)
> > +		return -1;
> > +	apparmor_entry__init(ae);
> > +
> > +	ae->n_namespaces = n_namespaces;
> > +	ae->namespaces = namespaces;
> > +
> > +	ret = pb_write_one(img_from_set(glob_imgset, CR_FD_APPARMOR), ae, PB_APPARMOR);
> > +
> > +	apparmor_entry__free_unpacked(ae, NULL);
> > +	n_namespaces = -1;
> > +	namespaces = NULL;
> > +
> > +	return ret;
> > +}
> > +
> > +bool check_aa_ns_dumping(void)
> > +{
> > +	char contents[48];
> > +	int major, minor, ret;
> > +	FILE *f;
> > +
> > +	f = fopen(AA_SECURITYFS_PATH "/features/domain/stack", "r");
> > +	if (!f)
> > +		return false;
> > +
> > +	ret = fscanf(f, "%48s", contents);
> > +	fclose(f);
> > +	if (ret != 1) {
> > +		pr_err("scanning aa stack feature failed\n");
> > +		return false;
> > +	}
> > +
> > +	if (strcmp("yes", contents)) {
> > +		pr_warn("aa stack featured disabled: %s\n", contents);
> > +		return false;
> > +	}
> > +
> > +	f = fopen(AA_SECURITYFS_PATH "/features/domain/version", "r");
> > +	if (!f)
> > +		return false;
> > +
> > +	ret = fscanf(f, "%d.%d", &major, &minor);
> > +	fclose(f);
> > +	if (ret != 2) {
> > +		pr_err("scanning aa stack version failed\n");
> > +		return false;
> > +	}
> > +
> > +	return major >= 1 && minor >= 2;
> > +}
> > +
> > +static int restore_aa_namespace(AaNamespace *ns, char *path, int offset)
> > +{
> > +	pid_t pid;
> > +	int status;
> > +
> > +	pid = fork();
> > +	if (pid < 0) {
> > +		pr_perror("fork failed");
> > +		return -1;
> > +	}
> > +
> > +	if (!pid) {
> > +		int i, my_offset, ret, fd;
> > +		char buf[1024];
> > +
> > +		ret = snprintf(buf, sizeof(buf), "changeprofile :%s:", ns->name);
> > +		if (ret < 0 || ret >= sizeof(buf)) {
> > +			pr_err("profile %s too big\n", ns->name);
> > +			exit(1);
> > +		}
> > +
> > +		my_offset = snprintf(path+offset, PATH_MAX-offset, "/namespaces/%s", ns->name);
> > +		if (my_offset < 0 || my_offset >= PATH_MAX-offset) {
> > +			pr_err("snprintf'd too many characters\n");
> > +			exit(1);
> > +		}
> > +
> > +		if (mkdir(path, 0755) < 0) {
> > +			if (errno == EEXIST) {
> > +				pr_warn("apparmor namespace %s already exists, restoring into it\n", path);
> > +			} else {
> > +				pr_perror("failed to create namespace %s", path);
> > +				exit(1);
> > +			}
> > +		}
> > +
> > +		fd = open_proc_rw(PROC_SELF, "attr/current");
> > +		if (fd < 0) {
> > +			pr_perror("couldn't open attr/current");
> > +			goto fail;
> > +		}
> > +
> > +		errno = 0;
> > +		ret = write(fd, buf, strlen(buf));
> > +		close(fd);
> > +		if (ret != strlen(buf)) {
> > +			pr_perror("failed to change aa namespace %s", buf);
> > +			goto fail;
> > +		}
> > +
> > +		for (i = 0; i < ns->n_namespaces; i++) {
> > +			if (restore_aa_namespace(ns, path, offset + my_offset) < 0)
> > +				goto fail;
> > +		}
> > +
> > +		for (i = 0; i < ns->n_policies; i++) {
> > +			int fd, n;
> > +			AaPolicy *p = ns->policies[i];
> > +
> > +			fd = open(AA_SECURITYFS_PATH "/.replace", O_WRONLY);
> > +			if (fd < 0) {
> > +				pr_perror("couldn't open apparmor load file");
> > +				goto fail;
> > +			}
> > +
> > +			n = write(fd, p->blob.data, p->blob.len);
> > +			close(fd);
> > +			if (n != p->blob.len) {
> > +				pr_perror("write AA policy failed");
> > +				goto fail;
> > +			}
> > +		}
> > +
> > +		exit(0);
> > +fail:
> > +		rmdir(path);
> > +		exit(1);
> > +	}
> > +
> > +	if (waitpid(pid, &status, 0) < 0) {
> > +		pr_perror("waitpid failed");
> > +		return -1;
> > +	}
> > +
> > +	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
> > +		return 0;
> > +
> > +	pr_err("failed to restore aa namespace, worker exited: %d\n", status);
> > +	return -1;
> > +}
> > +
> > +int prepare_apparmor_namespaces(void)
> > +{
> > +	struct cr_img *img;
> > +	int ret, i;
> > +	ApparmorEntry *ae;
> > +
> > +	img = open_image(CR_FD_APPARMOR, O_RSTR);
> > +	if (!img)
> > +		return -1;
> > +
> > +	ret = pb_read_one_eof(img, &ae, PB_APPARMOR);
> > +	close_image(img);
> > +	if (ret <= 0)
> > +		return 0; /* there was no AA namespace entry */
> > +
> > +	BUG_ON(!ae);
> > +
> > +	/* no real reason we couldn't do this in parallel, but in usually we
> > +	 * expect one namespace so there's probably not a lot to be gained.
> > +	 */
> > +	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) {
> > +			ret = -1;
> > +			goto out;
> > +		}
> > +	}
> > +
> > +	ret = 0;
> > +out:
> > +	apparmor_entry__free_unpacked(ae, NULL);
> > +	return ret;
> > +}
> > diff --git a/criu/cr-dump.c b/criu/cr-dump.c
> > index e15f178..360bfcc 100644
> > --- a/criu/cr-dump.c
> > +++ b/criu/cr-dump.c
> > @@ -81,7 +81,9 @@
> >  #include "seccomp.h"
> >  #include "seize.h"
> >  #include "fault-injection.h"
> > -#include "dump.h"
> > +#include "apparmor.h"
> > +
> > +#include "asm/dump.h"
> >  
> >  static char loc_buf[PAGE_SIZE];
> >  
> > @@ -1811,6 +1813,9 @@ int cr_dump_tasks(pid_t pid)
> >  		if (dump_namespaces(root_item, root_ns_mask) < 0)
> >  			goto err;
> >  
> > +	if (dump_aa_namespaces() < 0)
> > +		goto err;
> > +
> >  	ret = dump_cgroups();
> >  	if (ret)
> >  		goto err;
> > diff --git a/criu/cr-restore.c b/criu/cr-restore.c
> > index 9629c7b..1a03179 100644
> > --- a/criu/cr-restore.c
> > +++ b/criu/cr-restore.c
> > @@ -75,6 +75,7 @@
> >  #include "seccomp.h"
> >  #include "fault-injection.h"
> >  #include "sk-queue.h"
> > +#include "apparmor.h"
> >  
> >  #include "parasite-syscall.h"
> >  #include "files-reg.h"
> > @@ -144,6 +145,9 @@ static int crtools_prepare_shared(void)
> >  	if (prepare_cgroup())
> >  		return -1;
> >  
> > +	if (prepare_apparmor_namespaces())
> > +		return -1;
> > +
> >  	return 0;
> >  }
> >  
> > diff --git a/criu/image-desc.c b/criu/image-desc.c
> > index bac7ca2..3728aa3 100644
> > --- a/criu/image-desc.c
> > +++ b/criu/image-desc.c
> > @@ -99,6 +99,7 @@ struct cr_fd_desc_tmpl imgset_template[CR_FD_MAX] = {
> >  	FD_ENTRY(USERNS,	"userns-%d"),
> >  	FD_ENTRY(NETNF_CT,	"netns-ct-%d"),
> >  	FD_ENTRY(NETNF_EXP,	"netns-exp-%d"),
> > +	FD_ENTRY(APPARMOR,	"apparmor"),
> >  
> >  	[CR_FD_STATS] = {
> >  		.fmt	= "stats-%s",
> > diff --git a/criu/include/apparmor.h b/criu/include/apparmor.h
> > new file mode 100644
> > index 0000000..447e015
> > --- /dev/null
> > +++ b/criu/include/apparmor.h
> > @@ -0,0 +1,12 @@
> > +#ifndef __CR_APPARMOR_H__
> > +#define __CR_APPARMOR_H__
> > +
> > +int collect_aa_namespace(char *profile);
> > +int dump_aa_namespaces(void);
> > +
> > +extern bool ns_dumping_enabled;
> > +bool check_aa_ns_dumping(void);
> > +
> > +int prepare_apparmor_namespaces(void);
> > +
> > +#endif /* __CR_APPARMOR_H__ */
> > diff --git a/criu/include/image-desc.h b/criu/include/image-desc.h
> > index 09d187d..624fcd7 100644
> > --- a/criu/include/image-desc.h
> > +++ b/criu/include/image-desc.h
> > @@ -81,6 +81,7 @@ enum {
> >  	CR_FD_TIMERFD,
> >  	CR_FD_FILE_LOCKS,
> >  	CR_FD_SECCOMP,
> > +	CR_FD_APPARMOR,
> >  	_CR_FD_GLOB_TO,
> >  
> >  	CR_FD_TMPFS_IMG,
> > diff --git a/criu/include/magic.h b/criu/include/magic.h
> > index deb54b1..9eff82b 100644
> > --- a/criu/include/magic.h
> > +++ b/criu/include/magic.h
> > @@ -93,6 +93,7 @@
> >  #define SECCOMP_MAGIC		0x64413049 /* Kostomuksha */
> >  #define BINFMT_MISC_MAGIC	0x67343323 /* Apatity */
> >  #define AUTOFS_MAGIC		0x49353943 /* Sochi */
> > +#define APPARMOR_MAGIC		0x59423047 /* Sablino */
> >  
> >  #define IFADDR_MAGIC		RAW_IMAGE_MAGIC
> >  #define ROUTE_MAGIC		RAW_IMAGE_MAGIC
> > diff --git a/criu/include/protobuf-desc.h b/criu/include/protobuf-desc.h
> > index 6c76b49..9c3a9c7 100644
> > --- a/criu/include/protobuf-desc.h
> > +++ b/criu/include/protobuf-desc.h
> > @@ -59,6 +59,7 @@ enum {
> >  	PB_BINFMT_MISC,		/* 50 */
> >  	PB_TTY_DATA,
> >  	PB_AUTOFS,
> > +	PB_APPARMOR,
> >  
> >  	/* PB_AUTOGEN_STOP */
> >  
> > diff --git a/criu/lsm.c b/criu/lsm.c
> > index 27ca004..cd3ef6d 100644
> > --- a/criu/lsm.c
> > +++ b/criu/lsm.c
> > @@ -10,6 +10,7 @@
> >  #include "util.h"
> >  #include "cr_options.h"
> >  #include "lsm.h"
> > +#include "apparmor.h"
> >  
> >  #include "protobuf.h"
> >  #include "images/inventory.pb-c.h"
> > @@ -121,6 +122,7 @@ void kerndat_lsm(void)
> >  		get_label = apparmor_get_label;
> >  		lsmtype = LSMTYPE__APPARMOR;
> >  		name = "apparmor";
> > +		ns_dumping_enabled = check_aa_ns_dumping();
> >  		return;
> >  	}
> >  
> > @@ -158,6 +160,11 @@ int collect_lsm_profile(pid_t pid, CredsEntry *ce)
> >  	if (get_label(pid, &ce->lsm_profile) < 0)
> >  		return -1;
> >  
> > +	if (lsmtype == LSMTYPE__APPARMOR && collect_aa_namespace(ce->lsm_profile) < 0) {
> > +		pr_err("failed to collect AA namespace\n");
> > +		return -1;
> > +	}
> > +
> >  	if (ce->lsm_profile)
> >  		pr_info("%d has lsm profile %s\n", pid, ce->lsm_profile);
> >  
> > diff --git a/criu/protobuf-desc.c b/criu/protobuf-desc.c
> > index a6807e2..ca84711 100644
> > --- a/criu/protobuf-desc.c
> > +++ b/criu/protobuf-desc.c
> > @@ -62,6 +62,7 @@
> >  #include "images/seccomp.pb-c.h"
> >  #include "images/binfmt-misc.pb-c.h"
> >  #include "images/autofs.pb-c.h"
> > +#include "images/apparmor.pb-c.h"
> >  
> >  struct cr_pb_message_desc cr_pb_descs[PB_MAX];
> >  
> > diff --git a/images/Makefile b/images/Makefile
> > index eb18526..533c620 100644
> > --- a/images/Makefile
> > +++ b/images/Makefile
> > @@ -61,6 +61,7 @@ proto-obj-y	+= time.o
> >  proto-obj-y	+= sysctl.o
> >  proto-obj-y	+= autofs.o
> >  proto-obj-y	+= macvlan.o
> > +proto-obj-y	+= apparmor.o
> >  
> >  CFLAGS		+= -iquote $(obj)/
> >  
> > diff --git a/images/apparmor.proto b/images/apparmor.proto
> > new file mode 100644
> > index 0000000..0c84f80
> > --- /dev/null
> > +++ b/images/apparmor.proto
> > @@ -0,0 +1,16 @@
> > +syntax = "proto2";
> > +
> > +message aa_policy {
> > +	required string		name	= 1;
> > +	required bytes		blob	= 2;
> > +}
> > +
> > +message aa_namespace {
> > +	required string		name			= 1;
> > +	repeated aa_policy	policies		= 2;
> > +	repeated aa_namespace	namespaces		= 3;
> > +}
> > +
> > +message apparmor_entry {
> > +	repeated aa_namespace	namespaces		= 1;
> > +}
> > diff --git a/images/creds.proto b/images/creds.proto
> > index 29fb865..467a810 100644
> > --- a/images/creds.proto
> > +++ b/images/creds.proto
> > @@ -19,5 +19,6 @@ message creds_entry {
> >  
> >  	repeated uint32	groups	= 14;
> >  
> > -	optional string lsm_profile = 15;
> > +	optional string lsm_profile 	= 15;
> > +	optional bytes	apparmor_data	= 16;
> >  }
> > diff --git a/lib/py/images/images.py b/lib/py/images/images.py
> > index c593a3b..127f2b7 100644
> > --- a/lib/py/images/images.py
> > +++ b/lib/py/images/images.py
> > @@ -454,6 +454,7 @@ handlers = {
> >  	'USERNS'		: entry_handler(userns_entry),
> >  	'SECCOMP'		: entry_handler(seccomp_entry),
> >  	'AUTOFS'		: entry_handler(autofs_entry),
> > +	'APPARMOR'		: entry_handler(apparmor_entry),
> >  	}
> >  
> >  def __rhandler(f):
> > 
> 


More information about the CRIU mailing list