]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/rexec.c
0589b4a781e1e56e16e4febd6c81f93f035db1fe
[mirror_lxc.git] / src / lxc / rexec.c
1 /* liblxcapi
2 *
3 * Copyright © 2019 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2019 Canonical Ltd.
5 *
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,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
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
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>
28 #include <unistd.h>
29
30 #include "config.h"
31 #include "file_utils.h"
32 #include "macro.h"
33 #include "memory_utils.h"
34 #include "raw_syscalls.h"
35 #include "string_utils.h"
36 #include "syscall_wrappers.h"
37
38 #if IS_BIONIC
39 #include "../include/fexecve.h"
40 #endif
41
42 #define LXC_MEMFD_REXEC_SEALS \
43 (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
44
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
66 static int parse_argv(char ***argv)
67 {
68 __do_free char *cmdline = NULL;
69 int ret;
70 size_t cmdline_size;
71
72 cmdline = file_to_buf("/proc/self/cmdline", &cmdline_size);
73 if (!cmdline)
74 return -1;
75
76 ret = push_vargs(cmdline, cmdline_size, argv);
77 if (ret <= 0)
78 return -1;
79
80 steal_ptr(cmdline);
81 return 0;
82 }
83
84 static int is_memfd(void)
85 {
86 __do_close_prot_errno int fd = -EBADF;
87 int saved_errno, seals;
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);
94 if (seals < 0)
95 return -EINVAL;
96
97 return seals == LXC_MEMFD_REXEC_SEALS;
98 }
99
100 static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name)
101 {
102 __do_close_prot_errno int fd = -EBADF, memfd = -EBADF;
103 int saved_errno;
104 ssize_t bytes_sent;
105
106 memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
107 if (memfd < 0)
108 return;
109
110 fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
111 if (fd < 0)
112 return;
113
114 /* sendfile() handles up to 2GB. */
115 bytes_sent = lxc_sendfile_nointr(memfd, fd, NULL, LXC_SENDFILE_MAX);
116 if (bytes_sent < 0)
117 return;
118
119 if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS))
120 return;
121
122 fexecve(memfd, argv, envp);
123 }
124
125 /*
126 * Get cheap access to the environment. This must be declared by the user as
127 * mandated by POSIX. The definition is located in unistd.h.
128 */
129 extern char **environ;
130
131 int lxc_rexec(const char *memfd_name)
132 {
133 int ret;
134 char **argv = NULL;
135
136 ret = is_memfd();
137 if (ret < 0 && ret == -ENOTRECOVERABLE) {
138 fprintf(stderr,
139 "%s - Failed to determine whether this is a memfd\n",
140 strerror(errno));
141 return -1;
142 } else if (ret > 0) {
143 return 0;
144 }
145
146 ret = parse_argv(&argv);
147 if (ret < 0) {
148 fprintf(stderr,
149 "%s - Failed to parse command line parameters\n",
150 strerror(errno));
151 return -1;
152 }
153
154 lxc_rexec_as_memfd(argv, environ, memfd_name);
155 fprintf(stderr, "%s - Failed to rexec as memfd\n", strerror(errno));
156 return -1;
157 }
158
159 /**
160 * This function will copy any binary that calls liblxc into a memory file and
161 * will use the memfd to rexecute the binary. This is done to prevent attacks
162 * through the /proc/self/exe symlink to corrupt the host binary when host and
163 * container are in the same user namespace or have set up an identity id
164 * mapping: CVE-2019-5736.
165 */
166 __attribute__((constructor)) static void liblxc_rexec(void)
167 {
168 if (getenv("LXC_MEMFD_REXEC") && lxc_rexec("liblxc")) {
169 fprintf(stderr, "Failed to re-execute liblxc via memory file descriptor\n");
170 _exit(EXIT_FAILURE);
171 }
172 }