mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +00:00
Noticed this when I was looking over the LSM code for Cyrill's task => thread creds set. We set this to null to save some work later, but we forget to free it first. Signed-off-by: Tycho Andersen <tycho.andersen@canonical.com> Acked-by: Cyrill Gorcunov <gorcunov@openvz.org> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
251 lines
4.8 KiB
C
251 lines
4.8 KiB
C
#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 "cr_options.h"
|
|
|
|
#include "protobuf.h"
|
|
#include "protobuf/inventory.pb-c.h"
|
|
#include "protobuf/creds.pb-c.h"
|
|
|
|
#undef CONFIG_HAS_SELINUX
|
|
|
|
#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) {
|
|
free(*profile_name);
|
|
*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
|
|
|
|
void kerndat_lsm(void)
|
|
{
|
|
/* On restore, if someone passes --lsm-profile, we might end up doing
|
|
* detection twice, once during flag parsing and once for
|
|
* kerndat_init_rst(). Let's detect when we've already done detection
|
|
* and not do it again.
|
|
*/
|
|
if (name)
|
|
return;
|
|
|
|
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(void)
|
|
{
|
|
return lsmtype;
|
|
}
|
|
|
|
int collect_lsm_profile(pid_t pid, CredsEntry *ce)
|
|
{
|
|
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(char *lsm_profile)
|
|
{
|
|
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 (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;
|
|
}
|
|
|
|
int parse_lsm_arg(char *arg)
|
|
{
|
|
char *aux;
|
|
|
|
kerndat_lsm();
|
|
|
|
aux = strchr(arg, ':');
|
|
if (aux == NULL) {
|
|
pr_err("invalid argument %s for --lsm-profile", arg);
|
|
return -1;
|
|
}
|
|
|
|
*aux = '\0';
|
|
aux++;
|
|
|
|
if (strcmp(arg, "apparmor") == 0) {
|
|
if (lsmtype != LSMTYPE__APPARMOR) {
|
|
pr_err("apparmor LSM specified but apparmor not supported by kernel\n");
|
|
return -1;
|
|
}
|
|
|
|
opts.lsm_profile = aux;
|
|
} else if (strcmp(arg, "selinux") == 0) {
|
|
if (lsmtype != LSMTYPE__SELINUX) {
|
|
pr_err("selinux LSM specified but selinux not supported by kernel\n");
|
|
return -1;
|
|
}
|
|
|
|
opts.lsm_profile = aux;
|
|
} else if (strcmp(arg, "none") == 0) {
|
|
opts.lsm_profile = NULL;
|
|
} else {
|
|
pr_err("unknown lsm %s\n", arg);
|
|
return -1;
|
|
}
|
|
|
|
opts.lsm_supplied = true;
|
|
|
|
return 0;
|
|
}
|