Builds failed due to conflict in file names of patch files as the patch
file in runc is provided as source. Rename the patch file to fix the
failure.
Change-Id: I8be82493d3348f288a1061214224b809ac47afc4
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/6741
Reviewed-by: Srinidhi Rao <srinidhir@vmware.com>
Reviewed-by: Anish Swaminathan <anishs@vmware.com>
Tested-by: Anish Swaminathan <anishs@vmware.com>
| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,337 +0,0 @@ |
| 1 |
-From 39eeb1c74f3ea41488b888bb038b619e88d3619f Mon Sep 17 00:00:00 2001 |
|
| 2 |
-From: Aleksa Sarai <asarai@suse.de> |
|
| 3 |
-Date: Wed, 9 Jan 2019 13:40:01 +1100 |
|
| 4 |
-Subject: [PATCH] nsenter: clone /proc/self/exe to avoid exposing host binary |
|
| 5 |
- to container |
|
| 6 |
- |
|
| 7 |
-There are quite a few circumstances where /proc/self/exe pointing to a |
|
| 8 |
-pretty important container binary is a _bad_ thing, so to avoid this we |
|
| 9 |
-have to make a copy (preferably doing self-clean-up and not being |
|
| 10 |
-writeable). |
|
| 11 |
- |
|
| 12 |
-We require memfd_create(2) -- though there is an O_TMPFILE fallback -- |
|
| 13 |
-but we can always extend this to use a scratch MNT_DETACH overlayfs or |
|
| 14 |
-tmpfs. The main downside to this approach is no page-cache sharing for |
|
| 15 |
-the runc binary (which overlayfs would give us) but this is far less |
|
| 16 |
-complicated. |
|
| 17 |
- |
|
| 18 |
-This is only done during nsenter so that it happens transparently to the |
|
| 19 |
-Go code, and any libcontainer users benefit from it. This also makes |
|
| 20 |
-ExtraFiles and --preserve-fds handling trivial (because we don't need to |
|
| 21 |
-worry about it). |
|
| 22 |
- |
|
| 23 |
-Fixes: CVE-2019-5736 |
|
| 24 |
-Co-developed-by: Christian Brauner <christian.brauner@ubuntu.com> |
|
| 25 |
-Signed-off-by: Aleksa Sarai <asarai@suse.de> |
|
| 26 |
- libcontainer/nsenter/cloned_binary.c | 268 +++++++++++++++++++++++++++++++++++ |
|
| 27 |
- libcontainer/nsenter/nsexec.c | 11 ++ |
|
| 28 |
- 2 files changed, 279 insertions(+) |
|
| 29 |
- create mode 100644 libcontainer/nsenter/cloned_binary.c |
|
| 30 |
- |
|
| 31 |
-diff --git a/libcontainer/nsenter/cloned_binary.c b/libcontainer/nsenter/cloned_binary.c |
|
| 32 |
-new file mode 100644 |
|
| 33 |
-index 0000000..c8a42c2 |
|
| 34 |
-+++ b/libcontainer/nsenter/cloned_binary.c |
|
| 35 |
-@@ -0,0 +1,268 @@ |
|
| 36 |
-+/* |
|
| 37 |
-+ * Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com> |
|
| 38 |
-+ * Copyright (C) 2019 SUSE LLC |
|
| 39 |
-+ * |
|
| 40 |
-+ * Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 41 |
-+ * you may not use this file except in compliance with the License. |
|
| 42 |
-+ * You may obtain a copy of the License at |
|
| 43 |
-+ * |
|
| 44 |
-+ * http://www.apache.org/licenses/LICENSE-2.0 |
|
| 45 |
-+ * |
|
| 46 |
-+ * Unless required by applicable law or agreed to in writing, software |
|
| 47 |
-+ * distributed under the License is distributed on an "AS IS" BASIS, |
|
| 48 |
-+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 49 |
-+ * See the License for the specific language governing permissions and |
|
| 50 |
-+ * limitations under the License. |
|
| 51 |
-+ */ |
|
| 52 |
-+ |
|
| 53 |
-+#define _GNU_SOURCE |
|
| 54 |
-+#include <unistd.h> |
|
| 55 |
-+#include <stdio.h> |
|
| 56 |
-+#include <stdlib.h> |
|
| 57 |
-+#include <stdbool.h> |
|
| 58 |
-+#include <string.h> |
|
| 59 |
-+#include <limits.h> |
|
| 60 |
-+#include <fcntl.h> |
|
| 61 |
-+#include <errno.h> |
|
| 62 |
-+ |
|
| 63 |
-+#include <sys/types.h> |
|
| 64 |
-+#include <sys/stat.h> |
|
| 65 |
-+#include <sys/vfs.h> |
|
| 66 |
-+#include <sys/mman.h> |
|
| 67 |
-+#include <sys/sendfile.h> |
|
| 68 |
-+#include <sys/syscall.h> |
|
| 69 |
-+ |
|
| 70 |
-+/* Use our own wrapper for memfd_create. */ |
|
| 71 |
-+#if !defined(SYS_memfd_create) && defined(__NR_memfd_create) |
|
| 72 |
-+# define SYS_memfd_create __NR_memfd_create |
|
| 73 |
-+#endif |
|
| 74 |
-+#ifdef SYS_memfd_create |
|
| 75 |
-+# define HAVE_MEMFD_CREATE |
|
| 76 |
-+/* memfd_create(2) flags -- copied from <linux/memfd.h>. */ |
|
| 77 |
-+# ifndef MFD_CLOEXEC |
|
| 78 |
-+# define MFD_CLOEXEC 0x0001U |
|
| 79 |
-+# define MFD_ALLOW_SEALING 0x0002U |
|
| 80 |
-+# endif |
|
| 81 |
-+int memfd_create(const char *name, unsigned int flags) |
|
| 82 |
-+{
|
|
| 83 |
-+ return syscall(SYS_memfd_create, name, flags); |
|
| 84 |
-+} |
|
| 85 |
-+#endif |
|
| 86 |
-+ |
|
| 87 |
-+/* This comes directly from <linux/fcntl.h>. */ |
|
| 88 |
-+#ifndef F_LINUX_SPECIFIC_BASE |
|
| 89 |
-+# define F_LINUX_SPECIFIC_BASE 1024 |
|
| 90 |
-+#endif |
|
| 91 |
-+#ifndef F_ADD_SEALS |
|
| 92 |
-+# define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) |
|
| 93 |
-+# define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) |
|
| 94 |
-+#endif |
|
| 95 |
-+#ifndef F_SEAL_SEAL |
|
| 96 |
-+# define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ |
|
| 97 |
-+# define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ |
|
| 98 |
-+# define F_SEAL_GROW 0x0004 /* prevent file from growing */ |
|
| 99 |
-+# define F_SEAL_WRITE 0x0008 /* prevent writes */ |
|
| 100 |
-+#endif |
|
| 101 |
-+ |
|
| 102 |
-+#define RUNC_SENDFILE_MAX 0x7FFFF000 /* sendfile(2) is limited to 2GB. */ |
|
| 103 |
-+#ifdef HAVE_MEMFD_CREATE |
|
| 104 |
-+# define RUNC_MEMFD_COMMENT "runc_cloned:/proc/self/exe" |
|
| 105 |
-+# define RUNC_MEMFD_SEALS \ |
|
| 106 |
-+ (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) |
|
| 107 |
-+#endif |
|
| 108 |
-+ |
|
| 109 |
-+static void *must_realloc(void *ptr, size_t size) |
|
| 110 |
-+{
|
|
| 111 |
-+ void *old = ptr; |
|
| 112 |
-+ do {
|
|
| 113 |
-+ ptr = realloc(old, size); |
|
| 114 |
-+ } while(!ptr); |
|
| 115 |
-+ return ptr; |
|
| 116 |
-+} |
|
| 117 |
-+ |
|
| 118 |
-+/* |
|
| 119 |
-+ * Verify whether we are currently in a self-cloned program (namely, is |
|
| 120 |
-+ * /proc/self/exe a memfd). F_GET_SEALS will only succeed for memfds (or rather |
|
| 121 |
-+ * for shmem files), and we want to be sure it's actually sealed. |
|
| 122 |
-+ */ |
|
| 123 |
-+static int is_self_cloned(void) |
|
| 124 |
-+{
|
|
| 125 |
-+ int fd, ret, is_cloned = 0; |
|
| 126 |
-+ |
|
| 127 |
-+ fd = open("/proc/self/exe", O_RDONLY|O_CLOEXEC);
|
|
| 128 |
-+ if (fd < 0) |
|
| 129 |
-+ return -ENOTRECOVERABLE; |
|
| 130 |
-+ |
|
| 131 |
-+#ifdef HAVE_MEMFD_CREATE |
|
| 132 |
-+ ret = fcntl(fd, F_GET_SEALS); |
|
| 133 |
-+ is_cloned = (ret == RUNC_MEMFD_SEALS); |
|
| 134 |
-+#else |
|
| 135 |
-+ struct stat statbuf = {0};
|
|
| 136 |
-+ ret = fstat(fd, &statbuf); |
|
| 137 |
-+ if (ret >= 0) |
|
| 138 |
-+ is_cloned = (statbuf.st_nlink == 0); |
|
| 139 |
-+#endif |
|
| 140 |
-+ close(fd); |
|
| 141 |
-+ return is_cloned; |
|
| 142 |
-+} |
|
| 143 |
-+ |
|
| 144 |
-+/* |
|
| 145 |
-+ * Basic wrapper around mmap(2) that gives you the file length so you can |
|
| 146 |
-+ * safely treat it as an ordinary buffer. Only gives you read access. |
|
| 147 |
-+ */ |
|
| 148 |
-+static char *read_file(char *path, size_t *length) |
|
| 149 |
-+{
|
|
| 150 |
-+ int fd; |
|
| 151 |
-+ char buf[4096], *copy = NULL; |
|
| 152 |
-+ |
|
| 153 |
-+ if (!length) |
|
| 154 |
-+ return NULL; |
|
| 155 |
-+ |
|
| 156 |
-+ fd = open(path, O_RDONLY | O_CLOEXEC); |
|
| 157 |
-+ if (fd < 0) |
|
| 158 |
-+ return NULL; |
|
| 159 |
-+ |
|
| 160 |
-+ *length = 0; |
|
| 161 |
-+ for (;;) {
|
|
| 162 |
-+ int n; |
|
| 163 |
-+ |
|
| 164 |
-+ n = read(fd, buf, sizeof(buf)); |
|
| 165 |
-+ if (n < 0) |
|
| 166 |
-+ goto error; |
|
| 167 |
-+ if (!n) |
|
| 168 |
-+ break; |
|
| 169 |
-+ |
|
| 170 |
-+ copy = must_realloc(copy, (*length + n) * sizeof(*copy)); |
|
| 171 |
-+ memcpy(copy + *length, buf, n); |
|
| 172 |
-+ *length += n; |
|
| 173 |
-+ } |
|
| 174 |
-+ close(fd); |
|
| 175 |
-+ return copy; |
|
| 176 |
-+ |
|
| 177 |
-+error: |
|
| 178 |
-+ close(fd); |
|
| 179 |
-+ free(copy); |
|
| 180 |
-+ return NULL; |
|
| 181 |
-+} |
|
| 182 |
-+ |
|
| 183 |
-+/* |
|
| 184 |
-+ * A poor-man's version of "xargs -0". Basically parses a given block of |
|
| 185 |
-+ * NUL-delimited data, within the given length and adds a pointer to each entry |
|
| 186 |
-+ * to the array of pointers. |
|
| 187 |
-+ */ |
|
| 188 |
-+static int parse_xargs(char *data, int data_length, char ***output) |
|
| 189 |
-+{
|
|
| 190 |
-+ int num = 0; |
|
| 191 |
-+ char *cur = data; |
|
| 192 |
-+ |
|
| 193 |
-+ if (!data || *output != NULL) |
|
| 194 |
-+ return -1; |
|
| 195 |
-+ |
|
| 196 |
-+ while (cur < data + data_length) {
|
|
| 197 |
-+ num++; |
|
| 198 |
-+ *output = must_realloc(*output, (num + 1) * sizeof(**output)); |
|
| 199 |
-+ (*output)[num - 1] = cur; |
|
| 200 |
-+ cur += strlen(cur) + 1; |
|
| 201 |
-+ } |
|
| 202 |
-+ (*output)[num] = NULL; |
|
| 203 |
-+ return num; |
|
| 204 |
-+} |
|
| 205 |
-+ |
|
| 206 |
-+/* |
|
| 207 |
-+ * "Parse" out argv and envp from /proc/self/cmdline and /proc/self/environ. |
|
| 208 |
-+ * This is necessary because we are running in a context where we don't have a |
|
| 209 |
-+ * main() that we can just get the arguments from. |
|
| 210 |
-+ */ |
|
| 211 |
-+static int fetchve(char ***argv, char ***envp) |
|
| 212 |
-+{
|
|
| 213 |
-+ char *cmdline = NULL, *environ = NULL; |
|
| 214 |
-+ size_t cmdline_size, environ_size; |
|
| 215 |
-+ |
|
| 216 |
-+ cmdline = read_file("/proc/self/cmdline", &cmdline_size);
|
|
| 217 |
-+ if (!cmdline) |
|
| 218 |
-+ goto error; |
|
| 219 |
-+ environ = read_file("/proc/self/environ", &environ_size);
|
|
| 220 |
-+ if (!environ) |
|
| 221 |
-+ goto error; |
|
| 222 |
-+ |
|
| 223 |
-+ if (parse_xargs(cmdline, cmdline_size, argv) <= 0) |
|
| 224 |
-+ goto error; |
|
| 225 |
-+ if (parse_xargs(environ, environ_size, envp) <= 0) |
|
| 226 |
-+ goto error; |
|
| 227 |
-+ |
|
| 228 |
-+ return 0; |
|
| 229 |
-+ |
|
| 230 |
-+error: |
|
| 231 |
-+ free(environ); |
|
| 232 |
-+ free(cmdline); |
|
| 233 |
-+ return -EINVAL; |
|
| 234 |
-+} |
|
| 235 |
-+ |
|
| 236 |
-+static int clone_binary(void) |
|
| 237 |
-+{
|
|
| 238 |
-+ int binfd, memfd; |
|
| 239 |
-+ ssize_t sent = 0; |
|
| 240 |
-+ |
|
| 241 |
-+#ifdef HAVE_MEMFD_CREATE |
|
| 242 |
-+ memfd = memfd_create(RUNC_MEMFD_COMMENT, MFD_CLOEXEC | MFD_ALLOW_SEALING); |
|
| 243 |
-+#else |
|
| 244 |
-+ memfd = open("/tmp", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0711);
|
|
| 245 |
-+#endif |
|
| 246 |
-+ if (memfd < 0) |
|
| 247 |
-+ return -ENOTRECOVERABLE; |
|
| 248 |
-+ |
|
| 249 |
-+ binfd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
|
|
| 250 |
-+ if (binfd < 0) |
|
| 251 |
-+ goto error; |
|
| 252 |
-+ |
|
| 253 |
-+ sent = sendfile(memfd, binfd, NULL, RUNC_SENDFILE_MAX); |
|
| 254 |
-+ close(binfd); |
|
| 255 |
-+ if (sent < 0) |
|
| 256 |
-+ goto error; |
|
| 257 |
-+ |
|
| 258 |
-+#ifdef HAVE_MEMFD_CREATE |
|
| 259 |
-+ int err = fcntl(memfd, F_ADD_SEALS, RUNC_MEMFD_SEALS); |
|
| 260 |
-+ if (err < 0) |
|
| 261 |
-+ goto error; |
|
| 262 |
-+#else |
|
| 263 |
-+ /* Need to re-open "memfd" as read-only to avoid execve(2) giving -EXTBUSY. */ |
|
| 264 |
-+ int newfd; |
|
| 265 |
-+ char *fdpath = NULL; |
|
| 266 |
-+ |
|
| 267 |
-+ if (asprintf(&fdpath, "/proc/self/fd/%d", memfd) < 0) |
|
| 268 |
-+ goto error; |
|
| 269 |
-+ newfd = open(fdpath, O_RDONLY | O_CLOEXEC); |
|
| 270 |
-+ free(fdpath); |
|
| 271 |
-+ if (newfd < 0) |
|
| 272 |
-+ goto error; |
|
| 273 |
-+ |
|
| 274 |
-+ close(memfd); |
|
| 275 |
-+ memfd = newfd; |
|
| 276 |
-+#endif |
|
| 277 |
-+ return memfd; |
|
| 278 |
-+ |
|
| 279 |
-+error: |
|
| 280 |
-+ close(memfd); |
|
| 281 |
-+ return -EIO; |
|
| 282 |
-+} |
|
| 283 |
-+ |
|
| 284 |
-+int ensure_cloned_binary(void) |
|
| 285 |
-+{
|
|
| 286 |
-+ int execfd; |
|
| 287 |
-+ char **argv = NULL, **envp = NULL; |
|
| 288 |
-+ |
|
| 289 |
-+ /* Check that we're not self-cloned, and if we are then bail. */ |
|
| 290 |
-+ int cloned = is_self_cloned(); |
|
| 291 |
-+ if (cloned > 0 || cloned == -ENOTRECOVERABLE) |
|
| 292 |
-+ return cloned; |
|
| 293 |
-+ |
|
| 294 |
-+ if (fetchve(&argv, &envp) < 0) |
|
| 295 |
-+ return -EINVAL; |
|
| 296 |
-+ |
|
| 297 |
-+ execfd = clone_binary(); |
|
| 298 |
-+ if (execfd < 0) |
|
| 299 |
-+ return -EIO; |
|
| 300 |
-+ |
|
| 301 |
-+ fexecve(execfd, argv, envp); |
|
| 302 |
-+ return -ENOEXEC; |
|
| 303 |
-+} |
|
| 304 |
-diff --git a/libcontainer/nsenter/nsexec.c b/libcontainer/nsenter/nsexec.c |
|
| 305 |
-index 197e6d0..43f4b94 100644 |
|
| 306 |
-+++ b/libcontainer/nsenter/nsexec.c |
|
| 307 |
-@@ -431,6 +431,9 @@ void join_namespaces(char *nslist) |
|
| 308 |
- free(namespaces); |
|
| 309 |
- } |
|
| 310 |
- |
|
| 311 |
-+/* Defined in cloned_binary.c. */ |
|
| 312 |
-+extern int ensure_cloned_binary(void); |
|
| 313 |
-+ |
|
| 314 |
- void nsexec(void) |
|
| 315 |
- {
|
|
| 316 |
- int pipenum; |
|
| 317 |
-@@ -446,6 +449,14 @@ void nsexec(void) |
|
| 318 |
- if (pipenum == -1) |
|
| 319 |
- return; |
|
| 320 |
- |
|
| 321 |
-+ /* |
|
| 322 |
-+ * We need to re-exec if we are not in a cloned binary. This is necessary |
|
| 323 |
-+ * to ensure that containers won't be able to access the host binary |
|
| 324 |
-+ * through /proc/self/exe. See CVE-2019-5736. |
|
| 325 |
-+ */ |
|
| 326 |
-+ if (ensure_cloned_binary() < 0) |
|
| 327 |
-+ bail("could not ensure we are a cloned binary");
|
|
| 328 |
-+ |
|
| 329 |
- /* Parse all of the netlink configuration. */ |
|
| 330 |
- nl_parse(pipenum, &config); |
|
| 331 |
- |
|
| 332 |
-2.7.4 |
|
| 333 |
- |
| 334 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,337 @@ |
| 0 |
+From 39eeb1c74f3ea41488b888bb038b619e88d3619f Mon Sep 17 00:00:00 2001 |
|
| 1 |
+From: Aleksa Sarai <asarai@suse.de> |
|
| 2 |
+Date: Wed, 9 Jan 2019 13:40:01 +1100 |
|
| 3 |
+Subject: [PATCH] nsenter: clone /proc/self/exe to avoid exposing host binary |
|
| 4 |
+ to container |
|
| 5 |
+ |
|
| 6 |
+There are quite a few circumstances where /proc/self/exe pointing to a |
|
| 7 |
+pretty important container binary is a _bad_ thing, so to avoid this we |
|
| 8 |
+have to make a copy (preferably doing self-clean-up and not being |
|
| 9 |
+writeable). |
|
| 10 |
+ |
|
| 11 |
+We require memfd_create(2) -- though there is an O_TMPFILE fallback -- |
|
| 12 |
+but we can always extend this to use a scratch MNT_DETACH overlayfs or |
|
| 13 |
+tmpfs. The main downside to this approach is no page-cache sharing for |
|
| 14 |
+the runc binary (which overlayfs would give us) but this is far less |
|
| 15 |
+complicated. |
|
| 16 |
+ |
|
| 17 |
+This is only done during nsenter so that it happens transparently to the |
|
| 18 |
+Go code, and any libcontainer users benefit from it. This also makes |
|
| 19 |
+ExtraFiles and --preserve-fds handling trivial (because we don't need to |
|
| 20 |
+worry about it). |
|
| 21 |
+ |
|
| 22 |
+Fixes: CVE-2019-5736 |
|
| 23 |
+Co-developed-by: Christian Brauner <christian.brauner@ubuntu.com> |
|
| 24 |
+Signed-off-by: Aleksa Sarai <asarai@suse.de> |
|
| 25 |
+--- |
|
| 26 |
+ libcontainer/nsenter/cloned_binary.c | 268 +++++++++++++++++++++++++++++++++++ |
|
| 27 |
+ libcontainer/nsenter/nsexec.c | 11 ++ |
|
| 28 |
+ 2 files changed, 279 insertions(+) |
|
| 29 |
+ create mode 100644 libcontainer/nsenter/cloned_binary.c |
|
| 30 |
+ |
|
| 31 |
+diff --git a/libcontainer/nsenter/cloned_binary.c b/libcontainer/nsenter/cloned_binary.c |
|
| 32 |
+new file mode 100644 |
|
| 33 |
+index 0000000..c8a42c2 |
|
| 34 |
+--- /dev/null |
|
| 35 |
+@@ -0,0 +1,268 @@ |
|
| 36 |
++/* |
|
| 37 |
++ * Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com> |
|
| 38 |
++ * Copyright (C) 2019 SUSE LLC |
|
| 39 |
++ * |
|
| 40 |
++ * Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 41 |
++ * you may not use this file except in compliance with the License. |
|
| 42 |
++ * You may obtain a copy of the License at |
|
| 43 |
++ * |
|
| 44 |
++ * http://www.apache.org/licenses/LICENSE-2.0 |
|
| 45 |
++ * |
|
| 46 |
++ * Unless required by applicable law or agreed to in writing, software |
|
| 47 |
++ * distributed under the License is distributed on an "AS IS" BASIS, |
|
| 48 |
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 49 |
++ * See the License for the specific language governing permissions and |
|
| 50 |
++ * limitations under the License. |
|
| 51 |
++ */ |
|
| 52 |
++ |
|
| 53 |
++#define _GNU_SOURCE |
|
| 54 |
++#include <unistd.h> |
|
| 55 |
++#include <stdio.h> |
|
| 56 |
++#include <stdlib.h> |
|
| 57 |
++#include <stdbool.h> |
|
| 58 |
++#include <string.h> |
|
| 59 |
++#include <limits.h> |
|
| 60 |
++#include <fcntl.h> |
|
| 61 |
++#include <errno.h> |
|
| 62 |
++ |
|
| 63 |
++#include <sys/types.h> |
|
| 64 |
++#include <sys/stat.h> |
|
| 65 |
++#include <sys/vfs.h> |
|
| 66 |
++#include <sys/mman.h> |
|
| 67 |
++#include <sys/sendfile.h> |
|
| 68 |
++#include <sys/syscall.h> |
|
| 69 |
++ |
|
| 70 |
++/* Use our own wrapper for memfd_create. */ |
|
| 71 |
++#if !defined(SYS_memfd_create) && defined(__NR_memfd_create) |
|
| 72 |
++# define SYS_memfd_create __NR_memfd_create |
|
| 73 |
++#endif |
|
| 74 |
++#ifdef SYS_memfd_create |
|
| 75 |
++# define HAVE_MEMFD_CREATE |
|
| 76 |
++/* memfd_create(2) flags -- copied from <linux/memfd.h>. */ |
|
| 77 |
++# ifndef MFD_CLOEXEC |
|
| 78 |
++# define MFD_CLOEXEC 0x0001U |
|
| 79 |
++# define MFD_ALLOW_SEALING 0x0002U |
|
| 80 |
++# endif |
|
| 81 |
++int memfd_create(const char *name, unsigned int flags) |
|
| 82 |
++{
|
|
| 83 |
++ return syscall(SYS_memfd_create, name, flags); |
|
| 84 |
++} |
|
| 85 |
++#endif |
|
| 86 |
++ |
|
| 87 |
++/* This comes directly from <linux/fcntl.h>. */ |
|
| 88 |
++#ifndef F_LINUX_SPECIFIC_BASE |
|
| 89 |
++# define F_LINUX_SPECIFIC_BASE 1024 |
|
| 90 |
++#endif |
|
| 91 |
++#ifndef F_ADD_SEALS |
|
| 92 |
++# define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) |
|
| 93 |
++# define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) |
|
| 94 |
++#endif |
|
| 95 |
++#ifndef F_SEAL_SEAL |
|
| 96 |
++# define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ |
|
| 97 |
++# define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ |
|
| 98 |
++# define F_SEAL_GROW 0x0004 /* prevent file from growing */ |
|
| 99 |
++# define F_SEAL_WRITE 0x0008 /* prevent writes */ |
|
| 100 |
++#endif |
|
| 101 |
++ |
|
| 102 |
++#define RUNC_SENDFILE_MAX 0x7FFFF000 /* sendfile(2) is limited to 2GB. */ |
|
| 103 |
++#ifdef HAVE_MEMFD_CREATE |
|
| 104 |
++# define RUNC_MEMFD_COMMENT "runc_cloned:/proc/self/exe" |
|
| 105 |
++# define RUNC_MEMFD_SEALS \ |
|
| 106 |
++ (F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) |
|
| 107 |
++#endif |
|
| 108 |
++ |
|
| 109 |
++static void *must_realloc(void *ptr, size_t size) |
|
| 110 |
++{
|
|
| 111 |
++ void *old = ptr; |
|
| 112 |
++ do {
|
|
| 113 |
++ ptr = realloc(old, size); |
|
| 114 |
++ } while(!ptr); |
|
| 115 |
++ return ptr; |
|
| 116 |
++} |
|
| 117 |
++ |
|
| 118 |
++/* |
|
| 119 |
++ * Verify whether we are currently in a self-cloned program (namely, is |
|
| 120 |
++ * /proc/self/exe a memfd). F_GET_SEALS will only succeed for memfds (or rather |
|
| 121 |
++ * for shmem files), and we want to be sure it's actually sealed. |
|
| 122 |
++ */ |
|
| 123 |
++static int is_self_cloned(void) |
|
| 124 |
++{
|
|
| 125 |
++ int fd, ret, is_cloned = 0; |
|
| 126 |
++ |
|
| 127 |
++ fd = open("/proc/self/exe", O_RDONLY|O_CLOEXEC);
|
|
| 128 |
++ if (fd < 0) |
|
| 129 |
++ return -ENOTRECOVERABLE; |
|
| 130 |
++ |
|
| 131 |
++#ifdef HAVE_MEMFD_CREATE |
|
| 132 |
++ ret = fcntl(fd, F_GET_SEALS); |
|
| 133 |
++ is_cloned = (ret == RUNC_MEMFD_SEALS); |
|
| 134 |
++#else |
|
| 135 |
++ struct stat statbuf = {0};
|
|
| 136 |
++ ret = fstat(fd, &statbuf); |
|
| 137 |
++ if (ret >= 0) |
|
| 138 |
++ is_cloned = (statbuf.st_nlink == 0); |
|
| 139 |
++#endif |
|
| 140 |
++ close(fd); |
|
| 141 |
++ return is_cloned; |
|
| 142 |
++} |
|
| 143 |
++ |
|
| 144 |
++/* |
|
| 145 |
++ * Basic wrapper around mmap(2) that gives you the file length so you can |
|
| 146 |
++ * safely treat it as an ordinary buffer. Only gives you read access. |
|
| 147 |
++ */ |
|
| 148 |
++static char *read_file(char *path, size_t *length) |
|
| 149 |
++{
|
|
| 150 |
++ int fd; |
|
| 151 |
++ char buf[4096], *copy = NULL; |
|
| 152 |
++ |
|
| 153 |
++ if (!length) |
|
| 154 |
++ return NULL; |
|
| 155 |
++ |
|
| 156 |
++ fd = open(path, O_RDONLY | O_CLOEXEC); |
|
| 157 |
++ if (fd < 0) |
|
| 158 |
++ return NULL; |
|
| 159 |
++ |
|
| 160 |
++ *length = 0; |
|
| 161 |
++ for (;;) {
|
|
| 162 |
++ int n; |
|
| 163 |
++ |
|
| 164 |
++ n = read(fd, buf, sizeof(buf)); |
|
| 165 |
++ if (n < 0) |
|
| 166 |
++ goto error; |
|
| 167 |
++ if (!n) |
|
| 168 |
++ break; |
|
| 169 |
++ |
|
| 170 |
++ copy = must_realloc(copy, (*length + n) * sizeof(*copy)); |
|
| 171 |
++ memcpy(copy + *length, buf, n); |
|
| 172 |
++ *length += n; |
|
| 173 |
++ } |
|
| 174 |
++ close(fd); |
|
| 175 |
++ return copy; |
|
| 176 |
++ |
|
| 177 |
++error: |
|
| 178 |
++ close(fd); |
|
| 179 |
++ free(copy); |
|
| 180 |
++ return NULL; |
|
| 181 |
++} |
|
| 182 |
++ |
|
| 183 |
++/* |
|
| 184 |
++ * A poor-man's version of "xargs -0". Basically parses a given block of |
|
| 185 |
++ * NUL-delimited data, within the given length and adds a pointer to each entry |
|
| 186 |
++ * to the array of pointers. |
|
| 187 |
++ */ |
|
| 188 |
++static int parse_xargs(char *data, int data_length, char ***output) |
|
| 189 |
++{
|
|
| 190 |
++ int num = 0; |
|
| 191 |
++ char *cur = data; |
|
| 192 |
++ |
|
| 193 |
++ if (!data || *output != NULL) |
|
| 194 |
++ return -1; |
|
| 195 |
++ |
|
| 196 |
++ while (cur < data + data_length) {
|
|
| 197 |
++ num++; |
|
| 198 |
++ *output = must_realloc(*output, (num + 1) * sizeof(**output)); |
|
| 199 |
++ (*output)[num - 1] = cur; |
|
| 200 |
++ cur += strlen(cur) + 1; |
|
| 201 |
++ } |
|
| 202 |
++ (*output)[num] = NULL; |
|
| 203 |
++ return num; |
|
| 204 |
++} |
|
| 205 |
++ |
|
| 206 |
++/* |
|
| 207 |
++ * "Parse" out argv and envp from /proc/self/cmdline and /proc/self/environ. |
|
| 208 |
++ * This is necessary because we are running in a context where we don't have a |
|
| 209 |
++ * main() that we can just get the arguments from. |
|
| 210 |
++ */ |
|
| 211 |
++static int fetchve(char ***argv, char ***envp) |
|
| 212 |
++{
|
|
| 213 |
++ char *cmdline = NULL, *environ = NULL; |
|
| 214 |
++ size_t cmdline_size, environ_size; |
|
| 215 |
++ |
|
| 216 |
++ cmdline = read_file("/proc/self/cmdline", &cmdline_size);
|
|
| 217 |
++ if (!cmdline) |
|
| 218 |
++ goto error; |
|
| 219 |
++ environ = read_file("/proc/self/environ", &environ_size);
|
|
| 220 |
++ if (!environ) |
|
| 221 |
++ goto error; |
|
| 222 |
++ |
|
| 223 |
++ if (parse_xargs(cmdline, cmdline_size, argv) <= 0) |
|
| 224 |
++ goto error; |
|
| 225 |
++ if (parse_xargs(environ, environ_size, envp) <= 0) |
|
| 226 |
++ goto error; |
|
| 227 |
++ |
|
| 228 |
++ return 0; |
|
| 229 |
++ |
|
| 230 |
++error: |
|
| 231 |
++ free(environ); |
|
| 232 |
++ free(cmdline); |
|
| 233 |
++ return -EINVAL; |
|
| 234 |
++} |
|
| 235 |
++ |
|
| 236 |
++static int clone_binary(void) |
|
| 237 |
++{
|
|
| 238 |
++ int binfd, memfd; |
|
| 239 |
++ ssize_t sent = 0; |
|
| 240 |
++ |
|
| 241 |
++#ifdef HAVE_MEMFD_CREATE |
|
| 242 |
++ memfd = memfd_create(RUNC_MEMFD_COMMENT, MFD_CLOEXEC | MFD_ALLOW_SEALING); |
|
| 243 |
++#else |
|
| 244 |
++ memfd = open("/tmp", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0711);
|
|
| 245 |
++#endif |
|
| 246 |
++ if (memfd < 0) |
|
| 247 |
++ return -ENOTRECOVERABLE; |
|
| 248 |
++ |
|
| 249 |
++ binfd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
|
|
| 250 |
++ if (binfd < 0) |
|
| 251 |
++ goto error; |
|
| 252 |
++ |
|
| 253 |
++ sent = sendfile(memfd, binfd, NULL, RUNC_SENDFILE_MAX); |
|
| 254 |
++ close(binfd); |
|
| 255 |
++ if (sent < 0) |
|
| 256 |
++ goto error; |
|
| 257 |
++ |
|
| 258 |
++#ifdef HAVE_MEMFD_CREATE |
|
| 259 |
++ int err = fcntl(memfd, F_ADD_SEALS, RUNC_MEMFD_SEALS); |
|
| 260 |
++ if (err < 0) |
|
| 261 |
++ goto error; |
|
| 262 |
++#else |
|
| 263 |
++ /* Need to re-open "memfd" as read-only to avoid execve(2) giving -EXTBUSY. */ |
|
| 264 |
++ int newfd; |
|
| 265 |
++ char *fdpath = NULL; |
|
| 266 |
++ |
|
| 267 |
++ if (asprintf(&fdpath, "/proc/self/fd/%d", memfd) < 0) |
|
| 268 |
++ goto error; |
|
| 269 |
++ newfd = open(fdpath, O_RDONLY | O_CLOEXEC); |
|
| 270 |
++ free(fdpath); |
|
| 271 |
++ if (newfd < 0) |
|
| 272 |
++ goto error; |
|
| 273 |
++ |
|
| 274 |
++ close(memfd); |
|
| 275 |
++ memfd = newfd; |
|
| 276 |
++#endif |
|
| 277 |
++ return memfd; |
|
| 278 |
++ |
|
| 279 |
++error: |
|
| 280 |
++ close(memfd); |
|
| 281 |
++ return -EIO; |
|
| 282 |
++} |
|
| 283 |
++ |
|
| 284 |
++int ensure_cloned_binary(void) |
|
| 285 |
++{
|
|
| 286 |
++ int execfd; |
|
| 287 |
++ char **argv = NULL, **envp = NULL; |
|
| 288 |
++ |
|
| 289 |
++ /* Check that we're not self-cloned, and if we are then bail. */ |
|
| 290 |
++ int cloned = is_self_cloned(); |
|
| 291 |
++ if (cloned > 0 || cloned == -ENOTRECOVERABLE) |
|
| 292 |
++ return cloned; |
|
| 293 |
++ |
|
| 294 |
++ if (fetchve(&argv, &envp) < 0) |
|
| 295 |
++ return -EINVAL; |
|
| 296 |
++ |
|
| 297 |
++ execfd = clone_binary(); |
|
| 298 |
++ if (execfd < 0) |
|
| 299 |
++ return -EIO; |
|
| 300 |
++ |
|
| 301 |
++ fexecve(execfd, argv, envp); |
|
| 302 |
++ return -ENOEXEC; |
|
| 303 |
++} |
|
| 304 |
+diff --git a/libcontainer/nsenter/nsexec.c b/libcontainer/nsenter/nsexec.c |
|
| 305 |
+index 197e6d0..43f4b94 100644 |
|
| 306 |
+--- a/libcontainer/nsenter/nsexec.c |
|
| 307 |
+@@ -431,6 +431,9 @@ void join_namespaces(char *nslist) |
|
| 308 |
+ free(namespaces); |
|
| 309 |
+ } |
|
| 310 |
+ |
|
| 311 |
++/* Defined in cloned_binary.c. */ |
|
| 312 |
++extern int ensure_cloned_binary(void); |
|
| 313 |
++ |
|
| 314 |
+ void nsexec(void) |
|
| 315 |
+ {
|
|
| 316 |
+ int pipenum; |
|
| 317 |
+@@ -446,6 +449,14 @@ void nsexec(void) |
|
| 318 |
+ if (pipenum == -1) |
|
| 319 |
+ return; |
|
| 320 |
+ |
|
| 321 |
++ /* |
|
| 322 |
++ * We need to re-exec if we are not in a cloned binary. This is necessary |
|
| 323 |
++ * to ensure that containers won't be able to access the host binary |
|
| 324 |
++ * through /proc/self/exe. See CVE-2019-5736. |
|
| 325 |
++ */ |
|
| 326 |
++ if (ensure_cloned_binary() < 0) |
|
| 327 |
++ bail("could not ensure we are a cloned binary");
|
|
| 328 |
++ |
|
| 329 |
+ /* Parse all of the netlink configuration. */ |
|
| 330 |
+ nl_parse(pipenum, &config); |
|
| 331 |
+ |
|
| 332 |
+-- |
|
| 333 |
+2.7.4 |
|
| 334 |
+ |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
Summary: CLI tool for spawning and running containers per OCI spec. |
| 2 | 2 |
Name: runc |
| 3 | 3 |
Version: 1.0.0.rc4 |
| 4 |
-Release: 2%{?dist}
|
|
| 4 |
+Release: 3%{?dist}
|
|
| 5 | 5 |
License: ASL 2.0 |
| 6 | 6 |
URL: https://runc.io/ |
| 7 | 7 |
Source0: https://github.com/opencontainers/runc/archive/%{name}-1.0.0-rc4.tar.gz
|
| ... | ... |
@@ -16,7 +16,7 @@ Source4: https://github.com/golang/sys/archive/golang-sys-07c182904dbd53199946ba |
| 16 | 16 |
%define sha1 golang-sys=940b297797b1defc11d67820a92becefeaa88f59 |
| 17 | 17 |
Source5: https://github.com/golang/crypto/archive/golang-crypto-eb71ad9bd329b5ac0fd0148dd99bd62e8be8e035.zip |
| 18 | 18 |
%define sha1 golang-crypto=775ab62e664ee2c89f624d5be6c55775360653ee |
| 19 |
-Source6: CVE-2019-5736.patch |
|
| 19 |
+Source6: runc-CVE-2019-5736.patch |
|
| 20 | 20 |
Group: Virtualization/Libraries |
| 21 | 21 |
Vendor: VMware, Inc. |
| 22 | 22 |
Distribution: Photon |
| ... | ... |
@@ -78,6 +78,8 @@ make install BINDIR=%{buildroot}%{_sbindir}
|
| 78 | 78 |
%{_sbindir}/runc
|
| 79 | 79 |
|
| 80 | 80 |
%changelog |
| 81 |
+* Fri Feb 15 2019 Keerthana K <keerthanak@vmware.com> 1.0.0.rc4-3 |
|
| 82 |
+- Rename CVE-2019-5736 patch file. |
|
| 81 | 83 |
* Mon Feb 11 2019 Bo Gan <ganb@vmware.com> 1.0.0.rc4-2 |
| 82 | 84 |
- Fix CVE-2019-5736 |
| 83 | 85 |
* Tue Aug 22 2017 Dheeraj Shetty <dheerajs@vmware.com> 1.0.0.rc4-1 |