From 43f7a588f96aaf88e7b69441c3b50bc9c7b20df7 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Thu, 3 May 2018 05:55:15 +0200 Subject: Nuke the sandbox --- security/sandbox/linux/broker/SandboxBroker.cpp | 731 --------------------- security/sandbox/linux/broker/SandboxBroker.h | 132 ---- .../sandbox/linux/broker/SandboxBrokerCommon.cpp | 120 ---- .../sandbox/linux/broker/SandboxBrokerCommon.h | 72 -- .../linux/broker/SandboxBrokerPolicyFactory.cpp | 50 -- .../linux/broker/SandboxBrokerPolicyFactory.h | 28 - security/sandbox/linux/broker/SandboxBrokerUtils.h | 30 - security/sandbox/linux/broker/moz.build | 37 -- 8 files changed, 1200 deletions(-) delete mode 100644 security/sandbox/linux/broker/SandboxBroker.cpp delete mode 100644 security/sandbox/linux/broker/SandboxBroker.h delete mode 100644 security/sandbox/linux/broker/SandboxBrokerCommon.cpp delete mode 100644 security/sandbox/linux/broker/SandboxBrokerCommon.h delete mode 100644 security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp delete mode 100644 security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h delete mode 100644 security/sandbox/linux/broker/SandboxBrokerUtils.h delete mode 100644 security/sandbox/linux/broker/moz.build (limited to 'security/sandbox/linux/broker') diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp deleted file mode 100644 index a31d1fc66..000000000 --- a/security/sandbox/linux/broker/SandboxBroker.cpp +++ /dev/null @@ -1,731 +0,0 @@ -/* -*- 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 -#include -#include -#include -#include -#include -#include - -#ifdef XP_LINUX -#include -#endif - -#ifdef MOZ_WIDGET_GONK -#include -#include -#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 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::Create(UniquePtr aPolicy, int aChildPid, - ipc::FileDescriptor& aClientFdOut) -{ - int clientFd; - // Can't use MakeUnique here because the constructor is private. - UniquePtr 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(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(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 sent = SendWithFd(respfd, ios, numIO, openedFd); - close(respfd); - MOZ_ASSERT(sent < 0 || - static_cast(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 deleted file mode 100644 index bb4570a64..000000000 --- a/security/sandbox/linux/broker/SandboxBroker.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -*- 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 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 - Create(UniquePtr aPolicy, int aChildPid, - ipc::FileDescriptor& aClientFdOut); - virtual ~SandboxBroker(); - - private: - PlatformThreadHandle mThread; - int mFileDesc; - const int mChildPid; - const UniquePtr mPolicy; - - SandboxBroker(UniquePtr 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 deleted file mode 100644 index fe7bc8c45..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- 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 -#include -#include -#include - -#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(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(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(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(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 deleted file mode 100644 index dbd17e0b9..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerCommon.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- 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 - -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 deleted file mode 100644 index c3a15ea3d..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- 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; -} - -SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory() -{ - // Policy entries that are the same in every process go here, and - // are cached over the lifetime of the factory. -} - -} // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h deleted file mode 100644 index bf9be9856..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- 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(); - -private: - UniquePtr 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 deleted file mode 100644 index 1db4f4411..000000000 --- a/security/sandbox/linux/broker/SandboxBrokerUtils.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- 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 -#include -#include -#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 deleted file mode 100644 index 343a5cfad..000000000 --- a/security/sandbox/linux/broker/moz.build +++ /dev/null @@ -1,37 +0,0 @@ -# -*- 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' -- cgit v1.2.3