[Mac] Use the PID of the WebContent process when issuing local file read sandbox...
[WebKit-https.git] / Source / WebKit / Shared / Cocoa / SandboxExtensionCocoa.mm
1 /*
2  * Copyright (C) 2010-2019 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 #import "SandboxExtension.h"
28
29 #if ENABLE(SANDBOX_EXTENSIONS)
30
31 #import "DataReference.h"
32 #import "Decoder.h"
33 #import "Encoder.h"
34 #import <wtf/FileSystem.h>
35 #import <wtf/spi/darwin/SandboxSPI.h>
36 #import <wtf/text/CString.h>
37
38 namespace WebKit {
39
40 class SandboxExtensionImpl {
41 public:
42     static std::unique_ptr<SandboxExtensionImpl> create(const char* path, SandboxExtension::Type type, Optional<pid_t> pid = WTF::nullopt)
43     {
44         std::unique_ptr<SandboxExtensionImpl> impl { new SandboxExtensionImpl(path, type, pid) };
45         if (!impl->m_token)
46             return nullptr;
47         return impl;
48     }
49
50     SandboxExtensionImpl(const char* serializedFormat, size_t length)
51         : m_token { strndup(serializedFormat, length) }
52     {
53     }
54
55     ~SandboxExtensionImpl()
56     {
57         free(m_token);
58     }
59
60     bool WARN_UNUSED_RETURN consume()
61     {
62         m_handle = sandbox_extension_consume(m_token);
63 #if PLATFORM(IOS_FAMILY_SIMULATOR)
64         return !sandbox_check(getpid(), 0, SANDBOX_FILTER_NONE);
65 #else
66         if (m_handle == -1) {
67             LOG_ERROR("Could not create a sandbox extension for '%s', errno = %d", m_token, errno);
68             return false;
69         }
70         return m_handle;
71 #endif
72     }
73
74     bool invalidate()
75     {
76         return !sandbox_extension_release(std::exchange(m_handle, 0));
77     }
78
79     const char* WARN_UNUSED_RETURN getSerializedFormat(size_t& length)
80     {
81         length = strlen(m_token);
82         return m_token;
83     }
84
85 private:
86     char* sandboxExtensionForType(const char* path, SandboxExtension::Type type, Optional<pid_t> pid = WTF::nullopt)
87     {
88         switch (type) {
89         case SandboxExtension::Type::ReadOnly:
90             return sandbox_extension_issue_file(APP_SANDBOX_READ, path, 0);
91         case SandboxExtension::Type::ReadWrite:
92             return sandbox_extension_issue_file(APP_SANDBOX_READ_WRITE, path, 0);
93         case SandboxExtension::Type::Mach:
94 #if HAVE(SANDBOX_ISSUE_MACH_EXTENSION_TO_PROCESS_BY_PID)
95             return sandbox_extension_issue_mach_to_process_by_pid("com.apple.webkit.extension.mach"_s, path, 0, pid.value());
96 #else
97             UNUSED_PARAM(pid);
98             ASSERT_NOT_REACHED();
99             return nullptr;
100 #endif
101         case SandboxExtension::Type::Generic:
102             return sandbox_extension_issue_generic(path, 0);
103         case SandboxExtension::Type::ReadByPid:
104 #if HAVE(SANDBOX_ISSUE_READ_EXTENSION_TO_PROCESS_BY_PID)
105             return sandbox_extension_issue_file_to_process_by_pid(APP_SANDBOX_READ, path, 0, pid.value());
106 #else
107             UNUSED_PARAM(pid);
108             ASSERT_NOT_REACHED();
109             return nullptr;
110 #endif
111         }
112     }
113
114     SandboxExtensionImpl(const char* path, SandboxExtension::Type type, Optional<pid_t> pid = WTF::nullopt)
115         : m_token { sandboxExtensionForType(path, type, pid) }
116     {
117     }
118
119     char* m_token;
120     int64_t m_handle { 0 };
121 };
122
123 SandboxExtension::Handle::Handle()
124 {
125 }
126
127 SandboxExtension::Handle::Handle(Handle&&) = default;
128 SandboxExtension::Handle& SandboxExtension::Handle::operator=(Handle&&) = default;
129
130 SandboxExtension::Handle::~Handle()
131 {
132     if (m_sandboxExtension)
133         m_sandboxExtension->invalidate();
134 }
135
136 void SandboxExtension::Handle::encode(IPC::Encoder& encoder) const
137 {
138     if (!m_sandboxExtension) {
139         encoder << IPC::DataReference();
140         return;
141     }
142
143     size_t length = 0;
144     const char* serializedFormat = m_sandboxExtension->getSerializedFormat(length);
145     ASSERT(serializedFormat);
146
147     encoder << IPC::DataReference(reinterpret_cast<const uint8_t*>(serializedFormat), length);
148
149     // Encoding will destroy the sandbox extension locally.
150     m_sandboxExtension = 0;
151 }
152
153 auto SandboxExtension::Handle::decode(IPC::Decoder& decoder) -> Optional<Handle>
154 {
155     IPC::DataReference dataReference;
156     if (!decoder.decode(dataReference))
157         return WTF::nullopt;
158
159     if (dataReference.isEmpty())
160         return {{ }};
161
162     Handle handle;
163     handle.m_sandboxExtension = std::make_unique<SandboxExtensionImpl>(reinterpret_cast<const char*>(dataReference.data()), dataReference.size());
164     return WTFMove(handle);
165 }
166
167 SandboxExtension::HandleArray::HandleArray()
168 {
169 }
170
171 SandboxExtension::HandleArray::~HandleArray()
172 {
173 }
174
175 void SandboxExtension::HandleArray::allocate(size_t size)
176 {
177     if (!size)
178         return;
179
180     ASSERT(m_data.isEmpty());
181
182     m_data.resize(size);
183 }
184
185 SandboxExtension::Handle& SandboxExtension::HandleArray::operator[](size_t i)
186 {
187     RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(i < m_data.size());
188     return m_data[i];
189 }
190
191 const SandboxExtension::Handle& SandboxExtension::HandleArray::operator[](size_t i) const
192 {
193     RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(i < m_data.size());
194     return m_data[i];
195 }
196
197 size_t SandboxExtension::HandleArray::size() const
198 {
199     return m_data.size();
200 }
201
202 void SandboxExtension::HandleArray::encode(IPC::Encoder& encoder) const
203 {
204     encoder << static_cast<uint64_t>(size());
205     for (auto& handle : m_data)
206         encoder << handle;
207 }
208
209 Optional<SandboxExtension::HandleArray> SandboxExtension::HandleArray::decode(IPC::Decoder& decoder)
210 {
211     Optional<uint64_t> size;
212     decoder >> size;
213     if (!size)
214         return WTF::nullopt;
215     SandboxExtension::HandleArray handles;
216     handles.allocate(*size);
217     for (size_t i = 0; i < *size; ++i) {
218         Optional<SandboxExtension::Handle> handle;
219         decoder >> handle;
220         if (!handle)
221             return WTF::nullopt;
222         handles[i] = WTFMove(*handle);
223     }
224     return WTFMove(handles);
225 }
226
227 RefPtr<SandboxExtension> SandboxExtension::create(Handle&& handle)
228 {
229     if (!handle.m_sandboxExtension)
230         return nullptr;
231
232     return adoptRef(new SandboxExtension(handle));
233 }
234
235 String stringByResolvingSymlinksInPath(const String& path)
236 {
237     return [(NSString *)path stringByResolvingSymlinksInPath];
238 }
239
240 String resolveAndCreateReadWriteDirectoryForSandboxExtension(const String& path)
241 {
242     NSError *error = nil;
243     NSString *nsPath = path;
244
245     if (![[NSFileManager defaultManager] createDirectoryAtPath:nsPath withIntermediateDirectories:YES attributes:nil error:&error]) {
246         NSLog(@"could not create directory \"%@\" for future sandbox extension, error %@", nsPath, error);
247         return { };
248     }
249
250     return resolvePathForSandboxExtension(path);
251 }
252
253 String resolvePathForSandboxExtension(const String& path)
254 {
255     String resolvedPath = stringByResolvingSymlinksInPath(path);
256     if (resolvedPath.isNull()) {
257         LOG_ERROR("Could not create a valid file system representation for the string '%s' of length %lu", resolvedPath.utf8().data(), resolvedPath.length());
258         return { };
259     }
260
261     return resolvedPath;
262 }
263
264 bool SandboxExtension::createHandleWithoutResolvingPath(const String& path, Type type, Handle& handle)
265 {
266     ASSERT(!handle.m_sandboxExtension);
267
268     handle.m_sandboxExtension = SandboxExtensionImpl::create(path.utf8().data(), type);
269     if (!handle.m_sandboxExtension) {
270         LOG_ERROR("Could not create a sandbox extension for '%s'", path.utf8().data());
271         return false;
272     }
273     return true;
274 }
275
276 bool SandboxExtension::createHandle(const String& path, Type type, Handle& handle)
277 {
278     ASSERT(!handle.m_sandboxExtension);
279
280     return createHandleWithoutResolvingPath(resolvePathForSandboxExtension(path), type, handle);
281 }
282
283 bool SandboxExtension::createHandleForReadWriteDirectory(const String& path, SandboxExtension::Handle& handle)
284 {
285     String resolvedPath = resolveAndCreateReadWriteDirectoryForSandboxExtension(path);
286     if (resolvedPath.isNull())
287         return false;
288
289     return SandboxExtension::createHandleWithoutResolvingPath(resolvedPath, SandboxExtension::Type::ReadWrite, handle);
290 }
291
292 String SandboxExtension::createHandleForTemporaryFile(const String& prefix, Type type, Handle& handle)
293 {
294     ASSERT(!handle.m_sandboxExtension);
295     
296     Vector<char> path(PATH_MAX);
297     if (!confstr(_CS_DARWIN_USER_TEMP_DIR, path.data(), path.size()))
298         return String();
299     
300     // Shrink the vector.   
301     path.shrink(strlen(path.data()));
302
303     // FIXME: Change to a runtime assertion that the path ends with a slash once <rdar://problem/23579077> is
304     // fixed in all iOS Simulator versions that we use.
305     if (path.last() != '/')
306         path.append('/');
307     
308     // Append the file name.    
309     path.append(prefix.utf8().data(), prefix.length());
310     path.append('\0');
311     
312     handle.m_sandboxExtension = SandboxExtensionImpl::create(FileSystem::fileSystemRepresentation(path.data()).data(), type);
313
314     if (!handle.m_sandboxExtension) {
315         WTFLogAlways("Could not create a sandbox extension for temporary file '%s'", path.data());
316         return String();
317     }
318     return String(path.data());
319 }
320
321 bool SandboxExtension::createHandleForGenericExtension(const String& extensionClass, Handle& handle)
322 {
323     ASSERT(!handle.m_sandboxExtension);
324
325     handle.m_sandboxExtension = SandboxExtensionImpl::create(extensionClass.utf8().data(), Type::Generic);
326     if (!handle.m_sandboxExtension) {
327         WTFLogAlways("Could not create a '%s' sandbox extension", extensionClass.utf8().data());
328         return false;
329     }
330     
331     return true;
332 }
333
334 bool SandboxExtension::createHandleForMachLookupByPid(const String& service, pid_t pid, Handle& handle)
335 {
336     ASSERT(!handle.m_sandboxExtension);
337     
338     handle.m_sandboxExtension = SandboxExtensionImpl::create(service.utf8().data(), Type::Mach, pid);
339     if (!handle.m_sandboxExtension) {
340         WTFLogAlways("Could not create a '%s' sandbox extension", service.utf8().data());
341         return false;
342     }
343     
344     return true;
345 }
346
347 bool SandboxExtension::createHandleForReadByPid(const String& path, ProcessID pid, Handle& handle)
348 {
349     ASSERT(!handle.m_sandboxExtension);
350     
351     handle.m_sandboxExtension = SandboxExtensionImpl::create(path.utf8().data(), Type::ReadByPid, pid);
352     if (!handle.m_sandboxExtension) {
353         WTFLogAlways("Could not create a '%s' sandbox extension", path.utf8().data());
354         return false;
355     }
356     
357     return true;
358 }
359
360 SandboxExtension::SandboxExtension(const Handle& handle)
361     : m_sandboxExtension(WTFMove(handle.m_sandboxExtension))
362 {
363 }
364
365 SandboxExtension::~SandboxExtension()
366 {
367     if (!m_sandboxExtension)
368         return;
369
370     ASSERT(!m_useCount);
371 }
372
373 bool SandboxExtension::revoke()
374 {
375     ASSERT(m_sandboxExtension);
376     ASSERT(m_useCount);
377     
378     if (--m_useCount)
379         return true;
380
381     return m_sandboxExtension->invalidate();
382 }
383
384 bool SandboxExtension::consume()
385 {
386     ASSERT(m_sandboxExtension);
387
388     if (m_useCount++)
389         return true;
390
391     return m_sandboxExtension->consume();
392 }
393
394 bool SandboxExtension::consumePermanently()
395 {
396     ASSERT(m_sandboxExtension);
397
398     bool result = m_sandboxExtension->consume();
399
400     // Destroy the extension without invalidating it.
401     m_sandboxExtension = nullptr;
402
403     return result;
404 }
405
406 bool SandboxExtension::consumePermanently(const Handle& handle)
407 {
408     if (!handle.m_sandboxExtension)
409         return false;
410
411     bool result = handle.m_sandboxExtension->consume();
412     
413     // Destroy the extension without invalidating it.
414     handle.m_sandboxExtension = nullptr;
415
416     return result;
417 }
418
419 } // namespace WebKit
420
421 #endif // ENABLE(SANDBOX_EXTENSIONS)