Skip to content
This repository was archived by the owner on Jun 11, 2020. It is now read-only.

Commit a592beb

Browse files
authored
Merge pull request #11 from seemethere/apply_patches_1806
[18.06] [ENGSEC-23] Apply patches related to CVE-2019-5736
2 parents 69663f0 + 319172d commit a592beb

File tree

2 files changed

+249
-0
lines changed

2 files changed

+249
-0
lines changed

libcontainer/nsenter/cloned_binary.c

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#define _GNU_SOURCE
2+
#include <unistd.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <stdbool.h>
6+
#include <string.h>
7+
#include <limits.h>
8+
#include <fcntl.h>
9+
#include <errno.h>
10+
11+
#include <sys/types.h>
12+
#include <sys/stat.h>
13+
#include <sys/vfs.h>
14+
#include <sys/mman.h>
15+
#include <sys/sendfile.h>
16+
#include <sys/syscall.h>
17+
18+
#include <linux/magic.h>
19+
20+
/* flags for memfd_create(2) (unsigned int), taken from linux/memfd.h */
21+
#define MFD_CLOEXEC 0x0001U
22+
#define MFD_ALLOW_SEALING 0x0002U
23+
24+
/* Use our own wrapper for memfd_create. */
25+
#if !defined(__NR_memfd_create)
26+
# ifdef __i386__
27+
# define __NR_memfd_create 356
28+
# elif __x86_64__
29+
# define __NR_memfd_create 319
30+
# elif __powerpc64__
31+
# define __NR_memfd_create 360
32+
# elif __aarch64__
33+
# define __NR_memfd_create 279
34+
# elif __arm__
35+
# define __NR_memfd_create 385
36+
# endif
37+
#endif /* !__NR_memfd_create */
38+
39+
#if !defined(SYS_memfd_create) && defined(__NR_memfd_create)
40+
# define SYS_memfd_create __NR_memfd_create
41+
#endif
42+
#ifndef SYS_memfd_create
43+
# error "memfd_create(2) syscall not supported by this glibc version"
44+
#endif
45+
int memfd_create(const char *name, unsigned int flags)
46+
{
47+
return syscall(SYS_memfd_create, name, flags);
48+
}
49+
50+
/* This comes directly from <linux/fcntl.h>. */
51+
#ifndef F_LINUX_SPECIFIC_BASE
52+
# define F_LINUX_SPECIFIC_BASE 1024
53+
#endif
54+
#ifndef F_ADD_SEALS
55+
# define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
56+
# define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
57+
#endif
58+
#ifndef F_SEAL_SEAL
59+
# define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
60+
# define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
61+
# define F_SEAL_GROW 0x0004 /* prevent file from growing */
62+
# define F_SEAL_WRITE 0x0008 /* prevent writes */
63+
#endif
64+
65+
66+
#define OUR_MEMFD_COMMENT "runc_cloned:/proc/self/exe"
67+
#define OUR_MEMFD_SEALS \
68+
(F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
69+
70+
static void *must_realloc(void *ptr, size_t size)
71+
{
72+
void *old = ptr;
73+
do {
74+
ptr = realloc(old, size);
75+
} while(!ptr);
76+
return ptr;
77+
}
78+
79+
/*
80+
* Verify whether we are currently in a self-cloned program (namely, is
81+
* /proc/self/exe a memfd). F_GET_SEALS will only succeed for memfds (or rather
82+
* for shmem files), and we want to be sure it's actually sealed.
83+
*/
84+
static int is_self_cloned(void)
85+
{
86+
int fd, seals;
87+
88+
fd = open("/proc/self/exe", O_RDONLY|O_CLOEXEC);
89+
if (fd < 0)
90+
return -ENOTRECOVERABLE;
91+
92+
seals = fcntl(fd, F_GET_SEALS);
93+
close(fd);
94+
return seals == OUR_MEMFD_SEALS;
95+
}
96+
97+
/*
98+
* Basic wrapper around mmap(2) that gives you the file length so you can
99+
* safely treat it as an ordinary buffer. Only gives you read access.
100+
*/
101+
static char *read_file(char *path, size_t *length)
102+
{
103+
int fd;
104+
char buf[4096], *copy = NULL;
105+
106+
if (!length)
107+
return NULL;
108+
109+
fd = open(path, O_RDONLY | O_CLOEXEC);
110+
if (fd < 0)
111+
return NULL;
112+
113+
*length = 0;
114+
for (;;) {
115+
int n;
116+
117+
n = read(fd, buf, sizeof(buf));
118+
if (n < 0)
119+
goto error;
120+
if (!n)
121+
break;
122+
123+
copy = must_realloc(copy, (*length + n) * sizeof(*copy));
124+
memcpy(copy + *length, buf, n);
125+
*length += n;
126+
}
127+
close(fd);
128+
return copy;
129+
130+
error:
131+
close(fd);
132+
free(copy);
133+
return NULL;
134+
}
135+
136+
/*
137+
* A poor-man's version of "xargs -0". Basically parses a given block of
138+
* NUL-delimited data, within the given length and adds a pointer to each entry
139+
* to the array of pointers.
140+
*/
141+
static int parse_xargs(char *data, int data_length, char ***output)
142+
{
143+
int num = 0;
144+
char *cur = data;
145+
146+
if (!data || *output != NULL)
147+
return -1;
148+
149+
while (cur < data + data_length) {
150+
num++;
151+
*output = must_realloc(*output, (num + 1) * sizeof(**output));
152+
(*output)[num - 1] = cur;
153+
cur += strlen(cur) + 1;
154+
}
155+
(*output)[num] = NULL;
156+
return num;
157+
}
158+
159+
/*
160+
* "Parse" out argv and envp from /proc/self/cmdline and /proc/self/environ.
161+
* This is necessary because we are running in a context where we don't have a
162+
* main() that we can just get the arguments from.
163+
*/
164+
static int fetchve(char ***argv, char ***envp)
165+
{
166+
char *cmdline = NULL, *environ = NULL;
167+
size_t cmdline_size, environ_size;
168+
169+
cmdline = read_file("/proc/self/cmdline", &cmdline_size);
170+
if (!cmdline)
171+
goto error;
172+
environ = read_file("/proc/self/environ", &environ_size);
173+
if (!environ)
174+
goto error;
175+
176+
if (parse_xargs(cmdline, cmdline_size, argv) <= 0)
177+
goto error;
178+
if (parse_xargs(environ, environ_size, envp) <= 0)
179+
goto error;
180+
181+
return 0;
182+
183+
error:
184+
free(environ);
185+
free(cmdline);
186+
return -EINVAL;
187+
}
188+
189+
#define SENDFILE_MAX 0x7FFFF000 /* sendfile(2) is limited to 2GB. */
190+
static int clone_binary(void)
191+
{
192+
int binfd, memfd, err;
193+
ssize_t sent = 0;
194+
195+
memfd = memfd_create(OUR_MEMFD_COMMENT, MFD_CLOEXEC | MFD_ALLOW_SEALING);
196+
if (memfd < 0)
197+
return -ENOTRECOVERABLE;
198+
199+
binfd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
200+
if (binfd < 0)
201+
goto error;
202+
203+
sent = sendfile(memfd, binfd, NULL, SENDFILE_MAX);
204+
close(binfd);
205+
if (sent < 0)
206+
goto error;
207+
208+
err = fcntl(memfd, F_ADD_SEALS, OUR_MEMFD_SEALS);
209+
if (err < 0)
210+
goto error;
211+
212+
return memfd;
213+
214+
error:
215+
close(memfd);
216+
return -EIO;
217+
}
218+
219+
int ensure_cloned_binary(void)
220+
{
221+
int execfd;
222+
char **argv = NULL, **envp = NULL;
223+
224+
/* Check that we're not self-cloned, and if we are then bail. */
225+
int cloned = is_self_cloned();
226+
if (cloned > 0 || cloned == -ENOTRECOVERABLE)
227+
return cloned;
228+
229+
if (fetchve(&argv, &envp) < 0)
230+
return -EINVAL;
231+
232+
execfd = clone_binary();
233+
if (execfd < 0)
234+
return -EIO;
235+
236+
fexecve(execfd, argv, envp);
237+
return -ENOEXEC;
238+
}

libcontainer/nsenter/nsexec.c

+11
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,9 @@ void join_namespaces(char *nslist)
527527
free(namespaces);
528528
}
529529

530+
/* Defined in cloned_binary.c. */
531+
int ensure_cloned_binary(void);
532+
530533
void nsexec(void)
531534
{
532535
int pipenum;
@@ -542,6 +545,14 @@ void nsexec(void)
542545
if (pipenum == -1)
543546
return;
544547

548+
/*
549+
* We need to re-exec if we are not in a cloned binary. This is necessary
550+
* to ensure that containers won't be able to access the host binary
551+
* through /proc/self/exe. See CVE-2019-5736.
552+
*/
553+
if (ensure_cloned_binary() < 0)
554+
bail("could not ensure we are a cloned binary");
555+
545556
/* Parse all of the netlink configuration. */
546557
nl_parse(pipenum, &config);
547558

0 commit comments

Comments
 (0)