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

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

Issue 10389214: Revert setuid sandbox as a "init process" changes (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 7 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) 2012 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 #include "sandbox.h" 7 #include "sandbox.h"
8 8
9 #define _GNU_SOURCE 9 #define _GNU_SOURCE
10 #include <asm/unistd.h> 10 #include <asm/unistd.h>
11 #include <errno.h> 11 #include <errno.h>
12 #include <fcntl.h> 12 #include <fcntl.h>
13 #include <limits.h> 13 #include <limits.h>
14 #include <sched.h> 14 #include <sched.h>
15 #include <signal.h> 15 #include <signal.h>
16 #include <stdarg.h> 16 #include <stdarg.h>
17 #include <stdbool.h> 17 #include <stdbool.h>
18 #include <stdint.h> 18 #include <stdint.h>
19 #include <stdio.h> 19 #include <stdio.h>
20 #include <stdlib.h> 20 #include <stdlib.h>
21 #include <string.h> 21 #include <string.h>
22 #include <sys/prctl.h> 22 #include <sys/prctl.h>
23 #include <sys/resource.h> 23 #include <sys/resource.h>
24 #include <sys/socket.h> 24 #include <sys/socket.h>
25 #include <sys/stat.h> 25 #include <sys/stat.h>
26 #include <sys/time.h> 26 #include <sys/time.h>
27 #include <sys/types.h> 27 #include <sys/types.h>
28 #include <sys/vfs.h> 28 #include <sys/vfs.h>
29 #include <unistd.h> 29 #include <unistd.h>
30 30
31 #include "init_process.h"
32 #include "linux_util.h" 31 #include "linux_util.h"
33 #include "process_util.h" 32 #include "process_util.h"
34 #include "suid_unsafe_environment_variables.h" 33 #include "suid_unsafe_environment_variables.h"
35 34
36 #if !defined(CLONE_NEWPID) 35 #if !defined(CLONE_NEWPID)
37 #define CLONE_NEWPID 0x20000000 36 #define CLONE_NEWPID 0x20000000
38 #endif 37 #endif
39 #if !defined(CLONE_NEWNET) 38 #if !defined(CLONE_NEWNET)
40 #define CLONE_NEWNET 0x40000000 39 #define CLONE_NEWNET 0x40000000
41 #endif 40 #endif
(...skipping 23 matching lines...) Expand all
65 // 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.
66 // 65 //
67 // /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
68 // even if the helper survives as a zombie. 67 // even if the helper survives as a zombie.
69 // 68 //
70 // 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
71 // 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/
72 #define SAFE_DIR "/proc/self/fdinfo" 71 #define SAFE_DIR "/proc/self/fdinfo"
73 #define SAFE_DIR2 "/proc/self/fd" 72 #define SAFE_DIR2 "/proc/self/fd"
74 73
75 static bool DropRoot() { 74 static bool SpawnChrootHelper() {
76 if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) { 75 int sv[2];
77 perror("prctl(PR_SET_DUMPABLE)"); 76 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
77 perror("socketpair");
78 return false; 78 return false;
79 } 79 }
80 80
81 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
82 perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
83 return false;
84 }
85
86 gid_t rgid, egid, sgid;
87 if (getresgid(&rgid, &egid, &sgid)) {
88 perror("getresgid");
89 return false;
90 }
91
92 if (setresgid(rgid, rgid, rgid)) {
93 perror("setresgid");
94 return false;
95 }
96
97 uid_t ruid, euid, suid;
98 if (getresuid(&ruid, &euid, &suid)) {
99 perror("getresuid");
100 return false;
101 }
102
103 if (setresuid(ruid, ruid, ruid)) {
104 perror("setresuid");
105 return false;
106 }
107
108 return true;
109 }
110
111 static int SpawnChrootHelper() {
112 int sv[2];
113 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
114 perror("socketpair");
115 return -1;
116 }
117
118 char *safedir = NULL; 81 char *safedir = NULL;
119 struct stat sdir_stat; 82 struct stat sdir_stat;
120 if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) 83 if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
121 safedir = SAFE_DIR; 84 safedir = SAFE_DIR;
122 else 85 else
123 if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) 86 if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
124 safedir = SAFE_DIR2; 87 safedir = SAFE_DIR2;
125 else { 88 else {
126 fprintf(stderr, "Could not find %s\n", SAFE_DIR2); 89 fprintf(stderr, "Could not find %s\n", SAFE_DIR2);
127 return -1; 90 return false;
128 } 91 }
129 92
130 const pid_t pid = syscall( 93 const pid_t pid = syscall(
131 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); 94 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
132 95
133 if (pid == -1) { 96 if (pid == -1) {
134 perror("clone"); 97 perror("clone");
135 close(sv[0]); 98 close(sv[0]);
136 close(sv[1]); 99 close(sv[1]);
137 return -1; 100 return false;
138 } 101 }
139 102
140 if (pid == 0) { 103 if (pid == 0) {
141 // We share our files structure with an untrusted process. As a security in 104 // We share our files structure with an untrusted process. As a security in
142 // depth measure, we make sure that we can't open anything by mistake. 105 // depth measure, we make sure that we can't open anything by mistake.
143 // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT 106 // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT
144 107
145 const struct rlimit nofile = {0, 0}; 108 const struct rlimit nofile = {0, 0};
146 if (setrlimit(RLIMIT_NOFILE, &nofile)) 109 if (setrlimit(RLIMIT_NOFILE, &nofile))
147 FatalError("Setting RLIMIT_NOFILE"); 110 FatalError("Setting RLIMIT_NOFILE");
148 111
149 if (close(sv[1])) 112 if (close(sv[1]))
150 FatalError("close"); 113 FatalError("close");
151 114
152 // wait for message 115 // wait for message
153 char msg; 116 char msg;
154 ssize_t bytes; 117 ssize_t bytes;
155 do { 118 do {
156 bytes = read(sv[0], &msg, 1); 119 bytes = read(sv[0], &msg, 1);
157 } while (bytes == -1 && errno == EINTR); 120 } while (bytes == -1 && errno == EINTR);
158 121
159 if (bytes == 0) 122 if (bytes == 0)
160 _exit(0); 123 _exit(0);
161 if (bytes != 1) 124 if (bytes != 1)
162 FatalError("read"); 125 FatalError("read");
163 126
164 // do chrooting 127 // do chrooting
165 errno = 0;
166 if (msg != kMsgChrootMe) 128 if (msg != kMsgChrootMe)
167 FatalError("Unknown message from sandboxed process"); 129 FatalError("Unknown message from sandboxed process");
168 130
169 // sanity check 131 // sanity check
170 if (chdir(safedir)) 132 if (chdir(safedir))
171 FatalError("Cannot chdir into /proc/ directory"); 133 FatalError("Cannot chdir into /proc/ directory");
172 134
173 if (chroot(safedir)) 135 if (chroot(safedir))
174 FatalError("Cannot chroot into /proc/ directory"); 136 FatalError("Cannot chroot into /proc/ directory");
175 137
(...skipping 12 matching lines...) Expand all
188 // We now become a zombie. /proc/self/fd(info) is now an empty dir and we 150 // We now become a zombie. /proc/self/fd(info) is now an empty dir and we
189 // are chrooted there. 151 // are chrooted there.
190 // Our (unprivileged) parent should not even be able to open "." or "/" 152 // Our (unprivileged) parent should not even be able to open "." or "/"
191 // since they would need to pass the ptrace() check. If our parent wait() 153 // since they would need to pass the ptrace() check. If our parent wait()
192 // for us, our root directory will completely disappear. 154 // for us, our root directory will completely disappear.
193 } 155 }
194 156
195 if (close(sv[0])) { 157 if (close(sv[0])) {
196 close(sv[1]); 158 close(sv[1]);
197 perror("close"); 159 perror("close");
198 return -1; 160 return false;
199 } 161 }
200 162
201 // In the parent process, we install an environment variable containing the 163 // In the parent process, we install an environment variable containing the
202 // number of the file descriptor. 164 // number of the file descriptor.
203 char desc_str[64]; 165 char desc_str[64];
204 int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]); 166 int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]);
205 if (printed < 0 || printed >= (int)sizeof(desc_str)) { 167 if (printed < 0 || printed >= (int)sizeof(desc_str)) {
206 fprintf(stderr, "Failed to snprintf\n"); 168 fprintf(stderr, "Failed to snprintf\n");
207 close(sv[1]); 169 return false;
208 return -1;
209 } 170 }
210 171
211 if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) { 172 if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
212 perror("setenv"); 173 perror("setenv");
213 close(sv[1]); 174 close(sv[1]);
214 return -1; 175 return false;
215 } 176 }
216 177
217 // We also install an environment variable containing the pid of the child 178 // We also install an environment variable containing the pid of the child
218 char helper_pid_str[64]; 179 char helper_pid_str[64];
219 printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid); 180 printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid);
220 if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) { 181 if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) {
221 fprintf(stderr, "Failed to snprintf\n"); 182 fprintf(stderr, "Failed to snprintf\n");
222 close(sv[1]); 183 return false;
223 return -1;
224 } 184 }
225 185
226 if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) { 186 if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) {
227 perror("setenv"); 187 perror("setenv");
228 close(sv[1]); 188 close(sv[1]);
229 return -1; 189 return false;
230 } 190 }
231 191
232 return sv[1];
233 }
234
235 static bool JailMe() {
236 int fd = SpawnChrootHelper();
237 if (fd < 0) {
238 return false;
239 }
240 if (!DropRoot()) {
241 close(fd);
242 return false;
243 }
244 ssize_t bytes;
245 char ch = kMsgChrootMe;
246 do {
247 errno = 0;
248 bytes = write(fd, &ch, 1);
249 } while (bytes == -1 && errno == EINTR);
250 if (bytes != 1) {
251 perror("write");
252 close(fd);
253 return false;
254 }
255 do {
256 errno = 0;
257 bytes = read(fd, &ch, 1);
258 } while (bytes == -1 && errno == EINTR);
259 close(fd);
260 if (bytes != 1) {
261 perror("read");
262 return false;
263 }
264 if (ch != kMsgChrootSuccessful) {
265 return false;
266 }
267 return true; 192 return true;
268 } 193 }
269 194
270 static bool MoveToNewNamespaces() { 195 static bool MoveToNewNamespaces() {
271 // These are the sets of flags which we'll try, in order. 196 // These are the sets of flags which we'll try, in order.
272 const int kCloneExtraFlags[] = { 197 const int kCloneExtraFlags[] = {
273 CLONE_NEWPID | CLONE_NEWNET, 198 CLONE_NEWPID | CLONE_NEWNET,
274 CLONE_NEWPID, 199 CLONE_NEWPID,
275 }; 200 };
276 201
277 for (size_t i = 0; 202 for (size_t i = 0;
278 i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]); 203 i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
279 i++) { 204 i++) {
280 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0); 205 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
281 206
282 if (pid > 0) 207 if (pid > 0)
283 _exit(0); 208 _exit(0);
284 209
285 if (pid == 0) { 210 if (pid == 0) {
286 if (syscall(__NR_getpid) == 1) {
287 int fds[2];
288 char ch = 0;
289 if (pipe(fds)) {
290 perror("Failed to create pipe");
291 _exit(1);
292 }
293 pid = fork();
294 if (pid > 0) {
295 // The very first process in the new namespace takes on the
296 // role of the traditional "init" process. It must reap exit
297 // codes of daemon processes until the namespace is completely
298 // empty.
299 // We have to be careful that this "init" process doesn't
300 // provide a new attack surface. So, we also move it into
301 // a separate chroot and we drop all privileges. It does
302 // still need to access "/proc" and "/dev/null", though. So,
303 // we have to provide it with a file handles to these resources.
304 // These file handle are not accessible by any other processes in
305 // the sandbox and thus safe.
306 close(fds[0]);
307 int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
308 int null_fd = open("/dev/null", O_RDWR);
309 if (!JailMe()) {
310 FatalError("Could not remove privileges from "
311 "new \"init\" process");
312 }
313 SystemInitProcess(fds[1], pid, proc_fd, null_fd);
314 } else if (pid != 0) {
315 perror("Failed to fork");
316 _exit(1);
317 }
318 // Wait for the "init" process to complete initialization.
319 close(fds[1]);
320 errno = 0;
321 while (read(fds[0], &ch, 1) < 0 && errno == EINTR) {
322 }
323 close(fds[0]);
324 if (ch != ' ') {
325 // We'll likely never get here. If the "init" process fails, it's
326 // death typically takes everyone of its children with it.
327 FatalError("Failed to set up new \"init\" process inside sandbox");
328 }
329 }
330
331 if (kCloneExtraFlags[i] & CLONE_NEWPID) { 211 if (kCloneExtraFlags[i] & CLONE_NEWPID) {
332 setenv("SBX_PID_NS", "", 1 /* overwrite */); 212 setenv("SBX_PID_NS", "", 1 /* overwrite */);
333 } else { 213 } else {
334 unsetenv("SBX_PID_NS"); 214 unsetenv("SBX_PID_NS");
335 } 215 }
336 216
337 if (kCloneExtraFlags[i] & CLONE_NEWNET) { 217 if (kCloneExtraFlags[i] & CLONE_NEWNET) {
338 setenv("SBX_NET_NS", "", 1 /* overwrite */); 218 setenv("SBX_NET_NS", "", 1 /* overwrite */);
339 } else { 219 } else {
340 unsetenv("SBX_NET_NS"); 220 unsetenv("SBX_NET_NS");
341 } 221 }
342 222
343 break; 223 break;
344 } 224 }
345 225
346 if (errno != EINVAL) { 226 if (errno != EINVAL) {
347 perror("Failed to move to new PID namespace"); 227 perror("Failed to move to new PID namespace");
348 return false; 228 return false;
349 } 229 }
350 } 230 }
351 231
352 // If the system doesn't support NEWPID then we carry on anyway. 232 // If the system doesn't support NEWPID then we carry on anyway.
353 return true; 233 return true;
354 } 234 }
355 235
236 static bool DropRoot() {
237 if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
238 perror("prctl(PR_SET_DUMPABLE)");
239 return false;
240 }
241
242 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
243 perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
244 return false;
245 }
246
247 gid_t rgid, egid, sgid;
248 if (getresgid(&rgid, &egid, &sgid)) {
249 perror("getresgid");
250 return false;
251 }
252
253 if (setresgid(rgid, rgid, rgid)) {
254 perror("setresgid");
255 return false;
256 }
257
258 uid_t ruid, euid, suid;
259 if (getresuid(&ruid, &euid, &suid)) {
260 perror("getresuid");
261 return false;
262 }
263
264 if (setresuid(ruid, ruid, ruid)) {
265 perror("setresuid");
266 return false;
267 }
268
269 return true;
270 }
271
356 static bool SetupChildEnvironment() { 272 static bool SetupChildEnvironment() {
357 unsigned i; 273 unsigned i;
358 274
359 // ld.so may have cleared several environment variables because we are SUID. 275 // ld.so may have cleared several environment variables because we are SUID.
360 // However, the child process might need them so zygote_host_linux.cc saves a 276 // However, the child process might need them so zygote_host_linux.cc saves a
361 // copy in SANDBOX_$x. This is safe because we have dropped root by this 277 // copy in SANDBOX_$x. This is safe because we have dropped root by this
362 // point, so we can only exec a binary with the permissions of the user who 278 // point, so we can only exec a binary with the permissions of the user who
363 // ran us in the first place. 279 // ran us in the first place.
364 280
365 for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) { 281 for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 errno = 0; 356 errno = 0;
441 unsigned long margin_mb = strtoul(argv[2], &endptr, 10); 357 unsigned long margin_mb = strtoul(argv[2], &endptr, 10);
442 if (!endptr || *endptr || errno != 0) 358 if (!endptr || *endptr || errno != 0)
443 return 1; 359 return 1;
444 return AdjustLowMemoryMargin(margin_mb); 360 return AdjustLowMemoryMargin(margin_mb);
445 } 361 }
446 #endif 362 #endif
447 363
448 if (!MoveToNewNamespaces()) 364 if (!MoveToNewNamespaces())
449 return 1; 365 return 1;
450 if (SpawnChrootHelper() < 0) 366 if (!SpawnChrootHelper())
451 return 1; 367 return 1;
452 if (!DropRoot()) 368 if (!DropRoot())
453 return 1; 369 return 1;
454 if (!SetupChildEnvironment()) 370 if (!SetupChildEnvironment())
455 return 1; 371 return 1;
456 372
457 execv(argv[1], &argv[1]); 373 execv(argv[1], &argv[1]);
458 FatalError("execv failed"); 374 FatalError("execv failed");
459 375
460 return 1; 376 return 1;
461 } 377 }
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