summaryrefslogtreecommitdiffstats
path: root/sandbox/linux/seccomp/trusted_process.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/linux/seccomp/trusted_process.cc')
-rw-r--r--sandbox/linux/seccomp/trusted_process.cc258
1 files changed, 258 insertions, 0 deletions
diff --git a/sandbox/linux/seccomp/trusted_process.cc b/sandbox/linux/seccomp/trusted_process.cc
new file mode 100644
index 0000000..b4bee94
--- /dev/null
+++ b/sandbox/linux/seccomp/trusted_process.cc
@@ -0,0 +1,258 @@
+#include <dirent.h>
+#include <map>
+
+#include "debug.h"
+#include "sandbox_impl.h"
+#include "syscall_table.h"
+
+namespace playground {
+
+struct Thread {
+ int fdPub, fd;
+ SecureMem::Args* mem;
+};
+
+SecureMem::Args* Sandbox::getSecureMem() {
+ if (!secureMemPool_.empty()) {
+ SecureMem::Args* rc = secureMemPool_.back();
+ secureMemPool_.pop_back();
+ return rc;
+ }
+ return NULL;
+}
+
+void Sandbox::trustedProcess(int parentProc, int processFdPub, int sandboxFd,
+ int cloneFd, SecureMem::Args* secureArena) {
+ std::map<long long, struct Thread> threads;
+ SysCalls sys;
+ long long cookie = 0;
+
+ // The very first entry in the secure memory arena has been assigned to the
+ // initial thread. The remaining entries are available for allocation.
+ SecureMem::Args* startAddress = secureArena;
+ SecureMem::Args* nextThread = startAddress;
+ for (int i = 0; i < kMaxThreads-1; i++) {
+ secureMemPool_.push_back(++startAddress);
+ }
+
+newThreadCreated:
+ // Receive information from newly created thread
+ Thread *newThread = &threads[++cookie];
+ memset(newThread, 0, sizeof(Thread));
+ struct {
+ SecureMem::Args* self;
+ int tid;
+ int fdPub;
+ } __attribute__((packed)) data;
+
+ size_t dataLen = sizeof(data);
+ if (!getFd(cloneFd, &newThread->fdPub, &newThread->fd, &data, &dataLen) ||
+ dataLen != sizeof(data)) {
+ // We get here either because the sandbox got corrupted, or because our
+ // parent process has terminated.
+ if (newThread->fdPub || dataLen) {
+ die("Failed to receive new thread information");
+ }
+ die();
+ }
+ if (data.self != nextThread) {
+ // The only potentially security critical information received from the
+ // newly created thread is "self". The "tid" is for informational purposes
+ // (and for use in the new thread's TLS), and "fdPub" is uncritical as all
+ // file descriptors are considered untrusted.
+ // Thus, we only use "self" for a sanity check, but don't actually trust
+ // it beyond that.
+ die("Received corrupted thread information");
+ }
+ newThread->mem = nextThread;
+
+ // Set up TLS area and let thread know that the data is now ready
+ nextThread->cookie = cookie;
+ nextThread->threadId = data.tid;
+ nextThread->threadFdPub = data.fdPub;
+ write(sys, newThread->fd, "", 1);
+
+ // Dispatch system calls that have been forwarded from the trusted thread(s).
+ for (;;) {
+ struct {
+ unsigned int sysnum;
+ long long cookie;
+ } __attribute__((packed)) header;
+
+ int rc;
+ if ((rc = read(sys, sandboxFd, &header, sizeof(header))) !=sizeof(header)){
+ if (rc) {
+ die("Failed to read system call number and thread id");
+ }
+ die();
+ }
+ std::map<long long, struct Thread>::iterator iter =
+ threads.find(header.cookie);
+ if (iter == threads.end()) {
+ die("Received request from unknown thread");
+ }
+ struct Thread* currentThread = &iter->second;
+ if (header.sysnum > maxSyscall ||
+ !syscallTable[header.sysnum].trustedProcess) {
+ die("Trusted process encountered unexpected system call");
+ }
+
+ // Dispatch system call to handler function. Treat both exit() and clone()
+ // specially.
+ if (syscallTable[header.sysnum].trustedProcess(parentProc,
+ sandboxFd,
+ currentThread->fdPub,
+ currentThread->fd,
+ currentThread->mem) &&
+ header.sysnum == __NR_clone) {
+ nextThread = currentThread->mem->newSecureMem;
+ goto newThreadCreated;
+ } else if (header.sysnum == __NR_exit) {
+ NOINTR_SYS(sys.close(iter->second.fdPub));
+ NOINTR_SYS(sys.close(iter->second.fd));
+ SecureMem::Args* secureMem = currentThread->mem;
+ threads.erase(iter);
+ secureMemPool_.push_back(secureMem);
+ }
+ }
+}
+
+void Sandbox::initializeProtectedMap(int fd) {
+ int mapsFd;
+ if (!getFd(fd, &mapsFd, NULL, NULL, NULL)) {
+ maps_failure:
+ die("Cannot access /proc/self/maps");
+ }
+
+ // Read the memory mappings as they were before the sandbox takes effect.
+ // These mappings cannot be changed by the sandboxed process.
+ char line[80];
+ FILE *fp = fdopen(mapsFd, "r");
+ for (bool truncated = false;;) {
+ if (fgets(line, sizeof(line), fp) == NULL) {
+ if (feof(fp) || errno != EINTR) {
+ break;
+ }
+ continue;
+ }
+ if (!truncated) {
+ unsigned long start, stop;
+ char *ptr = line;
+ errno = 0;
+ start = strtoul(ptr, &ptr, 16);
+ if (errno || *ptr++ != '-') {
+ parse_failure:
+ die("Failed to parse /proc/self/maps");
+ }
+ stop = strtoul(ptr, &ptr, 16);
+ if (errno || *ptr++ != ' ') {
+ goto parse_failure;
+ }
+ protectedMap_[reinterpret_cast<void *>(start)] = stop - start;
+ }
+ truncated = strchr(line, '\n') == NULL;
+ }
+ SysCalls sys;
+ NOINTR_SYS(sys.close(mapsFd));
+
+ // Prevent low address memory allocations. Some buggy kernels allow those
+ if (protectedMap_[0] < (64 << 10)) {
+ protectedMap_[0] = 64 << 10;
+ }
+
+ // Let the sandbox know that we are done parsing the memory map.
+ if (write(sys, fd, &mapsFd, sizeof(mapsFd)) != sizeof(mapsFd)) {
+ goto maps_failure;
+ }
+}
+
+SecureMem::Args* Sandbox::createTrustedProcess(int processFdPub, int sandboxFd,
+ int cloneFdPub, int cloneFd) {
+ // Allocate memory that will be used by an arena for storing the secure
+ // memory. While we allow this memory area to be empty at times (e.g. when
+ // not all threads are in use), we make sure that it never gets overwritten
+ // by user-allocated memory. This happens in initializeProtectedMap() and
+ // snapshotMemoryMappings().
+ SecureMem::Args* secureArena = reinterpret_cast<SecureMem::Args*>(
+ mmap(NULL, 8192*kMaxThreads, PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_ANONYMOUS, -1, 0));
+ if (secureArena == MAP_FAILED) {
+ die("Failed to allocate secure memory arena");
+ }
+
+ // Set up the mutex to be accessible from the trusted process and from
+ // children of the trusted thread(s)
+ if (mmap(&syscall_mutex_, 4096, PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0) != &syscall_mutex_) {
+ die("Failed to initialize secure mutex");
+ }
+ syscall_mutex_ = 0x80000000;
+
+
+ // Hold on to a file handle in the parent's process directory. We can use
+ // this later to reliably tell if the parent died.
+ int parentProc = open("/proc/self/", O_RDONLY|O_DIRECTORY);
+ if (parentProc < 0) {
+ die("Failed to access /proc/self");
+ }
+
+ // Create a trusted process that can evaluate system call parameters and
+ // decide whether a system call should execute. This process runs outside of
+ // the seccomp sandbox. It communicates with the sandbox'd process through
+ // a socketpair() and through securely shared memory.
+ pid_t pid = fork();
+ if (pid < 0) {
+ die("Failed to create trusted process");
+ }
+ if (!pid) {
+ // Close all file handles except for sandboxFd, cloneFd, and stdio
+ DIR *dir = opendir("/proc/self/fd");
+ if (dir == 0) {
+ // If we don't know the list of our open file handles, just try closing
+ // all valid ones.
+ for (int fd = sysconf(_SC_OPEN_MAX); --fd > 2; ) {
+ if (fd != parentProc && fd != sandboxFd && fd != cloneFd) {
+ close(fd);
+ }
+ }
+ } else {
+ // If available, if is much more efficient to just close the file
+ // handles that show up in /proc/self/fd/
+ struct dirent de, *res;
+ while (!readdir_r(dir, &de, &res) && res) {
+ if (res->d_name[0] < '0')
+ continue;
+ int fd = atoi(res->d_name);
+ if (fd > 2 &&
+ fd != parentProc && fd != sandboxFd && fd != cloneFd &&
+ fd != dirfd(dir)) {
+ close(fd);
+ }
+ }
+ closedir(dir);
+ }
+
+ // Initialize secure memory used for threads
+ for (int i = 0; i < kMaxThreads; i++) {
+ SecureMem::Args* args = secureArena + i;
+ args->self = args;
+ #ifndef NDEBUG
+ args->allowAllSystemCalls= Debug::isEnabled();
+ #endif
+ }
+
+ initializeProtectedMap(sandboxFd);
+ trustedProcess(parentProc, processFdPub, sandboxFd, cloneFd, secureArena);
+ die();
+ }
+
+ // We are still in the untrusted code. Deny access to restricted resources.
+ mprotect(secureArena, 8192*kMaxThreads, PROT_NONE);
+ mprotect(&syscall_mutex_, 4096, PROT_NONE);
+ close(parentProc);
+ close(sandboxFd);
+
+ return secureArena;
+}
+
+} // namespace