blob: 90f03a5207afc5b08827b68a10bbc1898f399458 [file] [log] [blame]
/*
* Copyright (C) 2012-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#if PLATFORM(MAC) || PLATFORM(MACCATALYST)
#import "AuxiliaryProcess.h"
#import "ApplicationServicesSPI.h"
#import "CodeSigning.h"
#import "QuarantineSPI.h"
#import "SandboxInitializationParameters.h"
#import "SandboxUtilities.h"
#import "WKFoundation.h"
#import "XPCServiceEntryPoint.h"
#import <WebCore/FileHandle.h>
#import <WebCore/SystemVersion.h>
#import <mach-o/dyld.h>
#import <mach/mach.h>
#import <mach/task.h>
#import <pal/crypto/CryptoDigest.h>
#import <pal/spi/cocoa/CoreServicesSPI.h>
#import <pal/spi/cocoa/LaunchServicesSPI.h>
#import <pwd.h>
#import <stdlib.h>
#import <sys/sysctl.h>
#import <sysexits.h>
#import <wtf/DataLog.h>
#import <wtf/FileSystem.h>
#import <wtf/RandomNumber.h>
#import <wtf/Scope.h>
#import <wtf/SystemTracing.h>
#import <wtf/WallTime.h>
#import <wtf/spi/darwin/SandboxSPI.h>
#import <wtf/text/Base64.h>
#import <wtf/text/StringBuilder.h>
#if USE(APPLE_INTERNAL_SDK)
#import <rootless.h>
#endif
#if PLATFORM(MAC)
#define USE_CACHE_COMPILED_SANDBOX 1
#else
#define USE_CACHE_COMPILED_SANDBOX 0
#endif
#if PLATFORM(MACCATALYST) && USE(APPLE_INTERNAL_SDK)
enum LSSessionID {
kLSDefaultSessionID = -2,
};
#endif
namespace WebKit {
using namespace WebCore;
#if USE(CACHE_COMPILED_SANDBOX)
using SandboxProfile = typename std::remove_pointer<sandbox_profile_t>::type;
struct SandboxProfileDeleter {
void operator()(SandboxProfile* ptr)
{
sandbox_free_profile(ptr);
}
};
using SandboxProfilePtr = std::unique_ptr<SandboxProfile, SandboxProfileDeleter>;
using SandboxParameters = typename std::remove_pointer<sandbox_params_t>::type;
struct SandboxParametersDeleter {
void operator()(SandboxParameters* ptr)
{
sandbox_free_params(ptr);
}
};
using SandboxParametersPtr = std::unique_ptr<SandboxParameters, SandboxParametersDeleter>;
struct CachedSandboxHeader {
uint32_t versionNumber;
uint32_t libsandboxVersion;
uint32_t headerSize;
uint32_t builtinSize; // If a builtin doesn't exist, this is UINT_MAX.
uint32_t dataSize;
};
// The file is layed out on disk like:
// byte 0
// CachedSandboxHeader <- sizeof(CachedSandboxHeader) bytes
// SandboxHeader <- CachedSandboxHeader::headerSize bytes
// [SandboxBuiltin] optional. Present if CachedSandboxHeader::builtinSize is not UINT_MAX. If present, builtinSize bytes (not including null termination).
// SandboxData <- CachedSandboxHeader::dataSize bytes
// byte N
struct SandboxInfo {
SandboxInfo(const String& parentDirectoryPath, const String& directoryPath, const String& filePath, const SandboxParametersPtr& sandboxParameters, const CString& header, const AuxiliaryProcess::ProcessType& processType, const SandboxInitializationParameters& initializationParameters, const String& profileOrProfilePath, bool isProfilePath)
: parentDirectoryPath { parentDirectoryPath }
, directoryPath { directoryPath }
, filePath { filePath }
, sandboxParameters { sandboxParameters }
, header { header }
, processType { processType }
, initializationParameters { initializationParameters }
, profileOrProfilePath { profileOrProfilePath }
, isProfilePath { isProfilePath }
{
}
const String& parentDirectoryPath;
const String& directoryPath;
const String& filePath;
const SandboxParametersPtr& sandboxParameters;
const CString& header;
const AuxiliaryProcess::ProcessType& processType;
const SandboxInitializationParameters& initializationParameters;
const String& profileOrProfilePath;
const bool isProfilePath;
};
constexpr uint32_t CachedSandboxVersionNumber = 0;
#endif // USE(CACHE_COMPILED_SANDBOX)
static void initializeTimerCoalescingPolicy()
{
// Set task_latency and task_throughput QOS tiers as appropriate for a visible application.
struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_0, THROUGHPUT_QOS_TIER_0 };
kern_return_t kr = task_policy_set(mach_task_self(), TASK_BASE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
}
void AuxiliaryProcess::launchServicesCheckIn()
{
#if HAVE(CSCHECKFIXDISABLE)
// _CSCheckFixDisable() needs to be called before checking in with Launch Services.
_CSCheckFixDisable();
#endif
_LSSetApplicationLaunchServicesServerConnectionStatus(0, 0);
RetainPtr<CFDictionaryRef> unused = _LSApplicationCheckIn(kLSDefaultSessionID, CFBundleGetInfoDictionary(CFBundleGetMainBundle()));
}
void AuxiliaryProcess::platformInitialize()
{
initializeTimerCoalescingPolicy();
[[NSFileManager defaultManager] changeCurrentDirectoryPath:[[NSBundle mainBundle] bundlePath]];
}
static OSStatus enableSandboxStyleFileQuarantine()
{
#if !PLATFORM(MACCATALYST)
qtn_proc_t quarantineProperties = qtn_proc_alloc();
auto quarantinePropertiesDeleter = makeScopeExit([quarantineProperties]() {
qtn_proc_free(quarantineProperties);
});
if (qtn_proc_init_with_self(quarantineProperties)) {
// See <rdar://problem/13463752>.
qtn_proc_init(quarantineProperties);
}
if (auto error = qtn_proc_set_flags(quarantineProperties, QTN_FLAG_SANDBOX))
return error;
// QTN_FLAG_SANDBOX is silently ignored if security.mac.qtn.sandbox_enforce sysctl is 0.
// In that case, quarantine falls back to advisory QTN_FLAG_DOWNLOAD.
return qtn_proc_apply_to_self(quarantineProperties);
#else
return false;
#endif
}
#if USE(CACHE_COMPILED_SANDBOX)
static Optional<Vector<char>> fileContents(const String& path, bool shouldLock = false, OptionSet<FileSystem::FileLockMode> lockMode = FileSystem::FileLockMode::Exclusive)
{
FileHandle file = shouldLock ? FileHandle(path, FileSystem::FileOpenMode::Read, lockMode) : FileHandle(path, FileSystem::FileOpenMode::Read);
file.open();
if (!file)
return WTF::nullopt;
char chunk[4096];
constexpr size_t chunkSize = WTF_ARRAY_LENGTH(chunk);
size_t contentSize = 0;
Vector<char> contents;
contents.reserveInitialCapacity(chunkSize);
while (size_t bytesRead = file.read(chunk, chunkSize)) {
contents.append(chunk, bytesRead);
contentSize += bytesRead;
}
contents.resize(contentSize);
return contents;
}
#if USE(APPLE_INTERNAL_SDK)
// These strings must match the last segment of the "com.apple.rootless.storage.<this part must match>" entry in each
// process's restricted entitlements file (ex. Configurations/Networking-OSX-restricted.entitlements).
constexpr const char* processStorageClass(AuxiliaryProcess::ProcessType type)
{
switch (type) {
case AuxiliaryProcess::ProcessType::WebContent:
return "WebKitWebContentSandbox";
case AuxiliaryProcess::ProcessType::Network:
return "WebKitNetworkingSandbox";
case AuxiliaryProcess::ProcessType::Plugin:
return "WebKitPluginSandbox";
#if ENABLE(GPU_PROCESS)
case AuxiliaryProcess::ProcessType::GPU:
return "WebKitGPUSandbox";
#endif
}
}
#endif // USE(APPLE_INTERNAL_SDK)
static Optional<CString> setAndSerializeSandboxParameters(const SandboxInitializationParameters& initializationParameters, const SandboxParametersPtr& sandboxParameters, const String& profileOrProfilePath, bool isProfilePath)
{
StringBuilder builder;
for (size_t i = 0; i < initializationParameters.count(); ++i) {
const char* name = initializationParameters.name(i);
const char* value = initializationParameters.value(i);
if (sandbox_set_param(sandboxParameters.get(), name, value)) {
WTFLogAlways("%s: Could not set sandbox parameter: %s\n", getprogname(), strerror(errno));
CRASH();
}
builder.append(name, ':', value, ':');
}
if (isProfilePath) {
auto contents = fileContents(profileOrProfilePath);
if (!contents)
return WTF::nullopt;
builder.appendCharacters(contents->data(), contents->size());
} else
builder.append(profileOrProfilePath);
return builder.toString().ascii();
}
static String sandboxDataVaultParentDirectory()
{
char temp[PATH_MAX];
size_t length = confstr(_CS_DARWIN_USER_CACHE_DIR, temp, sizeof(temp));
if (!length) {
WTFLogAlways("%s: Could not retrieve user cache directory path: %s\n", getprogname(), strerror(errno));
exit(EX_NOPERM);
}
RELEASE_ASSERT(length <= sizeof(temp));
char resolvedPath[PATH_MAX];
if (!realpath(temp, resolvedPath)) {
WTFLogAlways("%s: Could not canonicalize user cache directory path: %s\n", getprogname(), strerror(errno));
exit(EX_NOPERM);
}
return resolvedPath;
}
static String sandboxDirectory(AuxiliaryProcess::ProcessType processType, const String& parentDirectory)
{
StringBuilder directory;
directory.append(parentDirectory);
switch (processType) {
case AuxiliaryProcess::ProcessType::WebContent:
directory.append("/com.apple.WebKit.WebContent.Sandbox");
break;
case AuxiliaryProcess::ProcessType::Network:
directory.append("/com.apple.WebKit.Networking.Sandbox");
break;
case AuxiliaryProcess::ProcessType::Plugin:
directory.append("/com.apple.WebKit.Plugin.Sandbox");
break;
#if ENABLE(GPU_PROCESS)
case AuxiliaryProcess::ProcessType::GPU:
directory.append("/com.apple.WebKit.GPU.Sandbox");
break;
#endif
}
#if !USE(APPLE_INTERNAL_SDK)
// Add .OpenSource suffix so that open source builds don't try to access a data vault used by system Safari.
directory.append(".OpenSource");
#endif
return directory.toString();
}
static String sandboxFilePath(const String& directoryPath, const CString& header)
{
StringBuilder sandboxFile;
sandboxFile.append(directoryPath);
sandboxFile.append("/CompiledSandbox+");
// Make the filename semi-unique based on the contents of the header.
auto crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
crypto->addBytes(header.data(), header.length());
Vector<uint8_t> hash = crypto->computeHash();
String readableHash = WTF::base64URLEncode(hash.data(), hash.size());
sandboxFile.append(readableHash);
return sandboxFile.toString();
}
static bool ensureSandboxCacheDirectory(const SandboxInfo& info)
{
if (!FileSystem::fileIsDirectory(info.parentDirectoryPath, FileSystem::ShouldFollowSymbolicLinks::Yes)) {
FileSystem::makeAllDirectories(info.parentDirectoryPath);
if (!FileSystem::fileIsDirectory(info.parentDirectoryPath, FileSystem::ShouldFollowSymbolicLinks::Yes)) {
WTFLogAlways("%s: Could not create sandbox directory\n", getprogname());
return false;
}
}
#if USE(APPLE_INTERNAL_SDK)
const char* storageClass = processStorageClass(info.processType);
CString directoryPath = FileSystem::fileSystemRepresentation(info.directoryPath);
if (directoryPath.isNull())
return false;
auto makeDataVault = [&] {
do {
if (!rootless_mkdir_datavault(directoryPath.data(), 0700, storageClass))
return true;
} while (errno == EAGAIN);
return false;
};
if (makeDataVault())
return true;
if (errno == EEXIST) {
// The directory already exists. First we'll check if it is a data vault. If it is then
// we are the ones who created it and we can continue. If it is not a datavault then we'll just
// delete it and try to make a new one.
if (!rootless_check_datavault_flag(directoryPath.data(), storageClass))
return true;
bool isDirectory = FileSystem::fileIsDirectory(info.directoryPath, FileSystem::ShouldFollowSymbolicLinks::No);
if (isDirectory) {
if (!FileSystem::deleteNonEmptyDirectory(info.directoryPath))
return false;
} else {
if (!FileSystem::deleteFile(info.directoryPath))
return false;
}
if (!makeDataVault())
return false;
} else {
WTFLogAlways("%s: Sandbox directory couldn't be created: ", getprogname(), strerror(errno));
return false;
}
#else
bool hasSandboxDirectory = FileSystem::fileIsDirectory(info.directoryPath, FileSystem::ShouldFollowSymbolicLinks::Yes);
if (!hasSandboxDirectory) {
if (FileSystem::makeAllDirectories(info.directoryPath)) {
ASSERT(FileSystem::fileIsDirectory(info.directoryPath, FileSystem::ShouldFollowSymbolicLinks::Yes));
hasSandboxDirectory = true;
} else {
// We may have raced with someone else making it. That's ok.
hasSandboxDirectory = FileSystem::fileIsDirectory(info.directoryPath, FileSystem::ShouldFollowSymbolicLinks::Yes);
}
}
if (!hasSandboxDirectory) {
// Bailing because we don't have a sandbox directory.
return false;
}
#endif // USE(APPLE_INTERNAL_SDK)
return true;
}
static bool writeSandboxDataToCacheFile(const SandboxInfo& info, const Vector<char>& cacheFile)
{
FileHandle file { info.filePath, FileSystem::FileOpenMode::Write, FileSystem::FileLockMode::Exclusive };
return file.write(cacheFile.data(), cacheFile.size()) == safeCast<int>(cacheFile.size());
}
static SandboxProfilePtr compileAndCacheSandboxProfile(const SandboxInfo& info)
{
if (!ensureSandboxCacheDirectory(info))
return nullptr;
char* error = nullptr;
CString profileOrProfilePath = info.isProfilePath ? FileSystem::fileSystemRepresentation(info.profileOrProfilePath) : info.profileOrProfilePath.utf8();
if (profileOrProfilePath.isNull())
return nullptr;
SandboxProfilePtr sandboxProfile { info.isProfilePath ? sandbox_compile_file(profileOrProfilePath.data(), info.sandboxParameters.get(), &error) : sandbox_compile_string(profileOrProfilePath.data(), info.sandboxParameters.get(), &error) };
if (!sandboxProfile) {
WTFLogAlways("%s: Could not compile WebContent sandbox: %s\n", getprogname(), error);
return nullptr;
}
const bool haveBuiltin = sandboxProfile->builtin;
int32_t libsandboxVersion = NSVersionOfRunTimeLibrary("sandbox");
RELEASE_ASSERT(libsandboxVersion > 0);
CachedSandboxHeader cachedHeader {
CachedSandboxVersionNumber,
static_cast<uint32_t>(libsandboxVersion),
safeCast<uint32_t>(info.header.length()),
haveBuiltin ? safeCast<uint32_t>(strlen(sandboxProfile->builtin)) : std::numeric_limits<uint32_t>::max(),
safeCast<uint32_t>(sandboxProfile->size)
};
const size_t expectedFileSize = sizeof(cachedHeader) + cachedHeader.headerSize + (haveBuiltin ? cachedHeader.builtinSize : 0) + cachedHeader.dataSize;
Vector<char> cacheFile;
cacheFile.reserveInitialCapacity(expectedFileSize);
cacheFile.append(bitwise_cast<uint8_t*>(&cachedHeader), sizeof(CachedSandboxHeader));
cacheFile.append(info.header.data(), info.header.length());
if (haveBuiltin)
cacheFile.append(sandboxProfile->builtin, cachedHeader.builtinSize);
cacheFile.append(sandboxProfile->data, cachedHeader.dataSize);
if (!writeSandboxDataToCacheFile(info, cacheFile))
WTFLogAlways("%s: Unable to cache compiled sandbox\n", getprogname());
return sandboxProfile;
}
static bool tryApplyCachedSandbox(const SandboxInfo& info)
{
#if USE(APPLE_INTERNAL_SDK)
CString directoryPath = FileSystem::fileSystemRepresentation(info.directoryPath);
if (directoryPath.isNull())
return false;
if (rootless_check_datavault_flag(directoryPath.data(), processStorageClass(info.processType)))
return false;
#endif
auto contents = fileContents(info.filePath, true, FileSystem::FileLockMode::Shared);
if (!contents || contents->isEmpty())
return false;
Vector<char> cachedSandboxContents = WTFMove(*contents);
if (sizeof(CachedSandboxHeader) > cachedSandboxContents.size())
return false;
// This data may be corrupted if the sandbox file was cached on a different platform with different endianness
CachedSandboxHeader cachedSandboxHeader;
memcpy(&cachedSandboxHeader, cachedSandboxContents.data(), sizeof(CachedSandboxHeader));
int32_t libsandboxVersion = NSVersionOfRunTimeLibrary("sandbox");
RELEASE_ASSERT(libsandboxVersion > 0);
if (static_cast<uint32_t>(libsandboxVersion) != cachedSandboxHeader.libsandboxVersion)
return false;
if (cachedSandboxHeader.versionNumber != CachedSandboxVersionNumber)
return false;
const bool haveBuiltin = cachedSandboxHeader.builtinSize != std::numeric_limits<uint32_t>::max();
// These values are computed based on the disk layout specified below the definition of the CachedSandboxHeader struct
// and must be changed if the layout changes.
const char* sandboxHeaderPtr = bitwise_cast<char *>(cachedSandboxContents.data()) + sizeof(CachedSandboxHeader);
const char* sandboxBuiltinPtr = sandboxHeaderPtr + cachedSandboxHeader.headerSize;
unsigned char* sandboxDataPtr = bitwise_cast<unsigned char*>(haveBuiltin ? sandboxBuiltinPtr + cachedSandboxHeader.builtinSize : sandboxBuiltinPtr);
size_t expectedFileSize = sizeof(CachedSandboxHeader) + cachedSandboxHeader.headerSize + cachedSandboxHeader.dataSize;
if (haveBuiltin)
expectedFileSize += cachedSandboxHeader.builtinSize;
if (cachedSandboxContents.size() != expectedFileSize)
return false;
if (cachedSandboxHeader.headerSize != info.header.length())
return false;
if (memcmp(sandboxHeaderPtr, info.header.data(), info.header.length()))
return false;
SandboxProfile profile { };
CString builtin;
profile.builtin = nullptr;
profile.size = cachedSandboxHeader.dataSize;
if (haveBuiltin) {
builtin = CString::newUninitialized(cachedSandboxHeader.builtinSize, profile.builtin);
if (builtin.isNull())
return false;
memcpy(profile.builtin, sandboxBuiltinPtr, cachedSandboxHeader.builtinSize);
}
ASSERT(static_cast<void *>(sandboxDataPtr + profile.size) <= static_cast<void *>(cachedSandboxContents.data() + cachedSandboxContents.size()));
profile.data = sandboxDataPtr;
if (sandbox_apply(&profile)) {
WTFLogAlways("%s: Could not apply cached sandbox: %s\n", getprogname(), strerror(errno));
return false;
}
return true;
}
#endif // USE(CACHE_COMPILED_SANDBOX)
static inline const NSBundle *webKit2Bundle()
{
const static NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(@"WKWebView")];
return bundle;
}
static void getSandboxProfileOrProfilePath(const SandboxInitializationParameters& parameters, String& profileOrProfilePath, bool& isProfilePath)
{
switch (parameters.mode()) {
case SandboxInitializationParameters::ProfileSelectionMode::UseDefaultSandboxProfilePath:
profileOrProfilePath = [webKit2Bundle() pathForResource:[[NSBundle mainBundle] bundleIdentifier] ofType:@"sb"];
isProfilePath = true;
return;
case SandboxInitializationParameters::ProfileSelectionMode::UseOverrideSandboxProfilePath:
profileOrProfilePath = parameters.overrideSandboxProfilePath();
isProfilePath = true;
return;
case SandboxInitializationParameters::ProfileSelectionMode::UseSandboxProfile:
profileOrProfilePath = parameters.sandboxProfile();
isProfilePath = false;
return;
}
}
static bool compileAndApplySandboxSlowCase(const String& profileOrProfilePath, bool isProfilePath, const SandboxInitializationParameters& parameters)
{
char* errorBuf;
CString temp = isProfilePath ? FileSystem::fileSystemRepresentation(profileOrProfilePath) : profileOrProfilePath.utf8();
uint64_t flags = isProfilePath ? SANDBOX_NAMED_EXTERNAL : 0;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (sandbox_init_with_parameters(temp.data(), flags, parameters.namedParameterArray(), &errorBuf)) {
ALLOW_DEPRECATED_DECLARATIONS_END
WTFLogAlways("%s: Could not initialize sandbox profile [%s], error '%s'\n", getprogname(), temp.data(), errorBuf);
for (size_t i = 0, count = parameters.count(); i != count; ++i)
WTFLogAlways("%s=%s\n", parameters.name(i), parameters.value(i));
return false;
}
return true;
}
static bool applySandbox(const AuxiliaryProcessInitializationParameters& parameters, const SandboxInitializationParameters& sandboxInitializationParameters, const String& dataVaultParentDirectory)
{
String profileOrProfilePath;
bool isProfilePath;
getSandboxProfileOrProfilePath(sandboxInitializationParameters, profileOrProfilePath, isProfilePath);
if (profileOrProfilePath.isEmpty()) {
WTFLogAlways("%s: Profile path is invalid\n", getprogname());
CRASH();
}
#if USE(CACHE_COMPILED_SANDBOX)
// The plugin process's DARWIN_USER_TEMP_DIR and DARWIN_USER_CACHE_DIR sandbox parameters are randomized so
// so the compiled sandbox should not be cached because it won't be reused.
if (parameters.processType == AuxiliaryProcess::ProcessType::Plugin)
return compileAndApplySandboxSlowCase(profileOrProfilePath, isProfilePath, sandboxInitializationParameters);
SandboxParametersPtr sandboxParameters { sandbox_create_params() };
if (!sandboxParameters) {
WTFLogAlways("%s: Could not create sandbox parameters\n", getprogname());
CRASH();
}
auto header = setAndSerializeSandboxParameters(sandboxInitializationParameters, sandboxParameters, profileOrProfilePath, isProfilePath);
if (!header) {
WTFLogAlways("%s: Sandbox parameters are invalid\n", getprogname());
CRASH();
}
String directoryPath { sandboxDirectory(parameters.processType, dataVaultParentDirectory) };
String filePath = sandboxFilePath(directoryPath, *header);
SandboxInfo info {
dataVaultParentDirectory,
directoryPath,
filePath,
sandboxParameters,
*header,
parameters.processType,
sandboxInitializationParameters,
profileOrProfilePath,
isProfilePath
};
if (tryApplyCachedSandbox(info))
return true;
SandboxProfilePtr sandboxProfile = compileAndCacheSandboxProfile(info);
if (!sandboxProfile)
return compileAndApplySandboxSlowCase(profileOrProfilePath, isProfilePath, sandboxInitializationParameters);
if (sandbox_apply(sandboxProfile.get())) {
WTFLogAlways("%s: Could not apply compiled sandbox: %s\n", getprogname(), strerror(errno));
CRASH();
}
return true;
#else
UNUSED_PARAM(parameters);
UNUSED_PARAM(dataVaultParentDirectory);
return compileAndApplySandboxSlowCase(profileOrProfilePath, isProfilePath, sandboxInitializationParameters);
#endif // USE(CACHE_COMPILED_SANDBOX)
}
static void initializeSandboxParameters(const AuxiliaryProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
{
// Verify user directory suffix.
if (sandboxParameters.userDirectorySuffix().isNull()) {
auto userDirectorySuffix = parameters.extraInitializationData.find("user-directory-suffix");
if (userDirectorySuffix != parameters.extraInitializationData.end())
sandboxParameters.setUserDirectorySuffix([makeString(userDirectorySuffix->value, '/', String([[NSBundle mainBundle] bundleIdentifier])) fileSystemRepresentation]);
else {
String clientIdentifier = codeSigningIdentifier(parameters.connectionIdentifier.xpcConnection.get());
if (clientIdentifier.isNull())
clientIdentifier = parameters.clientIdentifier;
String defaultUserDirectorySuffix = makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', clientIdentifier);
sandboxParameters.setUserDirectorySuffix(defaultUserDirectorySuffix);
}
}
String osSystemMarketingVersion = systemMarketingVersion();
Vector<String> osVersionParts = osSystemMarketingVersion.split('.');
if (osVersionParts.size() < 2) {
WTFLogAlways("%s: Couldn't find OS Version\n", getprogname());
exit(EX_NOPERM);
}
String osVersion = osVersionParts[0] + '.' + osVersionParts[1];
sandboxParameters.addParameter("_OS_VERSION", osVersion.utf8().data());
// Use private temporary and cache directories.
setenv("DIRHELPER_USER_DIR_SUFFIX", FileSystem::fileSystemRepresentation(sandboxParameters.userDirectorySuffix()).data(), 1);
char temporaryDirectory[PATH_MAX];
if (!confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof(temporaryDirectory))) {
WTFLogAlways("%s: couldn't retrieve private temporary directory path: %d\n", getprogname(), errno);
exit(EX_NOPERM);
}
setenv("TMPDIR", temporaryDirectory, 1);
sandboxParameters.addPathParameter("WEBKIT2_FRAMEWORK_DIR", [[webKit2Bundle() bundlePath] stringByDeletingLastPathComponent]);
sandboxParameters.addConfDirectoryParameter("DARWIN_USER_TEMP_DIR", _CS_DARWIN_USER_TEMP_DIR);
sandboxParameters.addConfDirectoryParameter("DARWIN_USER_CACHE_DIR", _CS_DARWIN_USER_CACHE_DIR);
char buffer[4096];
int bufferSize = sizeof(buffer);
struct passwd pwd;
struct passwd* result = 0;
if (getpwuid_r(getuid(), &pwd, buffer, bufferSize, &result) || !result) {
WTFLogAlways("%s: Couldn't find home directory\n", getprogname());
exit(EX_NOPERM);
}
sandboxParameters.addPathParameter("HOME_DIR", pwd.pw_dir);
String path = String::fromUTF8(pwd.pw_dir);
path.append("/Library");
sandboxParameters.addPathParameter("HOME_LIBRARY_DIR", FileSystem::fileSystemRepresentation(path).data());
path.append("/Preferences");
sandboxParameters.addPathParameter("HOME_LIBRARY_PREFERENCES_DIR", FileSystem::fileSystemRepresentation(path).data());
}
void AuxiliaryProcess::initializeSandbox(const AuxiliaryProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
{
TraceScope traceScope(InitializeSandboxStart, InitializeSandboxEnd);
#if USE(CACHE_COMPILED_SANDBOX)
// This must be called before initializeSandboxParameters so that the path does not include the user directory suffix.
// We don't want the user directory suffix because we want all processes of the same type to use the same cache directory.
String dataVaultParentDirectory { sandboxDataVaultParentDirectory() };
#else
String dataVaultParentDirectory;
#endif
initializeSandboxParameters(parameters, sandboxParameters);
if (!applySandbox(parameters, sandboxParameters, dataVaultParentDirectory)) {
WTFLogAlways("%s: Unable to apply sandbox\n", getprogname());
CRASH();
}
if (shouldOverrideQuarantine()) {
// This will override LSFileQuarantineEnabled from Info.plist unless sandbox quarantine is globally disabled.
OSStatus error = enableSandboxStyleFileQuarantine();
if (error) {
WTFLogAlways("%s: Couldn't enable sandbox style file quarantine: %ld\n", getprogname(), static_cast<long>(error));
exit(EX_NOPERM);
}
}
}
#if USE(APPKIT)
void AuxiliaryProcess::stopNSAppRunLoop()
{
ASSERT([NSApp isRunning]);
[NSApp stop:nil];
NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
[NSApp postEvent:event atStart:true];
}
#endif
#if !PLATFORM(MACCATALYST) && ENABLE(WEBPROCESS_NSRUNLOOP)
void AuxiliaryProcess::stopNSRunLoop()
{
ASSERT([NSRunLoop mainRunLoop]);
[[NSRunLoop mainRunLoop] performBlock:^{
exit(0);
}];
}
#endif
void AuxiliaryProcess::setQOS(int latencyQOS, int throughputQOS)
{
if (!latencyQOS && !throughputQOS)
return;
struct task_qos_policy qosinfo = {
latencyQOS ? LATENCY_QOS_TIER_0 + latencyQOS - 1 : LATENCY_QOS_TIER_UNSPECIFIED,
throughputQOS ? THROUGHPUT_QOS_TIER_0 + throughputQOS - 1 : THROUGHPUT_QOS_TIER_UNSPECIFIED
};
task_policy_set(mach_task_self(), TASK_OVERRIDE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
}
#if PLATFORM(MAC)
bool AuxiliaryProcess::isSystemWebKit()
{
static bool isSystemWebKit = []() -> bool {
#if HAVE(READ_ONLY_SYSTEM_VOLUME)
if ([[webKit2Bundle() bundlePath] hasPrefix:@"/Library/Apple/System/"])
return true;
#endif
return [[webKit2Bundle() bundlePath] hasPrefix:@"/System/"];
}();
return isSystemWebKit;
}
#endif
} // namespace WebKit
#endif