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

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

Issue 6675054: Revert 79618 - Landing for Julien Tinnes, jln@google.com:... (Closed) Base URL: svn://svn.chromium.org/chrome/branches/719/src/
Patch Set: Created 9 years, 8 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 | « no previous file | no next file » | 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) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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>
(...skipping 19 matching lines...) Expand all
30 #include "process_util.h" 30 #include "process_util.h"
31 #include "suid_unsafe_environment_variables.h" 31 #include "suid_unsafe_environment_variables.h"
32 32
33 #if !defined(CLONE_NEWPID) 33 #if !defined(CLONE_NEWPID)
34 #define CLONE_NEWPID 0x20000000 34 #define CLONE_NEWPID 0x20000000
35 #endif 35 #endif
36 #if !defined(CLONE_NEWNET) 36 #if !defined(CLONE_NEWNET)
37 #define CLONE_NEWNET 0x40000000 37 #define CLONE_NEWNET 0x40000000
38 #endif 38 #endif
39 39
40 #if !defined(BTRFS_SUPER_MAGIC)
41 #define BTRFS_SUPER_MAGIC 0x9123683E
42 #endif
43 #if !defined(EXT2_SUPER_MAGIC)
44 #define EXT2_SUPER_MAGIC 0xEF53
45 #endif
46 #if !defined(EXT3_SUPER_MAGIC)
47 #define EXT3_SUPER_MAGIC 0xEF53
48 #endif
49 #if !defined(EXT4_SUPER_MAGIC)
50 #define EXT4_SUPER_MAGIC 0xEF53
51 #endif
52 #if !defined(REISERFS_SUPER_MAGIC)
53 #define REISERFS_SUPER_MAGIC 0x52654973
54 #endif
55 #if !defined(TMPFS_MAGIC)
56 #define TMPFS_MAGIC 0x01021994
57 #endif
58 #if !defined(XFS_SUPER_MAGIC)
59 #define XFS_SUPER_MAGIC 0x58465342
60 #endif
61
40 static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; 62 static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D";
41 static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID";
42 63
43 // These are the magic byte values which the sandboxed process uses to request 64 // These are the magic byte values which the sandboxed process uses to request
44 // that it be chrooted. 65 // that it be chrooted.
45 static const char kMsgChrootMe = 'C'; 66 static const char kMsgChrootMe = 'C';
46 static const char kMsgChrootSuccessful = 'O'; 67 static const char kMsgChrootSuccessful = 'O';
47 68
48 static void FatalError(const char *msg, ...) 69 static void FatalError(const char *msg, ...)
49 __attribute__((noreturn, format(printf, 1, 2))); 70 __attribute__((noreturn, format(printf, 1, 2)));
50 71
51 static void FatalError(const char *msg, ...) { 72 static void FatalError(const char *msg, ...) {
52 va_list ap; 73 va_list ap;
53 va_start(ap, msg); 74 va_start(ap, msg);
54 75
55 vfprintf(stderr, msg, ap); 76 vfprintf(stderr, msg, ap);
56 fprintf(stderr, ": %s\n", strerror(errno)); 77 fprintf(stderr, ": %s\n", strerror(errno));
57 fflush(stderr); 78 fflush(stderr);
58 exit(1); 79 exit(1);
59 } 80 }
60 81
61 // We will chroot() to the helper's /proc/self directory. Anything there will 82 static int CloneChrootHelperProcess() {
62 // not exist anymore if we make sure to wait() for the helper.
63 //
64 // /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty
65 // even if the helper survives as a zombie.
66 //
67 // There is very little reason to use fdinfo/ instead of fd/ but we are
68 // paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/
69 #define SAFE_DIR "/proc/self/fdinfo"
70 #define SAFE_DIR2 "/proc/self/fd"
71
72 static bool SpawnChrootHelper() {
73 int sv[2]; 83 int sv[2];
74 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { 84 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
75 perror("socketpair"); 85 perror("socketpair");
76 return false; 86 return -1;
77 } 87 }
78 88
79 char *safedir = NULL; 89 // Some people mount /tmp on a non-POSIX filesystem (e.g. NFS). This
80 struct stat sdir_stat; 90 // breaks all sorts of assumption in our code. So, if we don't recognize the
81 if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) 91 // filesystem, we will try to use an alternative location for our temp
82 safedir = SAFE_DIR; 92 // directory.
83 else 93 char tempDirectoryTemplate[80] = "/tmp/chrome-sandbox-chroot-XXXXXX";
84 if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) 94 struct statfs sfs;
85 safedir = SAFE_DIR2; 95 if (!statfs("/tmp", &sfs) &&
86 else { 96 (unsigned long)sfs.f_type != BTRFS_SUPER_MAGIC &&
87 fprintf(stderr, "Could not find %s\n", SAFE_DIR2); 97 (unsigned long)sfs.f_type != EXT2_SUPER_MAGIC &&
88 return false; 98 (unsigned long)sfs.f_type != EXT3_SUPER_MAGIC &&
99 (unsigned long)sfs.f_type != EXT4_SUPER_MAGIC &&
100 (unsigned long)sfs.f_type != REISERFS_SUPER_MAGIC &&
101 (unsigned long)sfs.f_type != TMPFS_MAGIC &&
102 (unsigned long)sfs.f_type != XFS_SUPER_MAGIC) {
103 // If /dev/shm exists, it is supposed to be a tmpfs filesystem. While we
104 // are not actually using it for shared memory, moving our temp directory
105 // into a known tmpfs filesystem is preferable over using a potentially
106 // unreliable non-POSIX filesystem.
107 if (!statfs("/dev/shm", &sfs) && sfs.f_type == TMPFS_MAGIC) {
108 *tempDirectoryTemplate = '\000';
109 strncat(tempDirectoryTemplate, "/dev/shm/chrome-sandbox-chroot-XXXXXX",
110 sizeof(tempDirectoryTemplate) - 1);
111 } else {
112 // Neither /tmp is a well-known POSIX filesystem, nor /dev/shm is a
113 // tmpfs. After all, we now use /tmp as the location of our temp
114 // directory, but we quite likely fail the moment we try to access it
115 // through chroot_dir_fd. If so, we will print a verbose error message
116 // (see below)
89 } 117 }
118 }
119
120 // We create a temp directory for our chroot. Nobody should ever write into
121 // it, so it's root:root mode 000.
122 const char* temp_dir = mkdtemp(tempDirectoryTemplate);
123 if (!temp_dir) {
124 perror("Failed to create temp directory for chroot");
125 return -1;
126 }
127
128 const int chroot_dir_fd = open(temp_dir, O_DIRECTORY | O_RDONLY);
129 if (chroot_dir_fd < 0) {
130 rmdir(temp_dir);
131 perror("Failed to open chroot temp directory");
132 return -1;
133 }
134
135 if (rmdir(temp_dir)) {
136 perror("rmdir");
137 return -1;
138 }
139
140 char proc_self_fd_str[128];
141 int printed = snprintf(proc_self_fd_str, sizeof(proc_self_fd_str),
142 "/proc/self/fd/%d", chroot_dir_fd);
143 if (printed < 0 || printed >= (int)sizeof(proc_self_fd_str)) {
144 fprintf(stderr, "Error in snprintf");
145 return -1;
146 }
147
148 if (fchown(chroot_dir_fd, 0 /* root */, 0 /* root */)) {
149 fprintf(stderr, "Could not set up sandbox work directory. Maybe, /tmp is "
150 "a non-POSIX filesystem and /dev/shm doesn't exist "
151 "either. Consider mounting a \"tmpfs\" on /tmp.\n");
152 return -1;
153 }
154
155 if (fchmod(chroot_dir_fd, 0000 /* no-access */)) {
156 perror("fchmod");
157 return -1;
158 }
159
90 160
91 const pid_t pid = syscall( 161 const pid_t pid = syscall(
92 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); 162 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
93 163
94 if (pid == -1) { 164 if (pid == -1) {
95 perror("clone"); 165 perror("clone");
96 close(sv[0]); 166 close(sv[0]);
97 close(sv[1]); 167 close(sv[1]);
98 return false; 168 return -1;
99 } 169 }
100 170
101 if (pid == 0) { 171 if (pid == 0) {
102 // We share our files structure with an untrusted process. As a security in 172 // We share our files structure with an untrusted process. As a security in
103 // depth measure, we make sure that we can't open anything by mistake. 173 // depth measure, we make sure that we can't open anything by mistake.
104 // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT 174 // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT
105 175
106 const struct rlimit nofile = {0, 0}; 176 const struct rlimit nofile = {0, 0};
107 if (setrlimit(RLIMIT_NOFILE, &nofile)) 177 if (setrlimit(RLIMIT_NOFILE, &nofile))
108 FatalError("Setting RLIMIT_NOFILE"); 178 FatalError("Setting RLIMIT_NOFILE");
(...skipping 10 matching lines...) Expand all
119 189
120 if (bytes == 0) 190 if (bytes == 0)
121 _exit(0); 191 _exit(0);
122 if (bytes != 1) 192 if (bytes != 1)
123 FatalError("read"); 193 FatalError("read");
124 194
125 // do chrooting 195 // do chrooting
126 if (msg != kMsgChrootMe) 196 if (msg != kMsgChrootMe)
127 FatalError("Unknown message from sandboxed process"); 197 FatalError("Unknown message from sandboxed process");
128 198
129 // sanity check 199 if (fchdir(chroot_dir_fd))
130 if (chdir(safedir)) 200 FatalError("Cannot chdir into chroot temp directory");
131 FatalError("Cannot chdir into /proc/ directory");
132 201
133 if (chroot(safedir)) 202 struct stat st;
134 FatalError("Cannot chroot into /proc/ directory"); 203 if (fstat(chroot_dir_fd, &st))
204 FatalError("stat");
205
206 if (st.st_uid || st.st_gid || st.st_mode & 0777)
207 FatalError("Bad permissions on chroot temp directory");
208
209 if (chroot(proc_self_fd_str))
210 FatalError("Cannot chroot into temp directory");
135 211
136 if (chdir("/")) 212 if (chdir("/"))
137 FatalError("Cannot chdir to / after chroot"); 213 FatalError("Cannot chdir to / after chroot");
138 214
139 const char reply = kMsgChrootSuccessful; 215 const char reply = kMsgChrootSuccessful;
140 do { 216 do {
141 bytes = write(sv[0], &reply, 1); 217 bytes = write(sv[0], &reply, 1);
142 } while (bytes == -1 && errno == EINTR); 218 } while (bytes == -1 && errno == EINTR);
143 219
144 if (bytes != 1) 220 if (bytes != 1)
145 FatalError("Writing reply"); 221 FatalError("Writing reply");
146 222
147 _exit(0); 223 _exit(0);
148 // We now become a zombie. /proc/self/fd(info) is now an empty dir and we 224 }
149 // are chrooted there. 225
150 // Our (unprivileged) parent should not even be able to open "." or "/" 226 if (close(chroot_dir_fd)) {
151 // since they would need to pass the ptrace() check. If our parent wait() 227 close(sv[0]);
152 // for us, our root directory will completely disappear. 228 close(sv[1]);
229 perror("close(chroot_dir_fd)");
230 return false;
153 } 231 }
154 232
155 if (close(sv[0])) { 233 if (close(sv[0])) {
156 close(sv[1]); 234 close(sv[1]);
157 perror("close"); 235 perror("close");
158 return false; 236 return false;
159 } 237 }
160 238
239 return sv[1];
240 }
241
242 static bool SpawnChrootHelper() {
243 const int chroot_signal_fd = CloneChrootHelperProcess();
244
245 if (chroot_signal_fd == -1)
246 return false;
247
161 // In the parent process, we install an environment variable containing the 248 // In the parent process, we install an environment variable containing the
162 // number of the file descriptor. 249 // number of the file descriptor.
163 char desc_str[64]; 250 char desc_str[64];
164 int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]); 251 int printed = snprintf(desc_str, sizeof(desc_str), "%d", chroot_signal_fd);
165 if (printed < 0 || printed >= (int)sizeof(desc_str)) { 252 if (printed < 0 || printed >= (int)sizeof(desc_str)) {
166 fprintf(stderr, "Failed to snprintf\n"); 253 fprintf(stderr, "Failed to snprintf\n");
167 return false; 254 return false;
168 } 255 }
169 256
170 if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) { 257 if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
171 perror("setenv"); 258 perror("setenv");
172 close(sv[1]); 259 close(chroot_signal_fd);
173 return false;
174 }
175
176 // We also install an environment variable containing the pid of the child
177 char helper_pid_str[64];
178 printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid);
179 if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) {
180 fprintf(stderr, "Failed to snprintf\n");
181 return false;
182 }
183
184 if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) {
185 perror("setenv");
186 close(sv[1]);
187 return false; 260 return false;
188 } 261 }
189 262
190 return true; 263 return true;
191 } 264 }
192 265
193 static bool MoveToNewNamespaces() { 266 static bool MoveToNewNamespaces() {
194 // These are the sets of flags which we'll try, in order. 267 // These are the sets of flags which we'll try, in order.
195 const int kCloneExtraFlags[] = { 268 const int kCloneExtraFlags[] = {
196 CLONE_NEWPID | CLONE_NEWNET, 269 CLONE_NEWPID | CLONE_NEWNET,
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 if (!DropRoot()) 420 if (!DropRoot())
348 return 1; 421 return 1;
349 if (!SetupChildEnvironment()) 422 if (!SetupChildEnvironment())
350 return 1; 423 return 1;
351 424
352 execv(argv[1], &argv[1]); 425 execv(argv[1], &argv[1]);
353 FatalError("execv failed"); 426 FatalError("execv failed");
354 427
355 return 1; 428 return 1;
356 } 429 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698