Unverified Commit b7993256 by Stéphane Graber Committed by GitHub

Merge pull request #3517 from brauner/2020-08-10/fixes_2

lsm: rewrite
parents d333aeb4 3bb6ff01
...@@ -91,7 +91,9 @@ static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) ...@@ -91,7 +91,9 @@ static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
if (!found) if (!found)
return log_error_errno(NULL, ENOENT, "Failed to read capability bounding set from %s", proc_fn); return log_error_errno(NULL, ENOENT, "Failed to read capability bounding set from %s", proc_fn);
info->lsm_label = lsm_process_label_get(pid); info->lsm_ops = lsm_init();
info->lsm_label = info->lsm_ops->process_label_get(pid);
info->ns_inherited = 0; info->ns_inherited = 0;
for (int i = 0; i < LXC_NS_MAX; i++) for (int i = 0; i < LXC_NS_MAX; i++)
info->ns_fd[i] = -EBADF; info->ns_fd[i] = -EBADF;
...@@ -777,12 +779,12 @@ static int attach_child_main(struct attach_clone_payload *payload) ...@@ -777,12 +779,12 @@ static int attach_child_main(struct attach_clone_payload *payload)
/* Change into our new LSM profile. */ /* Change into our new LSM profile. */
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false;
ret = lsm_process_label_set_at(lsm_fd, init_ctx->lsm_label, on_exec); ret = init_ctx->lsm_ops->process_label_set_at(lsm_fd, init_ctx->lsm_label, on_exec);
close(lsm_fd); close(lsm_fd);
if (ret < 0) if (ret < 0)
goto on_error; goto on_error;
TRACE("Set %s LSM label to \"%s\"", lsm_name(), init_ctx->lsm_label); TRACE("Set %s LSM label to \"%s\"", init_ctx->lsm_ops->name, init_ctx->lsm_label);
} }
if ((init_ctx->container && init_ctx->container->lxc_conf && if ((init_ctx->container && init_ctx->container->lxc_conf &&
...@@ -1242,7 +1244,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, ...@@ -1242,7 +1244,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
ret = -1; ret = -1;
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false;
labelfd = lsm_process_label_fd_get(attached_pid, on_exec); labelfd = init_ctx->lsm_ops->process_label_fd_get(attached_pid, on_exec);
if (labelfd < 0) if (labelfd < 0)
goto close_mainloop; goto close_mainloop;
......
...@@ -19,6 +19,7 @@ struct lxc_proc_context_info { ...@@ -19,6 +19,7 @@ struct lxc_proc_context_info {
unsigned long long capability_mask; unsigned long long capability_mask;
int ns_inherited; int ns_inherited;
int ns_fd[LXC_NS_MAX]; int ns_fd[LXC_NS_MAX];
const struct lsm_ops *lsm_ops;
}; };
__hidden extern int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, __hidden extern int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
......
...@@ -3211,13 +3211,49 @@ static int lxc_setup_boot_id(void) ...@@ -3211,13 +3211,49 @@ static int lxc_setup_boot_id(void)
return 0; return 0;
} }
static int lxc_setup_keyring(const struct lsm_ops *lsm_ops, const struct lxc_conf *conf)
{
key_serial_t keyring;
int ret = 0;
if (conf->lsm_se_keyring_context)
ret = lsm_ops->keyring_label_set(conf->lsm_se_keyring_context);
else if (conf->lsm_se_context)
ret = lsm_ops->keyring_label_set(conf->lsm_se_context);
if (ret < 0)
return log_error_errno(-1, errno, "Failed to set keyring context");
/*
* Try to allocate a new session keyring for the container to prevent
* information leaks.
*/
keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, prctl_arg(0),
prctl_arg(0), prctl_arg(0), prctl_arg(0));
if (keyring < 0) {
switch (errno) {
case ENOSYS:
DEBUG("The keyctl() syscall is not supported or blocked");
break;
case EACCES:
__fallthrough;
case EPERM:
DEBUG("Failed to access kernel keyring. Continuing...");
break;
default:
SYSERROR("Failed to create kernel keyring");
break;
}
}
return ret;
}
int lxc_setup(struct lxc_handler *handler) int lxc_setup(struct lxc_handler *handler)
{ {
__do_close int pty_mnt_fd = -EBADF; __do_close int pty_mnt_fd = -EBADF;
int ret; int ret;
const char *lxcpath = handler->lxcpath, *name = handler->name; const char *lxcpath = handler->lxcpath, *name = handler->name;
struct lxc_conf *lxc_conf = handler->conf; struct lxc_conf *lxc_conf = handler->conf;
char *keyring_context = NULL;
ret = lxc_setup_rootfs_prepare_root(lxc_conf, name, lxcpath); ret = lxc_setup_rootfs_prepare_root(lxc_conf, name, lxcpath);
if (ret < 0) if (ret < 0)
...@@ -3230,15 +3266,9 @@ int lxc_setup(struct lxc_handler *handler) ...@@ -3230,15 +3266,9 @@ int lxc_setup(struct lxc_handler *handler)
} }
if (!lxc_conf->keyring_disable_session) { if (!lxc_conf->keyring_disable_session) {
if (lxc_conf->lsm_se_keyring_context) { ret = lxc_setup_keyring(handler->lsm_ops, lxc_conf);
keyring_context = lxc_conf->lsm_se_keyring_context;
} else if (lxc_conf->lsm_se_context) {
keyring_context = lxc_conf->lsm_se_context;
}
ret = lxc_setup_keyring(keyring_context);
if (ret < 0) if (ret < 0)
return -1; return log_error(-1, "Failed to setup container keyring");
} }
if (handler->ns_clone_flags & CLONE_NEWNET) { if (handler->ns_clone_flags & CLONE_NEWNET) {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#define _GNU_SOURCE 1 #define _GNU_SOURCE 1
#endif #endif
#include <errno.h> #include <errno.h>
#include <stdatomic.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/mount.h> #include <sys/mount.h>
...@@ -24,15 +25,14 @@ ...@@ -24,15 +25,14 @@
lxc_log_define(apparmor, lsm); lxc_log_define(apparmor, lsm);
/* set by lsm_apparmor_drv_init if true */ /* set by lsm_apparmor_ops_init if true */
static int aa_enabled = 0; static atomic_int aa_enabled ;
static bool aa_parser_available = false; static atomic_int aa_parser_available = -1;
static bool aa_supports_unix = false; static atomic_int aa_supports_unix;
static bool aa_can_stack = false; static atomic_int aa_can_stack;
static bool aa_is_stacked = false; static atomic_int aa_is_stacked;
static bool aa_admin = false; static atomic_int aa_admin;
static atomic_int mount_features_enabled;
static int mount_features_enabled = 0;
#define AA_DEF_PROFILE "lxc-container-default" #define AA_DEF_PROFILE "lxc-container-default"
#define AA_DEF_PROFILE_CGNS "lxc-container-default-cgns" #define AA_DEF_PROFILE_CGNS "lxc-container-default-cgns"
...@@ -375,7 +375,7 @@ static const char AA_PROFILE_UNPRIVILEGED[] = ...@@ -375,7 +375,7 @@ static const char AA_PROFILE_UNPRIVILEGED[] =
static bool check_mount_feature_enabled(void) static bool check_mount_feature_enabled(void)
{ {
return mount_features_enabled == 1; return atomic_load(&mount_features_enabled);
} }
static void load_mount_features_enabled(void) static void load_mount_features_enabled(void)
...@@ -385,13 +385,13 @@ static void load_mount_features_enabled(void) ...@@ -385,13 +385,13 @@ static void load_mount_features_enabled(void)
ret = stat(AA_MOUNT_RESTR, &statbuf); ret = stat(AA_MOUNT_RESTR, &statbuf);
if (ret == 0) if (ret == 0)
mount_features_enabled = 1; atomic_store(&mount_features_enabled, 1);
} }
/* aa_getcon is not working right now. Use our hand-rolled version below */ /* aa_getcon is not working right now. Use our hand-rolled version below */
static int apparmor_enabled(void) static int apparmor_enabled(void)
{ {
FILE *fin; __do_fclose FILE *fin = NULL;
char e; char e;
int ret; int ret;
...@@ -399,7 +399,6 @@ static int apparmor_enabled(void) ...@@ -399,7 +399,6 @@ static int apparmor_enabled(void)
if (!fin) if (!fin)
return 0; return 0;
ret = fscanf(fin, "%c", &e); ret = fscanf(fin, "%c", &e);
fclose(fin);
if (ret == 1 && e == 'Y') { if (ret == 1 && e == 'Y') {
load_mount_features_enabled(); load_mount_features_enabled();
return 1; return 1;
...@@ -545,14 +544,17 @@ static inline char *apparmor_namespace(const char *ctname, const char *lxcpath) ...@@ -545,14 +544,17 @@ static inline char *apparmor_namespace(const char *ctname, const char *lxcpath)
*/ */
static bool check_apparmor_parser_version() static bool check_apparmor_parser_version()
{ {
int major = 0, minor = 0, micro = 0, ret = 0;
struct lxc_popen_FILE *parserpipe; struct lxc_popen_FILE *parserpipe;
int rc; int rc;
int major = 0, minor = 0, micro = 0;
if (atomic_load(&aa_parser_available) >= 0)
return false;
parserpipe = lxc_popen("apparmor_parser --version"); parserpipe = lxc_popen("apparmor_parser --version");
if (!parserpipe) { if (!parserpipe) {
fprintf(stderr, "Failed to run check for apparmor_parser\n"); fprintf(stderr, "Failed to run check for apparmor_parser\n");
return false; goto out;
} }
rc = fscanf(parserpipe->f, "AppArmor parser version %d.%d.%d", &major, &minor, &micro); rc = fscanf(parserpipe->f, "AppArmor parser version %d.%d.%d", &major, &minor, &micro);
...@@ -562,24 +564,27 @@ static bool check_apparmor_parser_version() ...@@ -562,24 +564,27 @@ static bool check_apparmor_parser_version()
* lxc_popen executed failed to find the apparmor_parser binary. * lxc_popen executed failed to find the apparmor_parser binary.
* See the TODO comment above for details. * See the TODO comment above for details.
*/ */
return false; goto out;
} }
rc = lxc_pclose(parserpipe); rc = lxc_pclose(parserpipe);
if (rc < 0) { if (rc < 0) {
fprintf(stderr, "Error waiting for child process\n"); fprintf(stderr, "Error waiting for child process\n");
return false; goto out;
} }
if (rc != 0) { if (rc != 0) {
fprintf(stderr, "'apparmor_parser --version' executed with an error status\n"); fprintf(stderr, "'apparmor_parser --version' executed with an error status\n");
return false; goto out;
} }
aa_supports_unix = (major > 2) || if ((major > 2) || (major == 2 && minor > 10) || (major == 2 && minor == 10 && micro >= 95))
(major == 2 && minor > 10) || atomic_store(&aa_supports_unix, 1);
(major == 2 && minor == 10 && micro >= 95);
return true; ret = 1;
out:
atomic_store(&aa_parser_available, ret);
return ret == 1;
} }
static bool file_is_yes(const char *path) static bool file_is_yes(const char *path)
...@@ -744,7 +749,7 @@ static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxc ...@@ -744,7 +749,7 @@ static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxc
append_all_remount_rules(&profile, &size); append_all_remount_rules(&profile, &size);
if (aa_supports_unix) if (atomic_load(&aa_supports_unix))
must_append_sized(&profile, &size, AA_PROFILE_UNIX_SOCKETS, must_append_sized(&profile, &size, AA_PROFILE_UNIX_SOCKETS,
STRARRAYLEN(AA_PROFILE_UNIX_SOCKETS)); STRARRAYLEN(AA_PROFILE_UNIX_SOCKETS));
...@@ -752,7 +757,7 @@ static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxc ...@@ -752,7 +757,7 @@ static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxc
must_append_sized(&profile, &size, AA_PROFILE_CGROUP_NAMESPACES, must_append_sized(&profile, &size, AA_PROFILE_CGROUP_NAMESPACES,
STRARRAYLEN(AA_PROFILE_CGROUP_NAMESPACES)); STRARRAYLEN(AA_PROFILE_CGROUP_NAMESPACES));
if (aa_can_stack && !aa_is_stacked) { if (atomic_load(&aa_can_stack) && !atomic_load(&aa_is_stacked)) {
char *namespace, *temp; char *namespace, *temp;
must_append_sized(&profile, &size, AA_PROFILE_STACKING_BASE, must_append_sized(&profile, &size, AA_PROFILE_STACKING_BASE,
...@@ -775,7 +780,7 @@ static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxc ...@@ -775,7 +780,7 @@ static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxc
must_append_sized(&profile, &size, AA_PROFILE_NESTING_BASE, must_append_sized(&profile, &size, AA_PROFILE_NESTING_BASE,
STRARRAYLEN(AA_PROFILE_NESTING_BASE)); STRARRAYLEN(AA_PROFILE_NESTING_BASE));
if (!aa_can_stack || aa_is_stacked) { if (!atomic_load(&aa_can_stack) || atomic_load(&aa_is_stacked)) {
char *temp; char *temp;
temp = must_concat(NULL, " change_profile -> \"", temp = must_concat(NULL, " change_profile -> \"",
...@@ -836,7 +841,7 @@ static bool make_apparmor_namespace(struct lxc_conf *conf, const char *lxcpath) ...@@ -836,7 +841,7 @@ static bool make_apparmor_namespace(struct lxc_conf *conf, const char *lxcpath)
{ {
char *path; char *path;
if (!aa_can_stack || aa_is_stacked) if (!atomic_load(&aa_can_stack) || atomic_load(&aa_is_stacked))
return true; return true;
path = make_apparmor_namespace_path(conf->name, lxcpath); path = make_apparmor_namespace_path(conf->name, lxcpath);
...@@ -1021,7 +1026,7 @@ out_ok: ...@@ -1021,7 +1026,7 @@ out_ok:
*/ */
static void apparmor_cleanup(struct lxc_conf *conf, const char *lxcpath) static void apparmor_cleanup(struct lxc_conf *conf, const char *lxcpath)
{ {
if (!aa_admin) if (!atomic_load(&aa_admin))
return; return;
if (!conf->lsm_aa_profile_created) if (!conf->lsm_aa_profile_created)
...@@ -1039,7 +1044,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath) ...@@ -1039,7 +1044,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath)
const char *label; const char *label;
char *curlabel = NULL, *genlabel = NULL; char *curlabel = NULL, *genlabel = NULL;
if (!aa_enabled) { if (!atomic_load(&aa_enabled)) {
ERROR("AppArmor not enabled"); ERROR("AppArmor not enabled");
return -1; return -1;
} }
...@@ -1054,7 +1059,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath) ...@@ -1054,7 +1059,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath)
} }
if (label && strcmp(label, AA_GENERATED) == 0) { if (label && strcmp(label, AA_GENERATED) == 0) {
if (!aa_parser_available) { if (!check_apparmor_parser_version()) {
ERROR("Cannot use generated profile: apparmor_parser not available"); ERROR("Cannot use generated profile: apparmor_parser not available");
goto out; goto out;
} }
...@@ -1071,7 +1076,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath) ...@@ -1071,7 +1076,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath)
goto out; goto out;
} }
if (aa_can_stack && !aa_is_stacked) { if (atomic_load(&aa_can_stack) && !atomic_load(&aa_is_stacked)) {
char *namespace = apparmor_namespace(conf->name, lxcpath); char *namespace = apparmor_namespace(conf->name, lxcpath);
size_t llen = strlen(genlabel); size_t llen = strlen(genlabel);
must_append_sized(&genlabel, &llen, "//&:", STRARRAYLEN("//&:")); must_append_sized(&genlabel, &llen, "//&:", STRARRAYLEN("//&:"));
...@@ -1085,7 +1090,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath) ...@@ -1085,7 +1090,7 @@ static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath)
curlabel = apparmor_process_label_get(lxc_raw_getpid()); curlabel = apparmor_process_label_get(lxc_raw_getpid());
if (!aa_can_stack && aa_needs_transition(curlabel)) { if (!atomic_load(&aa_can_stack) && aa_needs_transition(curlabel)) {
/* we're already confined, and stacking isn't supported */ /* we're already confined, and stacking isn't supported */
if (!label || strcmp(curlabel, label) == 0) { if (!label || strcmp(curlabel, label) == 0) {
...@@ -1128,6 +1133,55 @@ out: ...@@ -1128,6 +1133,55 @@ out:
return ret; return ret;
} }
static int apparmor_keyring_label_set(const char *label)
{
return 0;
}
static int apparmor_process_label_fd_get(pid_t pid, bool on_exec)
{
int ret = -1;
int labelfd;
char path[LXC_LSMATTRLEN];
if (on_exec)
TRACE("On-exec not supported with AppArmor");
ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/current", pid);
if (ret < 0 || ret >= LXC_LSMATTRLEN)
return -1;
labelfd = open(path, O_RDWR);
if (labelfd < 0)
return log_error_errno(-errno, errno, "Unable to open AppArmor LSM label file descriptor");
return labelfd;
}
static int apparmor_process_label_set_at(int label_fd, const char *label, bool on_exec)
{
int ret = -1;
size_t len;
__do_free char *command = NULL;
if (on_exec)
log_trace(0, "Changing AppArmor profile on exec not supported");
len = strlen(label) + strlen("changeprofile ") + 1;
command = malloc(len);
if (!command)
return ret_errno(ENOMEM);
ret = snprintf(command, len, "changeprofile %s", label);
if (ret < 0 || (size_t)ret >= len)
return -EFBIG;
ret = lxc_write_nointr(label_fd, command, len - 1);
INFO("Set AppArmor label to \"%s\"", label);
return 0;
}
/* /*
* apparmor_process_label_set: Set AppArmor process profile * apparmor_process_label_set: Set AppArmor process profile
* *
...@@ -1147,7 +1201,7 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf ...@@ -1147,7 +1201,7 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf
pid_t tid; pid_t tid;
const char *label; const char *label;
if (!aa_enabled) { if (!atomic_load(&aa_enabled)) {
ERROR("AppArmor not enabled"); ERROR("AppArmor not enabled");
return -1; return -1;
} }
...@@ -1169,13 +1223,13 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf ...@@ -1169,13 +1223,13 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf
return 0; return 0;
} }
tid = lxc_raw_gettid(); tid = lxc_raw_gettid();
label_fd = lsm_process_label_fd_get(tid, on_exec); label_fd = apparmor_process_label_fd_get(tid, on_exec);
if (label_fd < 0) { if (label_fd < 0) {
SYSERROR("Failed to change AppArmor profile to %s", label); SYSERROR("Failed to change AppArmor profile to %s", label);
return -1; return -1;
} }
ret = lsm_process_label_set_at(label_fd, label, on_exec); ret = apparmor_process_label_set_at(label_fd, label, on_exec);
close(label_fd); close(label_fd);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to change AppArmor profile to %s", label); ERROR("Failed to change AppArmor profile to %s", label);
...@@ -1186,31 +1240,33 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf ...@@ -1186,31 +1240,33 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf
return 0; return 0;
} }
static struct lsm_drv apparmor_drv = { static struct lsm_ops apparmor_ops = {
.name = "AppArmor", .name = "AppArmor",
.cleanup = apparmor_cleanup,
.enabled = apparmor_enabled, .enabled = apparmor_enabled,
.keyring_label_set = apparmor_keyring_label_set,
.prepare = apparmor_prepare,
.process_label_fd_get = apparmor_process_label_fd_get,
.process_label_get = apparmor_process_label_get, .process_label_get = apparmor_process_label_get,
.process_label_set = apparmor_process_label_set, .process_label_set = apparmor_process_label_set,
.prepare = apparmor_prepare, .process_label_set_at = apparmor_process_label_set_at,
.cleanup = apparmor_cleanup,
}; };
struct lsm_drv *lsm_apparmor_drv_init(void) const struct lsm_ops *lsm_apparmor_ops_init(void)
{ {
bool have_mac_admin = false; bool have_mac_admin = false;
bool can_stack, is_stacked;
if (!apparmor_enabled()) if (!apparmor_enabled())
return NULL; return NULL;
/* We only support generated profiles when apparmor_parser is usable */ can_stack = apparmor_can_stack();
if (!check_apparmor_parser_version()) if (can_stack) {
goto out; atomic_store(&aa_can_stack, 1);
is_stacked = file_is_yes("/sys/kernel/security/apparmor/.ns_stacked");
aa_parser_available = true; if (is_stacked)
atomic_store(&aa_is_stacked, 1);
aa_can_stack = apparmor_can_stack(); }
if (aa_can_stack)
aa_is_stacked = file_is_yes("/sys/kernel/security/apparmor/.ns_stacked");
#if HAVE_LIBCAP #if HAVE_LIBCAP
have_mac_admin = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE); have_mac_admin = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
...@@ -1218,12 +1274,11 @@ struct lsm_drv *lsm_apparmor_drv_init(void) ...@@ -1218,12 +1274,11 @@ struct lsm_drv *lsm_apparmor_drv_init(void)
if (!have_mac_admin) if (!have_mac_admin)
WARN("Per-container AppArmor profiles are disabled because the mac_admin capability is missing"); WARN("Per-container AppArmor profiles are disabled because the mac_admin capability is missing");
else if (am_host_unpriv() && !aa_is_stacked) else if (am_host_unpriv() && !atomic_load(&aa_is_stacked))
WARN("Per-container AppArmor profiles are disabled because LXC is running in an unprivileged container without stacking"); WARN("Per-container AppArmor profiles are disabled because LXC is running in an unprivileged container without stacking");
else else
aa_admin = true; atomic_store(&aa_admin, 1);
out: atomic_store(&aa_enabled, 1);
aa_enabled = 1; return &apparmor_ops;
return &apparmor_drv;
} }
...@@ -17,193 +17,26 @@ ...@@ -17,193 +17,26 @@
lxc_log_define(lsm, lxc); lxc_log_define(lsm, lxc);
static struct lsm_drv *drv = NULL; __hidden extern struct lsm_ops *lsm_apparmor_ops_init(void);
__hidden extern struct lsm_ops *lsm_selinux_ops_init(void);
__hidden extern struct lsm_ops *lsm_nop_ops_init(void);
__hidden extern struct lsm_drv *lsm_apparmor_drv_init(void); const struct lsm_ops *lsm_init(void)
__hidden extern struct lsm_drv *lsm_selinux_drv_init(void);
__hidden extern struct lsm_drv *lsm_nop_drv_init(void);
__attribute__((constructor))
void lsm_init(void)
{ {
if (drv) { const struct lsm_ops *ops = NULL;
INFO("LSM security driver %s", drv->name);
return;
}
#if HAVE_APPARMOR #if HAVE_APPARMOR
drv = lsm_apparmor_drv_init(); ops = lsm_apparmor_ops_init();
#endif #endif
#if HAVE_SELINUX #if HAVE_SELINUX
if (!drv) if (!ops)
drv = lsm_selinux_drv_init(); ops = lsm_selinux_ops_init();
#endif #endif
if (!drv) if (!ops)
drv = lsm_nop_drv_init(); ops = lsm_nop_ops_init();
INFO("Initialized LSM security driver %s", drv->name);
}
int lsm_enabled(void)
{
if (drv)
return drv->enabled();
return 0;
}
const char *lsm_name(void)
{
if (drv)
return drv->name;
return "none";
}
char *lsm_process_label_get(pid_t pid)
{
if (!drv) {
ERROR("LSM driver not inited");
return NULL;
}
return drv->process_label_get(pid);
}
int lsm_process_label_fd_get(pid_t pid, bool on_exec)
{
int ret = -1;
int labelfd = -1;
const char *name;
char path[LXC_LSMATTRLEN];
name = lsm_name();
if (strcmp(name, "nop") == 0)
return 0;
if (strcmp(name, "none") == 0)
return 0;
/* We don't support on-exec with AppArmor */
if (strcmp(name, "AppArmor") == 0)
on_exec = 0;
if (on_exec)
ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/exec", pid);
else
ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/current", pid);
if (ret < 0 || ret >= LXC_LSMATTRLEN)
return -1;
labelfd = open(path, O_RDWR);
if (labelfd < 0) {
SYSERROR("Unable to %s LSM label file descriptor", name);
return -1;
}
return labelfd;
}
int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec)
{
int ret = -1;
const char *name;
name = lsm_name();
if (strcmp(name, "nop") == 0)
return 0;
if (strcmp(name, "none") == 0)
return 0;
/* We don't support on-exec with AppArmor */
if (strcmp(name, "AppArmor") == 0)
on_exec = false;
if (strcmp(name, "AppArmor") == 0) {
size_t len;
char *command;
if (on_exec) {
ERROR("Changing AppArmor profile on exec not supported");
return -1;
}
len = strlen(label) + strlen("changeprofile ") + 1;
command = malloc(len);
if (!command)
goto on_error;
ret = snprintf(command, len, "changeprofile %s", label);
if (ret < 0 || (size_t)ret >= len) {
int saved_errno = errno;
free(command);
errno = saved_errno;
goto on_error;
}
ret = lxc_write_nointr(label_fd, command, len - 1);
free(command);
} else if (strcmp(name, "SELinux") == 0) {
ret = lxc_write_nointr(label_fd, label, strlen(label));
} else {
errno = EINVAL;
ret = -1;
}
if (ret < 0) {
on_error:
SYSERROR("Failed to set %s label \"%s\"", name, label);
return -1;
}
INFO("Set %s label to \"%s\"", name, label);
return 0;
}
int lsm_process_label_set(const char *label, struct lxc_conf *conf,
bool on_exec)
{
if (!drv) {
ERROR("LSM driver not inited");
return -1;
}
return drv->process_label_set(label, conf, on_exec);
}
int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath)
{
if (!drv) {
ERROR("LSM driver not inited");
return 0;
}
if (!drv->prepare)
return 0;
return drv->prepare(conf, lxcpath);
}
void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath)
{
if (!drv) {
ERROR("LSM driver not inited");
return;
}
if (!drv->cleanup)
return;
drv->cleanup(conf, lxcpath);
}
int lsm_keyring_label_set(char *label) {
if (!drv) {
ERROR("LSM driver not inited");
return -1;
}
if (!drv->keyring_label_set)
return 0;
return drv->keyring_label_set(label); INFO("Initialized LSM security driver %s", ops->name);
return ops;
} }
...@@ -11,27 +11,19 @@ struct lxc_conf; ...@@ -11,27 +11,19 @@ struct lxc_conf;
#include "macro.h" #include "macro.h"
#include "utils.h" #include "utils.h"
struct lsm_drv { struct lsm_ops {
const char *name; const char *name;
int (*enabled)(void); int (*enabled)(void);
char *(*process_label_get)(pid_t pid); char *(*process_label_get)(pid_t pid);
int (*process_label_set)(const char *label, struct lxc_conf *conf, int (*process_label_set)(const char *label, struct lxc_conf *conf, bool on_exec);
bool on_exec); int (*keyring_label_set)(const char *label);
int (*keyring_label_set)(char* label);
int (*prepare)(struct lxc_conf *conf, const char *lxcpath); int (*prepare)(struct lxc_conf *conf, const char *lxcpath);
void (*cleanup)(struct lxc_conf *conf, const char *lxcpath); void (*cleanup)(struct lxc_conf *conf, const char *lxcpath);
int (*process_label_fd_get)(pid_t pid, bool on_exec);
int (*process_label_set_at)(int label_fd, const char *label, bool on_exec);
}; };
__hidden extern void lsm_init(void); __hidden extern const struct lsm_ops *lsm_init(void);
__hidden extern int lsm_enabled(void);
__hidden extern const char *lsm_name(void);
__hidden extern char *lsm_process_label_get(pid_t pid);
__hidden extern int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath);
__hidden extern int lsm_process_label_set(const char *label, struct lxc_conf *conf, bool on_exec);
__hidden extern int lsm_process_label_fd_get(pid_t pid, bool on_exec);
__hidden extern int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec);
__hidden extern void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath);
__hidden extern int lsm_keyring_label_set(char *label);
#endif /* __LXC_LSM_H */ #endif /* __LXC_LSM_H */
...@@ -24,14 +24,43 @@ static int nop_enabled(void) ...@@ -24,14 +24,43 @@ static int nop_enabled(void)
return 0; return 0;
} }
static struct lsm_drv nop_drv = { static int nop_keyring_label_set(const char *label)
{
return 0;
}
static int nop_prepare(struct lxc_conf *conf, const char *lxcpath)
{
return 0;
}
static void nop_cleanup(struct lxc_conf *conf, const char *lxcpath)
{
}
static int nop_process_label_fd_get(pid_t pid, bool on_exec)
{
return 0;
}
static int nop_process_label_set_at(int label_fd, const char *label, bool on_exec)
{
return 0;
}
static struct lsm_ops nop_ops = {
.name = "nop", .name = "nop",
.cleanup = nop_cleanup,
.enabled = nop_enabled, .enabled = nop_enabled,
.keyring_label_set = nop_keyring_label_set,
.prepare = nop_prepare,
.process_label_fd_get = nop_process_label_fd_get,
.process_label_get = nop_process_label_get, .process_label_get = nop_process_label_get,
.process_label_set = nop_process_label_set, .process_label_set = nop_process_label_set,
.process_label_set_at = nop_process_label_set_at,
}; };
struct lsm_drv *lsm_nop_drv_init(void) const struct lsm_ops *lsm_nop_ops_init(void)
{ {
return &nop_drv; return &nop_ops;
} }
...@@ -84,23 +84,71 @@ static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf, ...@@ -84,23 +84,71 @@ static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf,
* *
* Returns 0 on success, < 0 on failure * Returns 0 on success, < 0 on failure
*/ */
static int selinux_keyring_label_set(char *label) static int selinux_keyring_label_set(const char *label)
{ {
return setkeycreatecon_raw(label); return setkeycreatecon_raw(label);
}; }
static int selinux_prepare(struct lxc_conf *conf, const char *lxcpath)
{
return 0;
}
static void selinux_cleanup(struct lxc_conf *conf, const char *lxcpath)
{
}
static int selinux_process_label_fd_get(pid_t pid, bool on_exec)
{
int ret = -1;
int labelfd;
char path[LXC_LSMATTRLEN];
if (on_exec)
ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/exec", pid);
else
ret = snprintf(path, LXC_LSMATTRLEN, "/proc/%d/attr/current", pid);
if (ret < 0 || ret >= LXC_LSMATTRLEN)
return -1;
labelfd = open(path, O_RDWR);
if (labelfd < 0)
return log_error_errno(-errno, errno, "Unable to open SELinux LSM label file descriptor");
static struct lsm_drv selinux_drv = { return labelfd;
}
static int selinux_process_label_set_at(int label_fd, const char *label, bool on_exec)
{
int ret;
if (!label)
return 0;
ret = lxc_write_nointr(label_fd, label, strlen(label));
if (ret < 0)
return log_error_errno(-errno, errno, "Failed to set AppArmor SELinux label to \"%s\"", label);
INFO("Set SELinux label to \"%s\"", label);
return 0;
}
static struct lsm_ops selinux_ops = {
.name = "SELinux", .name = "SELinux",
.cleanup = selinux_cleanup,
.enabled = is_selinux_enabled, .enabled = is_selinux_enabled,
.keyring_label_set = selinux_keyring_label_set,
.prepare = selinux_prepare,
.process_label_fd_get = selinux_process_label_fd_get,
.process_label_get = selinux_process_label_get, .process_label_get = selinux_process_label_get,
.process_label_set = selinux_process_label_set, .process_label_set = selinux_process_label_set,
.keyring_label_set = selinux_keyring_label_set, .process_label_set_at = selinux_process_label_set_at,
}; };
struct lsm_drv *lsm_selinux_drv_init(void) const struct lsm_ops *lsm_selinux_ops_init(void)
{ {
if (!is_selinux_enabled()) if (!is_selinux_enabled())
return NULL; return NULL;
return &selinux_drv; return &selinux_ops;
} }
...@@ -728,7 +728,7 @@ int lxc_init(const char *name, struct lxc_handler *handler) ...@@ -728,7 +728,7 @@ int lxc_init(const char *name, struct lxc_handler *handler)
if (status_fd < 0) if (status_fd < 0)
return log_error_errno(-1, errno, "Failed to open monitor status fd"); return log_error_errno(-1, errno, "Failed to open monitor status fd");
lsm_init(); handler->lsm_ops = lsm_init();
TRACE("Initialized LSM"); TRACE("Initialized LSM");
/* Begin by setting the state to STARTING. */ /* Begin by setting the state to STARTING. */
...@@ -827,7 +827,7 @@ int lxc_init(const char *name, struct lxc_handler *handler) ...@@ -827,7 +827,7 @@ int lxc_init(const char *name, struct lxc_handler *handler)
return log_error(-1, "Failed loading seccomp policy"); return log_error(-1, "Failed loading seccomp policy");
TRACE("Read seccomp policy"); TRACE("Read seccomp policy");
ret = lsm_process_prepare(conf, handler->lxcpath); ret = handler->lsm_ops->prepare(conf, handler->lxcpath);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to initialize LSM"); ERROR("Failed to initialize LSM");
goto out_delete_terminal; goto out_delete_terminal;
...@@ -918,7 +918,7 @@ void lxc_end(struct lxc_handler *handler) ...@@ -918,7 +918,7 @@ void lxc_end(struct lxc_handler *handler)
while (namespace_count--) while (namespace_count--)
free(namespaces[namespace_count]); free(namespaces[namespace_count]);
lsm_process_cleanup(handler->conf, handler->lxcpath); handler->lsm_ops->cleanup(handler->conf, handler->lxcpath);
if (cgroup_ops) { if (cgroup_ops) {
cgroup_ops->payload_destroy(cgroup_ops, handler); cgroup_ops->payload_destroy(cgroup_ops, handler);
...@@ -1269,7 +1269,7 @@ static int do_start(void *data) ...@@ -1269,7 +1269,7 @@ static int do_start(void *data)
} }
/* Set the label to change to when we exec(2) the container's init. */ /* Set the label to change to when we exec(2) the container's init. */
ret = lsm_process_label_set(NULL, handler->conf, true); ret = handler->lsm_ops->process_label_set(NULL, handler->conf, true);
if (ret < 0) if (ret < 0)
goto out_warn_father; goto out_warn_father;
......
...@@ -127,6 +127,8 @@ struct lxc_handler { ...@@ -127,6 +127,8 @@ struct lxc_handler {
/* Internal fds that always need to stay open. */ /* Internal fds that always need to stay open. */
int keep_fds[3]; int keep_fds[3];
const struct lsm_ops *lsm_ops;
}; };
struct execute_args { struct execute_args {
......
...@@ -1838,41 +1838,6 @@ int lxc_rm_rf(const char *dirname) ...@@ -1838,41 +1838,6 @@ int lxc_rm_rf(const char *dirname)
return fret; return fret;
} }
int lxc_setup_keyring(char *keyring_label)
{
key_serial_t keyring;
int ret = 0;
if (keyring_label) {
if (lsm_keyring_label_set(keyring_label) < 0) {
ERROR("Couldn't set keyring label");
}
}
/* Try to allocate a new session keyring for the container to prevent
* information leaks.
*/
keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, prctl_arg(0),
prctl_arg(0), prctl_arg(0), prctl_arg(0));
if (keyring < 0) {
switch (errno) {
case ENOSYS:
DEBUG("The keyctl() syscall is not supported or blocked");
break;
case EACCES:
__fallthrough;
case EPERM:
DEBUG("Failed to access kernel keyring. Continuing...");
break;
default:
SYSERROR("Failed to create kernel keyring");
break;
}
}
return ret;
}
bool lxc_can_use_pidfd(int pidfd) bool lxc_can_use_pidfd(int pidfd)
{ {
int ret; int ret;
......
...@@ -229,7 +229,6 @@ __hidden extern uint64_t lxc_find_next_power2(uint64_t n); ...@@ -229,7 +229,6 @@ __hidden extern uint64_t lxc_find_next_power2(uint64_t n);
__hidden extern int lxc_set_death_signal(int signal, pid_t parent, int parent_status_fd); __hidden extern int lxc_set_death_signal(int signal, pid_t parent, int parent_status_fd);
__hidden extern int fd_cloexec(int fd, bool cloexec); __hidden extern int fd_cloexec(int fd, bool cloexec);
__hidden extern int lxc_rm_rf(const char *dirname); __hidden extern int lxc_rm_rf(const char *dirname);
__hidden extern int lxc_setup_keyring(char *keyring_label);
__hidden extern bool lxc_can_use_pidfd(int pidfd); __hidden extern bool lxc_can_use_pidfd(int pidfd);
__hidden extern int fix_stdio_permissions(uid_t uid); __hidden extern int fix_stdio_permissions(uid_t uid);
......
...@@ -47,14 +47,16 @@ ...@@ -47,14 +47,16 @@
static const char *lsm_config_key = NULL; static const char *lsm_config_key = NULL;
static const char *lsm_label = NULL; static const char *lsm_label = NULL;
const struct lsm_ops *lsm_ops;
static void test_lsm_detect(void) static void test_lsm_detect(void)
{ {
if (lsm_enabled()) { if (lsm_ops->enabled()) {
if (!strcmp(lsm_name(), "SELinux")) { if (!strcmp(lsm_ops->name, "SELinux")) {
lsm_config_key = "lxc.selinux.context"; lsm_config_key = "lxc.selinux.context";
lsm_label = "unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023"; lsm_label = "unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023";
} }
else if (!strcmp(lsm_name(), "AppArmor")) { else if (!strcmp(lsm_ops->name, "AppArmor")) {
lsm_config_key = "lxc.apparmor.profile"; lsm_config_key = "lxc.apparmor.profile";
if (file_exists("/proc/self/ns/cgroup")) if (file_exists("/proc/self/ns/cgroup"))
lsm_label = "lxc-container-default-cgns"; lsm_label = "lxc-container-default-cgns";
...@@ -62,7 +64,7 @@ static void test_lsm_detect(void) ...@@ -62,7 +64,7 @@ static void test_lsm_detect(void)
lsm_label = "lxc-container-default"; lsm_label = "lxc-container-default";
} }
else { else {
TSTERR("unknown lsm %s enabled, add test code here", lsm_name()); TSTERR("unknown lsm %s enabled, add test code here", lsm_ops->name);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
...@@ -78,7 +80,7 @@ static void test_attach_lsm_set_config(struct lxc_container *ct) ...@@ -78,7 +80,7 @@ static void test_attach_lsm_set_config(struct lxc_container *ct)
static int test_attach_lsm_func_func(void* payload) static int test_attach_lsm_func_func(void* payload)
{ {
TSTOUT("%s", lsm_process_label_get(syscall(SYS_getpid))); TSTOUT("%s", lsm_ops->process_label_get(syscall(SYS_getpid)));
return 0; return 0;
} }
...@@ -328,7 +330,7 @@ static struct lxc_container *test_ct_create(const char *lxcpath, ...@@ -328,7 +330,7 @@ static struct lxc_container *test_ct_create(const char *lxcpath,
goto out2; goto out2;
} }
if (lsm_enabled()) if (lsm_ops->enabled())
test_attach_lsm_set_config(ct); test_attach_lsm_set_config(ct);
ct->want_daemonize(ct, true); ct->want_daemonize(ct, true);
...@@ -368,7 +370,7 @@ static int test_attach(const char *lxcpath, const char *name, const char *templa ...@@ -368,7 +370,7 @@ static int test_attach(const char *lxcpath, const char *name, const char *templa
goto err2; goto err2;
} }
if (lsm_enabled()) { if (lsm_ops->enabled()) {
ret = test_attach_lsm_cmd(ct); ret = test_attach_lsm_cmd(ct);
if (ret < 0) { if (ret < 0) {
TSTERR("attach lsm cmd test failed"); TSTERR("attach lsm cmd test failed");
...@@ -398,6 +400,8 @@ int main(int argc, char *argv[]) ...@@ -398,6 +400,8 @@ int main(int argc, char *argv[])
(void)strlcpy(template, P_tmpdir"/attach_XXXXXX", sizeof(template)); (void)strlcpy(template, P_tmpdir"/attach_XXXXXX", sizeof(template));
lsm_ops = lsm_init();
i = lxc_make_tmpfile(template, false); i = lxc_make_tmpfile(template, false);
if (i < 0) { if (i < 0) {
lxc_error("Failed to create temporary log file for container %s\n", TSTNAME); lxc_error("Failed to create temporary log file for container %s\n", TSTNAME);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment