[lxc-devel] [lxc/master] implement PR_SET_NO_NEW_PRIVS in liblxc
brauner on Github
lxc-bot at linuxcontainers.org
Sat Sep 3 12:19:02 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 770 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160903/904ae97d/attachment.bin>
-------------- next part --------------
From 59c70350925998f0015df0244d9f3de50d7f6d8a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Fri, 2 Sep 2016 01:30:59 +0200
Subject: [PATCH 1/7] conf, confile: add option for PR_SET_NO_NEW_PRIVS
Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
src/lxc/conf.h | 3 +++
src/lxc/confile.c | 18 ++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 2593ce5..286879d 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -378,6 +378,9 @@ struct lxc_conf {
/* indicator if the container will be destroyed on shutdown */
int ephemeral;
+
+ /* Whether PR_SET_NO_NEW_PRIVS will be set for the container. */
+ bool no_new_privs;
};
#ifdef HAVE_TLS
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index fac919d..9064d6e 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -114,6 +114,7 @@ static int config_init_cmd(const char *, const char *, struct lxc_conf *);
static int config_init_uid(const char *, const char *, struct lxc_conf *);
static int config_init_gid(const char *, const char *, struct lxc_conf *);
static int config_ephemeral(const char *, const char *, struct lxc_conf *);
+static int config_no_new_privs(const char *, const char *, struct lxc_conf *);
static struct lxc_config_t config[] = {
@@ -187,6 +188,7 @@ static struct lxc_config_t config[] = {
{ "lxc.init_gid", config_init_gid },
{ "lxc.ephemeral", config_ephemeral },
{ "lxc.syslog", config_syslog },
+ { "lxc.no_new_privs", config_no_new_privs },
};
struct signame {
@@ -2577,6 +2579,8 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv,
return lxc_get_conf_int(c, retv, inlen, c->init_gid);
else if (strcmp(key, "lxc.ephemeral") == 0)
return lxc_get_conf_int(c, retv, inlen, c->ephemeral);
+ else if (strcmp(key, "lxc.no_new_privs") == 0)
+ return lxc_get_conf_int(c, retv, inlen, c->no_new_privs);
else return -1;
if (!v)
@@ -2973,3 +2977,17 @@ static int config_syslog(const char *key, const char *value,
ERROR("Wrong value for lxc.syslog");
return -1;
}
+
+static int config_no_new_privs(const char *key, const char *value,
+ struct lxc_conf *lxc_conf)
+{
+ int v = atoi(value);
+
+ if (v != 0 && v != 1) {
+ ERROR("Wrong value for lxc.no_new_privs. Can only be set to 0 or 1");
+ return -1;
+ }
+ lxc_conf->no_new_privs = v ? true : false;
+
+ return 0;
+}
From 703c9b6ccc5342e93e26d1b20970386d6737b57f Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Fri, 2 Sep 2016 01:40:39 +0200
Subject: [PATCH 2/7] start: set PR_SET_NO_NEW_PRIVS when requested
Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
src/lxc/start.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 2411626..4605d45 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -923,6 +923,14 @@ static int do_start(void *data)
setsid();
+ if (handler->conf->no_new_privs) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+ SYSERROR("Could not set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges.");
+ goto out_warn_father;
+ }
+ DEBUG("Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges.");
+ }
+
/* after this call, we are in error because this
* ops should not return as it execs */
handler->ops->start(handler, handler->data);
From 538ad7ad69d3a2fc8f3e342999eeb8c854373724 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Fri, 2 Sep 2016 18:17:11 +0200
Subject: [PATCH 3/7] attach_options: add LXC_ATTACH_NO_NEW_PRIVS
Add a flag for PR_SET_NO_NEW_PRIVS. It is off by default.
Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
src/lxc/attach_options.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/lxc/attach_options.h b/src/lxc/attach_options.h
index 3c54e7c..1df6992 100644
--- a/src/lxc/attach_options.h
+++ b/src/lxc/attach_options.h
@@ -49,6 +49,8 @@ enum {
/* the following are off by default */
LXC_ATTACH_REMOUNT_PROC_SYS = 0x00010000, //!< Remount /proc filesystem
LXC_ATTACH_LSM_NOW = 0x00020000, //!< FIXME: unknown
+ /* Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges. */
+ LXC_ATTACH_NO_NEW_PRIVS = 0x00040000, //!< PR_SET_NO_NEW_PRIVS
/* we have 16 bits for things that are on by default
* and 16 bits that are off by default, that should
From 28406b9292163925faa13b04ada9191aae818245 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Fri, 2 Sep 2016 18:39:11 +0200
Subject: [PATCH 4/7] attach: call lxc_container_new() earlier
We will reuse the newly initialized container for PR_SET_NO_NEW_PRIVS.
Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
src/lxc/attach.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 0d9e3d0..813d049 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -657,8 +657,8 @@ static int attach_child_main(void* data);
/* define default options if no options are supplied by the user */
static lxc_attach_options_t attach_static_default_options = LXC_ATTACH_OPTIONS_DEFAULT;
-static bool fetch_seccomp(const char *name, const char *lxcpath,
- struct lxc_proc_context_info *i, lxc_attach_options_t *options)
+static bool fetch_seccomp(struct lxc_proc_context_info *i,
+ lxc_attach_options_t *options)
{
struct lxc_container *c;
char *path;
@@ -666,10 +666,7 @@ static bool fetch_seccomp(const char *name, const char *lxcpath,
if (!(options->namespaces & CLONE_NEWNS) || !(options->attach_flags & LXC_ATTACH_LSM))
return true;
- c = lxc_container_new(name, lxcpath);
- if (!c)
- return false;
- i->container = c;
+ c = i->container;
/* Initialize an empty lxc_conf */
if (!c->set_config_item(c, "lxc.seccomp", "")) {
@@ -744,7 +741,11 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
}
init_ctx->personality = personality;
- if (!fetch_seccomp(name, lxcpath, init_ctx, options))
+ init_ctx->container = lxc_container_new(name, lxcpath);
+ if (!init_ctx->container)
+ return -1;
+
+ if (!fetch_seccomp(init_ctx, options))
WARN("Failed to get seccomp policy");
cwd = getcwd(NULL, 0);
From ddfb8a09ace17a13a08dfcb7698dafffd10e9623 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Fri, 2 Sep 2016 18:56:48 +0200
Subject: [PATCH 5/7] attach: use PR_SET_NO_NEW_PRIVS
- When we detect that the container, we want to attach to, has been stared with
PR_SET_NO_NEW_PRIVS we attach with PR_SET_NO_NEW_PRIVS as well. (We might
relax this restriction later but let's be strict for now.)
- When LXC_ATTACH_NO_NEW_PRIVS is set in the flags passed to
lxc_attach()/attach_child_main() then we set PR_SET_NO_NEW_PRIVS irrespective
of whether the container was started with PR_SET_NO_NEW_PRIVS or not.
Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
src/lxc/attach.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 48 insertions(+), 2 deletions(-)
diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 813d049..171d5a9 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -668,7 +668,7 @@ static bool fetch_seccomp(struct lxc_proc_context_info *i,
c = i->container;
- /* Initialize an empty lxc_conf */
+ /* Remove current setting. */
if (!c->set_config_item(c, "lxc.seccomp", "")) {
return false;
}
@@ -692,6 +692,37 @@ static bool fetch_seccomp(struct lxc_proc_context_info *i,
return false;
}
+ INFO("Retrieved seccomp policy.");
+ return true;
+}
+
+static bool no_new_privs(struct lxc_proc_context_info *ctx,
+ lxc_attach_options_t *options)
+{
+ struct lxc_container *c;
+ char *val;
+
+ c = ctx->container;
+
+ /* Remove current setting. */
+ if (!c->set_config_item(c, "lxc.no_new_privs", "")) {
+ return false;
+ }
+
+ /* Retrieve currently active setting. */
+ val = c->get_running_config_item(c, "lxc.no_new_privs");
+ if (!val) {
+ INFO("Failed to get running config item for lxc.no_new_privs.");
+ return false;
+ }
+
+ /* Set currently active setting. */
+ if (!c->set_config_item(c, "lxc.no_new_privs", val)) {
+ free(val);
+ return false;
+ }
+ free(val);
+
return true;
}
@@ -748,6 +779,9 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
if (!fetch_seccomp(init_ctx, options))
WARN("Failed to get seccomp policy");
+ if (!no_new_privs(init_ctx, options))
+ WARN("Could not determine whether PR_SET_NO_NEW_PRIVS is set.");
+
cwd = getcwd(NULL, 0);
/* determine which namespaces the container was created with
@@ -1162,7 +1196,6 @@ static int attach_child_main(void* data)
ERROR("Loading seccomp policy");
rexit(-1);
}
-
lxc_proc_put_context_info(init_ctx);
/* The following is done after the communication socket is
@@ -1206,6 +1239,19 @@ static int attach_child_main(void* data)
/* we don't need proc anymore */
close(procfd);
+ if ((init_ctx->container && init_ctx->container->lxc_conf &&
+ init_ctx->container->lxc_conf->no_new_privs) ||
+ (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+ SYSERROR("PR_SET_NO_NEW_PRIVS could not be set. "
+ "Process can use execve() gainable "
+ "privileges.");
+ rexit(-1);
+ }
+ INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() "
+ "gainable privileges.");
+ }
+
/* we're done, so we can now do whatever the user intended us to do */
rexit(payload->exec_function(payload->exec_payload));
}
From 5abe8cd9f9478c96f540cae43a9638afe31abe85 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Sat, 3 Sep 2016 08:00:20 +0200
Subject: [PATCH 6/7] doc: add lxc.no_new_privs to lxc.container.conf
Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
doc/lxc.container.conf.sgml.in | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index 1b740a5..e666ae1 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -1311,6 +1311,32 @@ mknod errno 0
</refsect2>
<refsect2>
+ <title>PR_SET_NO_NEW_PRIVS</title>
+ <para>
+ With PR_SET_NO_NEW_PRIVS active execve() promises not to grant
+ privileges to do anything that could not have been done without
+ the execve() call (for example, rendering the set-user-ID and
+ set-group-ID mode bits, and file capabilities non-func‐
+ tional). Once set, this bit cannot be unset. The setting of this
+ bit is inher ited by children created by fork() and clone(),
+ and preserved across execve().
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>lxc.no_new_privs</option>
+ </term>
+ <listitem>
+ <para>
+ Specify whether the PR_SET_NO_NEW_PRIVS flag should be set for the
+ container. Set to 1 to activate.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+
+ <refsect2>
<title>UID mappings</title>
<para>
A container can be started in a private user namespace with
From 66180d2062ad3b6c4a32a17acde9da18d6c80c09 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Sat, 3 Sep 2016 13:59:47 +0200
Subject: [PATCH 7/7] tests: add test for PR_SET_NO_NEW_PRIVS
Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
src/tests/Makefile.am | 8 +++-
src/tests/lxc-test-no-new-privs | 104 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 110 insertions(+), 2 deletions(-)
create mode 100755 src/tests/lxc-test-no-new-privs
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index cffc742..2e7dd56 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -53,8 +53,11 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
lxc-test-apparmor lxc-test-utils
-bin_SCRIPTS = lxc-test-automount lxc-test-autostart lxc-test-cloneconfig \
- lxc-test-createconfig
+bin_SCRIPTS = lxc-test-automount \
+ lxc-test-autostart \
+ lxc-test-cloneconfig \
+ lxc-test-createconfig \
+ lxc-test-no-new-privs
if DISTRO_UBUNTU
bin_SCRIPTS += \
@@ -91,6 +94,7 @@ EXTRA_DIST = \
lxc-test-checkpoint-restore \
lxc-test-cloneconfig \
lxc-test-createconfig \
+ lxc-test-no-new-privs \
lxc-test-snapdeps \
lxc-test-symlink \
lxc-test-ubuntu \
diff --git a/src/tests/lxc-test-no-new-privs b/src/tests/lxc-test-no-new-privs
new file mode 100755
index 0000000..251b19a
--- /dev/null
+++ b/src/tests/lxc-test-no-new-privs
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+# lxc: linux Container library
+
+# Authors:
+# Christian Brauner <christian.brauner at mailbox.org>
+#
+# This is a test script for unprivileged containers
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+set -eux
+
+DONE=0
+cleanup() {
+ cd /
+ lxc-destroy -n c1 -f || true
+ if [ $DONE -eq 0 ]; then
+ echo "FAIL"
+ exit 1
+ fi
+ echo "PASS"
+}
+
+trap cleanup EXIT SIGHUP SIGINT SIGTERM
+
+mkdir -p /etc/lxc/
+cat > /etc/lxc/default.conf << EOF
+lxc.network.type = veth
+lxc.network.link = lxcbr0
+EOF
+
+ARCH=i386
+if type dpkg >/dev/null 2>&1; then
+ ARCH=$(dpkg --print-architecture)
+fi
+
+lxc-create -t download -n c1 -- -d ubuntu -r xenial -a $ARCH
+echo "lxc.no_new_privs = 1" >> /var/lib/lxc/c1/config
+
+lxc-start -n c1
+p1=$(lxc-info -n c1 -p -H)
+[ "$p1" != "-1" ] || { echo "Failed to start container c1 (run $count)"; false; }
+sleep 5s
+lxc-attach -n c1 --clear-env -- apt update -y
+lxc-attach -n c1 --clear-env -- apt install -y gcc make
+
+# Here documents don't seem to like sudo -i.
+lxc-attach -n c1 --clear-env -- /bin/bash -c "cat <<EOF > /nnptest.c
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+int main(int argc, char *argv[])
+{
+ printf(\"%d\n\", geteuid());
+}
+EOF"
+lxc-attach -n c1 --clear-env -- cat /nnptest.c
+lxc-attach -n c1 --clear-env -- make -C / nnptest
+lxc-attach -n c1 --clear-env -- chmod u+s /nnptest
+
+# Check that lxc-attach obeys PR_SET_NO_NEW_PRIVS when it is set.
+NNP_EUID=$(lxc-attach -n c1 --clear-env -- sudo -u ubuntu /nnptest)
+if [ "$NNP_EUID" -ne 1000 ]; then
+ exit 1
+fi
+lxc-stop -n c1 -k
+
+# Check that lxc-attach obeys PR_SET_NO_NEW_PRIVS when it is not set.
+sed -i 's/lxc.no_new_privs = 1/lxc.no_new_privs = 0/' /var/lib/lxc/c1/config
+lxc-start -n c1
+NNP_EUID=$(lxc-attach -n c1 --clear-env -- sudo -u ubuntu /nnptest)
+if [ "$NNP_EUID" -ne 0 ]; then
+ exit 1
+fi
+lxc-stop -n c1 -k
+
+# Check that lxc-execute and lxc-start obey PR_SET_NO_NEW_PRIVS when it is set.
+NNP_EUID=$(lxc-execute -n c1 -- sudo -u ubuntu /nnptest)
+if [ "$NNP_EUID" -ne 0 ]; then
+ exit 1
+fi
+
+# Check that lxc-execute and lxc-start obey PR_SET_NO_NEW_PRIVS when it is not set.
+sed -i 's/lxc.no_new_privs = 0/lxc.no_new_privs = 1/' /var/lib/lxc/c1/config
+NNP_EUID=$(lxc-execute -n c1 -- sudo -u ubuntu /nnptest)
+if [ "$NNP_EUID" -ne 1000 ]; then
+ exit 1
+fi
+
+DONE=1
More information about the lxc-devel
mailing list