Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(668)

Side by Side Diff: sandbox/linux/suid/sandbox.c

Issue 9295005: Calling clone(CLONE_NEWPID) results in the new pid namespace getting a new "init" process. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Address AGL's comments Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sandbox/linux/suid/init_process.c ('k') | sandbox/sandbox.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox 5 // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
6 6
7 #define _GNU_SOURCE 7 #define _GNU_SOURCE
8 #include <asm/unistd.h> 8 #include <asm/unistd.h>
9 #include <errno.h> 9 #include <errno.h>
10 #include <fcntl.h> 10 #include <fcntl.h>
11 #include <limits.h> 11 #include <limits.h>
12 #include <sched.h> 12 #include <sched.h>
13 #include <signal.h> 13 #include <signal.h>
14 #include <stdarg.h> 14 #include <stdarg.h>
15 #include <stdbool.h> 15 #include <stdbool.h>
16 #include <stdint.h> 16 #include <stdint.h>
17 #include <stdio.h> 17 #include <stdio.h>
18 #include <stdlib.h> 18 #include <stdlib.h>
19 #include <string.h> 19 #include <string.h>
20 #include <sys/prctl.h> 20 #include <sys/prctl.h>
21 #include <sys/resource.h> 21 #include <sys/resource.h>
22 #include <sys/socket.h> 22 #include <sys/socket.h>
23 #include <sys/stat.h> 23 #include <sys/stat.h>
24 #include <sys/time.h> 24 #include <sys/time.h>
25 #include <sys/types.h> 25 #include <sys/types.h>
26 #include <sys/vfs.h> 26 #include <sys/vfs.h>
27 #include <unistd.h> 27 #include <unistd.h>
28 28
29 #include "init_process.h"
29 #include "linux_util.h" 30 #include "linux_util.h"
30 #include "process_util.h" 31 #include "process_util.h"
31 #include "suid_unsafe_environment_variables.h" 32 #include "suid_unsafe_environment_variables.h"
32 33
33 #if !defined(CLONE_NEWPID) 34 #if !defined(CLONE_NEWPID)
34 #define CLONE_NEWPID 0x20000000 35 #define CLONE_NEWPID 0x20000000
35 #endif 36 #endif
36 #if !defined(CLONE_NEWNET) 37 #if !defined(CLONE_NEWNET)
37 #define CLONE_NEWNET 0x40000000 38 #define CLONE_NEWNET 0x40000000
38 #endif 39 #endif
(...skipping 10 matching lines...) Expand all
49 static void FatalError(const char *msg, ...) 50 static void FatalError(const char *msg, ...)
50 __attribute__((noreturn, format(printf, 1, 2))); 51 __attribute__((noreturn, format(printf, 1, 2)));
51 52
52 static void FatalError(const char *msg, ...) { 53 static void FatalError(const char *msg, ...) {
53 va_list ap; 54 va_list ap;
54 va_start(ap, msg); 55 va_start(ap, msg);
55 56
56 vfprintf(stderr, msg, ap); 57 vfprintf(stderr, msg, ap);
57 fprintf(stderr, ": %s\n", strerror(errno)); 58 fprintf(stderr, ": %s\n", strerror(errno));
58 fflush(stderr); 59 fflush(stderr);
59 exit(1); 60 _exit(1);
60 } 61 }
61 62
62 // We will chroot() to the helper's /proc/self directory. Anything there will 63 // We will chroot() to the helper's /proc/self directory. Anything there will
63 // not exist anymore if we make sure to wait() for the helper. 64 // not exist anymore if we make sure to wait() for the helper.
64 // 65 //
65 // /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty 66 // /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty
66 // even if the helper survives as a zombie. 67 // even if the helper survives as a zombie.
67 // 68 //
68 // There is very little reason to use fdinfo/ instead of fd/ but we are 69 // There is very little reason to use fdinfo/ instead of fd/ but we are
69 // paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/ 70 // paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/
70 #define SAFE_DIR "/proc/self/fdinfo" 71 #define SAFE_DIR "/proc/self/fdinfo"
71 #define SAFE_DIR2 "/proc/self/fd" 72 #define SAFE_DIR2 "/proc/self/fd"
72 73
73 static bool SpawnChrootHelper() { 74 static bool DropRoot() {
75 if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
76 perror("prctl(PR_SET_DUMPABLE)");
77 return false;
78 }
79
80 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
81 perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
82 return false;
83 }
84
85 gid_t rgid, egid, sgid;
86 if (getresgid(&rgid, &egid, &sgid)) {
87 perror("getresgid");
88 return false;
89 }
90
91 if (setresgid(rgid, rgid, rgid)) {
92 perror("setresgid");
93 return false;
94 }
95
96 uid_t ruid, euid, suid;
97 if (getresuid(&ruid, &euid, &suid)) {
98 perror("getresuid");
99 return false;
100 }
101
102 if (setresuid(ruid, ruid, ruid)) {
103 perror("setresuid");
104 return false;
105 }
106
107 return true;
108 }
109
110 static int SpawnChrootHelper() {
74 int sv[2]; 111 int sv[2];
75 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { 112 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
76 perror("socketpair"); 113 perror("socketpair");
77 return false; 114 return -1;
78 } 115 }
79 116
80 char *safedir = NULL; 117 char *safedir = NULL;
81 struct stat sdir_stat; 118 struct stat sdir_stat;
82 if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) 119 if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
83 safedir = SAFE_DIR; 120 safedir = SAFE_DIR;
84 else 121 else
85 if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) 122 if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
86 safedir = SAFE_DIR2; 123 safedir = SAFE_DIR2;
87 else { 124 else {
88 fprintf(stderr, "Could not find %s\n", SAFE_DIR2); 125 fprintf(stderr, "Could not find %s\n", SAFE_DIR2);
89 return false; 126 return -1;
90 } 127 }
91 128
92 const pid_t pid = syscall( 129 const pid_t pid = syscall(
93 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); 130 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
94 131
95 if (pid == -1) { 132 if (pid == -1) {
96 perror("clone"); 133 perror("clone");
97 close(sv[0]); 134 close(sv[0]);
98 close(sv[1]); 135 close(sv[1]);
99 return false; 136 return -1;
100 } 137 }
101 138
102 if (pid == 0) { 139 if (pid == 0) {
103 // We share our files structure with an untrusted process. As a security in 140 // We share our files structure with an untrusted process. As a security in
104 // depth measure, we make sure that we can't open anything by mistake. 141 // depth measure, we make sure that we can't open anything by mistake.
105 // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT 142 // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT
106 143
107 const struct rlimit nofile = {0, 0}; 144 const struct rlimit nofile = {0, 0};
108 if (setrlimit(RLIMIT_NOFILE, &nofile)) 145 if (setrlimit(RLIMIT_NOFILE, &nofile))
109 FatalError("Setting RLIMIT_NOFILE"); 146 FatalError("Setting RLIMIT_NOFILE");
110 147
111 if (close(sv[1])) 148 if (close(sv[1]))
112 FatalError("close"); 149 FatalError("close");
113 150
114 // wait for message 151 // wait for message
115 char msg; 152 char msg;
116 ssize_t bytes; 153 ssize_t bytes;
117 do { 154 do {
118 bytes = read(sv[0], &msg, 1); 155 bytes = read(sv[0], &msg, 1);
119 } while (bytes == -1 && errno == EINTR); 156 } while (bytes == -1 && errno == EINTR);
120 157
121 if (bytes == 0) 158 if (bytes == 0)
122 _exit(0); 159 _exit(0);
123 if (bytes != 1) 160 if (bytes != 1)
124 FatalError("read"); 161 FatalError("read");
125 162
126 // do chrooting 163 // do chrooting
164 errno = 0;
127 if (msg != kMsgChrootMe) 165 if (msg != kMsgChrootMe)
128 FatalError("Unknown message from sandboxed process"); 166 FatalError("Unknown message from sandboxed process");
129 167
130 // sanity check 168 // sanity check
131 if (chdir(safedir)) 169 if (chdir(safedir))
132 FatalError("Cannot chdir into /proc/ directory"); 170 FatalError("Cannot chdir into /proc/ directory");
133 171
134 if (chroot(safedir)) 172 if (chroot(safedir))
135 FatalError("Cannot chroot into /proc/ directory"); 173 FatalError("Cannot chroot into /proc/ directory");
136 174
(...skipping 12 matching lines...) Expand all
149 // We now become a zombie. /proc/self/fd(info) is now an empty dir and we 187 // We now become a zombie. /proc/self/fd(info) is now an empty dir and we
150 // are chrooted there. 188 // are chrooted there.
151 // Our (unprivileged) parent should not even be able to open "." or "/" 189 // Our (unprivileged) parent should not even be able to open "." or "/"
152 // since they would need to pass the ptrace() check. If our parent wait() 190 // since they would need to pass the ptrace() check. If our parent wait()
153 // for us, our root directory will completely disappear. 191 // for us, our root directory will completely disappear.
154 } 192 }
155 193
156 if (close(sv[0])) { 194 if (close(sv[0])) {
157 close(sv[1]); 195 close(sv[1]);
158 perror("close"); 196 perror("close");
159 return false; 197 return -1;
160 } 198 }
161 199
162 // In the parent process, we install an environment variable containing the 200 // In the parent process, we install an environment variable containing the
163 // number of the file descriptor. 201 // number of the file descriptor.
164 char desc_str[64]; 202 char desc_str[64];
165 int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]); 203 int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]);
166 if (printed < 0 || printed >= (int)sizeof(desc_str)) { 204 if (printed < 0 || printed >= (int)sizeof(desc_str)) {
167 fprintf(stderr, "Failed to snprintf\n"); 205 fprintf(stderr, "Failed to snprintf\n");
168 return false; 206 close(sv[1]);
207 return -1;
169 } 208 }
170 209
171 if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) { 210 if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
172 perror("setenv"); 211 perror("setenv");
173 close(sv[1]); 212 close(sv[1]);
174 return false; 213 return -1;
175 } 214 }
176 215
177 // We also install an environment variable containing the pid of the child 216 // We also install an environment variable containing the pid of the child
178 char helper_pid_str[64]; 217 char helper_pid_str[64];
179 printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid); 218 printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid);
180 if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) { 219 if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) {
181 fprintf(stderr, "Failed to snprintf\n"); 220 fprintf(stderr, "Failed to snprintf\n");
182 return false; 221 close(sv[1]);
222 return -1;
183 } 223 }
184 224
185 if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) { 225 if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) {
186 perror("setenv"); 226 perror("setenv");
187 close(sv[1]); 227 close(sv[1]);
228 return -1;
229 }
230
231 return sv[1];
232 }
233
234 static bool JailMe() {
235 int fd = SpawnChrootHelper();
236 if (fd < 0) {
188 return false; 237 return false;
189 } 238 }
190 239 if (!DropRoot()) {
240 close(fd);
241 return false;
242 }
243 ssize_t bytes;
244 char ch = kMsgChrootMe;
245 do {
246 errno = 0;
247 bytes = write(fd, &ch, 1);
248 } while (bytes == -1 && errno == EINTR);
249 if (bytes != 1) {
250 perror("write");
251 close(fd);
252 return false;
253 }
254 do {
255 errno = 0;
256 bytes = read(fd, &ch, 1);
257 } while (bytes == -1 && errno == EINTR);
258 close(fd);
259 if (bytes != 1) {
260 perror("read");
261 return false;
262 }
263 if (ch != kMsgChrootSuccessful) {
264 return false;
265 }
191 return true; 266 return true;
192 } 267 }
193 268
194 static bool MoveToNewNamespaces() { 269 static bool MoveToNewNamespaces() {
195 // These are the sets of flags which we'll try, in order. 270 // These are the sets of flags which we'll try, in order.
196 const int kCloneExtraFlags[] = { 271 const int kCloneExtraFlags[] = {
197 CLONE_NEWPID | CLONE_NEWNET, 272 CLONE_NEWPID | CLONE_NEWNET,
198 CLONE_NEWPID, 273 CLONE_NEWPID,
199 }; 274 };
200 275
201 for (size_t i = 0; 276 for (size_t i = 0;
202 i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]); 277 i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
203 i++) { 278 i++) {
204 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0); 279 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
205 280
206 if (pid > 0) 281 if (pid > 0)
207 _exit(0); 282 _exit(0);
208 283
209 if (pid == 0) { 284 if (pid == 0) {
285 if (syscall(__NR_getpid) == 1) {
286 int fds[2];
287 char ch = 0;
288 if (pipe(fds)) {
289 perror("Failed to create pipe");
290 _exit(1);
291 }
292 pid = fork();
293 if (pid > 0) {
294 // The very first process in the new namespace takes on the
295 // role of the traditional "init" process. It must reap exit
296 // codes of daemon processes until the namespace is completely
297 // empty.
298 // We have to be careful that this "init" process doesn't
299 // provide a new attack surface. So, we also move it into
300 // a separate chroot and we drop all privileges. It does
301 // still need to access "/proc" and "/dev/null", though. So,
302 // we have to provide it with a file handles to these resources.
303 // These file handle are not accessible by any other processes in
304 // the sandbox and thus safe.
305 close(fds[0]);
306 int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
307 int null_fd = open("/dev/null", O_RDWR);
308 if (!JailMe()) {
309 FatalError("Could not remove privileges from "
310 "new \"init\" process");
311 }
312 SystemInitProcess(fds[1], pid, proc_fd, null_fd);
313 } else if (pid != 0) {
314 perror("Failed to fork");
315 _exit(1);
316 }
317 // Wait for the "init" process to complete initialization.
318 close(fds[1]);
319 errno = 0;
320 while (read(fds[0], &ch, 1) < 0 && errno == EINTR) {
321 }
322 close(fds[0]);
323 if (ch != ' ') {
324 // We'll likely never get here. If the "init" process fails, it's
325 // death typically takes everyone of its children with it.
326 FatalError("Failed to set up new \"init\" process inside sandbox");
327 }
328 }
329
210 if (kCloneExtraFlags[i] & CLONE_NEWPID) { 330 if (kCloneExtraFlags[i] & CLONE_NEWPID) {
211 setenv("SBX_PID_NS", "", 1 /* overwrite */); 331 setenv("SBX_PID_NS", "", 1 /* overwrite */);
212 } else { 332 } else {
213 unsetenv("SBX_PID_NS"); 333 unsetenv("SBX_PID_NS");
214 } 334 }
215 335
216 if (kCloneExtraFlags[i] & CLONE_NEWNET) { 336 if (kCloneExtraFlags[i] & CLONE_NEWNET) {
217 setenv("SBX_NET_NS", "", 1 /* overwrite */); 337 setenv("SBX_NET_NS", "", 1 /* overwrite */);
218 } else { 338 } else {
219 unsetenv("SBX_NET_NS"); 339 unsetenv("SBX_NET_NS");
220 } 340 }
221 341
222 break; 342 break;
223 } 343 }
224 344
225 if (errno != EINVAL) { 345 if (errno != EINVAL) {
226 perror("Failed to move to new PID namespace"); 346 perror("Failed to move to new PID namespace");
227 return false; 347 return false;
228 } 348 }
229 } 349 }
230 350
231 // If the system doesn't support NEWPID then we carry on anyway. 351 // If the system doesn't support NEWPID then we carry on anyway.
232 return true; 352 return true;
233 } 353 }
234 354
235 static bool DropRoot() {
236 if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
237 perror("prctl(PR_SET_DUMPABLE)");
238 return false;
239 }
240
241 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
242 perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
243 return false;
244 }
245
246 gid_t rgid, egid, sgid;
247 if (getresgid(&rgid, &egid, &sgid)) {
248 perror("getresgid");
249 return false;
250 }
251
252 if (setresgid(rgid, rgid, rgid)) {
253 perror("setresgid");
254 return false;
255 }
256
257 uid_t ruid, euid, suid;
258 if (getresuid(&ruid, &euid, &suid)) {
259 perror("getresuid");
260 return false;
261 }
262
263 if (setresuid(ruid, ruid, ruid)) {
264 perror("setresuid");
265 return false;
266 }
267
268 return true;
269 }
270
271 static bool SetupChildEnvironment() { 355 static bool SetupChildEnvironment() {
272 unsigned i; 356 unsigned i;
273 357
274 // ld.so may have cleared several environment variables because we are SUID. 358 // ld.so may have cleared several environment variables because we are SUID.
275 // However, the child process might need them so zygote_host_linux.cc saves a 359 // However, the child process might need them so zygote_host_linux.cc saves a
276 // copy in SANDBOX_$x. This is safe because we have dropped root by this 360 // copy in SANDBOX_$x. This is safe because we have dropped root by this
277 // point, so we can only exec a binary with the permissions of the user who 361 // point, so we can only exec a binary with the permissions of the user who
278 // ran us in the first place. 362 // ran us in the first place.
279 363
280 for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) { 364 for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 return 1; 420 return 1;
337 pid_t pid = pid_ul; 421 pid_t pid = pid_ul;
338 score = strtol(argv[3], &endptr, 10); 422 score = strtol(argv[3], &endptr, 10);
339 if (score == LONG_MAX || score == LONG_MIN || *endptr) 423 if (score == LONG_MAX || score == LONG_MIN || *endptr)
340 return 1; 424 return 1;
341 return AdjustOOMScore(pid, score); 425 return AdjustOOMScore(pid, score);
342 } 426 }
343 427
344 if (!MoveToNewNamespaces()) 428 if (!MoveToNewNamespaces())
345 return 1; 429 return 1;
346 if (!SpawnChrootHelper()) 430 if (SpawnChrootHelper() < 0)
347 return 1; 431 return 1;
348 if (!DropRoot()) 432 if (!DropRoot())
349 return 1; 433 return 1;
350 if (!SetupChildEnvironment()) 434 if (!SetupChildEnvironment())
351 return 1; 435 return 1;
352 436
353 execv(argv[1], &argv[1]); 437 execv(argv[1], &argv[1]);
354 FatalError("execv failed"); 438 FatalError("execv failed");
355 439
356 return 1; 440 return 1;
357 } 441 }
OLDNEW
« no previous file with comments | « sandbox/linux/suid/init_process.c ('k') | sandbox/sandbox.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698