7b6d52be7f15dc7e9a04abf23c9b068b11ef1432
[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         }
104     }
105
106     SandboxExtensionImpl(const char* path, SandboxExtension::Type type, Optional<pid_t> pid = WTF::nullopt)
107         : m_token { sandboxExtensionForType(path, type, pid) }
108     {
109     }
110
111     char* m_token;
112     int64_t m_handle { 0 };
113 };
114
115 SandboxExtension::Handle::Handle()
116 {
117 }
118
119 SandboxExtension::Handle::Handle(Handle&&) = default;
120 SandboxExtension::Handle& SandboxExtension::Handle::operator=(Handle&&) = default;
121
122 SandboxExtension::Handle::~Handle()
123 {
124     if (m_sandboxExtension)
125         m_sandboxExtension->invalidate();
126 }
127
128 void SandboxExtension::Handle::encode(IPC::Encoder& encoder) const
129 {
130     if (!m_sandboxExtension) {
131         encoder << IPC::DataReference();
132         return;
133     }
134
135     size_t length = 0;
136     const char* serializedFormat = m_sandboxExtension->getSerializedFormat(length);
137     ASSERT(serializedFormat);
138
139     encoder << IPC::DataReference(reinterpret_cast<const uint8_t*>(serializedFormat), length);
140
141     // Encoding will destroy the sandbox extension locally.
142     m_sandboxExtension = 0;
143 }
144
145 auto SandboxExtension::Handle::decode(IPC::Decoder& decoder) -> Optional<Handle>
146 {
147     IPC::DataReference dataReference;
148     if (!decoder.decode(dataReference))
149         return WTF::nullopt;
150
151     if (dataReference.isEmpty())
152         return {{ }};
153
154     Handle handle;
155     handle.m_sandboxExtension = std::make_unique<SandboxExtensionImpl>(reinterpret_cast<const char*>(dataReference.data()), dataReference.size());
156     return WTFMove(handle);
157 }
158
159 SandboxExtension::HandleArray::HandleArray()
160 {
161 }
162
163 SandboxExtension::HandleArray::~HandleArray()
164 {
165 }
166
167 void SandboxExtension::HandleArray::allocate(size_t size)
168 {
169     if (!size)
170         return;
171
172     ASSERT(m_data.isEmpty());
173
174     m_data.resize(size);
175 }
176
177 SandboxExtension::Handle& SandboxExtension::HandleArray::operator[](size_t i)
178 {
179     RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(i < m_data.size());
180     return m_data[i];
181 }
182
183 const SandboxExtension::Handle& SandboxExtension::HandleArray::operator[](size_t i) const
184 {
185     RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(i < m_data.size());
186     return m_data[i];
187 }
188
189 size_t SandboxExtension::HandleArray::size() const
190 {
191     return m_data.size();
192 }
193
194 void SandboxExtension::HandleArray::encode(IPC::Encoder& encoder) const
195 {
196     encoder << static_cast<uint64_t>(size());
197     for (auto& handle : m_data)
198         encoder << handle;
199 }
200
201 Optional<SandboxExtension::HandleArray> SandboxExtension::HandleArray::decode(IPC::Decoder& decoder)
202 {
203     Optional<uint64_t> size;
204     decoder >> size;
205     if (!size)
206         return WTF::nullopt;
207     SandboxExtension::HandleArray handles;
208     handles.allocate(*size);
209     for (size_t i = 0; i < *size; ++i) {
210         Optional<SandboxExtension::Handle> handle;
211         decoder >> handle;
212         if (!handle)
213             return WTF::nullopt;
214         handles[i] = WTFMove(*handle);
215     }
216     return WTFMove(handles);
217 }
218
219 RefPtr<SandboxExtension> SandboxExtension::create(Handle&& handle)
220 {
221     if (!handle.m_sandboxExtension)
222         return nullptr;
223
224     return adoptRef(new SandboxExtension(handle));
225 }
226
227 String stringByResolvingSymlinksInPath(const String& path)
228 {
229     return [(NSString *)path stringByResolvingSymlinksInPath];
230 }
231
232 String resolveAndCreateReadWriteDirectoryForSandboxExtension(const String& path)
233 {
234     NSError *error = nil;
235     NSString *nsPath = path;
236
237     if (![[NSFileManager defaultManager] createDirectoryAtPath:nsPath withIntermediateDirectories:YES attributes:nil error:&error]) {
238         NSLog(@"could not create directory \"%@\" for future sandbox extension, error %@", nsPath, error);
239         return { };
240     }
241
242     return resolvePathForSandboxExtension(path);
243 }
244
245 String resolvePathForSandboxExtension(const String& path)
246 {
247     String resolvedPath = stringByResolvingSymlinksInPath(path);
248     if (resolvedPath.isNull()) {
249         LOG_ERROR("Could not create a valid file system representation for the string '%s' of length %lu", resolvedPath.utf8().data(), resolvedPath.length());
250         return { };
251     }
252
253     return resolvedPath;
254 }
255
256 bool SandboxExtension::createHandleWithoutResolvingPath(const String& path, Type type, Handle& handle)
257 {
258     ASSERT(!handle.m_sandboxExtension);
259
260     handle.m_sandboxExtension = SandboxExtensionImpl::create(path.utf8().data(), type);
261     if (!handle.m_sandboxExtension) {
262         LOG_ERROR("Could not create a sandbox extension for '%s'", path.utf8().data());
263         return false;
264     }
265     return true;
266 }
267
268 bool SandboxExtension::createHandle(const String& path, Type type, Handle& handle)
269 {
270     ASSERT(!handle.m_sandboxExtension);
271
272     return createHandleWithoutResolvingPath(resolvePathForSandboxExtension(path), type, handle);
273 }
274
275 bool SandboxExtension::createHandleForReadWriteDirectory(const String& path, SandboxExtension::Handle& handle)
276 {
277     String resolvedPath = resolveAndCreateReadWriteDirectoryForSandboxExtension(path);
278     if (resolvedPath.isNull())
279         return false;
280
281     return SandboxExtension::createHandleWithoutResolvingPath(resolvedPath, SandboxExtension::Type::ReadWrite, handle);
282 }
283
284 String SandboxExtension::createHandleForTemporaryFile(const String& prefix, Type type, Handle& handle)
285 {
286     ASSERT(!handle.m_sandboxExtension);
287     
288     Vector<char> path(PATH_MAX);
289     if (!confstr(_CS_DARWIN_USER_TEMP_DIR, path.data(), path.size()))
290         return String();
291     
292     // Shrink the vector.   
293     path.shrink(strlen(path.data()));
294
295     // FIXME: Change to a runtime assertion that the path ends with a slash once <rdar://problem/23579077> is
296     // fixed in all iOS Simulator versions that we use.
297     if (path.last() != '/')
298         path.append('/');
299     
300     // Append the file name.    
301     path.append(prefix.utf8().data(), prefix.length());
302     path.append('\0');
303     
304     handle.m_sandboxExtension = SandboxExtensionImpl::create(FileSystem::fileSystemRepresentation(path.data()).data(), type);
305
306     if (!handle.m_sandboxExtension) {
307         WTFLogAlways("Could not create a sandbox extension for temporary file '%s'", path.data());
308         return String();
309     }
310     return String(path.data());
311 }
312
313 bool SandboxExtension::createHandleForGenericExtension(const String& extensionClass, Handle& handle)
314 {
315     ASSERT(!handle.m_sandboxExtension);
316
317     handle.m_sandboxExtension = SandboxExtensionImpl::create(extensionClass.utf8().data(), Type::Generic);
318     if (!handle.m_sandboxExtension) {
319         WTFLogAlways("Could not create a '%s' sandbox extension", extensionClass.utf8().data());
320         return false;
321     }
322     
323     return true;
324 }
325
326 bool SandboxExtension::createHandleForMachLookupByPid(const String& service, pid_t pid, Handle& handle)
327 {
328     ASSERT(!handle.m_sandboxExtension);
329     
330     handle.m_sandboxExtension = SandboxExtensionImpl::create(service.utf8().data(), Type::Mach, pid);
331     if (!handle.m_sandboxExtension) {
332         WTFLogAlways("Could not create a '%s' sandbox extension", service.utf8().data());
333         return false;
334     }
335     
336     return true;
337 }
338
339 SandboxExtension::SandboxExtension(const Handle& handle)
340     : m_sandboxExtension(WTFMove(handle.m_sandboxExtension))
341 {
342 }
343
344 SandboxExtension::~SandboxExtension()
345 {
346     if (!m_sandboxExtension)
347         return;
348
349     ASSERT(!m_useCount);
350 }
351
352 bool SandboxExtension::revoke()
353 {
354     ASSERT(m_sandboxExtension);
355     ASSERT(m_useCount);
356     
357     if (--m_useCount)
358         return true;
359
360     return m_sandboxExtension->invalidate();
361 }
362
363 bool SandboxExtension::consume()
364 {
365     ASSERT(m_sandboxExtension);
366
367     if (m_useCount++)
368         return true;
369
370     return m_sandboxExtension->consume();
371 }
372
373 bool SandboxExtension::consumePermanently()
374 {
375     ASSERT(m_sandboxExtension);
376
377     bool result = m_sandboxExtension->consume();
378
379     // Destroy the extension without invalidating it.
380     m_sandboxExtension = nullptr;
381
382     return result;
383 }
384
385 bool SandboxExtension::consumePermanently(const Handle& handle)
386 {
387     if (!handle.m_sandboxExtension)
388         return false;
389
390     bool result = handle.m_sandboxExtension->consume();
391     
392     // Destroy the extension without invalidating it.
393     handle.m_sandboxExtension = nullptr;
394
395     return result;
396 }
397
398 } // namespace WebKit
399
400 #endif // ENABLE(SANDBOX_EXTENSIONS)