Unverified Commit b5b200c6 by Serge Hallyn Committed by GitHub

Merge pull request #2047 from brauner/2017-12-18/attach_lsm_confinement

attach: simplify significantly
parents 6c049d3a 57de839f
......@@ -22,14 +22,15 @@
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/un.h>
#include "log.h"
......
......@@ -88,12 +88,12 @@ lxc_log_define(lxc_attach, lxc);
/* /proc/pid-to-str/current\0 = (5 + 21 + 7 + 1) */
#define __LSMATTRLEN (5 + (LXC_NUMSTRLEN64) + 7 + 1)
static int lsm_openat(int procfd, pid_t pid, int on_exec)
static int lsm_open(pid_t pid, int on_exec)
{
int ret = -1;
int labelfd = -1;
const char *name;
char path[__LSMATTRLEN];
int ret = -1;
int labelfd = -1;
name = lsm_name();
......@@ -108,15 +108,16 @@ static int lsm_openat(int procfd, pid_t pid, int on_exec)
on_exec = 0;
if (on_exec)
ret = snprintf(path, __LSMATTRLEN, "%d/attr/exec", pid);
ret = snprintf(path, __LSMATTRLEN, "/proc/%d/attr/exec", pid);
else
ret = snprintf(path, __LSMATTRLEN, "%d/attr/current", pid);
ret = snprintf(path, __LSMATTRLEN, "/proc/%d/attr/current", pid);
if (ret < 0 || ret >= __LSMATTRLEN)
return -1;
labelfd = openat(procfd, path, O_RDWR);
labelfd = open(path, O_RDWR);
if (labelfd < 0) {
SYSERROR("Unable to open file descriptor to set LSM label.");
SYSERROR("%s - Unable to open file descriptor to set LSM label",
strerror(errno));
return -1;
}
......@@ -402,9 +403,10 @@ static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
continue;
if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) {
SYSERROR("Failed to remove capability id %d.", cap);
SYSERROR("Failed to drop capability %d", cap);
return -1;
}
TRACE("Dropped capability %d", cap);
}
return 0;
......@@ -711,16 +713,6 @@ static void lxc_attach_get_init_uidgid(uid_t *init_uid, gid_t *init_gid)
*/
}
struct attach_clone_payload {
int ipc_socket;
lxc_attach_options_t *options;
struct lxc_proc_context_info *init_ctx;
lxc_attach_exec_t exec_function;
void *exec_payload;
};
static int attach_child_main(void* data);
/* Help the optimizer along if it doesn't know that exit always exits. */
#define rexit(c) \
do { \
......@@ -818,6 +810,227 @@ static signed long get_personality(const char *name, const char *lxcpath)
return ret;
}
struct attach_clone_payload {
int ipc_socket;
lxc_attach_options_t *options;
struct lxc_proc_context_info *init_ctx;
lxc_attach_exec_t exec_function;
void *exec_payload;
};
static int attach_child_main(struct attach_clone_payload *payload)
{
int fd, lsm_fd, ret;
long flags;
#if HAVE_SYS_PERSONALITY_H
long new_personality;
#endif
uid_t new_uid;
gid_t new_gid;
int ipc_socket = payload->ipc_socket;
lxc_attach_options_t* options = payload->options;
struct lxc_proc_context_info* init_ctx = payload->init_ctx;
bool needs_lsm = (options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_LSM) &&
init_ctx->lsm_label;
/* A description of the purpose of this functionality is provided in the
* lxc-attach(1) manual page. We have to remount here and not in the
* parent process, otherwise /proc may not properly reflect the new pid
* namespace.
*/
if (!(options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) {
ret = lxc_attach_remount_sys_proc();
if (ret < 0) {
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
/* Now perform additional attachments. */
#if HAVE_SYS_PERSONALITY_H
if (options->personality < 0)
new_personality = init_ctx->personality;
else
new_personality = options->personality;
if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) {
ret = personality(new_personality);
if (ret < 0) {
SYSERROR("Could not ensure correct architecture");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
#endif
if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) {
ret = lxc_attach_drop_privs(init_ctx);
if (ret < 0) {
ERROR("Could not drop privileges");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
/* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL)
* if you want this to be a no-op).
*/
ret = lxc_attach_set_environment(options->env_policy,
options->extra_env_vars,
options->extra_keep_env);
if (ret < 0) {
ERROR("Failed to set initial environment for attached process");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
/* This remark only affects fully unprivileged containers:
* Receive fd for LSM security module before we set{g,u}id(). The reason
* is that on set{g,u}id() the kernel will a) make us undumpable and b)
* we will change our effective uid. This means our effective uid will
* be different from the effective uid of the process that created us
* which means that this processs no longer has capabilities in our
* namespace including CAP_SYS_PTRACE. This means we will not be able to
* read and /proc/<pid> files for the process anymore when /proc is
* mounted with hidepid={1,2}. So let's get the lsm label fd before the
* set{g,u}id().
*/
if (needs_lsm) {
ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_fd, 1, NULL, 0);
if (ret <= 0) {
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
TRACE("Received LSM label file descriptor %d from parent", lsm_fd);
}
/* Set {u,g}id. */
new_uid = 0;
new_gid = 0;
/* Ignore errors, we will fall back to root in that case (/proc was not
* mounted etc.).
*/
if (options->namespaces & CLONE_NEWUSER)
lxc_attach_get_init_uidgid(&new_uid, &new_gid);
if (options->uid != (uid_t)-1)
new_uid = options->uid;
if (options->gid != (gid_t)-1)
new_gid = options->gid;
/* Setup the controlling tty. */
if (options->stdin_fd && isatty(options->stdin_fd)) {
if (setsid() < 0) {
SYSERROR("Unable to setsid.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) {
SYSERROR("Unable to set TIOCSTTY.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
/* Try to set the {u,g}id combination. */
if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) {
if (setgid(new_gid) || setgroups(0, NULL)) {
SYSERROR("Switching to container gid.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) &&
setuid(new_uid)) {
SYSERROR("Switching to container uid.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
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.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() "
"gainable privileges.");
}
if (needs_lsm) {
int on_exec;
/* Change into our new LSM profile. */
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
ret = lsm_set_label_at(lsm_fd, on_exec, init_ctx->lsm_label);
if (ret < 0) {
SYSERROR("Failed to set LSM label.");
shutdown(ipc_socket, SHUT_RDWR);
close(lsm_fd);
rexit(-1);
}
close(lsm_fd);
}
if (init_ctx->container && init_ctx->container->lxc_conf &&
init_ctx->container->lxc_conf->seccomp &&
(lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) {
ERROR("Failed to load seccomp policy.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
shutdown(ipc_socket, SHUT_RDWR);
close(ipc_socket);
lxc_proc_put_context_info(init_ctx);
/* The following is done after the communication socket is shut down.
* That way, all errors that might (though unlikely) occur up until this
* point will have their messages printed to the original stderr (if
* logging is so configured) and not the fd the user supplied, if any.
*/
/* Fd handling for stdin, stdout and stderr; ignore errors here, user
* may want to make sure the fds are closed, for example.
*/
if (options->stdin_fd >= 0 && options->stdin_fd != 0)
dup2(options->stdin_fd, 0);
if (options->stdout_fd >= 0 && options->stdout_fd != 1)
dup2(options->stdout_fd, 1);
if (options->stderr_fd >= 0 && options->stderr_fd != 2)
dup2(options->stderr_fd, 2);
/* close the old fds */
if (options->stdin_fd > 2)
close(options->stdin_fd);
if (options->stdout_fd > 2)
close(options->stdout_fd);
if (options->stderr_fd > 2)
close(options->stderr_fd);
/* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also
* here, ignore errors.
*/
for (fd = 0; fd <= 2; fd++) {
flags = fcntl(fd, F_GETFL);
if (flags < 0)
continue;
if (flags & FD_CLOEXEC)
if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0)
SYSERROR("Unable to clear FD_CLOEXEC from file descriptor.");
}
/* We're done, so we can now do whatever the user intended us to do. */
rexit(payload->exec_function(payload->exec_payload));
}
int lxc_attach(const char *name, const char *lxcpath,
lxc_attach_exec_t exec_function, void *exec_payload,
lxc_attach_options_t *options, pid_t *attached_process)
......@@ -828,6 +1041,7 @@ int lxc_attach(const char *name, const char *lxcpath,
signed long personality;
pid_t attached_pid, expected, init_pid, pid;
struct lxc_proc_context_info *init_ctx;
struct attach_clone_payload payload = {0};
ret = access("/proc/self/ns", X_OK);
if (ret) {
......@@ -907,7 +1121,7 @@ int lxc_attach(const char *name, const char *lxcpath,
}
}
pid = getpid();
pid = syscall(SYS_getpid);
for (i = 0; i < LXC_NS_MAX; i++) {
int j, saved_errno;
......@@ -1003,12 +1217,8 @@ int lxc_attach(const char *name, const char *lxcpath,
}
if (pid) {
int procfd = -1;
pid_t to_cleanup_pid = pid;
/* close file namespace descriptors */
lxc_proc_close_ns_fd(init_ctx);
/* Initial thread, we close the socket that is for the
* subprocesses.
*/
......@@ -1019,6 +1229,8 @@ int lxc_attach(const char *name, const char *lxcpath,
if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
if (!cgroup_attach(name, lxcpath, pid))
goto on_error;
TRACE("Moved intermediate process %d into container's "
"cgroups", pid);
}
/* Setup resource limits */
......@@ -1026,32 +1238,18 @@ int lxc_attach(const char *name, const char *lxcpath,
if (setup_resource_limits(&init_ctx->container->lxc_conf->limits, pid) < 0)
goto on_error;
/* Open /proc before setns() to the containers namespace so we
* don't rely on any information from inside the container.
*/
procfd = open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
if (procfd < 0) {
SYSERROR("Unable to open /proc.");
goto on_error;
}
/* Let the child process know to go ahead. */
status = 0;
ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
if (ret <= 0) {
ERROR("Intended to send sequence number 0: %s.",
strerror(errno));
if (ret != sizeof(status))
goto on_error;
}
TRACE("Told intermediate process to start initializing");
/* Get pid of attached process from intermediate process. */
ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid,
sizeof(attached_pid), NULL);
if (ret <= 0) {
if (ret != 0)
ERROR("Expected to receive pid: %s.", strerror(errno));
ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid));
if (ret != sizeof(attached_pid))
goto on_error;
}
TRACE("Received pid %d of attached process in parent pid namespace", attached_pid);
/* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
if (options->stdin_fd == 0) {
......@@ -1063,73 +1261,34 @@ int lxc_attach(const char *name, const char *lxcpath,
ret = wait_for_pid(pid);
if (ret < 0)
goto on_error;
TRACE("Intermediate process %d exited", pid);
/* We will always have to reap the attached process now. */
to_cleanup_pid = attached_pid;
/* Tell attached process it may start initializing. */
status = 0;
ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
if (ret <= 0) {
ERROR("Intended to send sequence number 0: %s.", strerror(errno));
goto on_error;
}
/* Wait for the attached process to finish initializing. */
expected = 1;
ret = lxc_read_nointr_expect(ipc_sockets[0], &status,
sizeof(status), &expected);
if (ret <= 0) {
if (ret != 0)
ERROR("Expected to receive sequence number 1: %s.", strerror(errno));
goto on_error;
}
/* Tell attached process we're done. */
status = 2;
ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
if (ret <= 0) {
ERROR("Intended to send sequence number 2: %s.", strerror(errno));
goto on_error;
}
/* Wait for the (grand)child to tell us that it's ready to set
* up its LSM labels.
*/
expected = 3;
ret = lxc_read_nointr_expect(ipc_sockets[0], &status,
sizeof(status), &expected);
if (ret <= 0) {
ERROR("Expected to receive sequence number 3: %s.",
strerror(errno));
goto on_error;
}
/* Open LSM fd and send it to child. */
if ((options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_LSM) &&
init_ctx->lsm_label) {
int on_exec, saved_errno;
int labelfd = -1;
int labelfd, on_exec;
int ret = -1;
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
/* Open fd for the LSM security module. */
labelfd = lsm_openat(procfd, attached_pid, on_exec);
labelfd = lsm_open(attached_pid, on_exec);
if (labelfd < 0)
goto on_error;
TRACE("Opened LSM label file descriptor %d", labelfd);
/* Send child fd of the LSM security module to write to. */
ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0);
saved_errno = errno;
close(labelfd);
if (ret <= 0) {
ERROR("Intended to send file descriptor %d: %s.", labelfd, strerror(saved_errno));
SYSERROR("%d", (int)ret);
goto on_error;
}
TRACE("Sent LSM label file descriptor %d to child", labelfd);
}
if (procfd >= 0)
close(procfd);
/* Now shut down communication with child, we're done. */
shutdown(ipc_sockets[0], SHUT_RDWR);
close(ipc_sockets[0]);
......@@ -1147,8 +1306,6 @@ int lxc_attach(const char *name, const char *lxcpath,
/* First shut down the socket, then wait for the pid, otherwise
* the pid we're waiting for may never exit.
*/
if (procfd >= 0)
close(procfd);
shutdown(ipc_sockets[0], SHUT_RDWR);
close(ipc_sockets[0]);
if (to_cleanup_pid)
......@@ -1164,22 +1321,20 @@ int lxc_attach(const char *name, const char *lxcpath,
/* Wait for the parent to have setup cgroups. */
expected = 0;
status = -1;
ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status),
&expected);
if (ret <= 0) {
ERROR("Expected to receive sequence number 0: %s.", strerror(errno));
ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status));
if (ret != sizeof(status) || status != expected) {
shutdown(ipc_sockets[1], SHUT_RDWR);
lxc_proc_put_context_info(init_ctx);
rexit(-1);
}
TRACE("Intermediate process starting to initialize");
/* Attach now, create another subprocess later, since pid namespaces
* only really affect the children of the current process.
*/
ret = lxc_attach_to_ns(init_pid, init_ctx);
if (ret < 0) {
ERROR("Failed to enter namespaces.");
ERROR("Failed to enter namespaces");
shutdown(ipc_sockets[1], SHUT_RDWR);
lxc_proc_put_context_info(init_ctx);
rexit(-1);
......@@ -1199,31 +1354,28 @@ int lxc_attach(const char *name, const char *lxcpath,
}
free(cwd);
/* Now create the real child process. */
{
struct attach_clone_payload payload = {
.ipc_socket = ipc_sockets[1],
.options = options,
.init_ctx = init_ctx,
.exec_function = exec_function,
.exec_payload = exec_payload,
};
/* We use clone_parent here to make this subprocess a direct
* child of the initial process. Then this intermediate process
* can exit and the parent can directly track the attached
* process.
*/
pid = lxc_clone(attach_child_main, &payload, CLONE_PARENT);
}
/* Create attached process. */
payload.ipc_socket = ipc_sockets[1];
payload.options = options;
payload.init_ctx = init_ctx;
payload.exec_function = exec_function;
payload.exec_payload = exec_payload;
/* Shouldn't happen, clone() should always return positive pid. */
if (pid <= 0) {
SYSERROR("Failed to create subprocess.");
pid = lxc_raw_clone(CLONE_PARENT);
if (pid < 0) {
SYSERROR("Failed to clone attached process");
shutdown(ipc_sockets[1], SHUT_RDWR);
lxc_proc_put_context_info(init_ctx);
rexit(-1);
}
if (pid == 0) {
ret = attach_child_main(&payload);
if (ret < 0)
ERROR("Failed to exec");
_exit(EXIT_FAILURE);
}
/* Tell grandparent the pid of the pid of the newly created child. */
ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid));
if (ret != sizeof(pid)) {
......@@ -1233,257 +1385,17 @@ int lxc_attach(const char *name, const char *lxcpath,
* CLONE_PARENT) so the parent won't be able to reap it and the
* attached process will remain a zombie.
*/
ERROR("Intended to send pid %d: %s.", pid, strerror(errno));
shutdown(ipc_sockets[1], SHUT_RDWR);
lxc_proc_put_context_info(init_ctx);
rexit(-1);
}
TRACE("Sending pid %d of attached process", pid);
/* The rest is in the hands of the initial and the attached process. */
lxc_proc_put_context_info(init_ctx);
rexit(0);
}
static int attach_child_main(void* data)
{
int expected, fd, lsm_labelfd, ret, status;
long flags;
#if HAVE_SYS_PERSONALITY_H
long new_personality;
#endif
uid_t new_uid;
gid_t new_gid;
struct attach_clone_payload* payload = (struct attach_clone_payload*)data;
int ipc_socket = payload->ipc_socket;
lxc_attach_options_t* options = payload->options;
struct lxc_proc_context_info* init_ctx = payload->init_ctx;
/* Wait for the initial thread to signal us that it's ready for us to
* start initializing.
*/
expected = 0;
status = -1;
ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected);
if (ret <= 0) {
ERROR("Expected to receive sequence number 0: %s.", strerror(errno));
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
/* A description of the purpose of this functionality is provided in the
* lxc-attach(1) manual page. We have to remount here and not in the
* parent process, otherwise /proc may not properly reflect the new pid
* namespace.
*/
if (!(options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) {
ret = lxc_attach_remount_sys_proc();
if (ret < 0) {
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
/* Now perform additional attachments. */
#if HAVE_SYS_PERSONALITY_H
if (options->personality < 0)
new_personality = init_ctx->personality;
else
new_personality = options->personality;
if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) {
ret = personality(new_personality);
if (ret < 0) {
SYSERROR("Could not ensure correct architecture.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
#endif
if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) {
ret = lxc_attach_drop_privs(init_ctx);
if (ret < 0) {
ERROR("Could not drop privileges.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
/* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL)
* if you want this to be a no-op).
*/
ret = lxc_attach_set_environment(options->env_policy,
options->extra_env_vars,
options->extra_keep_env);
if (ret < 0) {
ERROR("Could not set initial environment for attached process.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
/* Set {u,g}id. */
new_uid = 0;
new_gid = 0;
/* Ignore errors, we will fall back to root in that case (/proc was not
* mounted etc.).
*/
if (options->namespaces & CLONE_NEWUSER)
lxc_attach_get_init_uidgid(&new_uid, &new_gid);
if (options->uid != (uid_t)-1)
new_uid = options->uid;
if (options->gid != (gid_t)-1)
new_gid = options->gid;
/* Setup the controlling tty. */
if (options->stdin_fd && isatty(options->stdin_fd)) {
if (setsid() < 0) {
SYSERROR("Unable to setsid.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) {
SYSERROR("Unable to set TIOCSTTY.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
/* Try to set the {u,g}id combination. */
if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) {
if (setgid(new_gid) || setgroups(0, NULL)) {
SYSERROR("Switching to container gid.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
}
if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) &&
setuid(new_uid)) {
SYSERROR("Switching to container uid.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
/* Tell initial process it may now put us into cgroups. */
status = 1;
ret = lxc_write_nointr(ipc_socket, &status, sizeof(status));
if (ret != sizeof(status)) {
ERROR("Intended to send sequence number 1: %s.", strerror(errno));
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
/* Wait for the initial thread to signal us that it has done everything
* for us when it comes to cgroups etc.
*/
expected = 2;
status = -1;
ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected);
if (ret <= 0) {
ERROR("Expected to receive sequence number 2: %s", strerror(errno));
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
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.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() "
"gainable privileges.");
}
/* Tell the (grand)parent to send us LSM label fd. */
status = 3;
ret = lxc_write_nointr(ipc_socket, &status, sizeof(status));
if (ret <= 0) {
ERROR("Intended to send sequence number 3: %s.", strerror(errno));
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
if ((options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) {
int on_exec;
/* Receive fd for LSM security module. */
ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0);
if (ret <= 0) {
ERROR("Expected to receive file descriptor: %s.", strerror(errno));
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
/* Change into our new LSM profile. */
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
if (lsm_set_label_at(lsm_labelfd, on_exec, init_ctx->lsm_label) < 0) {
SYSERROR("Failed to set LSM label.");
shutdown(ipc_socket, SHUT_RDWR);
close(lsm_labelfd);
rexit(-1);
}
close(lsm_labelfd);
}
if (init_ctx->container && init_ctx->container->lxc_conf &&
init_ctx->container->lxc_conf->seccomp &&
(lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) {
ERROR("Failed to load seccomp policy.");
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
}
shutdown(ipc_socket, SHUT_RDWR);
close(ipc_socket);
lxc_proc_put_context_info(init_ctx);
/* The following is done after the communication socket is shut down.
* That way, all errors that might (though unlikely) occur up until this
* point will have their messages printed to the original stderr (if
* logging is so configured) and not the fd the user supplied, if any.
*/
/* Fd handling for stdin, stdout and stderr; ignore errors here, user
* may want to make sure the fds are closed, for example.
*/
if (options->stdin_fd >= 0 && options->stdin_fd != 0)
dup2(options->stdin_fd, 0);
if (options->stdout_fd >= 0 && options->stdout_fd != 1)
dup2(options->stdout_fd, 1);
if (options->stderr_fd >= 0 && options->stderr_fd != 2)
dup2(options->stderr_fd, 2);
/* close the old fds */
if (options->stdin_fd > 2)
close(options->stdin_fd);
if (options->stdout_fd > 2)
close(options->stdout_fd);
if (options->stderr_fd > 2)
close(options->stderr_fd);
/* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also
* here, ignore errors.
*/
for (fd = 0; fd <= 2; fd++) {
flags = fcntl(fd, F_GETFL);
if (flags < 0)
continue;
if (flags & FD_CLOEXEC)
if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0)
SYSERROR("Unable to clear FD_CLOEXEC from file descriptor.");
}
/* We're done, so we can now do whatever the user intended us to do. */
rexit(payload->exec_function(payload->exec_payload));
}
int lxc_attach_run_command(void* payload)
{
lxc_attach_command_t* cmd = (lxc_attach_command_t*)payload;
......
......@@ -25,9 +25,10 @@
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <errno.h>
#include <unistd.h>
#define TSTNAME "lxc-attach-test"
#define TSTOUT(fmt, ...) do { \
......@@ -78,7 +79,7 @@ static void test_attach_lsm_set_config(struct lxc_container *ct)
static int test_attach_lsm_func_func(void* payload)
{
TSTOUT("%s", lsm_process_label_get(getpid()));
TSTOUT("%s", lsm_process_label_get(syscall(SYS_getpid)));
return 0;
}
......@@ -189,7 +190,7 @@ static int test_attach_lsm_cmd(struct lxc_container *ct) { return 0; }
static int test_attach_func_func(void* payload)
{
TSTOUT("%d", getpid());
TSTOUT("%d", (int)syscall(SYS_getpid));
return 0;
}
......
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