64aa3c7f857cf41be59b233e0173a56cbd7d4be7
[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 #if USE(APPLE_INTERNAL_SDK)
45 #include <HIServices/ProcessesPriv.h>
46 #endif
47
48 typedef bool (^LSServerConnectionAllowedBlock) ( CFDictionaryRef optionsRef );
49 extern "C" void _LSSetApplicationLaunchServicesServerConnectionStatus(uint64_t flags, LSServerConnectionAllowedBlock block);
50 extern "C" CFDictionaryRef _LSApplicationCheckIn(int sessionID, CFDictionaryRef applicationInfo);
51
52 extern "C" OSStatus SetApplicationIsDaemon(Boolean isDaemon);
53
54 using namespace WebCore;
55
56 namespace WebKit {
57
58 static void initializeTimerCoalescingPolicy()
59 {
60     // Set task_latency and task_throughput QOS tiers as appropriate for a visible application.
61     struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_0, THROUGHPUT_QOS_TIER_0 };
62     kern_return_t kr = task_policy_set(mach_task_self(), TASK_BASE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
63     ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
64 }
65
66 void ChildProcess::setApplicationIsDaemon()
67 {
68     OSStatus error = SetApplicationIsDaemon(true);
69     ASSERT_UNUSED(error, error == noErr);
70
71     _LSSetApplicationLaunchServicesServerConnectionStatus(0, 0);
72     RetainPtr<CFDictionaryRef> unused = _LSApplicationCheckIn(-2, CFBundleGetInfoDictionary(CFBundleGetMainBundle()));
73 }
74
75 void ChildProcess::platformInitialize()
76 {
77     initializeTimerCoalescingPolicy();
78     [[NSFileManager defaultManager] changeCurrentDirectoryPath:[[NSBundle mainBundle] bundlePath]];
79 }
80
81 static String codeSigningIdentifierForProcess(pid_t pid, OSStatus& errorCode)
82 {
83     RetainPtr<CFNumberRef> pidCFNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid));
84     const void* keys[] = { kSecGuestAttributePid };
85     const void* values[] = { pidCFNumber.get() };
86     RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
87     SecCodeRef code = nullptr;
88     if ((errorCode = SecCodeCopyGuestWithAttributes(nullptr, attributes.get(), kSecCSDefaultFlags, &code)))
89         return String();
90     RetainPtr<SecCodeRef> codePtr = adoptCF(code);
91     RELEASE_ASSERT(codePtr);
92
93     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])");
94     SecRequirementRef signingRequirement = nullptr;
95     RELEASE_ASSERT(!SecRequirementCreateWithString(appleSignedOrMacAppStoreSignedOrAppleDeveloperSignedRequirement, kSecCSDefaultFlags, &signingRequirement));
96     RetainPtr<SecRequirementRef> signingRequirementPtr = adoptCF(signingRequirement);
97     errorCode = SecCodeCheckValidity(codePtr.get(), kSecCSDefaultFlags, signingRequirementPtr.get());
98     if (errorCode == errSecCSUnsigned || errorCode == errSecCSReqFailed)
99         return String(); // Unsigned or signed by a third-party
100     if (errorCode != errSecSuccess)
101         return emptyString(); // e.g. invalid/malformed signature
102     String codeSigningIdentifier;
103     CFDictionaryRef signingInfo = nullptr;
104     RELEASE_ASSERT(!SecCodeCopySigningInformation(codePtr.get(), kSecCSDefaultFlags, &signingInfo));
105     RetainPtr<CFDictionaryRef> signingInfoPtr = adoptCF(signingInfo);
106     if (CFDictionaryRef plist = dynamic_cf_cast<CFDictionaryRef>(CFDictionaryGetValue(signingInfoPtr.get(), kSecCodeInfoPList)))
107         codeSigningIdentifier = String(dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(plist, kCFBundleIdentifierKey)));
108     else
109         codeSigningIdentifier = String(dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(signingInfoPtr.get(), kSecCodeInfoIdentifier)));
110     RELEASE_ASSERT(!codeSigningIdentifier.isEmpty());
111     return codeSigningIdentifier;
112 }
113
114 void ChildProcess::initializeSandbox(const ChildProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
115 {
116     NSBundle *webkit2Bundle = [NSBundle bundleForClass:NSClassFromString(@"WKView")];
117     String defaultProfilePath = [webkit2Bundle pathForResource:[[NSBundle mainBundle] bundleIdentifier] ofType:@"sb"];
118
119     bool willUseUserDirectorySuffixInitializationParameter = false;
120     if (sandboxParameters.userDirectorySuffix().isNull()) {
121         auto userDirectorySuffix = parameters.extraInitializationData.find("user-directory-suffix");
122         if (userDirectorySuffix != parameters.extraInitializationData.end()) {
123             willUseUserDirectorySuffixInitializationParameter = true;
124             sandboxParameters.setUserDirectorySuffix([makeString(userDirectorySuffix->value, '/', String([[NSBundle mainBundle] bundleIdentifier])) fileSystemRepresentation]);
125         } else {
126             String defaultUserDirectorySuffix = makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', parameters.clientIdentifier);
127             sandboxParameters.setUserDirectorySuffix(defaultUserDirectorySuffix);
128         }
129     }
130
131     Vector<String> osVersionParts;
132     String osSystemMarketingVersion = systemMarketingVersion();
133     osSystemMarketingVersion.split('.', false, osVersionParts);
134     if (osVersionParts.size() < 2) {
135         WTFLogAlways("%s: Couldn't find OS Version\n", getprogname());
136         exit(EX_NOPERM);
137     }
138     String osVersion = osVersionParts[0] + '.' + osVersionParts[1];
139     sandboxParameters.addParameter("_OS_VERSION", osVersion.utf8().data());
140
141     // Use private temporary and cache directories.
142     setenv("DIRHELPER_USER_DIR_SUFFIX", fileSystemRepresentation(sandboxParameters.userDirectorySuffix()).data(), 1);
143     char temporaryDirectory[PATH_MAX];
144     if (!confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof(temporaryDirectory))) {
145         WTFLogAlways("%s: couldn't retrieve private temporary directory path: %d\n", getprogname(), errno);
146         exit(EX_NOPERM);
147     }
148     setenv("TMPDIR", temporaryDirectory, 1);
149
150     sandboxParameters.addPathParameter("WEBKIT2_FRAMEWORK_DIR", [[webkit2Bundle bundlePath] stringByDeletingLastPathComponent]);
151     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_TEMP_DIR", _CS_DARWIN_USER_TEMP_DIR);
152     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_CACHE_DIR", _CS_DARWIN_USER_CACHE_DIR);
153
154     char buffer[4096];
155     int bufferSize = sizeof(buffer);
156     struct passwd pwd;
157     struct passwd* result = 0;
158     if (getpwuid_r(getuid(), &pwd, buffer, bufferSize, &result) || !result) {
159         WTFLogAlways("%s: Couldn't find home directory\n", getprogname());
160         exit(EX_NOPERM);
161     }
162
163     sandboxParameters.addPathParameter("HOME_DIR", pwd.pw_dir);
164
165     String path = String::fromUTF8(pwd.pw_dir);
166     path.append("/Library");
167
168     sandboxParameters.addPathParameter("HOME_LIBRARY_DIR", fileSystemRepresentation(path).data());
169
170     path.append("/Preferences");
171
172     sandboxParameters.addPathParameter("HOME_LIBRARY_PREFERENCES_DIR", fileSystemRepresentation(path).data());
173
174     switch (sandboxParameters.mode()) {
175     case SandboxInitializationParameters::UseDefaultSandboxProfilePath:
176     case SandboxInitializationParameters::UseOverrideSandboxProfilePath: {
177         String sandboxProfilePath = sandboxParameters.mode() == SandboxInitializationParameters::UseDefaultSandboxProfilePath ? defaultProfilePath : sandboxParameters.overrideSandboxProfilePath();
178         if (!sandboxProfilePath.isEmpty()) {
179             CString profilePath = fileSystemRepresentation(sandboxProfilePath);
180             char* errorBuf;
181             if (sandbox_init_with_parameters(profilePath.data(), SANDBOX_NAMED_EXTERNAL, sandboxParameters.namedParameterArray(), &errorBuf)) {
182                 WTFLogAlways("%s: Couldn't initialize sandbox profile [%s], error '%s'\n", getprogname(), profilePath.data(), errorBuf);
183                 for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
184                     WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
185                 exit(EX_NOPERM);
186             }
187         }
188
189         break;
190     }
191     case SandboxInitializationParameters::UseSandboxProfile: {
192         char* errorBuf;
193         if (sandbox_init_with_parameters(sandboxParameters.sandboxProfile().utf8().data(), 0, sandboxParameters.namedParameterArray(), &errorBuf)) {
194             WTFLogAlways("%s: Couldn't initialize sandbox profile, error '%s'\n", getprogname(), errorBuf);
195             for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
196                 WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
197             exit(EX_NOPERM);
198         }
199
200         break;
201     }
202     }
203
204     // This will override LSFileQuarantineEnabled from Info.plist unless sandbox quarantine is globally disabled.
205     OSStatus error = WKEnableSandboxStyleFileQuarantine();
206     if (error) {
207         WTFLogAlways("%s: Couldn't enable sandbox style file quarantine: %ld\n", getprogname(), static_cast<long>(error));
208         exit(EX_NOPERM);
209     }
210
211     error = noErr;
212     String clientCodeSigningIdentifier = codeSigningIdentifierForProcess(xpc_connection_get_pid(parameters.connectionIdentifier.xpcConnection.get()), error);
213     bool isClientCodeSigned = !clientCodeSigningIdentifier.isNull();
214     if (isClientCodeSigned && willUseUserDirectorySuffixInitializationParameter) {
215         WTFLogAlways("%s: Only unsigned clients can specify parameter user-directory-suffix\n", getprogname());
216         exit(EX_NOPERM);
217     }
218     if (isClientCodeSigned && clientCodeSigningIdentifier != parameters.clientIdentifier) {
219         WTFLogAlways("%s: Code signing identifier of client differs from passed client identifier: %ld\n", getprogname(), static_cast<long>(error));
220         exit(EX_NOPERM);
221     }
222 }
223
224 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
225 void ChildProcess::setSharedHTTPCookieStorage(const Vector<uint8_t>& identifier)
226 {
227     // FIXME: Remove the runtime check when it's not needed (soon).
228     if (![NSHTTPCookieStorage respondsToSelector:@selector(_setSharedHTTPCookieStorage:)])
229         return;
230
231     RetainPtr<CFDataRef> cookieStorageData = adoptCF(CFDataCreate(kCFAllocatorDefault, identifier.data(), identifier.size()));
232     RetainPtr<CFHTTPCookieStorageRef> uiProcessCookieStorage = adoptCF(CFHTTPCookieStorageCreateFromIdentifyingData(kCFAllocatorDefault, cookieStorageData.get()));
233     [NSHTTPCookieStorage _setSharedHTTPCookieStorage:adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:uiProcessCookieStorage.get()]).get()];
234 }
235 #endif
236
237
238 #if USE(APPKIT)
239 void ChildProcess::stopNSAppRunLoop()
240 {
241     ASSERT([NSApp isRunning]);
242     [NSApp stop:nil];
243
244 #pragma clang diagnostic push
245 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
246     NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
247 #pragma clang diagnostic pop
248     [NSApp postEvent:event atStart:true];
249 }
250 #endif
251
252 void ChildProcess::setQOS(int latencyQOS, int throughputQOS)
253 {
254     if (!latencyQOS && !throughputQOS)
255         return;
256
257     struct task_qos_policy qosinfo = {
258         latencyQOS ? LATENCY_QOS_TIER_0 + latencyQOS - 1 : LATENCY_QOS_TIER_UNSPECIFIED,
259         throughputQOS ? THROUGHPUT_QOS_TIER_0 + throughputQOS - 1 : THROUGHPUT_QOS_TIER_UNSPECIFIED
260     };
261
262     task_policy_set(mach_task_self(), TASK_OVERRIDE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
263 }
264
265 } // namespace WebKit
266
267 #endif