From: Christian Brauner Date: Tue, 19 Feb 2019 16:38:42 +0000 (+0100) Subject: rexec: handle legacy kernels X-Git-Tag: lxc-2.0.10~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b372592c44125cf06f7d856be5691b68a06354dd;p=thirdparty%2Flxc.git rexec: handle legacy kernels Signed-off-by: Christian Brauner --- diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index c27c10505..d86d2e58f 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -31,8 +31,10 @@ noinst_HEADERS = \ log.h \ lxc.h \ lxclock.h \ + macro.h \ monitor.h \ namespace.h \ + rexec.h \ start.h \ state.h \ utils.h \ @@ -122,6 +124,7 @@ liblxc_la_SOURCES = \ \ caps.c caps.h \ lxcseccomp.h \ + macro.h \ mainloop.c mainloop.h \ af_unix.c af_unix.h \ \ @@ -159,7 +162,7 @@ liblxc_la_SOURCES += ../include/strlcat.c ../include/strlcat.h endif if ENFORCE_MEMFD_REXEC -liblxc_la_SOURCES += rexec.c +liblxc_la_SOURCES += rexec.c rexec.h endif AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ @@ -269,7 +272,7 @@ AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) endif LDADD=liblxc.la @CAP_LIBS@ @SELINUX_LIBS@ @SECCOMP_LIBS@ -lxc_attach_SOURCES = tools/lxc_attach.c tools/arguments.c +lxc_attach_SOURCES = tools/lxc_attach.c tools/arguments.c rexec.c rexec.h lxc_autostart_SOURCES = tools/lxc_autostart.c tools/arguments.c lxc_cgroup_SOURCES = tools/lxc_cgroup.c tools/arguments.c lxc_config_SOURCES = tools/lxc_config.c tools/arguments.c diff --git a/src/lxc/commands.h b/src/lxc/commands.h index 2b758a3cb..11e0cc3b2 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -29,10 +29,9 @@ #include #include "lxccontainer.h" +#include "macro.h" #include "state.h" -#define LXC_CMD_DATA_MAX (MAXPATHLEN * 2) - /* Length of abstract unix domain socket socket address. */ #define LXC_AUDS_ADDR_LEN sizeof(((struct sockaddr_un *)0)->sun_path) diff --git a/src/lxc/lsm/lsm.h b/src/lxc/lsm/lsm.h index cafb2ac7c..3a2524077 100644 --- a/src/lxc/lsm/lsm.h +++ b/src/lxc/lsm/lsm.h @@ -28,10 +28,9 @@ struct lxc_conf; #include +#include "macro.h" #include "utils.h" -#define LXC_LSMATTRLEN (5 + (LXC_NUMSTRLEN64) + 7 + 1) - struct lsm_drv { const char *name; diff --git a/src/lxc/macro.h b/src/lxc/macro.h new file mode 100644 index 000000000..73abe124e --- /dev/null +++ b/src/lxc/macro.h @@ -0,0 +1,411 @@ +/* liblxcapi + * + * Copyright © 2018 Christian Brauner . + * Copyright © 2018 Canonical Ltd. + * + * 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 + */ + +#ifndef __LXC_MACRO_H +#define __LXC_MACRO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +/* Define __S_ISTYPE if missing from the C library. */ +#ifndef __S_ISTYPE +#define __S_ISTYPE(mode, mask) (((mode)&S_IFMT) == (mask)) +#endif + +/* capabilities */ +#ifndef CAP_SYS_ADMIN +#define CAP_SYS_ADMIN 21 +#endif + +#ifndef CAP_SETFCAP +#define CAP_SETFCAP 31 +#endif + +#ifndef CAP_MAC_OVERRIDE +#define CAP_MAC_OVERRIDE 32 +#endif + +#ifndef CAP_MAC_ADMIN +#define CAP_MAC_ADMIN 33 +#endif + +#ifndef CAP_SETUID +#define CAP_SETUID 7 +#endif + +#ifndef CAP_SETGID +#define CAP_SETGID 6 +#endif + +/* prctl */ +#ifndef PR_CAPBSET_READ +#define PR_CAPBSET_READ 23 +#endif + +#ifndef PR_CAPBSET_DROP +#define PR_CAPBSET_DROP 24 +#endif + +/* Control the ambient capability set */ +#ifndef PR_CAP_AMBIENT +#define PR_CAP_AMBIENT 47 +#endif + +#ifndef PR_CAP_AMBIENT_IS_SET +#define PR_CAP_AMBIENT_IS_SET 1 +#endif + +#ifndef PR_CAP_AMBIENT_RAISE +#define PR_CAP_AMBIENT_RAISE 2 +#endif + +#ifndef PR_CAP_AMBIENT_LOWER +#define PR_CAP_AMBIENT_LOWER 3 +#endif + +#ifndef PR_CAP_AMBIENT_CLEAR_ALL +#define PR_CAP_AMBIENT_CLEAR_ALL 4 +#endif + +#ifndef PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + +#ifndef PR_GET_NO_NEW_PRIVS +#define PR_GET_NO_NEW_PRIVS 39 +#endif + +/* filesystem magic values */ +#ifndef CGROUP_SUPER_MAGIC +#define CGROUP_SUPER_MAGIC 0x27e0eb +#endif + +#ifndef CGROUP2_SUPER_MAGIC +#define CGROUP2_SUPER_MAGIC 0x63677270 +#endif + +#ifndef NSFS_MAGIC +#define NSFS_MAGIC 0x6e736673 +#endif + +/* current overlayfs */ +#ifndef OVERLAY_SUPER_MAGIC +#define OVERLAY_SUPER_MAGIC 0x794c7630 +#endif + +/* legacy overlayfs */ +#ifndef OVERLAYFS_SUPER_MAGIC +#define OVERLAYFS_SUPER_MAGIC 0x794c764f +#endif + +/* Calculate the number of chars needed to represent a given integer as a C + * string. Include room for '-' to indicate negative numbers and the \0 byte. + * This is based on systemd. + */ +#define INTTYPE_TO_STRLEN(type) \ + (2 + (sizeof(type) <= 1 \ + ? 3 \ + : sizeof(type) <= 2 \ + ? 5 \ + : sizeof(type) <= 4 \ + ? 10 \ + : sizeof(type) <= 8 \ + ? 20 \ + : sizeof(int[-2 * (sizeof(type) > 8)]))) + +/* Useful macros */ +#define LXC_LINELEN 4096 +#define LXC_IDMAPLEN 4096 +#define LXC_MAX_BUFFER 4096 +#define LXC_NUMSTRLEN64 (INTTYPE_TO_STRLEN(intmax_t)) + +/* /proc/ = 6 + * + + * = INTTYPE_TO_STRLEN(pid_t) + * + + * /fd/ = 4 + * + + * = INTTYPE_TO_STRLEN(int) + * + + * \0 = 1 + */ +#define LXC_PROC_PID_FD_LEN \ + (6 + INTTYPE_TO_STRLEN(pid_t) + 4 + INTTYPE_TO_STRLEN(int) + 1) + +/* /proc/ = 6 + * + + * = INTTYPE_TO_STRLEN(pid_t) + * + + * /status = 7 + * + + * \0 = 1 + */ +#define LXC_PROC_STATUS_LEN (6 + INTTYPE_TO_STRLEN(pid_t) + 7 + 1) + +/* /proc/ = 6 + * + + * = INTTYPE_TO_STRLEN(pid_t) + * + + * /attr/ = 6 + * + + * /current = 8 + * + + * \0 = 1 + */ +#define LXC_LSMATTRLEN (6 + INTTYPE_TO_STRLEN(pid_t) + 6 + 8 + 1) + +#define LXC_CMD_DATA_MAX (PATH_MAX * 2) + +/* loop devices */ +#ifndef LO_FLAGS_AUTOCLEAR +#define LO_FLAGS_AUTOCLEAR 4 +#endif + +#ifndef LOOP_CTL_GET_FREE +#define LOOP_CTL_GET_FREE 0x4C82 +#endif + +/* memfd_create() */ +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +/** + * BUILD_BUG_ON - break compile if a condition is true. + * @condition: the condition which the compiler should know is false. + * + * If you have some code which relies on certain constants being equal, or + * other compile-time-evaluated condition, you should use BUILD_BUG_ON to + * detect if someone changes it. + * + * The implementation uses gcc's reluctance to create a negative array, but + * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments + * to inline functions). So as a fallback we use the optimizer; if it can't + * prove the condition is false, it will cause a link error on the undefined + * "__build_bug_on_failed". This error message can be harder to track down + * though, hence the two different methods. + */ +#ifndef __OPTIMIZE__ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)])) +#else +extern int __build_bug_on_failed; +#define BUILD_BUG_ON(condition) \ + do { \ + ((void)sizeof(char[1 - 2 * !!(condition)])); \ + if (condition) \ + __build_bug_on_failed = 1; \ + } while (0) +#endif + +#define lxc_iterate_parts(__iterator, __splitme, __separators) \ + for (char *__p = NULL, *__it = strtok_r(__splitme, __separators, &__p); \ + (__iterator = __it); \ + __iterator = __it = strtok_r(NULL, __separators, &__p)) + +#define prctl_arg(x) ((unsigned long)x) + +/* networking */ +#ifndef NETLINK_DUMP_STRICT_CHK +#define NETLINK_DUMP_STRICT_CHK 12 +#endif + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#ifndef IFLA_LINKMODE +#define IFLA_LINKMODE 17 +#endif + +#ifndef IFLA_LINKINFO +#define IFLA_LINKINFO 18 +#endif + +#ifndef IFLA_NET_NS_PID +#define IFLA_NET_NS_PID 19 +#endif + +#ifndef IFLA_INFO_KIND +#define IFLA_INFO_KIND 1 +#endif + +#ifndef IFLA_VLAN_ID +#define IFLA_VLAN_ID 1 +#endif + +#ifndef IFLA_INFO_DATA +#define IFLA_INFO_DATA 2 +#endif + +#ifndef VETH_INFO_PEER +#define VETH_INFO_PEER 1 +#endif + +#ifndef IFLA_MACVLAN_MODE +#define IFLA_MACVLAN_MODE 1 +#endif + +#ifndef IFLA_NEW_NETNSID +#define IFLA_NEW_NETNSID 45 +#endif + +#ifdef IFLA_IF_NETNSID +#ifndef IFLA_TARGET_NETNSID +#define IFLA_TARGET_NETNSID = IFLA_IF_NETNSID +#endif +#else +#define IFLA_IF_NETNSID 46 +#define IFLA_TARGET_NETNSID 46 +#endif + +#ifndef IFA_TARGET_NETNSID +#define IFA_TARGET_NETNSID 10 +#endif + +#ifndef IFLA_STATS +#define IFLA_STATS 7 +#endif + +#ifndef IFLA_STATS64 +#define IFLA_STATS64 23 +#endif + +#ifndef RTM_NEWNSID +#define RTM_NEWNSID 88 +#endif + +#ifndef RTM_GETNSID +#define RTM_GETNSID 90 +#endif + +#ifndef NLMSG_ERROR +#define NLMSG_ERROR 0x2 +#endif + +#ifndef MACVLAN_MODE_PRIVATE +#define MACVLAN_MODE_PRIVATE 1 +#endif + +#ifndef MACVLAN_MODE_VEPA +#define MACVLAN_MODE_VEPA 2 +#endif + +#ifndef MACVLAN_MODE_BRIDGE +#define MACVLAN_MODE_BRIDGE 4 +#endif + +#ifndef MACVLAN_MODE_PASSTHRU +#define MACVLAN_MODE_PASSTHRU 8 +#endif + +/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ +enum { + __LXC_NETNSA_NONE, +#define __LXC_NETNSA_NSID_NOT_ASSIGNED -1 + __LXC_NETNSA_NSID, + __LXC_NETNSA_PID, + __LXC_NETNSA_FD, + __LXC_NETNSA_MAX, +}; + +/* Length of abstract unix domain socket socket address. */ +#define LXC_AUDS_ADDR_LEN sizeof(((struct sockaddr_un *)0)->sun_path) + +/* mount */ +#ifndef MS_PRIVATE +#define MS_PRIVATE (1<<18) +#endif + +#ifndef MS_SLAVE +#define MS_SLAVE (1 << 19) +#endif + +#ifndef MS_LAZYTIME +#define MS_LAZYTIME (1<<25) +#endif + +#ifndef MS_REC +#define MS_REC 16384 +#endif + +/* open */ +#ifndef O_PATH +#define O_PATH 010000000 +#endif + +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 00400000 +#endif + +/* sockets */ +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 02000000 +#endif + +/* pointer conversion macros */ +#define PTR_TO_INT(p) ((int)((intptr_t)(p))) +#define INT_TO_PTR(u) ((void *)((intptr_t)(u))) + +#define PTR_TO_INTMAX(p) ((intmax_t)((intptr_t)(p))) +#define INTMAX_TO_PTR(u) ((void *)((intptr_t)(u))) + +#define LXC_INVALID_UID ((uid_t)-1) +#define LXC_INVALID_GID ((gid_t)-1) + +#define STRLITERALLEN(x) (sizeof(""x"") - 1) +#define STRARRAYLEN(x) (sizeof(x) - 1) + +/* Maximum number of bytes sendfile() is able to send in one go. */ +#define LXC_SENDFILE_MAX 0x7ffff000 + +#define move_ptr(ptr) \ + ({ \ + typeof(ptr) __internal_ptr__ = (ptr); \ + (ptr) = NULL; \ + __internal_ptr__; \ + }) + +#define move_fd(fd) \ + ({ \ + int __internal_fd__ = (fd); \ + (fd) = -EBADF; \ + __internal_fd__; \ + }) + +#endif /* __LXC_MACRO_H */ diff --git a/src/lxc/memory_utils.h b/src/lxc/memory_utils.h new file mode 100644 index 000000000..c1dafb441 --- /dev/null +++ b/src/lxc/memory_utils.h @@ -0,0 +1,68 @@ +/* liblxcapi + * + * Copyright © 2019 Christian Brauner . + * Copyright © 2019 Canonical Ltd. + * + * 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 + */ + +#ifndef __LXC_MEMORY_UTILS_H +#define __LXC_MEMORY_UTILS_H + +#include +#include +#include +#include +#include +#include + +#include "macro.h" + +static inline void __auto_free__(void *p) +{ + free(*(void **)p); +} + +static inline void __auto_fclose__(FILE **f) +{ + if (*f) + fclose(*f); +} + +static inline void __auto_closedir__(DIR **d) +{ + if (*d) + closedir(*d); +} + +#define close_prot_errno_disarm(fd) \ + if (fd >= 0) { \ + int _e_ = errno; \ + close(fd); \ + errno = _e_; \ + fd = -EBADF; \ + } + +static inline void __auto_close__(int *fd) +{ + close_prot_errno_disarm(*fd); +} + +#define __do_close_prot_errno __attribute__((__cleanup__(__auto_close__))) +#define __do_free __attribute__((__cleanup__(__auto_free__))) +#define __do_fclose __attribute__((__cleanup__(__auto_fclose__))) +#define __do_closedir __attribute__((__cleanup__(__auto_closedir__))) + +#endif /* __LXC_MEMORY_UTILS_H */ diff --git a/src/lxc/rexec.c b/src/lxc/rexec.c index 29bfd859e..ad1d5f689 100644 --- a/src/lxc/rexec.c +++ b/src/lxc/rexec.c @@ -3,18 +3,19 @@ * Copyright © 2019 Christian Brauner . * Copyright © 2019 Canonical Ltd. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, + * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * 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 */ #ifndef _GNU_SOURCE @@ -32,6 +33,8 @@ #include #include "config.h" +#include "macro.h" +#include "memory_utils.h" #include "utils.h" #if IS_BIONIC @@ -101,51 +104,42 @@ on_error: return NULL; } -static int parse_exec_params(char ***argv, char ***envp) +static int parse_argv(char ***argv) { + __do_free char *cmdline = NULL; int ret; - char *cmdline = NULL, *env = NULL; - size_t cmdline_size, env_size; + size_t cmdline_size; cmdline = file_to_buf("/proc/self/cmdline", &cmdline_size); if (!cmdline) - goto on_error; - - env = file_to_buf("/proc/self/environ", &env_size); - if (!env) - goto on_error; + return -1; ret = push_vargs(cmdline, cmdline_size, argv); if (ret <= 0) - goto on_error; - - ret = push_vargs(env, env_size, envp); - if (ret <= 0) - goto on_error; + return -1; + move_ptr(cmdline); return 0; - -on_error: - free(env); - free(cmdline); - - return -1; } static int is_memfd(void) { - int fd, saved_errno, seals; + __do_close_prot_errno int fd = -EBADF; + int seals; fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); if (fd < 0) return -ENOTRECOVERABLE; seals = fcntl(fd, F_GET_SEALS); - saved_errno = errno; - close(fd); - errno = saved_errno; - if (seals < 0) + if (seals < 0) { + struct stat s = {0}; + + if (fstat(fd, &s) == 0) + return (s.st_nlink == 0); + return -EINVAL; + } return seals == LXC_MEMFD_REXEC_SEALS; } @@ -168,43 +162,122 @@ again: return ret; } +static int fd_to_fd(int from, int to) +{ + for (;;) { + uint8_t buf[PATH_MAX]; + uint8_t *p = buf; + ssize_t bytes_to_write; + ssize_t bytes_read; + + bytes_read = lxc_read_nointr(from, buf, sizeof buf); + if (bytes_read < 0) + return -1; + if (bytes_read == 0) + break; + + bytes_to_write = (size_t)bytes_read; + do { + ssize_t bytes_written; + + bytes_written = lxc_write_nointr(to, p, bytes_to_write); + if (bytes_written < 0) + return -1; + + bytes_to_write -= bytes_written; + p += bytes_written; + } while (bytes_to_write > 0); + } + + return 0; +} + static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name) { - int saved_errno; - ssize_t bytes_sent; - int fd = -1, memfd = -1; + __do_close_prot_errno int execfd = -EBADF, fd = -EBADF, memfd = -EBADF, + tmpfd = -EBADF; + int ret; + ssize_t bytes_sent = 0; + struct stat st = {0}; memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC); - if (memfd < 0) - return; + if (memfd < 0) { + char template[PATH_MAX]; + + ret = snprintf(template, sizeof(template), + P_tmpdir "/.%s_XXXXXX", memfd_name); + if (ret < 0 || (size_t)ret >= sizeof(template)) + return; + + tmpfd = lxc_make_tmpfile(template, true); + if (tmpfd < 0) + return; + + ret = fchmod(tmpfd, 0700); + if (ret) + return; + } fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); if (fd < 0) - goto on_error; + return; /* sendfile() handles up to 2GB. */ - bytes_sent = lxc_sendfile_nointr(memfd, fd, NULL, LXC_SENDFILE_MAX); - saved_errno = errno; - close(fd); - errno = saved_errno; - if (bytes_sent < 0) - goto on_error; + ret = fstat(fd, &st); + if (ret) + return; - if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS)) - goto on_error; + while (bytes_sent < st.st_size) { + ssize_t sent; + + sent = lxc_sendfile_nointr(memfd >= 0 ? memfd : tmpfd, fd, NULL, + st.st_size - bytes_sent); + if (sent < 0) { + /* Fallback to shoveling data between kernel- and + * userspace. + */ + lseek(fd, 0, SEEK_SET); + if (fd_to_fd(fd, memfd >= 0 ? memfd : tmpfd)) + break; + + return; + } + bytes_sent += sent; + } + close_prot_errno_disarm(fd); - fexecve(memfd, argv, envp); + if (memfd >= 0) { + if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS)) + return; -on_error: - saved_errno = errno; - close(memfd); - errno = saved_errno; + execfd = memfd; + } else { + char procfd[LXC_PROC_PID_FD_LEN]; + + ret = snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", tmpfd); + if (ret < 0 || (size_t)ret >= sizeof(procfd)) + return; + + execfd = open(procfd, O_PATH | O_CLOEXEC); + close_prot_errno_disarm(tmpfd); + + } + if (execfd < 0) + return; + + fexecve(execfd, argv, envp); } -static int lxc_rexec(const char *memfd_name) +/* + * Get cheap access to the environment. This must be declared by the user as + * mandated by POSIX. The definition is located in unistd.h. + */ +extern char **environ; + +int lxc_rexec(const char *memfd_name) { int ret; - char **argv = NULL, **envp = NULL; + char **argv = NULL; ret = is_memfd(); if (ret < 0 && ret == -ENOTRECOVERABLE) { @@ -216,7 +289,7 @@ static int lxc_rexec(const char *memfd_name) return 0; } - ret = parse_exec_params(&argv, &envp); + ret = parse_argv(&argv); if (ret < 0) { fprintf(stderr, "%s - Failed to parse command line parameters\n", @@ -224,7 +297,7 @@ static int lxc_rexec(const char *memfd_name) return -1; } - lxc_rexec_as_memfd(argv, envp, memfd_name); + lxc_rexec_as_memfd(argv, environ, memfd_name); fprintf(stderr, "%s - Failed to rexec as memfd\n", strerror(errno)); return -1; } @@ -238,7 +311,7 @@ static int lxc_rexec(const char *memfd_name) */ __attribute__((constructor)) static void liblxc_rexec(void) { - if (lxc_rexec("liblxc")) { + if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) { fprintf(stderr, "Failed to re-execute liblxc via memory file descriptor\n"); _exit(EXIT_FAILURE); } diff --git a/src/lxc/rexec.h b/src/lxc/rexec.h new file mode 100644 index 000000000..088ded932 --- /dev/null +++ b/src/lxc/rexec.h @@ -0,0 +1,26 @@ +/* liblxcapi + * + * Copyright © 2019 Christian Brauner . + * Copyright © 2019 Canonical Ltd. + * + * 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 + */ + +#ifndef __LXC_REXEC_H +#define __LXC_REXEC_H + +extern int lxc_rexec(const char *memfd_name); + +#endif /* __LXC_REXEC_H */ diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c index d88f7da56..af9808928 100644 --- a/src/lxc/tools/lxc_attach.c +++ b/src/lxc/tools/lxc_attach.c @@ -21,8 +21,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "config.h" - +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif #include #include #include @@ -35,15 +36,17 @@ #include -#include "attach.h" #include "arguments.h" +#include "attach.h" #include "caps.h" #include "conf.h" +#include "config.h" #include "confile.h" #include "console.h" -#include "log.h" #include "list.h" +#include "log.h" #include "mainloop.h" +#include "rexec.h" #include "utils.h" #if HAVE_PTY_H @@ -52,6 +55,23 @@ #include <../include/openpty.h> #endif +/** + * This function will copy any binary that calls liblxc into a memory file and + * will use the memfd to rexecute the binary. This is done to prevent attacks + * through the /proc/self/exe symlink to corrupt the host binary when host and + * container are in the same user namespace or have set up an identity id + * mapping: CVE-2019-5736. + */ +#ifdef ENFORCE_MEMFD_REXEC +__attribute__((constructor)) static void lxc_attach_rexec(void) +{ + if (!getenv("LXC_MEMFD_REXEC") && lxc_rexec("lxc-attach")) { + fprintf(stderr, "Failed to re-execute lxc-attach via memory file descriptor\n"); + _exit(EXIT_FAILURE); + } +} +#endif + static const struct option my_longopts[] = { {"elevated-privileges", optional_argument, 0, 'e'}, {"arch", required_argument, 0, 'a'}, diff --git a/src/lxc/utils.c b/src/lxc/utils.c index c4c827c59..8d315038e 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -47,6 +47,7 @@ #include "log.h" #include "lxclock.h" +#include "memory_utils.h" #include "namespace.h" #include "parse.h" #include "utils.h" @@ -2396,7 +2397,8 @@ bool lxc_nic_exists(char *nic) int lxc_make_tmpfile(char *template, bool rm) { - int fd, ret; + __do_close_prot_errno int fd = -EBADF; + int ret; mode_t msk; msk = umask(0022); @@ -2405,16 +2407,17 @@ int lxc_make_tmpfile(char *template, bool rm) if (fd < 0) return -1; + if (lxc_set_cloexec(fd)) + return -1; + if (!rm) - return fd; + return move_fd(fd); ret = unlink(template); - if (ret < 0) { - close(fd); + if (ret < 0) return -1; - } - return fd; + return move_fd(fd); } int parse_byte_size_string(const char *s, int64_t *converted) diff --git a/src/lxc/utils.h b/src/lxc/utils.h index d86584e83..61e4d9962 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -45,76 +45,7 @@ #endif #include "initutils.h" - -/* Define __S_ISTYPE if missing from the C library. */ -#ifndef __S_ISTYPE -#define __S_ISTYPE(mode, mask) (((mode)&S_IFMT) == (mask)) -#endif - -#if HAVE_LIBCAP -#ifndef CAP_SETFCAP -#define CAP_SETFCAP 31 -#endif - -#ifndef CAP_MAC_OVERRIDE -#define CAP_MAC_OVERRIDE 32 -#endif - -#ifndef CAP_MAC_ADMIN -#define CAP_MAC_ADMIN 33 -#endif -#endif - -#ifndef PR_CAPBSET_DROP -#define PR_CAPBSET_DROP 24 -#endif - -#ifndef LO_FLAGS_AUTOCLEAR -#define LO_FLAGS_AUTOCLEAR 4 -#endif - -#ifndef CAP_SETUID -#define CAP_SETUID 7 -#endif - -#ifndef CAP_SETGID -#define CAP_SETGID 6 -#endif - -/* needed for cgroup automount checks, regardless of whether we - * have included linux/capability.h or not */ -#ifndef CAP_SYS_ADMIN -#define CAP_SYS_ADMIN 21 -#endif - -#ifndef CGROUP_SUPER_MAGIC -#define CGROUP_SUPER_MAGIC 0x27e0eb -#endif - -#ifndef CGROUP2_SUPER_MAGIC -#define CGROUP2_SUPER_MAGIC 0x63677270 -#endif - -#ifndef NSFS_MAGIC -#define NSFS_MAGIC 0x6e736673 -#endif - -/* Useful macros */ -/* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */ -#define LXC_NUMSTRLEN64 21 -#define LXC_LINELEN 4096 -#define LXC_IDMAPLEN 4096 -/* /proc/ = 6 - * + - * = LXC_NUMSTRLEN64 - * + - * /fd/ = 4 - * + - * = LXC_NUMSTRLEN64 - * + - * \0 = 1 - */ -#define LXC_PROC_PID_FD_LEN (6 + LXC_NUMSTRLEN64 + 4 + LXC_NUMSTRLEN64 + 1) +#include "macro.h" /* returns 1 on success, 0 if there were any failures */ extern int lxc_rmdir_onedev(char *path, const char *exclude); @@ -376,32 +307,6 @@ extern struct lxc_popen_FILE *lxc_popen(const char *command); */ extern int lxc_pclose(struct lxc_popen_FILE *fp); -/** - * BUILD_BUG_ON - break compile if a condition is true. - * @condition: the condition which the compiler should know is false. - * - * If you have some code which relies on certain constants being equal, or - * other compile-time-evaluated condition, you should use BUILD_BUG_ON to - * detect if someone changes it. - * - * The implementation uses gcc's reluctance to create a negative array, but - * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments - * to inline functions). So as a fallback we use the optimizer; if it can't - * prove the condition is false, it will cause a link error on the undefined - * "__build_bug_on_failed". This error message can be harder to track down - * though, hence the two different methods. - */ -#ifndef __OPTIMIZE__ -#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) -#else -extern int __build_bug_on_failed; -#define BUILD_BUG_ON(condition) \ - do { \ - ((void)sizeof(char[1 - 2*!!(condition)])); \ - if (condition) __build_bug_on_failed = 1; \ - } while(0) -#endif - /* * wait on a child we forked */