Unreviewed, rolling out r198856.
[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 // FIXME: Remove this macro guard once we fix <rdar://problem/24308793>.
82 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
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 #endif
95
96 void ChildProcess::initializeSandbox(const ChildProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
97 {
98     NSBundle *webkit2Bundle = [NSBundle bundleForClass:NSClassFromString(@"WKView")];
99     String defaultProfilePath = [webkit2Bundle pathForResource:[[NSBundle mainBundle] bundleIdentifier] ofType:@"sb"];
100
101     if (sandboxParameters.userDirectorySuffix().isNull()) {
102         // FIXME: Remove this macro guard once we fix <rdar://problem/24308793>.
103 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
104         if (const OSObjectPtr<xpc_connection_t>& xpcConnection = parameters.connectionIdentifier.xpcConnection) {
105             pid_t clientProcessID = xpc_connection_get_pid(xpcConnection.get());
106             RetainPtr<SecCodeRef> code = findSecCodeForProcess(clientProcessID);
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 = nullptr;
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                 String clientIdentifierToUse;
117                 CFDictionaryRef signingInfo = nullptr;
118                 status = SecCodeCopySigningInformation(code.get(), kSecCSDefaultFlags, &signingInfo);
119                 RELEASE_ASSERT(status == errSecSuccess);
120                 if (CFDictionaryRef plist = dynamic_cf_cast<CFDictionaryRef>(CFDictionaryGetValue(signingInfo, kSecCodeInfoPList)))
121                     clientIdentifierToUse = String(dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(plist, kCFBundleIdentifierKey)));
122                 else
123                     clientIdentifierToUse = String(dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(signingInfo, kSecCodeInfoIdentifier)));
124                 CFRelease(signingInfo);
125                 RELEASE_ASSERT(!clientIdentifierToUse.isEmpty());
126                 sandboxParameters.setUserDirectorySuffix(makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', clientIdentifierToUse));
127             } else {
128                 // Unsigned, signed by a third party, or has an invalid/malformed signature
129                 auto userDirectorySuffix = parameters.extraInitializationData.find("user-directory-suffix");
130                 if (userDirectorySuffix != parameters.extraInitializationData.end())
131                     sandboxParameters.setUserDirectorySuffix([makeString(userDirectorySuffix->value, '/', String([[NSBundle mainBundle] bundleIdentifier])) fileSystemRepresentation]);
132                 sandboxParameters.setUserDirectorySuffix(makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', parameters.clientIdentifier));
133             }
134             CFRelease(signingRequirement);
135         } else {
136             // Legacy client
137             sandboxParameters.setUserDirectorySuffix(makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', parameters.clientIdentifier));
138         }
139 #else
140         sandboxParameters.setUserDirectorySuffix(makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', parameters.clientIdentifier));
141 #endif
142     }
143
144     Vector<String> osVersionParts;
145     String osSystemMarketingVersion = systemMarketingVersion();
146     osSystemMarketingVersion.split('.', false, osVersionParts);
147     if (osVersionParts.size() < 2) {
148         WTFLogAlways("%s: Couldn't find OS Version\n", getprogname());
149         exit(EX_NOPERM);
150     }
151     String osVersion = osVersionParts[0] + '.' + osVersionParts[1];
152     sandboxParameters.addParameter("_OS_VERSION", osVersion.utf8().data());
153
154     // Use private temporary and cache directories.
155     setenv("DIRHELPER_USER_DIR_SUFFIX", fileSystemRepresentation(sandboxParameters.userDirectorySuffix()).data(), 1);
156     char temporaryDirectory[PATH_MAX];
157     if (!confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof(temporaryDirectory))) {
158         WTFLogAlways("%s: couldn't retrieve private temporary directory path: %d\n", getprogname(), errno);
159         exit(EX_NOPERM);
160     }
161     setenv("TMPDIR", temporaryDirectory, 1);
162
163     sandboxParameters.addPathParameter("WEBKIT2_FRAMEWORK_DIR", [[webkit2Bundle bundlePath] stringByDeletingLastPathComponent]);
164     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_TEMP_DIR", _CS_DARWIN_USER_TEMP_DIR);
165     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_CACHE_DIR", _CS_DARWIN_USER_CACHE_DIR);
166
167     char buffer[4096];
168     int bufferSize = sizeof(buffer);
169     struct passwd pwd;
170     struct passwd* result = 0;
171     if (getpwuid_r(getuid(), &pwd, buffer, bufferSize, &result) || !result) {
172         WTFLogAlways("%s: Couldn't find home directory\n", getprogname());
173         exit(EX_NOPERM);
174     }
175
176     sandboxParameters.addPathParameter("HOME_DIR", pwd.pw_dir);
177
178     String path = String::fromUTF8(pwd.pw_dir);
179     path.append("/Library");
180
181     sandboxParameters.addPathParameter("HOME_LIBRARY_DIR", fileSystemRepresentation(path).data());
182
183     path.append("/Preferences");
184
185     sandboxParameters.addPathParameter("HOME_LIBRARY_PREFERENCES_DIR", fileSystemRepresentation(path).data());
186
187     switch (sandboxParameters.mode()) {
188     case SandboxInitializationParameters::UseDefaultSandboxProfilePath:
189     case SandboxInitializationParameters::UseOverrideSandboxProfilePath: {
190         String sandboxProfilePath = sandboxParameters.mode() == SandboxInitializationParameters::UseDefaultSandboxProfilePath ? defaultProfilePath : sandboxParameters.overrideSandboxProfilePath();
191         if (!sandboxProfilePath.isEmpty()) {
192             CString profilePath = fileSystemRepresentation(sandboxProfilePath);
193             char* errorBuf;
194             if (sandbox_init_with_parameters(profilePath.data(), SANDBOX_NAMED_EXTERNAL, sandboxParameters.namedParameterArray(), &errorBuf)) {
195                 WTFLogAlways("%s: Couldn't initialize sandbox profile [%s], error '%s'\n", getprogname(), profilePath.data(), errorBuf);
196                 for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
197                     WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
198                 exit(EX_NOPERM);
199             }
200         }
201
202         break;
203     }
204     case SandboxInitializationParameters::UseSandboxProfile: {
205         char* errorBuf;
206         if (sandbox_init_with_parameters(sandboxParameters.sandboxProfile().utf8().data(), 0, sandboxParameters.namedParameterArray(), &errorBuf)) {
207             WTFLogAlways("%s: Couldn't initialize sandbox profile, error '%s'\n", getprogname(), errorBuf);
208             for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
209                 WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
210             exit(EX_NOPERM);
211         }
212
213         break;
214     }
215     }
216
217     // This will override LSFileQuarantineEnabled from Info.plist unless sandbox quarantine is globally disabled.
218     OSStatus error = WKEnableSandboxStyleFileQuarantine();
219     if (error) {
220         WTFLogAlways("%s: Couldn't enable sandbox style file quarantine: %ld\n", getprogname(), (long)error);
221         exit(EX_NOPERM);
222     }
223 }
224
225 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
226 void ChildProcess::setSharedHTTPCookieStorage(const Vector<uint8_t>& identifier)
227 {
228     // FIXME: Remove the runtime check when it's not needed (soon).
229     if (![NSHTTPCookieStorage respondsToSelector:@selector(_setSharedHTTPCookieStorage:)])
230         return;
231
232     RetainPtr<CFDataRef> cookieStorageData = adoptCF(CFDataCreate(kCFAllocatorDefault, identifier.data(), identifier.size()));
233     RetainPtr<CFHTTPCookieStorageRef> uiProcessCookieStorage = adoptCF(CFHTTPCookieStorageCreateFromIdentifyingData(kCFAllocatorDefault, cookieStorageData.get()));
234     [NSHTTPCookieStorage _setSharedHTTPCookieStorage:adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:uiProcessCookieStorage.get()]).get()];
235 }
236 #endif
237
238
239 #if USE(APPKIT)
240 void ChildProcess::stopNSAppRunLoop()
241 {
242     ASSERT([NSApp isRunning]);
243     [NSApp stop:nil];
244
245 #pragma clang diagnostic push
246 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
247     NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
248 #pragma clang diagnostic pop
249     [NSApp postEvent:event atStart:true];
250 }
251 #endif
252
253 void ChildProcess::setQOS(int latencyQOS, int throughputQOS)
254 {
255     if (!latencyQOS && !throughputQOS)
256         return;
257
258     struct task_qos_policy qosinfo = {
259         latencyQOS ? LATENCY_QOS_TIER_0 + latencyQOS - 1 : LATENCY_QOS_TIER_UNSPECIFIED,
260         throughputQOS ? THROUGHPUT_QOS_TIER_0 + throughputQOS - 1 : THROUGHPUT_QOS_TIER_UNSPECIFIED
261     };
262
263     task_policy_set(mach_task_self(), TASK_OVERRIDE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
264 }
265
266 } // namespace WebKit
267
268 #endif