| /* |
| * 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/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 |
| |
| typedef bool (^LSServerConnectionAllowedBlock) ( CFDictionaryRef optionsRef ); |
| extern "C" void _LSSetApplicationLaunchServicesServerConnectionStatus(uint64_t flags, LSServerConnectionAllowedBlock block); |
| extern "C" CFDictionaryRef _LSApplicationCheckIn(LSSessionID sessionID, CFDictionaryRef applicationInfo); |
| |
| 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() |
| { |
| _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(ALTERNATE_SYSTEM_LAYOUT) |
| if ([[webKit2Bundle() bundlePath] hasPrefix:@"/Library/Apple/System/"]) |
| return true; |
| #endif |
| return [[webKit2Bundle() bundlePath] hasPrefix:@"/System/"]; |
| }(); |
| return isSystemWebKit; |
| } |
| #endif |
| |
| } // namespace WebKit |
| |
| #endif |