int on_exec;
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
- ret = lsm_process_label_set(init_ctx->lsm_label, 0, on_exec);
+ ret = lsm_process_label_set(init_ctx->lsm_label,
+ init_ctx->container->lxc_conf, 0, on_exec);
if (ret < 0) {
rexit(-1);
}
struct lxc_list hooks[NUM_LXC_HOOKS];
char *lsm_aa_profile;
+ int lsm_aa_allow_incomplete;
char *lsm_se_context;
int tmp_umount_proc;
char *seccomp; // filename with the seccomp rules
static int config_ttydir(const char *, const char *, struct lxc_conf *);
static int config_kmsg(const char *, const char *, struct lxc_conf *);
static int config_lsm_aa_profile(const char *, const char *, struct lxc_conf *);
+static int config_lsm_aa_incomplete(const char *, const char *, struct lxc_conf *);
static int config_lsm_se_context(const char *, const char *, struct lxc_conf *);
static int config_cgroup(const char *, const char *, struct lxc_conf *);
static int config_idmap(const char *, const char *, struct lxc_conf *);
{ "lxc.devttydir", config_ttydir },
{ "lxc.kmsg", config_kmsg },
{ "lxc.aa_profile", config_lsm_aa_profile },
+ { "lxc.aa_allow_incomplete", config_lsm_aa_incomplete },
{ "lxc.se_context", config_lsm_se_context },
{ "lxc.cgroup", config_cgroup },
{ "lxc.id_map", config_idmap },
return config_string_item(&lxc_conf->lsm_aa_profile, value);
}
+static int config_lsm_aa_incomplete(const char *key, const char *value,
+ struct lxc_conf *lxc_conf)
+{
+ int v = atoi(value);
+
+ lxc_conf->lsm_aa_allow_incomplete = v == 1 ? 1 : 0;
+
+ return 0;
+}
+
static int config_lsm_se_context(const char *key, const char *value,
struct lxc_conf *lxc_conf)
{
return lxc_get_arch_entry(c, retv, inlen);
else if (strcmp(key, "lxc.aa_profile") == 0)
v = c->lsm_aa_profile;
+ else if (strcmp(key, "lxc.aa_allow_incomplete") == 0)
+ return lxc_get_conf_int(c, retv, inlen, c->lsm_aa_allow_incomplete);
else if (strcmp(key, "lxc.se_context") == 0)
v = c->lsm_se_context;
else if (strcmp(key, "lxc.logfile") == 0)
#include "log.h"
#include "lsm/lsm.h"
+#include "conf.h"
lxc_log_define(lxc_apparmor, lxc);
#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
+static int mount_feature_enabled(void)
+{
+ struct stat statbuf;
+ int ret;
+ ret = stat(AA_MOUNT_RESTR, &statbuf);
+ if (ret != 0)
+ return 0;
+ return 1;
+}
+
/* aa_getcon is not working right now. Use our hand-rolled version below */
static int apparmor_enabled(void)
{
- struct stat statbuf;
FILE *fin;
char e;
int ret;
- ret = stat(AA_MOUNT_RESTR, &statbuf);
- if (ret != 0)
- return 0;
fin = fopen(AA_ENABLED_FILE, "r");
if (!fin)
return 0;
* apparmor_process_label_set: Set AppArmor process profile
*
* @label : the profile to set
+ * @conf : the container configuration to use @label is NULL
* @default : use the default profile if label is NULL
* @on_exec : this is ignored. Apparmor profile will be changed immediately
*
*
* Notes: This relies on /proc being available.
*/
-static int apparmor_process_label_set(const char *label, int use_default,
- int on_exec)
+static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf,
+ int use_default, int on_exec)
{
+ const char *label = inlabel ? inlabel : conf->lsm_aa_profile;
+
if (!aa_enabled)
return 0;
if (use_default)
label = AA_DEF_PROFILE;
else
- return 0;
+ label = "unconfined";
+ }
+
+ if (!mount_feature_enabled() && strcmp(label, "unconfined") != 0) {
+ WARN("Incomplete AppArmor support in your kernel");
+ if (!conf->lsm_aa_allow_incomplete) {
+ ERROR("If you really want to start this container, set");
+ ERROR("lxc.aa_allow_incomplete = 1");
+ ERROR("in your container configuration file");
+ return -1;
+ }
}
+
if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
INFO("apparmor profile unchanged");
return 0;
return drv->process_label_get(pid);
}
-int lsm_process_label_set(const char *label, int use_default, int on_exec)
+int lsm_process_label_set(const char *label, struct lxc_conf *conf,
+ int use_default, int on_exec)
{
if (!drv) {
ERROR("LSM driver not inited");
return -1;
}
- return drv->process_label_set(label, use_default, on_exec);
+ return drv->process_label_set(label, conf, use_default, on_exec);
}
#endif
int (*enabled)(void);
char *(*process_label_get)(pid_t pid);
- int (*process_label_set)(const char *label, int use_default,
- int on_exec);
+ int (*process_label_set)(const char *label, struct lxc_conf *conf,
+ int use_default, int on_exec);
};
#if HAVE_APPARMOR || HAVE_SELINUX
int lsm_enabled(void);
const char *lsm_name(void);
char *lsm_process_label_get(pid_t pid);
-int lsm_process_label_set(const char *label, int use_default, int on_exec);
+int lsm_process_label_set(const char *label, struct lxc_conf *conf,
+ int use_default, int on_exec);
#else
static inline void lsm_init(void) { }
static inline int lsm_enabled(void) { return 0; }
static inline const char *lsm_name(void) { return "none"; }
static inline char *lsm_process_label_get(pid_t pid) { return NULL; }
-static inline int lsm_process_label_set(const char *label, int use_default, int on_exec) { return 0; }
+static inline int lsm_process_label_set(const char *label,
+ struct lxc-conf *conf, int use_default, int on_exec) { return 0; }
#endif
#endif
return NULL;
}
-static int nop_process_label_set(const char *label, int use_default,
- int on_exec)
+static int nop_process_label_set(const char *label, struct lxc_conf *conf,
+ int use_default, int on_exec)
{
return 0;
}
#include "log.h"
#include "lsm/lsm.h"
+#include "conf.h"
#define DEFAULT_LABEL "unconfined_t"
/*
* selinux_process_label_set: Set SELinux context of a process
*
- * @label : the context to set
+ * @label : label string
+ * @conf : the container configuration to use @label is NULL
* @default : use the default context if label is NULL
* @on_exec : the new context will take effect on exec(2) not immediately
*
*
* Notes: This relies on /proc being available.
*/
-static int selinux_process_label_set(const char *label, int use_default,
- int on_exec)
+static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf,
+ int use_default, int on_exec)
{
+ const char *label = inlabel ? inlabel : conf->lsm_se_context;
if (!label) {
if (use_default)
label = DEFAULT_LABEL;
{
struct lxc_list *iterator;
struct lxc_handler *handler = data;
- const char *lsm_label = NULL;
if (sigprocmask(SIG_SETMASK, &handler->oldmask, NULL)) {
SYSERROR("failed to set sigprocmask");
return -1;
/* Set the label to change to when we exec(2) the container's init */
- if (!strcmp(lsm_name(), "AppArmor"))
- lsm_label = handler->conf->lsm_aa_profile;
- else if (!strcmp(lsm_name(), "SELinux"))
- lsm_label = handler->conf->lsm_se_context;
- if (lsm_process_label_set(lsm_label, 1, 1) < 0)
+ if (lsm_process_label_set(NULL, handler->conf, 1, 1) < 0)
goto out_warn_father;
/* Some init's such as busybox will set sane tty settings on stdin,
if DISTRO_UBUNTU
bin_SCRIPTS += \
+ lxc-test-apparmor-mount \
lxc-test-checkpoint-restore \
lxc-test-ubuntu \
lxc-test-unpriv \
locktests.c \
lxcpath.c \
lxc-test-autostart \
+ lxc-test-apparmor-mount \
lxc-test-checkpoint-restore \
lxc-test-cloneconfig \
lxc-test-ubuntu \
--- /dev/null
+#!/bin/sh
+
+# apparmor_mount: test proper handling of apparmor in kernels
+# without mount features
+
+# These require the ubuntu lxc package to be installed.
+
+# This program 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
+
+# This test assumes an Ubuntu host
+
+set -e
+
+FAIL() {
+ echo -n "Failed " >&2
+ echo "$*" >&2
+ exit 1
+}
+
+run_cmd() {
+ sudo -i -u $TUSER \
+ env http_proxy=${http_proxy:-} https_proxy=${https_proxy:-} \
+ XDG_RUNTIME_DIR=/run/user/$(id -u $TUSER) $*
+}
+
+DONE=0
+MOUNTSR=/sys/kernel/security/apparmor/features/mount
+dnam=`mktemp -d`
+cname=`basename $dnam`
+cleanup() {
+ run_cmd lxc-destroy -f -n $cname || true
+ umount -l $MOUNTSR || true
+ rmdir $dnam || true
+ pkill -u $(id -u $TUSER) -9
+ sed -i '/lxcunpriv/d' /run/lxc/nics /etc/lxc/lxc-usernet
+ sed -i '/^lxcunpriv:/d' /etc/subuid /etc/subgid
+ rm -Rf $HDIR /run/user/$(id -u $TUSER)
+ deluser $TUSER
+ if [ $DONE -eq 0 ]; then
+ echo "FAIL"
+ exit 1
+ fi
+ echo "PASS"
+}
+
+trap cleanup exit
+
+# Only run on a normally configured ubuntu lxc system
+if [ ! -d /sys/class/net/lxcbr0 ]; then
+ echo "lxcbr0 is not configured."
+ exit 1
+fi
+if [ "$(id -u)" != "0" ]; then
+ echo "ERROR: Must run as root."
+ exit 1
+fi
+
+# This would be much simpler if we could run it as
+# root. However, in order to not have the bind mount
+# of an empty directory over the securitfs 'mount' directory
+# be removed, we need to do this as non-root.
+
+which newuidmap >/dev/null 2>&1 || { echo "'newuidmap' command is missing" >&2; exit 1; }
+# create a test user
+TUSER=lxcunpriv
+HDIR=/home/$TUSER
+
+ARCH=i386
+if type dpkg >/dev/null 2>&1; then
+ ARCH=$(dpkg --print-architecture)
+fi
+
+deluser $TUSER && rm -Rf $HDIR || true
+useradd $TUSER
+
+mkdir -p $HDIR
+echo "$TUSER veth lxcbr0 2" > /etc/lxc/lxc-usernet
+sed -i '/^lxcunpriv:/d' /etc/subuid /etc/subgid
+
+usermod -v 910000-919999 -w 910000-919999 $TUSER
+
+mkdir -p $HDIR/.config/lxc/
+cat > $HDIR/.config/lxc/default.conf << EOF
+lxc.network.type = veth
+lxc.network.link = lxcbr0
+lxc.id_map = u 0 910000 9999
+lxc.id_map = g 0 910000 9999
+EOF
+chown -R $TUSER: $HDIR
+
+mkdir -p /run/user/$(id -u $TUSER)
+chown -R $TUSER: /run/user/$(id -u $TUSER)
+
+cd $HDIR
+
+cgm create all $TUSER
+cgm chown all $TUSER $(id -u $TUSER) $(id -g $TUSER)
+cgm movepid all $TUSER $$
+
+run_cmd mkdir -p $HDIR/.cache/lxc
+[ -d /var/cache/lxc/download ] && \
+ cp -R /var/cache/lxc/download $HDIR/.cache/lxc && \
+ chown -R $TUSER: $HDIR/.cache/lxc
+
+run_cmd lxc-create -t download -n $cname -- -d ubuntu -r trusty -a $ARCH
+
+echo "test default confined container"
+run_cmd lxc-start -n $cname -d
+run_cmd lxc-wait -n $cname -s RUNNING
+pid=`run_cmd lxc-info -p -H -n $cname`
+profile=`cat /proc/$pid/attr/current`
+if [ "x$profile" != "xlxc-container-default (enforce)" ]; then
+ echo "FAIL: confined container was in profile $profile"
+ exit 1
+fi
+run_cmd lxc-stop -n $cname
+
+echo "test regular unconfined container"
+echo "lxc.aa_profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config
+run_cmd lxc-start -n $cname -d
+run_cmd lxc-wait -n $cname -s RUNNING
+pid=`run_cmd lxc-info -p -H -n $cname`
+profile=`cat /proc/$pid/attr/current`
+if [ "x$profile" != "xunconfined" ]; then
+ echo "FAIL: unconfined container was in profile $profile"
+ exit 1
+fi
+run_cmd lxc-stop -n $cname
+
+echo "masking $MOUNTSR"
+mount --bind $dnam $MOUNTSR
+
+echo "test default confined container"
+sed -i '/aa_profile/d' $HDIR/.local/share/lxc/$cname/config
+run_cmd lxc-start -n $cname -d || true
+sleep 3
+pid=`run_cmd lxc-info -p -H -n $cname` || true
+if [ -n "$pid" -a "$pid" != "-1" ]; then
+ echo "FAIL: confined container started without mount restrictions"
+ echo "pid was $pid"
+ exit 1
+fi
+
+echo "test regular unconfined container"
+echo "lxc.aa_profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config
+run_cmd lxc-start -n $cname -d
+run_cmd lxc-wait -n $cname -s RUNNING
+pid=`run_cmd lxc-info -p -H -n $cname`
+if [ "$pid" = "-1" ]; then
+ echo "FAIL: unconfined container failed to start without mount restrictions"
+ exit 1
+fi
+profile=`cat /proc/$pid/attr/current`
+if [ "x$profile" != "xunconfined" ]; then
+ echo "FAIL: confined container was in profile $profile"
+ exit 1
+fi
+run_cmd lxc-stop -n $cname
+
+echo "testing override"
+sed -i '/aa_profile/d' $HDIR/.local/share/lxc/$cname/config
+echo "lxc.aa_allow_incomplete = 1" >> $HDIR/.local/share/lxc/$cname/config
+run_cmd lxc-start -n $cname -d
+run_cmd lxc-wait -n $cname -s RUNNING
+pid=`run_cmd lxc-info -p -H -n $cname`
+if [ "$pid" = "-1" ]; then
+ echo "FAIL: excepted container failed to start without mount restrictions"
+ exit 1
+fi
+profile=`cat /proc/$pid/attr/current`
+if [ "x$profile" != "xlxc-container-default (enforce)" ]; then
+ echo "FAIL: confined container was in profile $profile"
+ exit 1
+fi
+run_cmd lxc-stop -n $cname
+
+DONE=1