Unreviewed, rolling out r234569.
[WebKit-https.git] / Source / WebKit / Shared / mac / ChildProcessMac.mm
1 /*
2  * Copyright (C) 2012-2018 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) || PLATFORM(IOSMAC)
29 #import "ChildProcess.h"
30
31 #import "CodeSigning.h"
32 #import "QuarantineSPI.h"
33 #import "SandboxInitializationParameters.h"
34 #import "WKFoundation.h"
35 #import "XPCServiceEntryPoint.h"
36 #import <WebCore/FileSystem.h>
37 #import <WebCore/SystemVersion.h>
38 #import <mach/mach.h>
39 #import <mach/task.h>
40 #import <pwd.h>
41 #import <stdlib.h>
42 #import <sysexits.h>
43 #import <wtf/Scope.h>
44 #import <wtf/spi/darwin/SandboxSPI.h>
45
46 #if USE(APPLE_INTERNAL_SDK)
47 #include <HIServices/ProcessesPriv.h>
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 #if !PLATFORM(IOSMAC)
71     OSStatus error = SetApplicationIsDaemon(true);
72     ASSERT_UNUSED(error, error == noErr);
73 #endif
74
75     launchServicesCheckIn();
76 }
77
78 void ChildProcess::launchServicesCheckIn()
79 {
80     _LSSetApplicationLaunchServicesServerConnectionStatus(0, 0);
81     RetainPtr<CFDictionaryRef> unused = _LSApplicationCheckIn(-2, CFBundleGetInfoDictionary(CFBundleGetMainBundle()));
82 }
83
84 void ChildProcess::platformInitialize()
85 {
86     initializeTimerCoalescingPolicy();
87     [[NSFileManager defaultManager] changeCurrentDirectoryPath:[[NSBundle mainBundle] bundlePath]];
88 }
89
90 static OSStatus enableSandboxStyleFileQuarantine()
91 {
92 #if !PLATFORM(IOSMAC)
93     qtn_proc_t quarantineProperties = qtn_proc_alloc();
94     auto quarantinePropertiesDeleter = makeScopeExit([quarantineProperties]() {
95         qtn_proc_free(quarantineProperties);
96     });
97
98
99     if (qtn_proc_init_with_self(quarantineProperties)) {
100         // See <rdar://problem/13463752>.
101         qtn_proc_init(quarantineProperties);
102     }
103
104     if (auto error = qtn_proc_set_flags(quarantineProperties, QTN_FLAG_SANDBOX))
105         return error;
106
107     // QTN_FLAG_SANDBOX is silently ignored if security.mac.qtn.sandbox_enforce sysctl is 0.
108     // In that case, quarantine falls back to advisory QTN_FLAG_DOWNLOAD.
109     return qtn_proc_apply_to_self(quarantineProperties);
110 #else
111     return false;
112 #endif
113 }
114
115 void ChildProcess::initializeSandbox(const ChildProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
116 {
117 #if WK_API_ENABLED
118     NSBundle *webKit2Bundle = [NSBundle bundleForClass:NSClassFromString(@"WKWebView")];
119 #else
120     NSBundle *webKit2Bundle = [NSBundle bundleForClass:NSClassFromString(@"WKView")];
121 #endif
122     String defaultProfilePath = [webKit2Bundle pathForResource:[[NSBundle mainBundle] bundleIdentifier] ofType:@"sb"];
123
124     if (sandboxParameters.userDirectorySuffix().isNull()) {
125         auto userDirectorySuffix = parameters.extraInitializationData.find("user-directory-suffix");
126         if (userDirectorySuffix != parameters.extraInitializationData.end())
127             sandboxParameters.setUserDirectorySuffix([makeString(userDirectorySuffix->value, '/', String([[NSBundle mainBundle] bundleIdentifier])) fileSystemRepresentation]);
128         else {
129             String clientIdentifier = codeSigningIdentifier(parameters.connectionIdentifier.xpcConnection.get());
130             if (clientIdentifier.isNull())
131                 clientIdentifier = parameters.clientIdentifier;
132             String defaultUserDirectorySuffix = makeString(String([[NSBundle mainBundle] bundleIdentifier]), '+', clientIdentifier);
133             sandboxParameters.setUserDirectorySuffix(defaultUserDirectorySuffix);
134         }
135     }
136
137     String osSystemMarketingVersion = systemMarketingVersion();
138     Vector<String> osVersionParts = osSystemMarketingVersion.split('.');
139     if (osVersionParts.size() < 2) {
140         WTFLogAlways("%s: Couldn't find OS Version\n", getprogname());
141         exit(EX_NOPERM);
142     }
143     String osVersion = osVersionParts[0] + '.' + osVersionParts[1];
144     sandboxParameters.addParameter("_OS_VERSION", osVersion.utf8().data());
145
146     // Use private temporary and cache directories.
147     setenv("DIRHELPER_USER_DIR_SUFFIX", FileSystem::fileSystemRepresentation(sandboxParameters.userDirectorySuffix()).data(), 1);
148     char temporaryDirectory[PATH_MAX];
149     if (!confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof(temporaryDirectory))) {
150         WTFLogAlways("%s: couldn't retrieve private temporary directory path: %d\n", getprogname(), errno);
151         exit(EX_NOPERM);
152     }
153     setenv("TMPDIR", temporaryDirectory, 1);
154
155     sandboxParameters.addPathParameter("WEBKIT2_FRAMEWORK_DIR", [[webKit2Bundle bundlePath] stringByDeletingLastPathComponent]);
156     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_TEMP_DIR", _CS_DARWIN_USER_TEMP_DIR);
157     sandboxParameters.addConfDirectoryParameter("DARWIN_USER_CACHE_DIR", _CS_DARWIN_USER_CACHE_DIR);
158
159     char buffer[4096];
160     int bufferSize = sizeof(buffer);
161     struct passwd pwd;
162     struct passwd* result = 0;
163     if (getpwuid_r(getuid(), &pwd, buffer, bufferSize, &result) || !result) {
164         WTFLogAlways("%s: Couldn't find home directory\n", getprogname());
165         exit(EX_NOPERM);
166     }
167
168     sandboxParameters.addPathParameter("HOME_DIR", pwd.pw_dir);
169
170     String path = String::fromUTF8(pwd.pw_dir);
171     path.append("/Library");
172
173     sandboxParameters.addPathParameter("HOME_LIBRARY_DIR", FileSystem::fileSystemRepresentation(path).data());
174
175     path.append("/Preferences");
176
177     sandboxParameters.addPathParameter("HOME_LIBRARY_PREFERENCES_DIR", FileSystem::fileSystemRepresentation(path).data());
178
179     switch (sandboxParameters.mode()) {
180     case SandboxInitializationParameters::UseDefaultSandboxProfilePath:
181     case SandboxInitializationParameters::UseOverrideSandboxProfilePath: {
182         String sandboxProfilePath = sandboxParameters.mode() == SandboxInitializationParameters::UseDefaultSandboxProfilePath ? defaultProfilePath : sandboxParameters.overrideSandboxProfilePath();
183         if (!sandboxProfilePath.isEmpty()) {
184             CString profilePath = FileSystem::fileSystemRepresentation(sandboxProfilePath);
185             char* errorBuf;
186 #pragma clang diagnostic push
187 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
188             if (sandbox_init_with_parameters(profilePath.data(), SANDBOX_NAMED_EXTERNAL, sandboxParameters.namedParameterArray(), &errorBuf)) {
189 #pragma clang diagnostic pop
190                 WTFLogAlways("%s: Couldn't initialize sandbox profile [%s], error '%s'\n", getprogname(), profilePath.data(), errorBuf);
191                 for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
192                     WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
193                 exit(EX_NOPERM);
194             }
195         }
196
197         break;
198     }
199     case SandboxInitializationParameters::UseSandboxProfile: {
200         char* errorBuf;
201 #pragma clang diagnostic push
202 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
203         if (sandbox_init_with_parameters(sandboxParameters.sandboxProfile().utf8().data(), 0, sandboxParameters.namedParameterArray(), &errorBuf)) {
204 #pragma clang diagnostic pop
205             WTFLogAlways("%s: Couldn't initialize sandbox profile, error '%s'\n", getprogname(), errorBuf);
206             for (size_t i = 0, count = sandboxParameters.count(); i != count; ++i)
207                 WTFLogAlways("%s=%s\n", sandboxParameters.name(i), sandboxParameters.value(i));
208             exit(EX_NOPERM);
209         }
210
211         break;
212     }
213     }
214
215     if (shouldOverrideQuarantine()) {
216         // This will override LSFileQuarantineEnabled from Info.plist unless sandbox quarantine is globally disabled.
217         OSStatus error = enableSandboxStyleFileQuarantine();
218         if (error) {
219             WTFLogAlways("%s: Couldn't enable sandbox style file quarantine: %ld\n", getprogname(), static_cast<long>(error));
220             exit(EX_NOPERM);
221         }
222     }
223 }
224
225 #if USE(APPKIT)
226 void ChildProcess::stopNSAppRunLoop()
227 {
228     ASSERT([NSApp isRunning]);
229     [NSApp stop:nil];
230
231     NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
232     [NSApp postEvent:event atStart:true];
233 }
234 #endif
235
236 #if !PLATFORM(IOSMAC) && ENABLE(WEBPROCESS_NSRUNLOOP)
237 void ChildProcess::stopNSRunLoop()
238 {
239     ASSERT([NSRunLoop mainRunLoop]);
240     [[NSRunLoop mainRunLoop] performBlock:^{
241         exit(0);
242     }];
243 }
244 #endif
245
246 #if PLATFORM(IOSMAC)
247 void ChildProcess::platformStopRunLoop()
248 {
249     XPCServiceExit(WTFMove(m_priorityBoostMessage));
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