Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix SELinux process labeling and label sockets correctly #648

Merged
merged 3 commits into from
Mar 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions criu/lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,45 +62,41 @@ static int apparmor_get_label(pid_t pid, char **profile_name)
static int selinux_get_label(pid_t pid, char **output)
{
security_context_t ctx;
char *pos, *last;
char *pos;
int i;
int ret = -1;

if (getpidcon_raw(pid, &ctx) < 0) {
pr_perror("getting selinux profile failed");
return -1;
}

*output = NULL;
*output = xstrdup((char *)ctx);
if (!*output)
goto err;

/*
* 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:
* Make sure it is a valid SELinux label. It should look like this:
*
* 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;
xfree(*output);
goto err;
}

*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;
ret = 0;
err:
freecon(ctx);
return ret;
}
#endif

Expand Down
70 changes: 70 additions & 0 deletions criu/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include <libnl3/netlink/msg.h>
#include <libnl3/netlink/netlink.h>

#ifdef CONFIG_HAS_SELINUX
#include <selinux/selinux.h>
#endif

#include "../soccr/soccr.h"

#include "imgset.h"
Expand All @@ -40,6 +44,7 @@

#include "protobuf.h"
#include "images/netdev.pb-c.h"
#include "images/inventory.pb-c.h"

#ifndef IFLA_LINK_NETNSID
#define IFLA_LINK_NETNSID 37
Expand Down Expand Up @@ -2745,13 +2750,78 @@ static int prep_ns_sockets(struct ns_id *ns, bool for_dump)
} else
ns->net.nlsk = -1;

#ifdef CONFIG_HAS_SELINUX
/*
* If running on a system with SELinux enabled the socket for the
* communication between parasite daemon and the main
* CRIU process needs to be correctly labeled.
* Initially this was motivated by Podman's use case: The container
* is usually running as something like '...:...:container_t:...:....'
* and CRIU started from runc and Podman will run as
* '...:...:container_runtime_t:...:...'. As the parasite will be
* running with the same context as the container process: 'container_t'.
* Allowing a container process to connect via socket to the outside
* of the container ('container_runtime_t') is not desired and
* therefore CRIU needs to label the socket with the context of
* the container: 'container_t'.
* So this first gets the context of the root container process
* and tells SELinux to label the next created socket with
* the same label as the root container process.
* For this to work it is necessary to have the correct SELinux
* policies installed. For Fedora based systems this is part
* of the container-selinux package.
*/
security_context_t ctx;

/*
* This assumes that all processes CRIU wants to dump are labeled
* with the same SELinux context. If some of the child processes
* have different labels this will not work and needs additional
* SELinux policies. But the whole SELinux socket labeling relies
* on the correct SELinux being available.
*/
if (kdat.lsm == LSMTYPE__SELINUX) {
ret = getpidcon_raw(root_item->pid->real, &ctx);
if (ret < 0) {
pr_perror("Getting SELinux context for PID %d failed",
root_item->pid->real);
goto err_sq;
}

ret = setsockcreatecon(ctx);
freecon(ctx);
if (ret < 0) {
pr_perror("Setting SELinux socket context for PID %d failed",
root_item->pid->real);
goto err_sq;
}
}
#endif

ret = ns->net.seqsk = socket(PF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0);
if (ret < 0) {
pr_perror("Can't create seqsk for parasite");
goto err_sq;
}

ret = 0;

#ifdef CONFIG_HAS_SELINUX
/*
* Once the socket has been created, reset the SELinux socket labelling
* back to the default value of this process.
*/
if (kdat.lsm == LSMTYPE__SELINUX) {
ret = setsockcreatecon_raw(NULL);
if (ret < 0) {
pr_perror("Resetting SELinux socket context to "
"default for PID %d failed",
root_item->pid->real);
goto err_ret;
}
}
#endif

out:
if (nsret >= 0 && restore_ns(nsret, &net_ns_desc) < 0) {
nsret = -1;
Expand Down
1 change: 1 addition & 0 deletions test/zdtm/static/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ TST_NOFILE := \
config_inotify_irmap \
thp_disable \
pid_file \
selinux00 \
# jobctl00 \
ifneq ($(SRCARCH),arm)
Expand Down
108 changes: 108 additions & 0 deletions test/zdtm/static/selinux00.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <linux/limits.h>
#include <signal.h>
#include "zdtmtst.h"

/* Enabling the right policy happens in selinux00.hook and selinx00.checkskip */

const char *test_doc = "Check that a SELinux profile is restored";
const char *test_author = "Adrian Reber <areber@redhat.com>";

/* This is all based on Tycho's apparmor code */

#define CONTEXT "unconfined_u:unconfined_r:unconfined_dbusd_t:s0"

/*
* This is used to store the state of SELinux. For this test
* SELinux is switched to permissive mode and later the previous
* SELinux state is restored.
*/
char state;

int check_for_selinux()
{
if (access("/sys/fs/selinux", F_OK) == 0)
return 0;
return 1;
}

int setprofile()
{
int fd, len;

fd = open("/proc/self/attr/current", O_WRONLY);
if (fd < 0) {
fail("Could not open /proc/self/attr/current\n");
return -1;
}

len = write(fd, CONTEXT, strlen(CONTEXT));
close(fd);

if (len < 0) {
fail("Could not write context\n");
return -1;
}

return 0;
}

int checkprofile()
{
int fd;
char context[1024];
int len;


fd = open("/proc/self/attr/current", O_RDONLY);
if (fd < 0) {
fail("Could not open /proc/self/attr/current\n");
return -1;
}

len = read(fd, context, strlen(CONTEXT));
close(fd);
if (len != strlen(CONTEXT)) {
fail("SELinux context has unexpected length %d, expected %zd\n",
len, strlen(CONTEXT));
return -1;
}

if (strncmp(context, CONTEXT, strlen(CONTEXT)) != 0) {
fail("Wrong SELinux context %s expected %s\n", context, CONTEXT);
return -1;
}

return 0;
}

int main(int argc, char **argv)
{
test_init(argc, argv);

if (check_for_selinux()) {
skip("SELinux not found on this system.");
test_daemon();
test_waitsig();
pass();
return 0;
}

if (setprofile())
return -1;

test_daemon();
test_waitsig();

if (checkprofile() == 0)
pass();

return 0;
}
25 changes: 25 additions & 0 deletions test/zdtm/static/selinux00.checkskip
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

test -d /sys/fs/selinux || exit 1

# See selinux00.hook for details

getsebool unconfined_dyntrans_all > /dev/null 2>&1
RESULT=$?
BOOLEAN=0

if [ "$RESULT" = "0" ]; then
BOOLEAN=1
fi

if [ "$BOOLEAN" = "1" ]; then
getsebool unconfined_dyntrans_all | grep off -q
RESULT=$?
echo $RESULT > /tmp/zdtm.selinux.state
if [ "$RESULT" = "0" ]; then
setsebool -P unconfined_dyntrans_all 1
fi
else
cat /sys/fs/selinux/enforce > /tmp/zdtm.selinux.state
setenforce 0
fi
1 change: 1 addition & 0 deletions test/zdtm/static/selinux00.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{'flavor': 'h'}
32 changes: 32 additions & 0 deletions test/zdtm/static/selinux00.hook
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/sh

# This script configures SELinux in such a way to enable the
# test 'selinux00' to be able to dyntransition from one
# SELinux context to another, as well as CRIU to change the
# context of a restored process.
# If a new enough selinux-policy is installed which includes
# https://github.com/fedora-selinux/selinux-policy/commit/2d537cabbb2df614ea598ac20873c653cbf271a8
# then the boolean 'unconfined_dyntrans_all' will be changed
# to enable this test. If that boolean is not available,
# this just does 'setenforce 0'.

# also see selinux00.checkskip

getsebool unconfined_dyntrans_all > /dev/null 2>&1
RESULT=$?
BOOLEAN=0

if [ "$RESULT" = "0" ]; then
BOOLEAN=1
fi

[ "$1" = "--post-restore" ] && {
if [ "$BOOLEAN" = "1" ]; then
setsebool -P unconfined_dyntrans_all `cat /tmp/zdtm.selinux.state`
else
setenforce `cat /tmp/zdtm.selinux.state`
rm -f /tmp/zdtm.selinux.state
fi
}

exit 0