| OLD | NEW |
| 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 |
| 6 |
| 5 #include <asm/unistd.h> | 7 #include <asm/unistd.h> |
| 6 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <fcntl.h> |
| 7 #include <sched.h> | 10 #include <sched.h> |
| 8 #include <signal.h> | 11 #include <signal.h> |
| 9 #include <stdarg.h> | 12 #include <stdarg.h> |
| 10 #include <stdio.h> | 13 #include <stdio.h> |
| 11 #include <stdlib.h> | 14 #include <stdlib.h> |
| 12 #include <string.h> | 15 #include <string.h> |
| 13 #include <sys/prctl.h> | 16 #include <sys/prctl.h> |
| 14 #include <sys/resource.h> | 17 #include <sys/resource.h> |
| 15 #include <sys/socket.h> | 18 #include <sys/socket.h> |
| 16 #include <sys/stat.h> | 19 #include <sys/stat.h> |
| 17 #include <sys/time.h> | 20 #include <sys/time.h> |
| 18 #include <sys/types.h> | 21 #include <sys/types.h> |
| 19 #include <unistd.h> | 22 #include <unistd.h> |
| 20 | 23 |
| 21 #if !defined(CLONE_NEWPID) | 24 #if !defined(CLONE_NEWPID) |
| 22 #define CLONE_NEWPID 0x20000000 | 25 #define CLONE_NEWPID 0x20000000 |
| 23 #endif | 26 #endif |
| 24 | 27 |
| 25 static const char kSandboxPath[] = "/var/run/chrome-sandbox"; | |
| 26 static const char kChromeBinary[] = "/opt/google/chrome/chrome"; | 28 static const char kChromeBinary[] = "/opt/google/chrome/chrome"; |
| 27 static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; | 29 static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; |
| 28 | 30 |
| 29 // These are the magic byte values which the sandboxed process uses to request | 31 // These are the magic byte values which the sandboxed process uses to request |
| 30 // that it be chrooted. | 32 // that it be chrooted. |
| 31 static const char kMsgChrootMe = 'C'; | 33 static const char kMsgChrootMe = 'C'; |
| 32 static const char kMsgChrootSuccessful = 'O'; | 34 static const char kMsgChrootSuccessful = 'O'; |
| 33 | 35 |
| 34 static void FatalError(const char *msg, ...) | 36 static void FatalError(const char *msg, ...) |
| 35 __attribute__((noreturn, format(printf,1,2))); | 37 __attribute__((noreturn, format(printf,1,2))); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 55 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); | 57 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); |
| 56 | 58 |
| 57 if (pid == -1) { | 59 if (pid == -1) { |
| 58 perror("clone"); | 60 perror("clone"); |
| 59 close(sv[0]); | 61 close(sv[0]); |
| 60 close(sv[1]); | 62 close(sv[1]); |
| 61 return -1; | 63 return -1; |
| 62 } | 64 } |
| 63 | 65 |
| 64 if (pid == 0) { | 66 if (pid == 0) { |
| 67 // We create a temp directory for our chroot. Nobody should ever write into |
| 68 // it, so it's root:root 0555. |
| 69 char kTempDirectoryTemplate[] = "/tmp/chrome-sandbox-chroot-XXXXXX"; |
| 70 const char* temp_dir = mkdtemp(kTempDirectoryTemplate); |
| 71 if (!temp_dir) |
| 72 FatalError("Failed to create temp directory for chroot"); |
| 73 |
| 74 const int chroot_dir_fd = open(temp_dir, O_DIRECTORY | O_RDONLY); |
| 75 if (chroot_dir_fd < 0) { |
| 76 rmdir(temp_dir); |
| 77 FatalError("Failed to open chroot temp directory"); |
| 78 } |
| 79 |
| 80 rmdir(temp_dir); |
| 81 fchown(chroot_dir_fd, 0, 0); |
| 82 fchmod(chroot_dir_fd, 0555); |
| 83 |
| 65 // We share our files structure with an untrusted process. As a security in | 84 // We share our files structure with an untrusted process. As a security in |
| 66 // depth measure, we make sure that we can't open anything by mistake. | 85 // depth measure, we make sure that we can't open anything by mistake. |
| 67 // TODO: drop CAP_SYS_RESOURCE | 86 // TODO: drop CAP_SYS_RESOURCE |
| 68 | 87 |
| 69 const struct rlimit nofile = {0, 0}; | 88 const struct rlimit nofile = {0, 0}; |
| 70 if (setrlimit(RLIMIT_NOFILE, &nofile)) | 89 if (setrlimit(RLIMIT_NOFILE, &nofile)) |
| 71 FatalError("Setting RLIMIT_NOFILE"); | 90 FatalError("Setting RLIMIT_NOFILE"); |
| 72 | 91 |
| 73 if (close(sv[1])) | 92 if (close(sv[1])) |
| 74 FatalError("close"); | 93 FatalError("close"); |
| 75 | 94 |
| 76 char msg; | 95 char msg; |
| 77 ssize_t bytes; | 96 ssize_t bytes; |
| 78 do { | 97 do { |
| 79 bytes = read(sv[0], &msg, 1); | 98 bytes = read(sv[0], &msg, 1); |
| 80 } while (bytes == -1 && errno == EINTR); | 99 } while (bytes == -1 && errno == EINTR); |
| 81 | 100 |
| 82 if (bytes == 0) | 101 if (bytes == 0) |
| 83 _exit(0); | 102 _exit(0); |
| 84 if (bytes != 1) | 103 if (bytes != 1) |
| 85 FatalError("read"); | 104 FatalError("read"); |
| 86 | 105 |
| 87 if (msg != kMsgChrootMe) | 106 if (msg != kMsgChrootMe) |
| 88 FatalError("Unknown message from sandboxed process"); | 107 FatalError("Unknown message from sandboxed process"); |
| 89 | 108 |
| 90 if (chdir(kSandboxPath)) | 109 if (fchdir(chroot_dir_fd)) |
| 91 FatalError("Cannot chdir into %s", kSandboxPath); | 110 FatalError("Cannot chdir into chroot temp directory"); |
| 92 | 111 |
| 93 struct stat st; | 112 struct stat st; |
| 94 if (stat(".", &st)) | 113 if (stat(".", &st)) |
| 95 FatalError("stat"); | 114 FatalError("stat"); |
| 96 | 115 |
| 97 if (st.st_uid || st.st_gid || st.st_mode & S_IWOTH) | 116 if (st.st_uid || st.st_gid || st.st_mode & S_IWOTH) |
| 98 FatalError("Bad permissions on chroot directory (%s)", kSandboxPath); | 117 FatalError("Bad permissions on chroot temp directory"); |
| 99 | 118 |
| 100 if (chroot(".")) | 119 if (chroot(".")) |
| 101 FatalError("Cannot chroot into %s", kSandboxPath); | 120 FatalError("Cannot chroot into temp directory"); |
| 102 | 121 |
| 103 if (chdir("/")) | 122 if (chdir("/")) |
| 104 FatalError("Cannot chdir to / after chroot"); | 123 FatalError("Cannot chdir to / after chroot"); |
| 105 | 124 |
| 106 const char reply = kMsgChrootSuccessful; | 125 const char reply = kMsgChrootSuccessful; |
| 107 do { | 126 do { |
| 108 bytes = write(sv[0], &reply, 1); | 127 struct msghdr msg = {0}; |
| 128 struct iovec iov = {(char *) &reply, 1}; |
| 129 |
| 130 msg.msg_iov = &iov; |
| 131 msg.msg_iovlen = 1; |
| 132 |
| 133 char control_buffer[CMSG_SPACE(sizeof(int))]; |
| 134 msg.msg_control = control_buffer; |
| 135 msg.msg_controllen = sizeof(control_buffer); |
| 136 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); |
| 137 cmsg->cmsg_level = SOL_SOCKET; |
| 138 cmsg->cmsg_type = SCM_RIGHTS; |
| 139 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
| 140 memcpy(CMSG_DATA(cmsg), &chroot_dir_fd, sizeof(int)); |
| 141 msg.msg_controllen = cmsg->cmsg_len; |
| 142 |
| 143 bytes = sendmsg(sv[0], &msg, 0); |
| 109 } while (bytes == -1 && errno == EINTR); | 144 } while (bytes == -1 && errno == EINTR); |
| 110 | 145 |
| 111 if (bytes != 1) | 146 if (bytes != 1) |
| 112 FatalError("Writing reply"); | 147 FatalError("Writing reply"); |
| 113 | 148 |
| 114 _exit(0); | 149 _exit(0); |
| 115 } | 150 } |
| 116 | 151 |
| 117 if (close(sv[0])) { | 152 if (close(sv[0])) { |
| 118 close(sv[1]); | 153 close(sv[1]); |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 215 if (!SpawnChrootHelper()) | 250 if (!SpawnChrootHelper()) |
| 216 return 1; | 251 return 1; |
| 217 if (!DropRoot()) | 252 if (!DropRoot()) |
| 218 return 1; | 253 return 1; |
| 219 | 254 |
| 220 execv(argv[1], &argv[1]); | 255 execv(argv[1], &argv[1]); |
| 221 FatalError("execv failed"); | 256 FatalError("execv failed"); |
| 222 | 257 |
| 223 return 1; | 258 return 1; |
| 224 } | 259 } |
| OLD | NEW |