From: Christian Brauner Date: Mon, 18 Feb 2019 22:36:03 +0000 (+0100) Subject: rexec: handle old kernels X-Git-Tag: lxc-3.0.4~269 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9690cdba7201b437cca46e636b24a0a7d6536cf5;p=thirdparty%2Flxc.git rexec: handle old kernels Signed-off-by: Christian Brauner --- diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index e6d544d20..604acbc16 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -20,9 +20,11 @@ noinst_HEADERS = attach.h \ lxc.h \ lxclock.h \ macro.h \ + memory_utils.h \ monitor.h \ namespace.h \ raw_syscalls.h \ + rexec.h \ start.h \ state.h \ storage/btrfs.h \ @@ -111,6 +113,7 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \ lxclock.c lxclock.h \ lxcseccomp.h \ macro.h \ + memory_utils.h \ mainloop.c mainloop.h \ namespace.c namespace.h \ nl.c nl.h \ @@ -176,7 +179,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)\" \ @@ -295,6 +298,7 @@ LDADD = liblxc.la \ if ENABLE_TOOLS lxc_attach_SOURCES = tools/lxc_attach.c \ + rexec.c rexec.h \ tools/arguments.c tools/arguments.h lxc_autostart_SOURCES = tools/lxc_autostart.c \ tools/arguments.c tools/arguments.h diff --git a/src/lxc/file_utils.c b/src/lxc/file_utils.c index 930fd738a..f80aa6421 100644 --- a/src/lxc/file_utils.c +++ b/src/lxc/file_utils.c @@ -31,7 +31,9 @@ #include "config.h" #include "file_utils.h" #include "macro.h" +#include "memory_utils.h" #include "string_utils.h" +#include "utils.h" int lxc_write_to_file(const char *filename, const void *buf, size_t count, bool add_newline, mode_t mode) @@ -218,7 +220,8 @@ int lxc_count_file_lines(const char *fn) 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); @@ -227,16 +230,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); } bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val) @@ -366,3 +370,33 @@ on_error: return NULL; } + +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; +} diff --git a/src/lxc/file_utils.h b/src/lxc/file_utils.h index 518a61af3..cc8f69e18 100644 --- a/src/lxc/file_utils.h +++ b/src/lxc/file_utils.h @@ -3,18 +3,19 @@ * Copyright © 2018 Christian Brauner . * Copyright © 2018 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 __LXC_FILE_UTILS_H @@ -56,5 +57,6 @@ extern FILE *fopen_cloexec(const char *path, const char *mode); extern ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset, size_t count); extern char *file_to_buf(char *path, size_t *length); +extern int fd_to_fd(int from, int to); #endif /* __LXC_FILE_UTILS_H */ diff --git a/src/lxc/macro.h b/src/lxc/macro.h index 30aa2a5a5..6e07516f2 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -3,18 +3,19 @@ * Copyright © 2018 Christian Brauner . * Copyright © 2018 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 __LXC_MACRO_H @@ -392,4 +393,18 @@ enum { /* 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 f08985525..39399826f 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 @@ -24,9 +25,12 @@ #include #include #include +#include #include "config.h" #include "file_utils.h" +#include "macro.h" +#include "memory_utils.h" #include "raw_syscalls.h" #include "string_utils.h" #include "syscall_wrappers.h" @@ -59,92 +63,132 @@ static int push_vargs(char *data, int data_length, char ***output) return num; } -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; } 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; - - if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS)) - goto on_error; - - fexecve(memfd, argv, envp); - -on_error: - saved_errno = errno; - close(memfd); - errno = saved_errno; + ret = fstat(fd, &st); + if (ret) + return; + + 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); + + if (memfd >= 0) { + if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS)) + return; + + 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) { @@ -156,7 +200,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", @@ -164,7 +208,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; } @@ -178,7 +222,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 8c8e7d397..80b3693d3 100644 --- a/src/lxc/tools/lxc_attach.c +++ b/src/lxc/tools/lxc_attach.c @@ -44,10 +44,28 @@ #include "config.h" #include "confile.h" #include "log.h" +#include "rexec.h" #include "utils.h" lxc_log_define(lxc_attach, lxc); +/** + * 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 int my_parser(struct lxc_arguments *args, int c, char *arg); static int add_to_simple_array(char ***array, ssize_t *capacity, char *value); static bool stdfd_is_pty(void);