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

Pavel Emelyanov xemul at virtuozzo.com
Wed Oct 26 08:55:39 PDT 2016


On 10/25/2016 07:16 PM, Tycho Andersen wrote:
> 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

OK, can you then describe in more details what exactly we're trying
to support :) As far as I see from the patch message there are two
options -- apparmor namesace (is that a new CLONE_ flag?) and stacking
of profiles. I can imagine what the former is, but what about the latter?

> 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