Unverified Commit 326bb02c by Stéphane Graber Committed by GitHub

Merge pull request #3641 from brauner/2021-01-30/fixes

attach: pidfd-based hardening and file-descriptor-only LSM interactions
parents ee4aad1e fbf281d3
......@@ -135,6 +135,7 @@ typedef struct lxc_attach_options_t {
/* .stdout_fd = */ 1, \
/* .stderr_fd = */ 2, \
/* .log_fd = */ -EBADF, \
/* .lsm_label = */ NULL, \
}
/*!
......
......@@ -3454,33 +3454,33 @@ struct cgroup_ops *cgfsng_ops_init(struct lxc_conf *conf)
if (cg_init(cgfsng_ops, conf))
return NULL;
cgfsng_ops->data_init = cgfsng_data_init;
cgfsng_ops->payload_destroy = cgfsng_payload_destroy;
cgfsng_ops->monitor_destroy = cgfsng_monitor_destroy;
cgfsng_ops->monitor_create = cgfsng_monitor_create;
cgfsng_ops->monitor_enter = cgfsng_monitor_enter;
cgfsng_ops->monitor_delegate_controllers = cgfsng_monitor_delegate_controllers;
cgfsng_ops->payload_delegate_controllers = cgfsng_payload_delegate_controllers;
cgfsng_ops->payload_create = cgfsng_payload_create;
cgfsng_ops->payload_enter = cgfsng_payload_enter;
cgfsng_ops->payload_finalize = cgfsng_payload_finalize;
cgfsng_ops->escape = cgfsng_escape;
cgfsng_ops->num_hierarchies = cgfsng_num_hierarchies;
cgfsng_ops->get_hierarchies = cgfsng_get_hierarchies;
cgfsng_ops->get_cgroup = cgfsng_get_cgroup;
cgfsng_ops->get = cgfsng_get;
cgfsng_ops->set = cgfsng_set;
cgfsng_ops->freeze = cgfsng_freeze;
cgfsng_ops->unfreeze = cgfsng_unfreeze;
cgfsng_ops->setup_limits_legacy = cgfsng_setup_limits_legacy;
cgfsng_ops->setup_limits = cgfsng_setup_limits;
cgfsng_ops->driver = "cgfsng";
cgfsng_ops->version = "1.0.0";
cgfsng_ops->attach = cgfsng_attach;
cgfsng_ops->chown = cgfsng_chown;
cgfsng_ops->mount = cgfsng_mount;
cgfsng_ops->devices_activate = cgfsng_devices_activate;
cgfsng_ops->get_limiting_cgroup = cgfsng_get_limiting_cgroup;
cgfsng_ops->data_init = cgfsng_data_init;
cgfsng_ops->payload_destroy = cgfsng_payload_destroy;
cgfsng_ops->monitor_destroy = cgfsng_monitor_destroy;
cgfsng_ops->monitor_create = cgfsng_monitor_create;
cgfsng_ops->monitor_enter = cgfsng_monitor_enter;
cgfsng_ops->monitor_delegate_controllers = cgfsng_monitor_delegate_controllers;
cgfsng_ops->payload_delegate_controllers = cgfsng_payload_delegate_controllers;
cgfsng_ops->payload_create = cgfsng_payload_create;
cgfsng_ops->payload_enter = cgfsng_payload_enter;
cgfsng_ops->payload_finalize = cgfsng_payload_finalize;
cgfsng_ops->escape = cgfsng_escape;
cgfsng_ops->num_hierarchies = cgfsng_num_hierarchies;
cgfsng_ops->get_hierarchies = cgfsng_get_hierarchies;
cgfsng_ops->get_cgroup = cgfsng_get_cgroup;
cgfsng_ops->get = cgfsng_get;
cgfsng_ops->set = cgfsng_set;
cgfsng_ops->freeze = cgfsng_freeze;
cgfsng_ops->unfreeze = cgfsng_unfreeze;
cgfsng_ops->setup_limits_legacy = cgfsng_setup_limits_legacy;
cgfsng_ops->setup_limits = cgfsng_setup_limits;
cgfsng_ops->driver = "cgfsng";
cgfsng_ops->version = "1.0.0";
cgfsng_ops->attach = cgfsng_attach;
cgfsng_ops->chown = cgfsng_chown;
cgfsng_ops->mount = cgfsng_mount;
cgfsng_ops->devices_activate = cgfsng_devices_activate;
cgfsng_ops->get_limiting_cgroup = cgfsng_get_limiting_cgroup;
return move_ptr(cgfsng_ops);
}
......@@ -441,6 +441,22 @@ again:
return buf;
}
static char *apparmor_process_label_get_at(struct lsm_ops *ops, int fd_pid)
{
__do_free char *label = NULL;
size_t len;
label = read_file_at(fd_pid, "attr/current");
if (!label)
return log_error_errno(NULL, errno, "Failed to get AppArmor context");
len = strcspn(label, "\n \t");
if (len)
label[len] = '\0';
return move_ptr(label);
}
/*
* Probably makes sense to reorganize these to only read
* the label once
......@@ -1180,45 +1196,33 @@ static int apparmor_process_label_set_at(struct lsm_ops *ops, int label_fd, cons
static int apparmor_process_label_set(struct lsm_ops *ops, const char *inlabel,
struct lxc_conf *conf, bool on_exec)
{
int label_fd, ret;
pid_t tid;
__do_close int label_fd = -EBADF;
int ret;
const char *label;
if (!ops->aa_enabled)
return log_error(-1, "AppArmor not enabled");
return log_error_errno(-EOPNOTSUPP, EOPNOTSUPP, "AppArmor not enabled");
label = inlabel ? inlabel : conf->lsm_aa_profile_computed;
if (!label) {
ERROR("LSM wasn't prepared");
return -1;
}
if (!label)
return log_error_errno(-EINVAL, EINVAL, "LSM wasn't prepared");
/* user may request that we just ignore apparmor */
if (strcmp(label, AA_UNCHANGED) == 0) {
INFO("AppArmor profile unchanged per user request");
return 0;
}
if (strcmp(label, AA_UNCHANGED) == 0)
return log_info(0, "AppArmor profile unchanged per user request");
if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined(ops)) {
INFO("AppArmor profile unchanged");
return 0;
}
tid = lxc_raw_gettid();
label_fd = apparmor_process_label_fd_get(ops, tid, on_exec);
if (label_fd < 0) {
SYSERROR("Failed to change AppArmor profile to %s", label);
return -1;
}
if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined(ops))
return log_info(0, "AppArmor profile unchanged");
label_fd = apparmor_process_label_fd_get(ops, lxc_raw_gettid(), on_exec);
if (label_fd < 0)
return log_error_errno(-EINVAL, EINVAL, "Failed to change AppArmor profile to %s", label);
ret = apparmor_process_label_set_at(ops, label_fd, label, on_exec);
close(label_fd);
if (ret < 0) {
ERROR("Failed to change AppArmor profile to %s", label);
return -1;
}
if (ret < 0)
return log_error_errno(-EINVAL, EINVAL, "Failed to change AppArmor profile to %s", label);
INFO("Changed AppArmor profile to %s", label);
return 0;
return log_info(0, "Changed AppArmor profile to %s", label);
}
static struct lsm_ops apparmor_ops = {
......@@ -1237,6 +1241,7 @@ static struct lsm_ops apparmor_ops = {
.process_label_fd_get = apparmor_process_label_fd_get,
.process_label_get = apparmor_process_label_get,
.process_label_set = apparmor_process_label_set,
.process_label_get_at = apparmor_process_label_get_at,
.process_label_set_at = apparmor_process_label_set_at,
};
......
......@@ -30,6 +30,7 @@ struct lsm_ops {
int (*prepare)(struct lsm_ops *ops, struct lxc_conf *conf, const char *lxcpath);
void (*cleanup)(struct lsm_ops *ops, struct lxc_conf *conf, const char *lxcpath);
int (*process_label_fd_get)(struct lsm_ops *ops, pid_t pid, bool on_exec);
char *(*process_label_get_at)(struct lsm_ops *ops, int fd_pid);
int (*process_label_set_at)(struct lsm_ops *ops, int label_fd, const char *label, bool on_exec);
};
......
......@@ -13,6 +13,11 @@ static char *nop_process_label_get(struct lsm_ops *ops, pid_t pid)
return NULL;
}
static char *nop_process_label_get_at(struct lsm_ops *ops, int fd_pid)
{
return NULL;
}
static int nop_process_label_set(struct lsm_ops *ops, const char *label, struct lxc_conf *conf,
bool on_exec)
{
......@@ -63,8 +68,9 @@ static struct lsm_ops nop_ops = {
.prepare = nop_prepare,
.process_label_fd_get = nop_process_label_fd_get,
.process_label_get = nop_process_label_get,
.process_label_set = nop_process_label_set,
.process_label_set_at = nop_process_label_set_at,
.process_label_set = nop_process_label_set,
.process_label_get_at = nop_process_label_get_at,
.process_label_set_at = nop_process_label_set_at,
};
struct lsm_ops *lsm_nop_ops_init(void)
......
......@@ -15,6 +15,7 @@
#include "config.h"
#include "log.h"
#include "lsm.h"
#include "memory_utils.h"
#define DEFAULT_LABEL "unconfined_t"
......@@ -41,6 +42,32 @@ static char *selinux_process_label_get(struct lsm_ops *ops, pid_t pid)
}
/*
* selinux_process_label_get_at: Get SELinux context of a process
*
* @fd_pid : file descriptor to /proc/<pid> of the process
*
* Returns the context of the given pid. The caller must free()
* the returned string.
*
* Note that this relies on /proc being available.
*/
static char *selinux_process_label_get_at(struct lsm_ops *ops, int fd_pid)
{
__do_free char *label = NULL;
size_t len;
label = read_file_at(fd_pid, "attr/current");
if (!label)
return log_error_errno(NULL, errno, "Failed to get SELinux context");
len = strcspn(label, "\n \t");
if (len)
label[len] = '\0';
return move_ptr(label);
}
/*
* selinux_process_label_set: Set SELinux context of a process
*
* @label : label string
......@@ -154,6 +181,7 @@ static struct lsm_ops selinux_ops = {
.process_label_fd_get = selinux_process_label_fd_get,
.process_label_get = selinux_process_label_get,
.process_label_set = selinux_process_label_set,
.process_label_get_at = selinux_process_label_get_at,
.process_label_set_at = selinux_process_label_set_at,
};
......
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