]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/rexec.c
Merge pull request #2864 from brauner/2019-02-18/rename_pointer_macros
[mirror_lxc.git] / src / lxc / rexec.c
CommitLineData
6400238d
CB
1/* liblxcapi
2 *
3 * Copyright © 2019 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2019 Canonical Ltd.
5 *
2b3153a8
CB
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
6400238d 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
2b3153a8
CB
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6400238d
CB
19 */
20
21#ifndef _GNU_SOURCE
22#define _GNU_SOURCE 1
23#endif
24#include <errno.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
cee55b59 28#include <unistd.h>
6400238d
CB
29
30#include "config.h"
31#include "file_utils.h"
cee55b59
CB
32#include "macro.h"
33#include "memory_utils.h"
6400238d
CB
34#include "raw_syscalls.h"
35#include "string_utils.h"
36#include "syscall_wrappers.h"
37
9d361e0f
CB
38#if IS_BIONIC
39#include "../include/fexecve.h"
40#endif
41
6400238d
CB
42#define LXC_MEMFD_REXEC_SEALS \
43 (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
44
7a80606d
CB
45static int push_vargs(char *data, int data_length, char ***output)
46{
47 int num = 0;
48 char *cur = data;
49
50 if (!data || *output)
51 return -1;
52
53 *output = must_realloc(NULL, sizeof(**output));
54
55 while (cur < data + data_length) {
56 num++;
57 *output = must_realloc(*output, (num + 1) * sizeof(**output));
58
59 (*output)[num - 1] = cur;
60 cur += strlen(cur) + 1;
61 }
62 (*output)[num] = NULL;
63 return num;
64}
65
cee55b59 66static int parse_argv(char ***argv)
7a80606d 67{
cee55b59 68 __do_free char *cmdline = NULL;
7a80606d 69 int ret;
cee55b59 70 size_t cmdline_size;
7a80606d
CB
71
72 cmdline = file_to_buf("/proc/self/cmdline", &cmdline_size);
73 if (!cmdline)
cee55b59 74 return -1;
7a80606d
CB
75
76 ret = push_vargs(cmdline, cmdline_size, argv);
77 if (ret <= 0)
cee55b59 78 return -1;
7a80606d 79
e4edf5d7 80 move_ptr(cmdline);
7a80606d 81 return 0;
7a80606d
CB
82}
83
6400238d
CB
84static int is_memfd(void)
85{
fed8112d 86 __do_close_prot_errno int fd = -EBADF;
4aa90f60 87 int seals;
6400238d
CB
88
89 fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
90 if (fd < 0)
91 return -ENOTRECOVERABLE;
92
93 seals = fcntl(fd, F_GET_SEALS);
4aa90f60
CB
94 if (seals < 0) {
95 struct stat s = {0};
96
97 if (fstat(fd, &s) == 0)
98 return (s.st_nlink == 0);
99
6400238d 100 return -EINVAL;
4aa90f60 101 }
6400238d
CB
102
103 return seals == LXC_MEMFD_REXEC_SEALS;
104}
105
106static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name)
107{
4aa90f60
CB
108 __do_close_prot_errno int fd = -EBADF, memfd = -EBADF, tmpfd = -EBADF;
109 int ret;
6400238d
CB
110
111 memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
4aa90f60
CB
112 if (memfd < 0) {
113 char template[PATH_MAX];
114
115 ret = snprintf(template, sizeof(template),
116 P_tmpdir "/.%s_XXXXXX", memfd_name);
117 if (ret < 0 || (size_t)ret >= sizeof(template))
118 return;
119
120 tmpfd = lxc_make_tmpfile(template, true);
121 if (tmpfd < 0)
122 return;
123
124 ret = fchmod(tmpfd, 0700);
125 if (ret)
126 return;
127 }
6400238d
CB
128
129 fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
130 if (fd < 0)
fed8112d 131 return;
6400238d
CB
132
133 /* sendfile() handles up to 2GB. */
4aa90f60
CB
134 if (memfd >= 0) {
135 ssize_t bytes_sent = 0;
136 struct stat st = {0};
137
138 ret = fstat(fd, &st);
139 if (ret)
140 return;
141
142 while (bytes_sent < st.st_size) {
143 ssize_t sent;
144 sent = lxc_sendfile_nointr(memfd, fd, NULL,
145 st.st_size - bytes_sent);
146 if (sent < 0)
147 return;
148 bytes_sent += sent;
149 }
150 } else if (fd_to_fd(fd, tmpfd)) {
fed8112d 151 return;
4aa90f60 152 }
6400238d 153
4aa90f60
CB
154 close_prot_errno_disarm(fd);
155
156 if (memfd >= 0 && fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS))
fed8112d 157 return;
6400238d 158
4aa90f60
CB
159 if (memfd >= 0) {
160 fexecve(memfd, argv, envp);
161 } else {
162 __do_close_prot_errno int execfd = -EBADF;
163 char procfd[LXC_PROC_PID_FD_LEN];
164
165 ret = snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", tmpfd);
166 if (ret < 0 || (size_t)ret >= sizeof(procfd))
167 return;
168
169 execfd = open(procfd, O_PATH | O_CLOEXEC);
170 close_prot_errno_disarm(tmpfd);
171 if (execfd < 0)
172 return;
173
174 fexecve(execfd, argv, envp);
175 }
6400238d
CB
176}
177
cee55b59
CB
178/*
179 * Get cheap access to the environment. This must be declared by the user as
180 * mandated by POSIX. The definition is located in unistd.h.
181 */
182extern char **environ;
183
7a80606d 184int lxc_rexec(const char *memfd_name)
6400238d
CB
185{
186 int ret;
cee55b59 187 char **argv = NULL;
6400238d
CB
188
189 ret = is_memfd();
190 if (ret < 0 && ret == -ENOTRECOVERABLE) {
191 fprintf(stderr,
192 "%s - Failed to determine whether this is a memfd\n",
193 strerror(errno));
194 return -1;
195 } else if (ret > 0) {
196 return 0;
197 }
198
cee55b59 199 ret = parse_argv(&argv);
7a80606d
CB
200 if (ret < 0) {
201 fprintf(stderr,
202 "%s - Failed to parse command line parameters\n",
203 strerror(errno));
204 return -1;
205 }
206
cee55b59 207 lxc_rexec_as_memfd(argv, environ, memfd_name);
6400238d
CB
208 fprintf(stderr, "%s - Failed to rexec as memfd\n", strerror(errno));
209 return -1;
210}
211
212/**
213 * This function will copy any binary that calls liblxc into a memory file and
214 * will use the memfd to rexecute the binary. This is done to prevent attacks
215 * through the /proc/self/exe symlink to corrupt the host binary when host and
216 * container are in the same user namespace or have set up an identity id
217 * mapping: CVE-2019-5736.
218 */
7a80606d 219__attribute__((constructor)) static void liblxc_rexec(void)
6400238d 220{
7a80606d 221 if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) {
6400238d
CB
222 fprintf(stderr, "Failed to re-execute liblxc via memory file descriptor\n");
223 _exit(EXIT_FAILURE);
224 }
225}