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