| OLD | NEW |
| 1 #include <dirent.h> | 1 #include <dirent.h> |
| 2 #include <map> | 2 #include <map> |
| 3 | 3 |
| 4 #include "debug.h" | 4 #include "debug.h" |
| 5 #include "sandbox_impl.h" | 5 #include "sandbox_impl.h" |
| 6 #include "syscall_table.h" | 6 #include "syscall_table.h" |
| 7 | 7 |
| 8 namespace playground { | 8 namespace playground { |
| 9 | 9 |
| 10 struct Thread { | 10 struct Thread { |
| 11 int fdPub, fd; | 11 int fdPub, fd; |
| 12 SecureMem::Args* mem; | 12 SecureMem::Args* mem; |
| 13 }; | 13 }; |
| 14 | 14 |
| 15 SecureMem::Args* Sandbox::getSecureMem() { | 15 SecureMem::Args* Sandbox::getSecureMem() { |
| 16 if (!secureMemPool_.empty()) { | 16 if (!secureMemPool_.empty()) { |
| 17 SecureMem::Args* rc = secureMemPool_.back(); | 17 SecureMem::Args* rc = secureMemPool_.back(); |
| 18 secureMemPool_.pop_back(); | 18 secureMemPool_.pop_back(); |
| 19 return rc; | 19 return rc; |
| 20 } | 20 } |
| 21 return NULL; | 21 return NULL; |
| 22 } | 22 } |
| 23 | 23 |
| 24 void Sandbox::trustedProcess(int parentProc, int processFdPub, int sandboxFd, | 24 void Sandbox::trustedProcess(int parentMapsFd, int processFdPub, int sandboxFd, |
| 25 int cloneFd, SecureMem::Args* secureArena) { | 25 int cloneFd, SecureMem::Args* secureArena) { |
| 26 std::map<long long, struct Thread> threads; | 26 std::map<long long, struct Thread> threads; |
| 27 SysCalls sys; | 27 SysCalls sys; |
| 28 long long cookie = 0; | 28 long long cookie = 0; |
| 29 | 29 |
| 30 // The very first entry in the secure memory arena has been assigned to the | 30 // The very first entry in the secure memory arena has been assigned to the |
| 31 // initial thread. The remaining entries are available for allocation. | 31 // initial thread. The remaining entries are available for allocation. |
| 32 SecureMem::Args* startAddress = secureArena; | 32 SecureMem::Args* startAddress = secureArena; |
| 33 SecureMem::Args* nextThread = startAddress; | 33 SecureMem::Args* nextThread = startAddress; |
| 34 for (int i = 0; i < kMaxThreads-1; i++) { | 34 for (int i = 0; i < kMaxThreads-1; i++) { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 92 die("Received request from unknown thread"); | 92 die("Received request from unknown thread"); |
| 93 } | 93 } |
| 94 struct Thread* currentThread = &iter->second; | 94 struct Thread* currentThread = &iter->second; |
| 95 if (header.sysnum > maxSyscall || | 95 if (header.sysnum > maxSyscall || |
| 96 !syscallTable[header.sysnum].trustedProcess) { | 96 !syscallTable[header.sysnum].trustedProcess) { |
| 97 die("Trusted process encountered unexpected system call"); | 97 die("Trusted process encountered unexpected system call"); |
| 98 } | 98 } |
| 99 | 99 |
| 100 // Dispatch system call to handler function. Treat both exit() and clone() | 100 // Dispatch system call to handler function. Treat both exit() and clone() |
| 101 // specially. | 101 // specially. |
| 102 if (syscallTable[header.sysnum].trustedProcess(parentProc, | 102 if (syscallTable[header.sysnum].trustedProcess(parentMapsFd, |
| 103 sandboxFd, | 103 sandboxFd, |
| 104 currentThread->fdPub, | 104 currentThread->fdPub, |
| 105 currentThread->fd, | 105 currentThread->fd, |
| 106 currentThread->mem) && | 106 currentThread->mem) && |
| 107 header.sysnum == __NR_clone) { | 107 header.sysnum == __NR_clone) { |
| 108 nextThread = currentThread->mem->newSecureMem; | 108 nextThread = currentThread->mem->newSecureMem; |
| 109 goto newThreadCreated; | 109 goto newThreadCreated; |
| 110 } else if (header.sysnum == __NR_exit) { | 110 } else if (header.sysnum == __NR_exit) { |
| 111 NOINTR_SYS(sys.close(iter->second.fdPub)); | 111 NOINTR_SYS(sys.close(iter->second.fdPub)); |
| 112 NOINTR_SYS(sys.close(iter->second.fd)); | 112 NOINTR_SYS(sys.close(iter->second.fd)); |
| 113 SecureMem::Args* secureMem = currentThread->mem; | 113 SecureMem::Args* secureMem = currentThread->mem; |
| 114 threads.erase(iter); | 114 threads.erase(iter); |
| 115 secureMemPool_.push_back(secureMem); | 115 secureMemPool_.push_back(secureMem); |
| 116 } | 116 } |
| 117 } | 117 } |
| 118 } | 118 } |
| 119 | 119 |
| 120 void Sandbox::initializeProtectedMap(int fd) { | 120 int Sandbox::initializeProtectedMap(int fd) { |
| 121 int mapsFd; | 121 int mapsFd; |
| 122 if (!getFd(fd, &mapsFd, NULL, NULL, NULL)) { | 122 if (!getFd(fd, &mapsFd, NULL, NULL, NULL)) { |
| 123 maps_failure: | 123 maps_failure: |
| 124 die("Cannot access /proc/self/maps"); | 124 die("Cannot access /proc/self/maps"); |
| 125 } | 125 } |
| 126 | 126 |
| 127 // Read the memory mappings as they were before the sandbox takes effect. | 127 // Read the memory mappings as they were before the sandbox takes effect. |
| 128 // These mappings cannot be changed by the sandboxed process. | 128 // These mappings cannot be changed by the sandboxed process. |
| 129 char line[80]; | 129 char line[80]; |
| 130 FILE *fp = fdopen(mapsFd, "r"); | 130 FILE *fp = fdopen(mapsFd, "r"); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 145 die("Failed to parse /proc/self/maps"); | 145 die("Failed to parse /proc/self/maps"); |
| 146 } | 146 } |
| 147 stop = strtoul(ptr, &ptr, 16); | 147 stop = strtoul(ptr, &ptr, 16); |
| 148 if (errno || *ptr++ != ' ') { | 148 if (errno || *ptr++ != ' ') { |
| 149 goto parse_failure; | 149 goto parse_failure; |
| 150 } | 150 } |
| 151 protectedMap_[reinterpret_cast<void *>(start)] = stop - start; | 151 protectedMap_[reinterpret_cast<void *>(start)] = stop - start; |
| 152 } | 152 } |
| 153 truncated = strchr(line, '\n') == NULL; | 153 truncated = strchr(line, '\n') == NULL; |
| 154 } | 154 } |
| 155 SysCalls sys; | |
| 156 NOINTR_SYS(sys.close(mapsFd)); | |
| 157 | 155 |
| 158 // Prevent low address memory allocations. Some buggy kernels allow those | 156 // Prevent low address memory allocations. Some buggy kernels allow those |
| 159 if (protectedMap_[0] < (64 << 10)) { | 157 if (protectedMap_[0] < (64 << 10)) { |
| 160 protectedMap_[0] = 64 << 10; | 158 protectedMap_[0] = 64 << 10; |
| 161 } | 159 } |
| 162 | 160 |
| 163 // Let the sandbox know that we are done parsing the memory map. | 161 // Let the sandbox know that we are done parsing the memory map. |
| 162 SysCalls sys; |
| 164 if (write(sys, fd, &mapsFd, sizeof(mapsFd)) != sizeof(mapsFd)) { | 163 if (write(sys, fd, &mapsFd, sizeof(mapsFd)) != sizeof(mapsFd)) { |
| 165 goto maps_failure; | 164 goto maps_failure; |
| 166 } | 165 } |
| 166 |
| 167 return mapsFd; |
| 167 } | 168 } |
| 168 | 169 |
| 169 SecureMem::Args* Sandbox::createTrustedProcess(int processFdPub, int sandboxFd, | 170 SecureMem::Args* Sandbox::createTrustedProcess(int processFdPub, int sandboxFd, |
| 170 int cloneFdPub, int cloneFd) { | 171 int cloneFdPub, int cloneFd) { |
| 171 // Allocate memory that will be used by an arena for storing the secure | 172 // Allocate memory that will be used by an arena for storing the secure |
| 172 // memory. While we allow this memory area to be empty at times (e.g. when | 173 // memory. While we allow this memory area to be empty at times (e.g. when |
| 173 // not all threads are in use), we make sure that it never gets overwritten | 174 // not all threads are in use), we make sure that it never gets overwritten |
| 174 // by user-allocated memory. This happens in initializeProtectedMap() and | 175 // by user-allocated memory. This happens in initializeProtectedMap() and |
| 175 // snapshotMemoryMappings(). | 176 // snapshotMemoryMappings(). |
| 176 SecureMem::Args* secureArena = reinterpret_cast<SecureMem::Args*>( | 177 SecureMem::Args* secureArena = reinterpret_cast<SecureMem::Args*>( |
| 177 mmap(NULL, 8192*kMaxThreads, PROT_READ|PROT_WRITE, | 178 mmap(NULL, 8192*kMaxThreads, PROT_READ|PROT_WRITE, |
| 178 MAP_SHARED|MAP_ANONYMOUS, -1, 0)); | 179 MAP_SHARED|MAP_ANONYMOUS, -1, 0)); |
| 179 if (secureArena == MAP_FAILED) { | 180 if (secureArena == MAP_FAILED) { |
| 180 die("Failed to allocate secure memory arena"); | 181 die("Failed to allocate secure memory arena"); |
| 181 } | 182 } |
| 182 | 183 |
| 183 // Set up the mutex to be accessible from the trusted process and from | 184 // Set up the mutex to be accessible from the trusted process and from |
| 184 // children of the trusted thread(s) | 185 // children of the trusted thread(s) |
| 185 if (mmap(&syscall_mutex_, 4096, PROT_READ|PROT_WRITE, | 186 if (mmap(&syscall_mutex_, 4096, PROT_READ|PROT_WRITE, |
| 186 MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0) != &syscall_mutex_) { | 187 MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0) != &syscall_mutex_) { |
| 187 die("Failed to initialize secure mutex"); | 188 die("Failed to initialize secure mutex"); |
| 188 } | 189 } |
| 189 syscall_mutex_ = 0x80000000; | 190 syscall_mutex_ = 0x80000000; |
| 190 | 191 |
| 191 | 192 |
| 192 // Hold on to a file handle in the parent's process directory. We can use | |
| 193 // this later to reliably tell if the parent died. | |
| 194 int parentProc = open("/proc/self/", O_RDONLY|O_DIRECTORY); | |
| 195 if (parentProc < 0) { | |
| 196 die("Failed to access /proc/self"); | |
| 197 } | |
| 198 | |
| 199 // Create a trusted process that can evaluate system call parameters and | 193 // Create a trusted process that can evaluate system call parameters and |
| 200 // decide whether a system call should execute. This process runs outside of | 194 // decide whether a system call should execute. This process runs outside of |
| 201 // the seccomp sandbox. It communicates with the sandbox'd process through | 195 // the seccomp sandbox. It communicates with the sandbox'd process through |
| 202 // a socketpair() and through securely shared memory. | 196 // a socketpair() and through securely shared memory. |
| 203 pid_t pid = fork(); | 197 pid_t pid = fork(); |
| 204 if (pid < 0) { | 198 if (pid < 0) { |
| 205 die("Failed to create trusted process"); | 199 die("Failed to create trusted process"); |
| 206 } | 200 } |
| 207 if (!pid) { | 201 if (!pid) { |
| 208 // Close all file handles except for sandboxFd, cloneFd, and stdio | 202 // Close all file handles except for sandboxFd, cloneFd, and stdio |
| 209 DIR *dir = opendir("/proc/self/fd"); | 203 DIR *dir = opendir("/proc/self/fd"); |
| 210 if (dir == 0) { | 204 if (dir == 0) { |
| 211 // If we don't know the list of our open file handles, just try closing | 205 // If we don't know the list of our open file handles, just try closing |
| 212 // all valid ones. | 206 // all valid ones. |
| 213 for (int fd = sysconf(_SC_OPEN_MAX); --fd > 2; ) { | 207 for (int fd = sysconf(_SC_OPEN_MAX); --fd > 2; ) { |
| 214 if (fd != parentProc && fd != sandboxFd && fd != cloneFd) { | 208 if (fd != sandboxFd && fd != cloneFd) { |
| 215 close(fd); | 209 close(fd); |
| 216 } | 210 } |
| 217 } | 211 } |
| 218 } else { | 212 } else { |
| 219 // If available, if is much more efficient to just close the file | 213 // If available, if is much more efficient to just close the file |
| 220 // handles that show up in /proc/self/fd/ | 214 // handles that show up in /proc/self/fd/ |
| 221 struct dirent de, *res; | 215 struct dirent de, *res; |
| 222 while (!readdir_r(dir, &de, &res) && res) { | 216 while (!readdir_r(dir, &de, &res) && res) { |
| 223 if (res->d_name[0] < '0') | 217 if (res->d_name[0] < '0') |
| 224 continue; | 218 continue; |
| 225 int fd = atoi(res->d_name); | 219 int fd = atoi(res->d_name); |
| 226 if (fd > 2 && | 220 if (fd > 2 && |
| 227 fd != parentProc && fd != sandboxFd && fd != cloneFd && | 221 fd != sandboxFd && fd != cloneFd && fd != dirfd(dir)) { |
| 228 fd != dirfd(dir)) { | |
| 229 close(fd); | 222 close(fd); |
| 230 } | 223 } |
| 231 } | 224 } |
| 232 closedir(dir); | 225 closedir(dir); |
| 233 } | 226 } |
| 234 | 227 |
| 235 // Initialize secure memory used for threads | 228 // Initialize secure memory used for threads |
| 236 for (int i = 0; i < kMaxThreads; i++) { | 229 for (int i = 0; i < kMaxThreads; i++) { |
| 237 SecureMem::Args* args = secureArena + i; | 230 SecureMem::Args* args = secureArena + i; |
| 238 args->self = args; | 231 args->self = args; |
| 239 #ifndef NDEBUG | 232 #ifndef NDEBUG |
| 240 args->allowAllSystemCalls= Debug::isEnabled(); | 233 args->allowAllSystemCalls= Debug::isEnabled(); |
| 241 #endif | 234 #endif |
| 242 } | 235 } |
| 243 | 236 |
| 244 initializeProtectedMap(sandboxFd); | 237 int parentMapsFd = initializeProtectedMap(sandboxFd); |
| 245 trustedProcess(parentProc, processFdPub, sandboxFd, cloneFd, secureArena); | 238 trustedProcess(parentMapsFd, processFdPub, sandboxFd, |
| 239 cloneFd, secureArena); |
| 246 die(); | 240 die(); |
| 247 } | 241 } |
| 248 | 242 |
| 249 // We are still in the untrusted code. Deny access to restricted resources. | 243 // We are still in the untrusted code. Deny access to restricted resources. |
| 250 mprotect(secureArena, 8192*kMaxThreads, PROT_NONE); | 244 mprotect(secureArena, 8192*kMaxThreads, PROT_NONE); |
| 251 mprotect(&syscall_mutex_, 4096, PROT_NONE); | 245 mprotect(&syscall_mutex_, 4096, PROT_NONE); |
| 252 close(parentProc); | |
| 253 close(sandboxFd); | 246 close(sandboxFd); |
| 254 | 247 |
| 255 return secureArena; | 248 return secureArena; |
| 256 } | 249 } |
| 257 | 250 |
| 258 } // namespace | 251 } // namespace |
| OLD | NEW |