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

Pavel Emelyanov xemul at virtuozzo.com
Tue Oct 25 05:45:59 PDT 2016


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?

> 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