]>
Commit | Line | Data |
---|---|---|
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 |
45 | static 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 | 66 | static 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 |
84 | static 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 | ||
106 | static 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 | */ | |
182 | extern char **environ; | |
183 | ||
7a80606d | 184 | int 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 | } |