Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/checkpoint-restore/criu.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTycho Andersen <tycho.andersen@canonical.com>2015-05-07 01:18:42 +0300
committerPavel Emelyanov <xemul@parallels.com>2015-05-08 15:31:05 +0300
commit5fe3a138df9d157e3e29c910ca9ff9186cd5ad7f (patch)
treed86a611e933af5826f52c128422f6f5b86c686a4
parenta8b7e53b46cac77f55e3039d07b8404b43e22909 (diff)
lsm: add support for c/ring LSM profiles
This patch adds support for checkpoint and restore of two linux security modules (apparmor and selinux). The actual checkpoint or restore code isn't that interesting, other than that we have to do the LSM restore in the restorer blob since it may block any number of things that we want to do as part of the restore process. I tried originally to get this to work using libraries in the restorer blob, but I could _not_ get things to work correctly (I assume I was doing something wrong with all the static linking, you can see my draft attempts here: https://github.com/tych0/criu/commits/apparmor-using-libraries ). I can try to resurrect this if it makes more sense, to do it that way, though. v2: lsm_profile lives in creds.proto instead of the task core, look in a more canonical place for selinuxfs and don't try to special case any selinux profile names. v3: only allow unconfined selinux profiles Signed-off-by: Tycho Andersen <tycho.andersen@canonical.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
-rw-r--r--Makefile.config5
-rw-r--r--Makefile.crtools1
-rw-r--r--cr-dump.c4
-rw-r--r--cr-restore.c61
-rw-r--r--image.c5
-rw-r--r--include/lsm.h29
-rw-r--r--include/restorer.h4
-rw-r--r--lsm.c206
-rw-r--r--pie/restorer.c24
-rw-r--r--protobuf/creds.proto2
-rw-r--r--protobuf/inventory.proto7
-rw-r--r--scripts/utilities.mak4
12 files changed, 347 insertions, 5 deletions
diff --git a/Makefile.config b/Makefile.config
index ac54775fc..e1d2a3b8d 100644
--- a/Makefile.config
+++ b/Makefile.config
@@ -8,6 +8,11 @@ ifeq ($(call try-cc,$(LIBBSD_DEV_TEST),-lbsd),y)
DEFINES += -DCONFIG_HAS_LIBBSD
endif
+ifeq ($(call pkg-config-check,libselinux),y)
+ LIBS := -lselinux $(LIBS)
+ DEFINES += -DCONFIG_HAS_SELINUX
+endif
+
$(CONFIG): scripts/utilities.mak scripts/feature-tests.mak include/config-base.h
$(E) " GEN " $@
$(Q) @echo '#ifndef __CR_CONFIG_H__' > $@
diff --git a/Makefile.crtools b/Makefile.crtools
index 650b9b0c3..403d6fa1c 100644
--- a/Makefile.crtools
+++ b/Makefile.crtools
@@ -64,6 +64,7 @@ obj-y += timerfd.o
obj-y += aio.o
obj-y += string.o
obj-y += sigframe.o
+obj-y += lsm.o
ifeq ($(VDSO),y)
obj-y += $(ARCH_DIR)/vdso.o
endif
diff --git a/cr-dump.c b/cr-dump.c
index ac4186508..f865967db 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -74,6 +74,7 @@
#include "action-scripts.h"
#include "aio.h"
#include "security.h"
+#include "lsm.h"
#include "asm/dump.h"
@@ -485,6 +486,9 @@ static int dump_task_creds(struct parasite_ctl *ctl,
if (parasite_dump_creds(ctl, &ce) < 0)
return -1;
+ if (collect_lsm_profile(ctl->pid.real, &ce) < 0)
+ return -1;
+
return pb_write_one(img_from_set(fds, CR_FD_CREDS), &ce, PB_CREDS);
}
diff --git a/cr-restore.c b/cr-restore.c
index 73bb9ca2d..aa00dc2eb 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -74,6 +74,7 @@
#include "action-scripts.h"
#include "aio.h"
#include "security.h"
+#include "lsm.h"
#include "parasite-syscall.h"
@@ -2248,7 +2249,7 @@ static inline int verify_cap_size(CredsEntry *ce)
(ce->n_cap_prm == CR_CAP_SIZE) && (ce->n_cap_bnd == CR_CAP_SIZE));
}
-static int prepare_creds(int pid, struct task_restore_args *args)
+static int prepare_creds(int pid, struct task_restore_args *args, char **lsm_profile)
{
int ret;
struct cr_img *img;
@@ -2295,6 +2296,17 @@ static int prepare_creds(int pid, struct task_restore_args *args)
return -1;
}
+ *lsm_profile = NULL;
+
+ if (ce->lsm_profile) {
+ if (validate_lsm(ce) < 0)
+ return -1;
+
+ *lsm_profile = xstrdup(ce->lsm_profile);
+ if (!*lsm_profile)
+ return -1;
+ }
+
creds_entry__free_unpacked(ce, NULL);
args->cap_last_cap = kdat.last_cap;
@@ -2631,6 +2643,10 @@ static int sigreturn_restore(pid_t pid, CoreEntry *core)
unsigned long aio_rings;
MmEntry *mm = rsti(current)->mm;
+ char *lsm = NULL;
+ int lsm_profile_len = 0;
+ unsigned long lsm_pos = 0;
+
struct vm_area_list self_vmas;
struct vm_area_list *vmas = &rsti(current)->vmas;
int i;
@@ -2783,6 +2799,32 @@ static int sigreturn_restore(pid_t pid, CoreEntry *core)
task_args = mem;
thread_args = (struct thread_restore_args *)(task_args + 1);
+ ret = prepare_creds(pid, task_args, &lsm);
+ if (ret < 0)
+ goto err;
+
+ if (lsm) {
+ char *rendered;
+ int ret;
+
+ ret = render_lsm_profile(lsm, &rendered);
+ xfree(lsm);
+ if (ret < 0) {
+ goto err_nv;
+ }
+
+ lsm_pos = rst_mem_cpos(RM_PRIVATE);
+ lsm_profile_len = strlen(rendered);
+ lsm = rst_mem_alloc(lsm_profile_len + 1, RM_PRIVATE);
+ if (!lsm) {
+ xfree(rendered);
+ goto err_nv;
+ }
+
+ strncpy(lsm, rendered, lsm_profile_len);
+ xfree(rendered);
+ }
+
/*
* Get a reference to shared memory area which is
* used to signal if shmem restoration complete
@@ -2837,6 +2879,19 @@ static int sigreturn_restore(pid_t pid, CoreEntry *core)
else
task_args->helpers = NULL;
+ if (lsm) {
+ task_args->proc_attr_current = open_proc_rw(PROC_SELF, "attr/current");
+ if (task_args->proc_attr_current < 0) {
+ pr_perror("Can't open attr/current");
+ goto err;
+ }
+
+ task_args->lsm_profile = rst_mem_remap_ptr(lsm_pos, RM_PRIVATE);
+ task_args->lsm_profile_len = lsm_profile_len;
+ } else {
+ task_args->lsm_profile = NULL;
+ }
+
/*
* Arguments for task restoration.
*/
@@ -2937,10 +2992,6 @@ static int sigreturn_restore(pid_t pid, CoreEntry *core)
if (ret < 0)
goto err;
- ret = prepare_creds(pid, task_args);
- if (ret < 0)
- goto err;
-
ret = prepare_mm(pid, task_args);
if (ret < 0)
goto err;
diff --git a/image.c b/image.c
index 5906bc889..bb0edaa04 100644
--- a/image.c
+++ b/image.c
@@ -8,6 +8,7 @@
#include "pstree.h"
#include "stats.h"
#include "cgroup.h"
+#include "lsm.h"
#include "protobuf.h"
#include "protobuf/inventory.pb-c.h"
#include "protobuf/pagemap.pb-c.h"
@@ -17,6 +18,7 @@ bool ns_per_id = false;
bool img_common_magic = true;
TaskKobjIdsEntry *root_ids;
u32 root_cg_set;
+Lsmtype image_lsm;
int check_img_inventory(void)
{
@@ -51,6 +53,8 @@ int check_img_inventory(void)
root_cg_set = he->root_cg_set;
}
+ image_lsm = he->lsmtype;
+
switch (he->img_version) {
case CRTOOLS_IMAGES_V1:
/* good old images. OK */
@@ -93,6 +97,7 @@ int write_img_inventory(void)
he.has_fdinfo_per_id = true;
he.ns_per_id = true;
he.has_ns_per_id = true;
+ he.lsmtype = host_lsm_type();
crt.i.state = TASK_ALIVE;
crt.i.pid.real = getpid();
diff --git a/include/lsm.h b/include/lsm.h
new file mode 100644
index 000000000..d3b0c973c
--- /dev/null
+++ b/include/lsm.h
@@ -0,0 +1,29 @@
+#ifndef __CR_LSM_H__
+#define __CR_LSM_H__
+
+#include "protobuf/inventory.pb-c.h"
+#include "protobuf/creds.pb-c.h"
+
+/*
+ * Get the Lsmtype for the current host.
+ */
+extern Lsmtype host_lsm_type();
+
+/*
+ * Read the LSM profile for the pstree item
+ */
+extern int collect_lsm_profile(pid_t, CredsEntry *);
+
+/*
+ * Validate that the LSM profiles can be correctly applied (must happen after
+ * pstree is set up).
+ */
+extern int validate_lsm();
+
+/*
+ * Render the profile name in the way that the LSM wants it written to
+ * /proc/<pid>/attr/current.
+ */
+int render_lsm_profile(char *profile, char **val);
+
+#endif /* __CR_LSM_H__ */
diff --git a/include/restorer.h b/include/restorer.h
index 8b63a95fc..34396e342 100644
--- a/include/restorer.h
+++ b/include/restorer.h
@@ -154,6 +154,10 @@ struct task_restore_args {
pid_t *helpers /* the TASK_HELPERS to wait on at the end of restore */;
int n_helpers;
+ int proc_attr_current;
+ char *lsm_profile;
+ int lsm_profile_len;
+
#ifdef CONFIG_VDSO
unsigned long vdso_rt_size;
struct vdso_symtable vdso_sym_rt; /* runtime vdso symbols */
diff --git a/lsm.c b/lsm.c
new file mode 100644
index 000000000..90b395f5d
--- /dev/null
+++ b/lsm.c
@@ -0,0 +1,206 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "pstree.h"
+#include "util.h"
+
+#include "protobuf.h"
+#include "protobuf/inventory.pb-c.h"
+#include "protobuf/creds.pb-c.h"
+
+#ifdef CONFIG_HAS_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+static Lsmtype lsmtype;
+static int (*get_label)(pid_t, char **) = NULL;
+static char *name = NULL;
+
+static int apparmor_get_label(pid_t pid, char **profile_name)
+{
+ FILE *f;
+ char *space;
+
+ f = fopen_proc(pid, "attr/current");
+ if (!f)
+ return -1;
+
+ if (fscanf(f, "%ms", profile_name) != 1) {
+ fclose(f);
+ pr_perror("err scanfing");
+ return -1;
+ }
+
+ fclose(f);
+
+ /*
+ * A profile name can be followed by an enforcement mode, e.g.
+ * lxc-default-with-nesting (enforced)
+ * but the profile name is just the part before the space.
+ */
+ space = strstr(*profile_name, " ");
+ if (space)
+ *space = 0;
+
+ /*
+ * An "unconfined" value means there is no profile, so we don't need to
+ * worry about trying to restore one.
+ */
+ if (strcmp(*profile_name, "unconfined") == 0)
+ *profile_name = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_SELINUX
+static int selinux_get_label(pid_t pid, char **output)
+{
+ security_context_t ctx;
+ char *pos, *last;
+ int i;
+
+ if (getpidcon_raw(pid, &ctx) < 0) {
+ pr_perror("getting selinux profile failed");
+ return -1;
+ }
+
+ *output = NULL;
+
+ /*
+ * Since SELinux attributes can be finer grained than at the task
+ * level, and we currently don't try to dump any of these other bits,
+ * let's only allow unconfined profiles, which look something like:
+ *
+ * unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
+ */
+ pos = (char*)ctx;
+ for (i = 0; i < 3; i++) {
+ last = pos;
+ pos = strstr(pos, ":");
+ if (!pos) {
+ pr_err("Invalid selinux context %s\n", (char *)ctx);
+ freecon(ctx);
+ return -1;
+ }
+
+ *pos = 0;
+ if (!strstartswith(last, "unconfined_")) {
+ pr_err("Non unconfined selinux contexts not supported %s\n", last);
+ freecon(ctx);
+ return -1;
+ }
+
+ pos++;
+ }
+ freecon(ctx);
+
+ return 0;
+}
+#endif
+
+static void get_host_lsm()
+{
+ if (access("/sys/kernel/security/apparmor", F_OK) == 0) {
+ get_label = apparmor_get_label;
+ lsmtype = LSMTYPE__APPARMOR;
+ name = "apparmor";
+ return;
+ }
+
+#ifdef CONFIG_HAS_SELINUX
+ /*
+ * This seems to be the canonical place to mount this fs if it is
+ * enabled, although we may (?) want to check /selinux for posterity as
+ * well.
+ */
+ if (access("/sys/fs/selinux", F_OK) == 0) {
+ get_label = selinux_get_label;
+ lsmtype = LSMTYPE__SELINUX;
+ name = "selinux";
+ return;
+ }
+#endif
+
+ get_label = NULL;
+ lsmtype = LSMTYPE__NO_LSM;
+ name = "none";
+}
+
+Lsmtype host_lsm_type()
+{
+ if (name == NULL)
+ get_host_lsm();
+
+ return lsmtype;
+}
+
+int collect_lsm_profile(pid_t pid, CredsEntry *ce)
+{
+ if (name == NULL)
+ get_host_lsm();
+
+ ce->lsm_profile = NULL;
+
+ if (lsmtype == LSMTYPE__NO_LSM)
+ return 0;
+
+ if (get_label(pid, &ce->lsm_profile) < 0)
+ return -1;
+
+ if (ce->lsm_profile)
+ pr_info("%d has lsm profile %s\n", pid, ce->lsm_profile);
+
+ return 0;
+}
+
+// in inventory.c
+extern Lsmtype image_lsm;
+
+int validate_lsm(CredsEntry *ce)
+{
+ if (name == NULL)
+ get_host_lsm();
+
+ if (image_lsm == LSMTYPE__NO_LSM || image_lsm == lsmtype)
+ return 0;
+
+ /*
+ * 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 (ce->lsm_profile) {
+ pr_err("mismatched lsm types and lsm profile specified\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int render_lsm_profile(char *profile, char **val)
+{
+ *val = NULL;
+
+ switch (lsmtype) {
+ case LSMTYPE__APPARMOR:
+ if (strcmp(profile, "unconfined") != 0 && asprintf(val, "changeprofile %s", profile) < 0) {
+ *val = NULL;
+ return -1;
+ }
+ break;
+ case LSMTYPE__SELINUX:
+ if (asprintf(val, "%s", profile) < 0) {
+ *val = NULL;
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/pie/restorer.c b/pie/restorer.c
index d64fbf09d..8713c6a96 100644
--- a/pie/restorer.c
+++ b/pie/restorer.c
@@ -740,6 +740,25 @@ static int wait_helpers(struct task_restore_args *task_args)
return 0;
}
+static int lsm_set_label(struct task_restore_args *args)
+{
+ int ret = -1;
+
+ if (!args->lsm_profile)
+ return 0;
+
+ pr_info("restoring lsm profile %s\n", args->lsm_profile);
+
+ ret = sys_write(args->proc_attr_current, args->lsm_profile, args->lsm_profile_len);
+ sys_close(args->proc_attr_current);
+ if (ret < 0) {
+ pr_err("can't write lsm profile\n");
+ return -1;
+ }
+
+ return ret;
+}
+
/*
* The main routine to restore task via sigreturn.
* This one is very special, we never return there
@@ -1160,6 +1179,11 @@ long __export_restore_task(struct task_restore_args *args)
ret = ret || restore_dumpable_flag(&args->mm);
ret = ret || restore_pdeath_sig(args->t);
+ if (lsm_set_label(args) < 0) {
+ pr_err("lsm_set_label failed\n");
+ goto core_restore_end;
+ }
+
futex_set_and_wake(&thread_inprogress, args->nr_threads);
restore_finish_stage(CR_STATE_RESTORE_CREDS);
diff --git a/protobuf/creds.proto b/protobuf/creds.proto
index 68894ac07..1bf840513 100644
--- a/protobuf/creds.proto
+++ b/protobuf/creds.proto
@@ -16,4 +16,6 @@ message creds_entry {
required uint32 secbits = 13;
repeated uint32 groups = 14;
+
+ optional string lsm_profile = 15;
}
diff --git a/protobuf/inventory.proto b/protobuf/inventory.proto
index 97f572f22..a107bb11f 100644
--- a/protobuf/inventory.proto
+++ b/protobuf/inventory.proto
@@ -1,9 +1,16 @@
import "core.proto";
+enum lsmtype {
+ NO_LSM = 0;
+ SELINUX = 1;
+ APPARMOR = 2;
+}
+
message inventory_entry {
required uint32 img_version = 1;
optional bool fdinfo_per_id = 2;
optional task_kobj_ids_entry root_ids = 3;
optional bool ns_per_id = 4;
optional uint32 root_cg_set = 5;
+ optional lsmtype lsmtype = 6;
}
diff --git a/scripts/utilities.mak b/scripts/utilities.mak
index d1b68faa5..6fb81c396 100644
--- a/scripts/utilities.mak
+++ b/scripts/utilities.mak
@@ -5,3 +5,7 @@ try-cc = $(shell sh -c \
echo "$(1)" | \
$(CC) $(DEFINES) -x c - $(2) $(3) -o "$$TMP" > /dev/null 2>&1 && echo y; \
rm -f "$$TMP"')
+
+# pkg-config-check
+# Usage: ifeq ($(call pkg-config-check, library),y)
+pkg-config-check = $(shell sh -c 'pkg-config $(1) && echo y')