]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/rexec.c
github: Update for main branch
[mirror_lxc.git] / src / lxc / rexec.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
6400238d 2
1160ce89
CB
3#include "config.h"
4
6400238d
CB
5#include <errno.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
cee55b59 9#include <unistd.h>
6400238d 10
6400238d 11#include "file_utils.h"
cee55b59
CB
12#include "macro.h"
13#include "memory_utils.h"
f40988c7 14#include "process_utils.h"
59eac805 15#include "rexec.h"
6400238d
CB
16#include "string_utils.h"
17#include "syscall_wrappers.h"
18
61c40023 19#if IS_BIONIC && !HAVE_FEXECVE
58db1a61 20#include "fexecve.h"
9d361e0f
CB
21#endif
22
6400238d
CB
23#define LXC_MEMFD_REXEC_SEALS \
24 (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
25
7a80606d
CB
26static int push_vargs(char *data, int data_length, char ***output)
27{
28 int num = 0;
29 char *cur = data;
30
31 if (!data || *output)
32 return -1;
33
34 *output = must_realloc(NULL, sizeof(**output));
35
36 while (cur < data + data_length) {
37 num++;
38 *output = must_realloc(*output, (num + 1) * sizeof(**output));
39
40 (*output)[num - 1] = cur;
41 cur += strlen(cur) + 1;
42 }
43 (*output)[num] = NULL;
44 return num;
45}
46
cee55b59 47static int parse_argv(char ***argv)
7a80606d 48{
cee55b59 49 __do_free char *cmdline = NULL;
7a80606d 50 int ret;
cee55b59 51 size_t cmdline_size;
7a80606d
CB
52
53 cmdline = file_to_buf("/proc/self/cmdline", &cmdline_size);
54 if (!cmdline)
cee55b59 55 return -1;
7a80606d
CB
56
57 ret = push_vargs(cmdline, cmdline_size, argv);
58 if (ret <= 0)
cee55b59 59 return -1;
7a80606d 60
e4edf5d7 61 move_ptr(cmdline);
7a80606d 62 return 0;
7a80606d
CB
63}
64
6400238d
CB
65static int is_memfd(void)
66{
f62cf1d4 67 __do_close int fd = -EBADF;
4aa90f60 68 int seals;
6400238d
CB
69
70 fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
71 if (fd < 0)
72 return -ENOTRECOVERABLE;
73
74 seals = fcntl(fd, F_GET_SEALS);
4aa90f60
CB
75 if (seals < 0) {
76 struct stat s = {0};
77
78 if (fstat(fd, &s) == 0)
79 return (s.st_nlink == 0);
80
6400238d 81 return -EINVAL;
4aa90f60 82 }
6400238d
CB
83
84 return seals == LXC_MEMFD_REXEC_SEALS;
85}
86
87static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name)
88{
f62cf1d4 89 __do_close int execfd = -EBADF, fd = -EBADF, memfd = -EBADF,
67e72461 90 tmpfd = -EBADF;
4aa90f60 91 int ret;
5e6e38bd
CB
92 ssize_t bytes_sent = 0;
93 struct stat st = {0};
6400238d
CB
94
95 memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
4aa90f60
CB
96 if (memfd < 0) {
97 char template[PATH_MAX];
98
608a39c2
CB
99 ret = strnprintf(template, sizeof(template),
100 P_tmpdir "/.%s_XXXXXX", memfd_name);
101 if (ret < 0)
4aa90f60
CB
102 return;
103
104 tmpfd = lxc_make_tmpfile(template, true);
105 if (tmpfd < 0)
106 return;
107
108 ret = fchmod(tmpfd, 0700);
109 if (ret)
110 return;
111 }
6400238d
CB
112
113 fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
114 if (fd < 0)
fed8112d 115 return;
6400238d
CB
116
117 /* sendfile() handles up to 2GB. */
5e6e38bd
CB
118 ret = fstat(fd, &st);
119 if (ret)
120 return;
4aa90f60 121
5e6e38bd
CB
122 while (bytes_sent < st.st_size) {
123 ssize_t sent;
4aa90f60 124
5e6e38bd
CB
125 sent = lxc_sendfile_nointr(memfd >= 0 ? memfd : tmpfd, fd, NULL,
126 st.st_size - bytes_sent);
127 if (sent < 0) {
6b69d7f8
CB
128 /*
129 * Fallback to shoveling data between kernel- and
5e6e38bd
CB
130 * userspace.
131 */
6b69d7f8
CB
132 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
133 fprintf(stderr, "Failed to seek to beginning of file");
134
5e6e38bd
CB
135 if (fd_to_fd(fd, memfd >= 0 ? memfd : tmpfd))
136 break;
137
138 return;
4aa90f60 139 }
5e6e38bd 140 bytes_sent += sent;
4aa90f60 141 }
4aa90f60
CB
142 close_prot_errno_disarm(fd);
143
4aa90f60 144 if (memfd >= 0) {
5e6e38bd
CB
145 if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS))
146 return;
147
67e72461 148 execfd = move_fd(memfd);
4aa90f60 149 } else {
4aa90f60
CB
150 char procfd[LXC_PROC_PID_FD_LEN];
151
608a39c2
CB
152 ret = strnprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", tmpfd);
153 if (ret < 0)
4aa90f60
CB
154 return;
155
156 execfd = open(procfd, O_PATH | O_CLOEXEC);
157 close_prot_errno_disarm(tmpfd);
4aa90f60 158
4aa90f60 159 }
5e6e38bd
CB
160 if (execfd < 0)
161 return;
162
163 fexecve(execfd, argv, envp);
6400238d
CB
164}
165
cee55b59
CB
166/*
167 * Get cheap access to the environment. This must be declared by the user as
168 * mandated by POSIX. The definition is located in unistd.h.
169 */
170extern char **environ;
171
7a80606d 172int lxc_rexec(const char *memfd_name)
6400238d 173{
0212dc6f 174 __do_free_string_list char **argv = NULL;
6400238d 175 int ret;
6400238d
CB
176
177 ret = is_memfd();
178 if (ret < 0 && ret == -ENOTRECOVERABLE) {
0212dc6f 179 fprintf(stderr, "%s - Failed to determine whether this is a memfd\n",
6400238d
CB
180 strerror(errno));
181 return -1;
182 } else if (ret > 0) {
183 return 0;
184 }
185
cee55b59 186 ret = parse_argv(&argv);
7a80606d 187 if (ret < 0) {
0212dc6f 188 fprintf(stderr, "%s - Failed to parse command line parameters\n",
7a80606d
CB
189 strerror(errno));
190 return -1;
191 }
192
cee55b59 193 lxc_rexec_as_memfd(argv, environ, memfd_name);
6400238d
CB
194 fprintf(stderr, "%s - Failed to rexec as memfd\n", strerror(errno));
195 return -1;
196}
197
198/**
199 * This function will copy any binary that calls liblxc into a memory file and
200 * will use the memfd to rexecute the binary. This is done to prevent attacks
201 * through the /proc/self/exe symlink to corrupt the host binary when host and
202 * container are in the same user namespace or have set up an identity id
203 * mapping: CVE-2019-5736.
204 */
7a80606d 205__attribute__((constructor)) static void liblxc_rexec(void)
6400238d 206{
7a80606d 207 if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) {
6400238d
CB
208 fprintf(stderr, "Failed to re-execute liblxc via memory file descriptor\n");
209 _exit(EXIT_FAILURE);
210 }
211}