a5beeac32b59d369cebf1b52906449046fddad8f
[WebKit-https.git] / Source / WebKit2 / Shared / mac / ChildProcessMac.mm
1 /*
2  * Copyright (C) 2012, 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #if PLATFORM(MAC)
29 #import "ChildProcess.h"
30
31 #import "SandboxInitializationParameters.h"
32 #import "WebKitSystemInterface.h"
33 #import <WebCore/CFNetworkSPI.h>
34 #import <WebCore/FileSystem.h>
35 #import <WebCore/SystemVersion.h>
36 #import <mach/mach.h>
37 #import <mach/task.h>
38 #import <pwd.h>
39 #import <stdlib.h>
40 #import <sysexits.h>
41 #import <wtf/cf/TypeCastsCF.h>
42 #import <wtf/spi/darwin/SandboxSPI.h>
43
44 #ifdef __has_include
45 #if __has_include(<HIServices/ProcessesPriv.h>)
46 #include <HIServices/ProcessesPriv.h>
47 #endif
48 #endif
49
50 typedef bool (^LSServerConnectionAllowedBlock) ( CFDictionaryRef optionsRef );
51 extern "C" void _LSSetApplicationLaunchServicesServerConnectionStatus(uint64_t flags, LSServerConnectionAllowedBlock block);
52 extern "C" CFDictionaryRef _LSApplicationCheckIn(int sessionID, CFDictionaryRef applicationInfo);
53
54 extern "C" OSStatus SetApplicationIsDaemon(Boolean isDaemon);
55
56 using namespace WebCore;
57
58 namespace WebKit {
59
60 static void initializeTimerCoalescingPolicy()
61 {
62     // Set task_latency and task_throughput QOS tiers as appropriate for a visible application.
63     struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_0, THROUGHPUT_QOS_TIER_0 };
64     kern_return_t kr = task_policy_set(mach_task_self(), TASK_BASE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
65     ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
66 }
67
68 void ChildProcess::setApplicationIsDaemon()
69 {
70     OSStatus error = SetApplicationIsDaemon(true);
71     ASSERT_UNUSED(error, error == noErr);
72
73     _LSSetApplicationLaunchServicesServerConnectionStatus(0, 0);
74     RetainPtr<CFDictionaryRef> unused = _LSApplicationCheckIn(-2, CFBundleGetInfoDictionary(CFBundleGetMainBundle()));
75 }
76
77 void ChildProcess::platformInitialize()
78 {
79     initializeTimerCoalescingPolicy();
80     [[NSFileManager defaultManager] changeCurrentDirectoryPath:[[NSBundle mainBundle] bundlePath]];
81 }
82
83 static RetainPtr<SecCodeRef> findSecCodeForProcess(pid_t pid)
84 {
85     RetainPtr<CFNumberRef> pidCFNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid));
86     const void* keys[] = { kSecGuestAttributePid };
87     const void* values[] = { pidCFNumber.get() };
88     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
89     SecCodeRef code = nullptr;
90     if (SecCodeCopyGuestWithAttributes(nullptr, attributes.get(), kSecCSDefaultFlags, &code))
91         return nullptr;
92     return adoptCF(code);
93 }
94
95 void ChildProcess::initializeSandbox(const ChildProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
96 {
97     NSBundle *webkit2Bundle = [NSBundle bundleForClass:NSClassFromString(@"WKView")];
98     String defaultProfilePath = [webkit2Bundle pathForResource:[[NSBundle mainBundle] bundleIdentifier] ofType:@"sb"];
99
100     if (sandboxParameters.userDirectorySuffix().isNull()) {
101         auto userDirectorySuffix = parameters.extraInitializationData.find("user-directory-suffix");
102         if (userDirectorySuffix != parameters.extraInitializationData.end())
103             sandboxParameters.setUserDirectorySuffix([makeString(userDirectorySuffix->value, '/', String([[NSBundle mainBundle] bundleIdentifier])) fileSystemRepresentation]);
104         else {
105             String clientIdentifierToUse;
106             RetainPtr<SecCodeRef> code = findSecCodeForProcess(xpc_connection_get_pid(parameters.connectionIdentifier.xpcConnection.get()));
107             RELEASE_ASSERT(code);
108
109             CFStringRef appleSignedOrMacAppStoreSignedOrAppleDeveloperSignedRequirement = CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]) or (anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13])");
110             SecRequirementRef signingRequirement;
111             OSStatus status = SecRequirementCreateWithString(appleSignedOrMacAppStoreSignedOrAppleDeveloperSignedRequirement, kSecCSDefaultFlags, &signingRequirement);
112             RELEASE_ASSERT(status == errSecSuccess);
113
114             status = SecCodeCheckValidity(code.get(), kSecCSDefaultFlags, signingRequirement);
115             if (status == errSecSuccess) {
116                 CFDictionaryRef signingInfo = nullptr;
117                 if (!SecCodeCopySigningInformation(code.get(), kSecCSDefaultFlags, &signingInfo)) {
118                     if (CFDictionaryRef plist = dynamic_cf_cast<CFDictionaryRef>(CFDictionaryGetValue(signingInfo, kSecCodeInfoPList)))
119                         clientIdentifierToUse = String(dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(plist, kCFBundleIdentifierKey)));
120                     else
121                         clientIdentifierToUse = String(dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(signingInfo, kSecCodeInfoIdentifier)));
122                     CFRelease(signingInfo);
123                 }
124             } else {
125                 // Unsigned, signed by a third party, or has an invalid/malformed signature
126                 clientIdentifierToUse = parameters.clientIdentifier;
127             }
128             CFRelease(signingRequirement);
129             if (clientIdentifierToUse.isEmpty()) {
130                 WTFLogAlways("%s: Couldn't get code signed identifier for client: %d\n", getprogname(), status);
131                 exit(EX_NOPERM);
132             }
133             sandboxParameters.setUserDirectorySuffix(makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', clientIdentifierToUse));
134         }
135     }
136
137     Vector<String> osVersionParts;
138     String osSystemMarketingVersion = systemMarketingVersion();
139     osSystemMarketingVersion.split('.', false, osVersionParts);
140     if (osVersionParts.size() < 2) {
141         WTFLogAlways("%s: Couldn't find OS Version\n", getprogname());
142         exit(EX_NOPERM);
143     }
144     String osVersion = osVersionParts[0] + '.' + osVersionParts[1];
145     sandboxParameters.addParameter("_OS_VERSION", osVersion.utf8().data());
146
147     // Use private temporary and cache directories.
148     setenv("DIRHELPER_USER_DIR_SUFFIX", fileSystemRepresentation(sandboxParameters.userDirectorySuffix()).data(), 1);
149     char temporaryDirectory[PATH_MAX];
150     if (!confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof(temporaryDirectory))) {
151         WTFLogAlways("%s: couldn't retrieve private temporary directory path: %d\n", getprogname(), errno);
152         exit(EX_NOPERM);
153     }
154     setenv("TMPDIR", temporaryDirectory, 1);
155
156     sandboxParameters.addPathParameter("WEBKIT2_FRAMEWORK_DIR", [[webkit2Bundle bundlePath] stringByDeletingLastPathComponent]);
157     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_TEMP_DIR", _CS_DARWIN_USER_TEMP_DIR);
158     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_CACHE_DIR", _CS_DARWIN_USER_CACHE_DIR);
159
160     char buffer[4096];
161     int bufferSize = sizeof(buffer);
162     struct passwd pwd;
163     struct passwd* result = 0;
164     if (getpwuid_r(getuid(), &pwd, buffer, bufferSize, &result) || !result) {
165         WTFLogAlways("%s: Couldn't find home directory\n", getprogname());
166         exit(EX_NOPERM);
167     }
168
169     sandboxParameters.addPathParameter("HOME_DIR", pwd.pw_dir);
170
171     String path = String::fromUTF8(pwd.pw_dir);
172     path.append("/Library");
173
174     sandboxParameters.addPathParameter("HOME_LIBRARY_DIR", fileSystemRepresentation(path).data());
175
176     path.append("/Preferences");
177
178     sandboxParameters.addPathParameter("HOME_LIBRARY_PREFERENCES_DIR", fileSystemRepresentation(path).data());
179
180     switch (sandboxParameters.mode()) {
181     case SandboxInitializationParameters::UseDefaultSandboxProfilePath:
182     case SandboxInitializationParameters::UseOverrideSandboxProfilePath: {
183         String sandboxProfilePath = sandboxParameters.mode() == SandboxInitializationParameters::UseDefaultSandboxProfilePath ? defaultProfilePath : sandboxParameters.overrideSandboxProfilePath();
184         if (!sandboxProfilePath.isEmpty()) {
185             CString profilePath = fileSystemRepresentation(sandboxProfilePath);
186             char* errorBuf;
187             if (sandbox_init_with_parameters(profilePath.data(), SANDBOX_NAMED_EXTERNAL, sandboxParameters.namedParameterArray(), &errorBuf)) {
188                 WTFLogAlways("%s: Couldn't initialize sandbox profile [%s], error '%s'\n", getprogname(), profilePath.data(), errorBuf);
189                 for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
190                     WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
191                 exit(EX_NOPERM);
192             }
193         }
194
195         break;
196     }
197     case SandboxInitializationParameters::UseSandboxProfile: {
198         char* errorBuf;
199         if (sandbox_init_with_parameters(sandboxParameters.sandboxProfile().utf8().data(), 0, sandboxParameters.namedParameterArray(), &errorBuf)) {
200             WTFLogAlways("%s: Couldn't initialize sandbox profile, error '%s'\n", getprogname(), errorBuf);
201             for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
202                 WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
203             exit(EX_NOPERM);
204         }
205
206         break;
207     }
208     }
209
210     // This will override LSFileQuarantineEnabled from Info.plist unless sandbox quarantine is globally disabled.
211     OSStatus error = WKEnableSandboxStyleFileQuarantine();
212     if (error) {
213         WTFLogAlways("%s: Couldn't enable sandbox style file quarantine: %ld\n", getprogname(), (long)error);
214         exit(EX_NOPERM);
215     }
216 }
217
218 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
219 void ChildProcess::setSharedHTTPCookieStorage(const Vector<uint8_t>& identifier)
220 {
221     // FIXME: Remove the runtime check when it's not needed (soon).
222     if (![NSHTTPCookieStorage respondsToSelector:@selector(_setSharedHTTPCookieStorage:)])
223         return;
224
225     RetainPtr<CFDataRef> cookieStorageData = adoptCF(CFDataCreate(kCFAllocatorDefault, identifier.data(), identifier.size()));
226     RetainPtr<CFHTTPCookieStorageRef> uiProcessCookieStorage = adoptCF(CFHTTPCookieStorageCreateFromIdentifyingData(kCFAllocatorDefault, cookieStorageData.get()));
227     [NSHTTPCookieStorage _setSharedHTTPCookieStorage:adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:uiProcessCookieStorage.get()]).get()];
228 }
229 #endif
230
231
232 #if USE(APPKIT)
233 void ChildProcess::stopNSAppRunLoop()
234 {
235     ASSERT([NSApp isRunning]);
236     [NSApp stop:nil];
237
238     NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
239     [NSApp postEvent:event atStart:true];
240 }
241 #endif
242
243 void ChildProcess::setQOS(int latencyQOS, int throughputQOS)
244 {
245     if (!latencyQOS && !throughputQOS)
246         return;
247
248     struct task_qos_policy qosinfo = {
249         latencyQOS ? LATENCY_QOS_TIER_0 + latencyQOS - 1 : LATENCY_QOS_TIER_UNSPECIFIED,
250         throughputQOS ? THROUGHPUT_QOS_TIER_0 + throughputQOS - 1 : THROUGHPUT_QOS_TIER_UNSPECIFIED
251     };
252
253     task_policy_set(mach_task_self(), TASK_OVERRIDE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
254 }
255
256 } // namespace WebKit
257
258 #endif