diff options
Diffstat (limited to 'security/sandbox/linux/broker')
-rw-r--r-- | security/sandbox/linux/broker/SandboxBroker.cpp | 731 | ||||
-rw-r--r-- | security/sandbox/linux/broker/SandboxBroker.h | 132 | ||||
-rw-r--r-- | security/sandbox/linux/broker/SandboxBrokerCommon.cpp | 120 | ||||
-rw-r--r-- | security/sandbox/linux/broker/SandboxBrokerCommon.h | 72 | ||||
-rw-r--r-- | security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp | 194 | ||||
-rw-r--r-- | security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h | 32 | ||||
-rw-r--r-- | security/sandbox/linux/broker/SandboxBrokerUtils.h | 30 | ||||
-rw-r--r-- | security/sandbox/linux/broker/moz.build | 37 |
8 files changed, 1348 insertions, 0 deletions
diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp new file mode 100644 index 000000000..a31d1fc66 --- /dev/null +++ b/security/sandbox/linux/broker/SandboxBroker.cpp @@ -0,0 +1,731 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SandboxBroker.h" +#include "SandboxInfo.h" +#include "SandboxLogging.h" +#include "SandboxBrokerUtils.h" + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#ifdef XP_LINUX +#include <sys/prctl.h> +#endif + +#ifdef MOZ_WIDGET_GONK +#include <private/android_filesystem_config.h> +#include <sys/syscall.h> +#endif + +#include "mozilla/Assertions.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Move.h" +#include "mozilla/NullPtr.h" +#include "mozilla/Sprintf.h" +#include "mozilla/ipc/FileDescriptor.h" +#include "sandbox/linux/system_headers/linux_syscalls.h" + +namespace mozilla { + +// This constructor signals failure by setting mFileDesc and aClientFd to -1. +SandboxBroker::SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, + int& aClientFd) + : mChildPid(aChildPid), mPolicy(Move(aPolicy)) +{ + int fds[2]; + if (0 != socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) { + SANDBOX_LOG_ERROR("SandboxBroker: socketpair failed: %s", strerror(errno)); + mFileDesc = -1; + aClientFd = -1; + return; + } + mFileDesc = fds[0]; + aClientFd = fds[1]; + + if (!PlatformThread::Create(0, this, &mThread)) { + SANDBOX_LOG_ERROR("SandboxBroker: thread creation failed: %s", + strerror(errno)); + close(mFileDesc); + close(aClientFd); + mFileDesc = -1; + aClientFd = -1; + } +} + +UniquePtr<SandboxBroker> +SandboxBroker::Create(UniquePtr<const Policy> aPolicy, int aChildPid, + ipc::FileDescriptor& aClientFdOut) +{ + int clientFd; + // Can't use MakeUnique here because the constructor is private. + UniquePtr<SandboxBroker> rv(new SandboxBroker(Move(aPolicy), aChildPid, + clientFd)); + if (clientFd < 0) { + rv = nullptr; + } else { + aClientFdOut = ipc::FileDescriptor(clientFd); + } + return Move(rv); +} + +SandboxBroker::~SandboxBroker() { + // If the constructor failed, there's nothing to be done here. + if (mFileDesc < 0) { + return; + } + + shutdown(mFileDesc, SHUT_RD); + // The thread will now get EOF even if the client hasn't exited. + PlatformThread::Join(mThread); + // Now that the thread has exited, the fd will no longer be accessed. + close(mFileDesc); + // Having ensured that this object outlives the thread, this + // destructor can now return. +} + +SandboxBroker::Policy::Policy() { } +SandboxBroker::Policy::~Policy() { } + +SandboxBroker::Policy::Policy(const Policy& aOther) { + for (auto iter = aOther.mMap.ConstIter(); !iter.Done(); iter.Next()) { + mMap.Put(iter.Key(), iter.Data()); + } +} + +// Chromium +// sandbox/linux/syscall_broker/broker_file_permission.cc +// Async signal safe +bool +SandboxBroker::Policy::ValidatePath(const char* path) const { + if (!path) + return false; + + const size_t len = strlen(path); + // No empty paths + if (len == 0) + return false; + // Paths must be absolute and not relative + if (path[0] != '/') + return false; + // No trailing / (but "/" is valid) + if (len > 1 && path[len - 1] == '/') + return false; + // No trailing /. + if (len >= 2 && path[len - 2] == '/' && path[len - 1] == '.') + return false; + // No trailing /.. + if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' && + path[len - 1] == '.') + return false; + // No /../ anywhere + for (size_t i = 0; i < len; i++) { + if (path[i] == '/' && (len - i) > 3) { + if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') { + return false; + } + } + } + return true; +} + +void +SandboxBroker::Policy::AddPath(int aPerms, const char* aPath, + AddCondition aCond) +{ + nsDependentCString path(aPath); + MOZ_ASSERT(path.Length() <= kMaxPathLen); + int perms; + if (aCond == AddIfExistsNow) { + struct stat statBuf; + if (lstat(aPath, &statBuf) != 0) { + return; + } + } + if (!mMap.Get(path, &perms)) { + perms = MAY_ACCESS; + } else { + MOZ_ASSERT(perms & MAY_ACCESS); + } + if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { + SANDBOX_LOG_ERROR("policy for %s: %d -> %d", aPath, perms, perms | aPerms); + } + perms |= aPerms; + mMap.Put(path, perms); +} + +void +SandboxBroker::Policy::AddTree(int aPerms, const char* aPath) +{ + struct stat statBuf; + + if (stat(aPath, &statBuf) != 0) { + return; + } + if (!S_ISDIR(statBuf.st_mode)) { + AddPath(aPerms, aPath, AddAlways); + } else { + DIR* dirp = opendir(aPath); + if (!dirp) { + return; + } + while (struct dirent* de = readdir(dirp)) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) { + continue; + } + // Note: could optimize the string handling. + nsAutoCString subPath; + subPath.Assign(aPath); + subPath.Append('/'); + subPath.Append(de->d_name); + AddTree(aPerms, subPath.get()); + } + closedir(dirp); + } +} + +void +SandboxBroker::Policy::AddDir(int aPerms, const char* aPath) +{ + struct stat statBuf; + + if (stat(aPath, &statBuf) != 0) { + return; + } + + if (!S_ISDIR(statBuf.st_mode)) { + return; + } + + nsDependentCString path(aPath); + MOZ_ASSERT(path.Length() <= kMaxPathLen - 1); + // Enforce trailing / on aPath + if (path[path.Length() - 1] != '/') { + path.Append('/'); + } + int origPerms; + if (!mMap.Get(path, &origPerms)) { + origPerms = MAY_ACCESS; + } else { + MOZ_ASSERT(origPerms & MAY_ACCESS); + } + int newPerms = origPerms | aPerms | RECURSIVE; + if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { + SANDBOX_LOG_ERROR("policy for %s: %d -> %d", aPath, origPerms, newPerms); + } + mMap.Put(path, newPerms); +} + +void +SandboxBroker::Policy::AddPrefix(int aPerms, const char* aDir, + const char* aPrefix) +{ + size_t prefixLen = strlen(aPrefix); + DIR* dirp = opendir(aDir); + struct dirent* de; + if (!dirp) { + return; + } + while ((de = readdir(dirp))) { + if (strncmp(de->d_name, aPrefix, prefixLen) == 0) { + nsAutoCString subPath; + subPath.Assign(aDir); + subPath.Append('/'); + subPath.Append(de->d_name); + AddPath(aPerms, subPath.get(), AddAlways); + } + } + closedir(dirp); +} + +int +SandboxBroker::Policy::Lookup(const nsACString& aPath) const +{ + // Early exit for paths explicitly found in the + // whitelist. + // This means they will not gain extra permissions + // from recursive paths. + int perms = mMap.Get(aPath); + if (perms) { + return perms; + } + + // Not a legally constructed path + if (!ValidatePath(PromiseFlatCString(aPath).get())) + return 0; + + // Now it's either an illegal access, or a recursive + // directory permission. We'll have to check the entire + // whitelist for the best match (slower). + int allPerms = 0; + for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) { + const nsACString& whiteListPath = iter.Key(); + const int& perms = iter.Data(); + + if (!(perms & RECURSIVE)) + continue; + + // passed part starts with something on the whitelist + if (StringBeginsWith(aPath, whiteListPath)) { + allPerms |= perms; + } + } + + // Strip away the RECURSIVE flag as it doesn't + // necessarily apply to aPath. + return allPerms & ~RECURSIVE; +} + +static bool +AllowOperation(int aReqFlags, int aPerms) +{ + int needed = 0; + if (aReqFlags & R_OK) { + needed |= SandboxBroker::MAY_READ; + } + if (aReqFlags & W_OK) { + needed |= SandboxBroker::MAY_WRITE; + } + // We don't really allow executing anything, + // so in true unix tradition we hijack this + // for directories. + if (aReqFlags & X_OK) { + needed |= SandboxBroker::MAY_CREATE; + } + return (aPerms & needed) == needed; +} + +static bool +AllowAccess(int aReqFlags, int aPerms) +{ + if (aReqFlags & ~(R_OK|W_OK|F_OK)) { + return false; + } + int needed = 0; + if (aReqFlags & R_OK) { + needed |= SandboxBroker::MAY_READ; + } + if (aReqFlags & W_OK) { + needed |= SandboxBroker::MAY_WRITE; + } + return (aPerms & needed) == needed; +} + +// These flags are added to all opens to prevent possible side-effects +// on this process. These shouldn't be relevant to the child process +// in any case due to the sandboxing restrictions on it. (See also +// the use of MSG_CMSG_CLOEXEC in SandboxBrokerCommon.cpp). +static const int kRequiredOpenFlags = O_CLOEXEC | O_NOCTTY; + +// Linux originally assigned a flag bit to O_SYNC but implemented the +// semantics standardized as O_DSYNC; later, that bit was renamed and +// a new bit was assigned to the full O_SYNC, and O_SYNC was redefined +// to be both bits. As a result, this #define is needed to compensate +// for outdated kernel headers like Android's. +#define O_SYNC_NEW 04010000 +static const int kAllowedOpenFlags = + O_APPEND | O_ASYNC | O_DIRECT | O_DIRECTORY | O_EXCL | O_LARGEFILE + | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK | O_NDELAY | O_SYNC_NEW + | O_TRUNC | O_CLOEXEC | O_CREAT; +#undef O_SYNC_NEW + +static bool +AllowOpen(int aReqFlags, int aPerms) +{ + if (aReqFlags & ~O_ACCMODE & ~kAllowedOpenFlags) { + return false; + } + int needed; + switch(aReqFlags & O_ACCMODE) { + case O_RDONLY: + needed = SandboxBroker::MAY_READ; + break; + case O_WRONLY: + needed = SandboxBroker::MAY_WRITE; + break; + case O_RDWR: + needed = SandboxBroker::MAY_READ | SandboxBroker::MAY_WRITE; + break; + default: + return false; + } + if (aReqFlags & O_CREAT) { + needed |= SandboxBroker::MAY_CREATE; + } + return (aPerms & needed) == needed; +} + +static int +DoStat(const char* aPath, void* aBuff, int aFlags) +{ + if (aFlags & O_NOFOLLOW) { + return lstatsyscall(aPath, (statstruct*)aBuff); + } + return statsyscall(aPath, (statstruct*)aBuff); +} + +static int +DoLink(const char* aPath, const char* aPath2, + SandboxBrokerCommon::Operation aOper) +{ + if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_LINK) { + return link(aPath, aPath2); + } else if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_SYMLINK) { + return symlink(aPath, aPath2); + } + MOZ_CRASH("SandboxBroker: Unknown link operation"); +} + +size_t +SandboxBroker::ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen) +{ + if (strstr(aPath, "..") != NULL) { + char* result = realpath(aPath, NULL); + if (result != NULL) { + strncpy(aPath, result, aBufSize); + aPath[aBufSize - 1] = '\0'; + free(result); + // Size changed, but guaranteed to be 0 terminated + aPathLen = strlen(aPath); + } + // ValidatePath will handle failure to translate + } + return aPathLen; +} + +void +SandboxBroker::ThreadMain(void) +{ + char threadName[16]; + SprintfLiteral(threadName, "FS Broker %d", mChildPid); + PlatformThread::SetName(threadName); + + // Permissive mode can only be enabled through an environment variable, + // therefore it is sufficient to fetch the value once + // before the main thread loop starts + bool permissive = SandboxInfo::Get().Test(SandboxInfo::kPermissive); + +#ifdef MOZ_WIDGET_GONK +#ifdef __NR_setreuid32 + static const long nr_setreuid = __NR_setreuid32; + static const long nr_setregid = __NR_setregid32; +#else + static const long nr_setreuid = __NR_setreuid; + static const long nr_setregid = __NR_setregid; +#endif + if (syscall(nr_setregid, getgid(), AID_APP + mChildPid) != 0 || + syscall(nr_setreuid, getuid(), AID_APP + mChildPid) != 0) { + MOZ_CRASH("SandboxBroker: failed to drop privileges"); + } +#endif + + while (true) { + struct iovec ios[2]; + // We will receive the path strings in 1 buffer and split them back up. + char recvBuf[2 * (kMaxPathLen + 1)]; + char pathBuf[kMaxPathLen + 1]; + char pathBuf2[kMaxPathLen + 1]; + size_t pathLen; + size_t pathLen2; + char respBuf[kMaxPathLen + 1]; // Also serves as struct stat + Request req; + Response resp; + int respfd; + + // Make sure stat responses fit in the response buffer + MOZ_ASSERT((kMaxPathLen + 1) > sizeof(struct stat)); + + // This makes our string handling below a bit less error prone. + memset(recvBuf, 0, sizeof(recvBuf)); + + ios[0].iov_base = &req; + ios[0].iov_len = sizeof(req); + ios[1].iov_base = recvBuf; + ios[1].iov_len = sizeof(recvBuf); + + const ssize_t recvd = RecvWithFd(mFileDesc, ios, 2, &respfd); + if (recvd == 0) { + if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { + SANDBOX_LOG_ERROR("EOF from pid %d", mChildPid); + } + break; + } + // It could be possible to continue after errors and short reads, + // at least in some cases, but protocol violation indicates a + // hostile client, so terminate the broker instead. + if (recvd < 0) { + SANDBOX_LOG_ERROR("bad read from pid %d: %s", + mChildPid, strerror(errno)); + shutdown(mFileDesc, SHUT_RD); + break; + } + if (recvd < static_cast<ssize_t>(sizeof(req))) { + SANDBOX_LOG_ERROR("bad read from pid %d (%d < %d)", + mChildPid, recvd, sizeof(req)); + shutdown(mFileDesc, SHUT_RD); + break; + } + if (respfd == -1) { + SANDBOX_LOG_ERROR("no response fd from pid %d", mChildPid); + shutdown(mFileDesc, SHUT_RD); + break; + } + + // Initialize the response with the default failure. + memset(&resp, 0, sizeof(resp)); + memset(&respBuf, 0, sizeof(respBuf)); + resp.mError = -EACCES; + ios[0].iov_base = &resp; + ios[0].iov_len = sizeof(resp); + ios[1].iov_base = nullptr; + ios[1].iov_len = 0; + int openedFd = -1; + + // Clear permissions + int perms; + + // Find end of first string, make sure the buffer is still + // 0 terminated. + size_t recvBufLen = static_cast<size_t>(recvd) - sizeof(req); + if (recvBufLen > 0 && recvBuf[recvBufLen - 1] != 0) { + SANDBOX_LOG_ERROR("corrupted path buffer from pid %d", mChildPid); + shutdown(mFileDesc, SHUT_RD); + break; + } + + // First path should fit in maximum path length buffer. + size_t first_len = strlen(recvBuf); + if (first_len <= kMaxPathLen) { + strcpy(pathBuf, recvBuf); + // Skip right over the terminating 0, and try to copy in the + // second path, if any. If there's no path, this will hit a + // 0 immediately (we nulled the buffer before receiving). + // We do not assume the second path is 0-terminated, this is + // enforced below. + strncpy(pathBuf2, recvBuf + first_len + 1, kMaxPathLen + 1); + + // First string is guaranteed to be 0-terminated. + pathLen = first_len; + + // Look up the first pathname but first translate relative paths. + pathLen = ConvertToRealPath(pathBuf, sizeof(pathBuf), pathLen); + perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen)); + + // Same for the second path. + pathLen2 = strnlen(pathBuf2, kMaxPathLen); + if (pathLen2 > 0) { + // Force 0 termination. + pathBuf2[pathLen2] = '\0'; + pathLen2 = ConvertToRealPath(pathBuf2, sizeof(pathBuf2), pathLen2); + int perms2 = mPolicy->Lookup(nsDependentCString(pathBuf2, pathLen2)); + + // Take the intersection of the permissions for both paths. + perms &= perms2; + } + } else { + // Failed to receive intelligible paths. + perms = 0; + } + + // And now perform the operation if allowed. + if (perms & CRASH_INSTEAD) { + // This is somewhat nonmodular, but it works. + resp.mError = -ENOSYS; + } else if (permissive || perms & MAY_ACCESS) { + // If the operation was only allowed because of permissive mode, log it. + if (permissive && !(perms & MAY_ACCESS)) { + AuditPermissive(req.mOp, req.mFlags, perms, pathBuf); + } + + switch(req.mOp) { + case SANDBOX_FILE_OPEN: + if (permissive || AllowOpen(req.mFlags, perms)) { + // Permissions for O_CREAT hardwired to 0600; if that's + // ever a problem we can change the protocol (but really we + // should be trying to remove uses of MAY_CREATE, not add + // new ones). + openedFd = open(pathBuf, req.mFlags | kRequiredOpenFlags, 0600); + if (openedFd >= 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_ACCESS: + if (permissive || AllowAccess(req.mFlags, perms)) { + // This can't use access() itself because that uses the ruid + // and not the euid. In theory faccessat() with AT_EACCESS + // would work, but Linux doesn't actually implement the + // flags != 0 case; glibc has a hack which doesn't even work + // in this case so it'll ignore the flag, and Bionic just + // passes through the syscall and always ignores the flags. + // + // Instead, because we've already checked the requested + // r/w/x bits against the policy, just return success if the + // file exists and hope that's close enough. + if (stat(pathBuf, (struct stat*)&respBuf) == 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_STAT: + if (DoStat(pathBuf, (struct stat*)&respBuf, req.mFlags) == 0) { + resp.mError = 0; + ios[1].iov_base = &respBuf; + ios[1].iov_len = req.mBufSize; + } else { + resp.mError = -errno; + } + break; + + case SANDBOX_FILE_CHMOD: + if (permissive || AllowOperation(W_OK, perms)) { + if (chmod(pathBuf, req.mFlags) == 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_LINK: + case SANDBOX_FILE_SYMLINK: + if (permissive || AllowOperation(W_OK, perms)) { + if (DoLink(pathBuf, pathBuf2, req.mOp) == 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_RENAME: + if (permissive || AllowOperation(W_OK, perms)) { + if (rename(pathBuf, pathBuf2) == 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_MKDIR: + if (permissive || AllowOperation(W_OK | X_OK, perms)) { + if (mkdir(pathBuf, req.mFlags) == 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_UNLINK: + if (permissive || AllowOperation(W_OK, perms)) { + if (unlink(pathBuf) == 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_RMDIR: + if (permissive || AllowOperation(W_OK | X_OK, perms)) { + if (rmdir(pathBuf) == 0) { + resp.mError = 0; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + + case SANDBOX_FILE_READLINK: + if (permissive || AllowOperation(R_OK, perms)) { + ssize_t respSize = readlink(pathBuf, (char*)&respBuf, sizeof(respBuf)); + if (respSize >= 0) { + resp.mError = respSize; + ios[1].iov_base = &respBuf; + ios[1].iov_len = respSize; + } else { + resp.mError = -errno; + } + } else { + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + break; + } + } else { + MOZ_ASSERT(perms == 0); + AuditDenial(req.mOp, req.mFlags, perms, pathBuf); + } + + const size_t numIO = ios[1].iov_len > 0 ? 2 : 1; + DebugOnly<const ssize_t> sent = SendWithFd(respfd, ios, numIO, openedFd); + close(respfd); + MOZ_ASSERT(sent < 0 || + static_cast<size_t>(sent) == ios[0].iov_len + ios[1].iov_len); + + if (openedFd >= 0) { + close(openedFd); + } + } +} + +void +SandboxBroker::AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath) +{ + MOZ_RELEASE_ASSERT(SandboxInfo::Get().Test(SandboxInfo::kPermissive)); + + struct stat statBuf; + + if (lstat(aPath, &statBuf) == 0) { + // Path exists, set errno to 0 to indicate "success". + errno = 0; + } + + SANDBOX_LOG_ERROR("SandboxBroker: would have denied op=%d rflags=%o perms=%d path=%s for pid=%d" \ + " permissive=1 error=\"%s\"", aOp, aFlags, aPerms, + aPath, mChildPid, strerror(errno)); +} + +void +SandboxBroker::AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath) +{ + if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { + SANDBOX_LOG_ERROR("SandboxBroker: denied op=%d rflags=%o perms=%d path=%s for pid=%d" \ + " error=\"%s\"", aOp, aFlags, aPerms, aPath, mChildPid, + strerror(errno)); + } +} + + +} // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBroker.h b/security/sandbox/linux/broker/SandboxBroker.h new file mode 100644 index 000000000..bb4570a64 --- /dev/null +++ b/security/sandbox/linux/broker/SandboxBroker.h @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_SandboxBroker_h +#define mozilla_SandboxBroker_h + +#include "mozilla/SandboxBrokerCommon.h" + +#include "base/platform_thread.h" +#include "mozilla/Attributes.h" +#include "mozilla/UniquePtr.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsString.h" + +namespace mozilla { + +namespace ipc { +class FileDescriptor; +} + +// This class implements a broker for filesystem operations requested +// by a sandboxed child process -- opening files and accessing their +// metadata. (This is necessary in order to restrict access by path; +// seccomp-bpf can filter only on argument register values, not +// parameters passed in memory like pathnames.) +// +// The broker currently runs on a thread in the parent process (with +// effective uid changed on B2G), which is for memory efficiency +// (compared to forking a process) and simplicity (compared to having +// a separate executable and serializing/deserializing the policy). +// +// See also ../SandboxBrokerClient.h for the corresponding client. + +class SandboxBroker final + : private SandboxBrokerCommon + , public PlatformThread::Delegate +{ + public: + enum Perms { + MAY_ACCESS = 1 << 0, + MAY_READ = 1 << 1, + MAY_WRITE = 1 << 2, + MAY_CREATE = 1 << 3, + // This flag is for testing policy changes -- when the client is + // used with the seccomp-bpf integration, an access to this file + // will invoke a crash dump with the context of the syscall. + // (This overrides all other flags.) + CRASH_INSTEAD = 1 << 4, + // Applies to everything below this path, including subdirs created + // at runtime + RECURSIVE = 1 << 5, + }; + // Bitwise operations on enum values return ints, so just use int in + // the hash table type (and below) to avoid cluttering code with casts. + typedef nsDataHashtable<nsCStringHashKey, int> PathMap; + + class Policy { + PathMap mMap; + public: + Policy(); + Policy(const Policy& aOther); + ~Policy(); + + enum AddCondition { + AddIfExistsNow, + AddAlways, + }; + // Typically, files that don't exist at policy creation time don't + // need to be whitelisted, but this allows adding entries for + // them if they'll exist later. See also the overload below. + void AddPath(int aPerms, const char* aPath, AddCondition aCond); + // This adds all regular files (not directories) in the tree + // rooted at the given path. + void AddTree(int aPerms, const char* aPath); + // A directory, and all files and directories under it, even those + // added after creation (the dir itself must exist). + void AddDir(int aPerms, const char* aPath); + // All files in a directory with a given prefix; useful for devices. + void AddPrefix(int aPerms, const char* aDir, const char* aPrefix); + // Default: add file if it exists when creating policy or if we're + // conferring permission to create it (log files, etc.). + void AddPath(int aPerms, const char* aPath) { + AddPath(aPerms, aPath, + (aPerms & MAY_CREATE) ? AddAlways : AddIfExistsNow); + } + int Lookup(const nsACString& aPath) const; + int Lookup(const char* aPath) const { + return Lookup(nsDependentCString(aPath)); + } + private: + // ValidatePath checks |path| and returns true if these conditions are met + // * Greater than 0 length + // * Is an absolute path + // * No trailing slash + // * No /../ path traversal + bool ValidatePath(const char* path) const; + }; + + // Constructing a broker involves creating a socketpair and a + // background thread to handle requests, so it can fail. If this + // returns nullptr, do not use the value of aClientFdOut. + static UniquePtr<SandboxBroker> + Create(UniquePtr<const Policy> aPolicy, int aChildPid, + ipc::FileDescriptor& aClientFdOut); + virtual ~SandboxBroker(); + + private: + PlatformThreadHandle mThread; + int mFileDesc; + const int mChildPid; + const UniquePtr<const Policy> mPolicy; + + SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, + int& aClientFd); + void ThreadMain(void) override; + void AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath); + void AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath); + // Remap relative paths to absolute paths. + size_t ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen); + + // Holding a UniquePtr should disallow copying, but to make that explicit: + SandboxBroker(const SandboxBroker&) = delete; + void operator=(const SandboxBroker&) = delete; +}; + +} // namespace mozilla + +#endif // mozilla_SandboxBroker_h diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp new file mode 100644 index 000000000..fe7bc8c45 --- /dev/null +++ b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SandboxBrokerCommon.h" + +#include "mozilla/Assertions.h" + +#include <errno.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#ifndef MSG_CMSG_CLOEXEC +#ifdef XP_LINUX +// As always, Android's kernel headers are somewhat old. +#define MSG_CMSG_CLOEXEC 0x40000000 +#else +// Most of this code can support other POSIX OSes, but being able to +// receive fds and atomically make them close-on-exec is important, +// because this is running in a multithreaded process that can fork. +// In the future, if the broker becomes a dedicated executable, this +// can change. +#error "No MSG_CMSG_CLOEXEC?" +#endif // XP_LINUX +#endif // MSG_CMSG_CLOEXEC + +namespace mozilla { + +/* static */ ssize_t +SandboxBrokerCommon::RecvWithFd(int aFd, const iovec* aIO, size_t aNumIO, + int* aPassedFdPtr) +{ + struct msghdr msg = {}; + msg.msg_iov = const_cast<iovec*>(aIO); + msg.msg_iovlen = aNumIO; + + char cmsg_buf[CMSG_SPACE(sizeof(int))]; + if (aPassedFdPtr) { + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + *aPassedFdPtr = -1; + } + + ssize_t rv; + do { + // MSG_CMSG_CLOEXEC is needed to prevent the parent process from + // accidentally leaking a copy of the child's response socket to a + // new child process. (The child won't be able to exec, so this + // doesn't matter as much for that direction.) + rv = recvmsg(aFd, &msg, MSG_CMSG_CLOEXEC); + } while (rv < 0 && errno == EINTR); + + if (rv <= 0) { + return rv; + } + if (msg.msg_controllen > 0) { + MOZ_ASSERT(aPassedFdPtr); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { + // A client could, for example, send an extra 32-bit int if + // CMSG_SPACE pads to 64-bit size_t alignment. If so, treat + // it as an error, but also don't leak the fds. + for (size_t i = 0; CMSG_LEN(sizeof(int) * i) < cmsg->cmsg_len; ++i) { + close(fds[i]); + } + errno = EMSGSIZE; + return -1; + } + *aPassedFdPtr = fds[0]; + } else { + errno = EPROTO; + return -1; + } + } + if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { + if (aPassedFdPtr && *aPassedFdPtr >= 0) { + close(*aPassedFdPtr); + *aPassedFdPtr = -1; + } + errno = EMSGSIZE; + return -1; + } + + return rv; +} + +/* static */ ssize_t +SandboxBrokerCommon::SendWithFd(int aFd, const iovec* aIO, size_t aNumIO, + int aPassedFd) +{ + struct msghdr msg = {}; + msg.msg_iov = const_cast<iovec*>(aIO); + msg.msg_iovlen = aNumIO; + + char cmsg_buf[CMSG_SPACE(sizeof(int))]; + if (aPassedFd != -1) { + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = aPassedFd; + } + + ssize_t rv; + do { + rv = sendmsg(aFd, &msg, MSG_NOSIGNAL); + } while (rv < 0 && errno == EINTR); + + return rv; +} + +} // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.h b/security/sandbox/linux/broker/SandboxBrokerCommon.h new file mode 100644 index 000000000..dbd17e0b9 --- /dev/null +++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_SandboxBrokerCommon_h +#define mozilla_SandboxBrokerCommon_h + +#include <sys/types.h> + +struct iovec; + +// This file defines the protocol between the filesystem broker, +// described in SandboxBroker.h, and its client, described in +// ../SandboxBrokerClient.h; and it defines some utility functions +// used by both. +// +// In order to keep the client simple while allowing it to be thread +// safe and async signal safe, the main broker socket is used only for +// requests; responses arrive on a per-request socketpair sent with +// the request. (This technique is also used by Chromium and Breakpad.) + +namespace mozilla { + +class SandboxBrokerCommon { +public: + enum Operation { + SANDBOX_FILE_OPEN, + SANDBOX_FILE_ACCESS, + SANDBOX_FILE_STAT, + SANDBOX_FILE_CHMOD, + SANDBOX_FILE_LINK, + SANDBOX_FILE_SYMLINK, + SANDBOX_FILE_MKDIR, + SANDBOX_FILE_RENAME, + SANDBOX_FILE_RMDIR, + SANDBOX_FILE_UNLINK, + SANDBOX_FILE_READLINK, + }; + + struct Request { + Operation mOp; + // For open, flags; for access, "mode"; for stat, O_NOFOLLOW for lstat. + int mFlags; + // Size of return value buffer, if any + size_t mBufSize; + // The rest of the packet is the pathname. + // SCM_RIGHTS for response socket attached. + }; + + struct Response { + // Syscall result, -errno if failure, or 0 for no error + int mError; + // Followed by struct stat for stat/lstat. + // SCM_RIGHTS attached for successful open. + }; + + // This doesn't need to be the system's maximum path length, just + // the largest path that would be allowed by any policy. (It's used + // to size a stack-allocated buffer.) + static const size_t kMaxPathLen = 4096; + + static ssize_t RecvWithFd(int aFd, const iovec* aIO, size_t aNumIO, + int* aPassedFdPtr); + static ssize_t SendWithFd(int aFd, const iovec* aIO, size_t aNumIO, + int aPassedFd); +}; + +} // namespace mozilla + +#endif // mozilla_SandboxBrokerCommon_h diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp new file mode 100644 index 000000000..8e698606e --- /dev/null +++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SandboxBrokerPolicyFactory.h" +#include "SandboxInfo.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Preferences.h" +#include "nsPrintfCString.h" +#include "nsString.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" +#include "SpecialSystemDirectory.h" + +#ifdef ANDROID +#include "cutils/properties.h" +#endif + +namespace mozilla { + +/* static */ bool +SandboxBrokerPolicyFactory::IsSystemSupported() { +#ifdef ANDROID + char hardware[PROPERTY_VALUE_MAX]; + int length = property_get("ro.hardware", hardware, nullptr); + // "goldfish" -> emulator. Other devices can be added when we're + // reasonably sure they work. Eventually this won't be needed.... + if (length > 0 && strcmp(hardware, "goldfish") == 0) { + return true; + } + + // When broker is running in permissive mode, we enable it + // automatically regardless of the device. + if (SandboxInfo::Get().Test(SandboxInfo::kPermissive)) { + return true; + } +#endif + return false; +} + +#if defined(MOZ_CONTENT_SANDBOX) +namespace { +static const int rdonly = SandboxBroker::MAY_READ; +static const int wronly = SandboxBroker::MAY_WRITE; +static const int rdwr = rdonly | wronly; +static const int rdwrcr = rdwr | SandboxBroker::MAY_CREATE; +#if defined(MOZ_WIDGET_GONK) +static const int wrlog = wronly | SandboxBroker::MAY_CREATE; +#endif +} +#endif + +SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory() +{ + // Policy entries that are the same in every process go here, and + // are cached over the lifetime of the factory. +#if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_WIDGET_GONK) + SandboxBroker::Policy* policy = new SandboxBroker::Policy; + + // Devices that need write access: + policy->AddPath(rdwr, "/dev/genlock"); // bug 980924 + policy->AddPath(rdwr, "/dev/ashmem"); // bug 980947 + policy->AddTree(wronly, "/dev/log"); // bug 1199857 + // Graphics devices are a significant source of attack surface, but + // there's not much we can do about it without proxying (which is + // very difficult and a perforamnce hit). + policy->AddPrefix(rdwr, "/dev", "kgsl"); // bug 995072 + policy->AddPath(rdwr, "/dev/qemu_pipe"); // but 1198410: goldfish gralloc. + + // Bug 1198475: mochitest logs. (This is actually passed in via URL + // query param to the mochitest page, and is configurable, so this + // isn't enough in general, but hopefully it's good enough for B2G.) + // Conditional on tests being run, using the same check seen in + // DirectoryProvider.js to set ProfD. + if (access("/data/local/tests/profile", R_OK) == 0) { + policy->AddPath(wrlog, "/data/local/tests/log/mochitest.log"); + } + + // Read-only items below this line. + + policy->AddPath(rdonly, "/dev/urandom"); // bug 964500, bug 995069 + policy->AddPath(rdonly, "/dev/ion"); // bug 980937 + policy->AddPath(rdonly, "/proc/cpuinfo"); // bug 995067 + policy->AddPath(rdonly, "/proc/meminfo"); // bug 1025333 + policy->AddPath(rdonly, "/sys/devices/system/cpu/present"); // bug 1025329 + policy->AddPath(rdonly, "/sys/devices/system/soc/soc0/id"); // bug 1025339 + policy->AddPath(rdonly, "/etc/media_profiles.xml"); // bug 1198419 + policy->AddPath(rdonly, "/etc/media_codecs.xml"); // bug 1198460 + policy->AddTree(rdonly, "/system/fonts"); // bug 1026063 + + // Bug 1199051 (crossplatformly, this is NS_GRE_DIR). + policy->AddTree(rdonly, "/system/b2g"); + + // Bug 1026356: dynamic library loading from assorted frameworks we + // don't control (media codecs, maybe others). + // + // Bug 1198515: Also, the profiler calls breakpad code to get info + // on all loaded ELF objects, which opens those files. + policy->AddTree(rdonly, "/system/lib"); + policy->AddTree(rdonly, "/vendor/lib"); + policy->AddPath(rdonly, "/system/bin/linker"); // (profiler only) + + // Bug 1199866: EGL/WebGL. + policy->AddPath(rdonly, "/system/lib/egl"); + policy->AddPath(rdonly, "/vendor/lib/egl"); + + // Bug 1198401: timezones. Yes, we need both of these; see bug. + policy->AddTree(rdonly, "/system/usr/share/zoneinfo"); + policy->AddTree(rdonly, "/system//usr/share/zoneinfo"); + + policy->AddPath(rdonly, "/data/local/tmp/profiler.options", + SandboxBroker::Policy::AddAlways); // bug 1029337 + + mCommonContentPolicy.reset(policy); +#elif defined(MOZ_CONTENT_SANDBOX) + SandboxBroker::Policy* policy = new SandboxBroker::Policy; + policy->AddDir(rdonly, "/"); + policy->AddDir(rdwrcr, "/dev/shm"); + // Add write permissions on the temporary directory. This can come + // from various environment variables (TMPDIR,TMP,TEMP,...) so + // make sure to use the full logic. + nsCOMPtr<nsIFile> tmpDir; + nsresult rv = GetSpecialSystemDirectory(OS_TemporaryDirectory, + getter_AddRefs(tmpDir)); + if (NS_SUCCEEDED(rv)) { + nsAutoCString tmpPath; + rv = tmpDir->GetNativePath(tmpPath); + if (NS_SUCCEEDED(rv)) { + policy->AddDir(rdwrcr, tmpPath.get()); + } + } + // If the above fails at any point, fall back to a very good guess. + if (NS_FAILED(rv)) { + policy->AddDir(rdwrcr, "/tmp"); + } + + // Bug 1308851: NVIDIA proprietary driver when using WebGL + policy->AddPrefix(rdwr, "/dev", "nvidia"); + + // Bug 1312678: radeonsi/Intel with DRI when using WebGL + policy->AddDir(rdwr, "/dev/dri"); + + mCommonContentPolicy.reset(policy); +#endif +} + +#ifdef MOZ_CONTENT_SANDBOX +UniquePtr<SandboxBroker::Policy> +SandboxBrokerPolicyFactory::GetContentPolicy(int aPid) +{ + // Policy entries that vary per-process (currently the only reason + // that can happen is because they contain the pid) are added here. + + MOZ_ASSERT(NS_IsMainThread()); + // File broker usage is controlled through a pref. + if (Preferences::GetInt("security.sandbox.content.level") <= 1) { + return nullptr; + } + + MOZ_ASSERT(mCommonContentPolicy); +#if defined(MOZ_WIDGET_GONK) + // Allow overriding "unsupported"ness with a pref, for testing. + if (!IsSystemSupported()) { + return nullptr; + } + UniquePtr<SandboxBroker::Policy> + policy(new SandboxBroker::Policy(*mCommonContentPolicy)); + + // Bug 1029337: where the profiler writes the data. + nsPrintfCString profilerLogPath("/data/local/tmp/profile_%d_%d.txt", + GeckoProcessType_Content, aPid); + policy->AddPath(wrlog, profilerLogPath.get()); + + // Bug 1198550: the profiler's replacement for dl_iterate_phdr + policy->AddPath(rdonly, nsPrintfCString("/proc/%d/maps", aPid).get()); + + // Bug 1198552: memory reporting. + policy->AddPath(rdonly, nsPrintfCString("/proc/%d/statm", aPid).get()); + policy->AddPath(rdonly, nsPrintfCString("/proc/%d/smaps", aPid).get()); + + return policy; +#else + UniquePtr<SandboxBroker::Policy> + policy(new SandboxBroker::Policy(*mCommonContentPolicy)); + // Return the common policy. + return policy; +#endif +} + +#endif // MOZ_CONTENT_SANDBOX +} // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h new file mode 100644 index 000000000..c66a09189 --- /dev/null +++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_SandboxBrokerPolicyFactory_h +#define mozilla_SandboxBrokerPolicyFactory_h + +#include "mozilla/SandboxBroker.h" + +namespace mozilla { + +class SandboxBrokerPolicyFactory { +public: + SandboxBrokerPolicyFactory(); + +#ifdef MOZ_CONTENT_SANDBOX + UniquePtr<SandboxBroker::Policy> GetContentPolicy(int aPid); +#endif + +private: + UniquePtr<const SandboxBroker::Policy> mCommonContentPolicy; + // B2G devices tend to have hardware-specific paths used by device + // drivers, so rollout of filesystem isolation will need per-device + // testing. This predicate allows that to happen gradually. + static bool IsSystemSupported(); +}; + +} // namespace mozilla + +#endif // mozilla_SandboxBrokerPolicyFactory_h diff --git a/security/sandbox/linux/broker/SandboxBrokerUtils.h b/security/sandbox/linux/broker/SandboxBrokerUtils.h new file mode 100644 index 000000000..1db4f4411 --- /dev/null +++ b/security/sandbox/linux/broker/SandboxBrokerUtils.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef mozilla_SandboxBrokerUtils_h +#define mozilla_SandboxBrokerUtils_h + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "sandbox/linux/system_headers/linux_syscalls.h" + +// On 32-bit Linux, stat calls are translated by libc into stat64 +// calls. We'll intercept those and handle them in the stat functions +// but must be sure to use the right structure layout. + +#if defined(__NR_stat64) +typedef struct stat64 statstruct; +#define statsyscall stat64 +#define lstatsyscall lstat64 +#elif defined(__NR_stat) +typedef struct stat statstruct; +#define statsyscall stat +#define lstatsyscall lstat +#else +#error Missing stat syscall include. +#endif + +#endif // mozilla_SandboxBrokerUtils_h diff --git a/security/sandbox/linux/broker/moz.build b/security/sandbox/linux/broker/moz.build new file mode 100644 index 000000000..343a5cfad --- /dev/null +++ b/security/sandbox/linux/broker/moz.build @@ -0,0 +1,37 @@ +# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla += [ + 'SandboxBroker.h', + 'SandboxBrokerCommon.h', + 'SandboxBrokerPolicyFactory.h', +] + +SOURCES += [ + 'SandboxBroker.cpp', + 'SandboxBrokerCommon.cpp', + 'SandboxBrokerPolicyFactory.cpp', +] + +if CONFIG['OS_TARGET'] == 'Android': + if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': + DEFINES['HAVE_ANDROID_OS'] = True + +LOCAL_INCLUDES += [ + '/security/sandbox/linux', # SandboxLogging.h, SandboxInfo.h +] + +# Need this for mozilla::ipc::FileDescriptor etc. +include('/ipc/chromium/chromium-config.mozbuild') + +# Need this for safe_sprintf.h used by SandboxLogging.h, +# but it has to be after ipc/chromium/src. +LOCAL_INCLUDES += [ + '/security/sandbox/chromium', +] + + +FINAL_LIBRARY = 'xul' |