[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