criu/lsm.c
Tycho Andersen 9b4d7acf83 lsm: don't leak apparmor string in the case of "unconfined"
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>
2015-12-18 17:22:00 +03:00

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;
}