blob: 441eb1e14808230aab404f2d6398d52fe64c4089 [file] [log] [blame]
/*
* Copyright (C) 2010-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "SandboxExtension.h"
#if ENABLE(SANDBOX_EXTENSIONS)
#import "DataReference.h"
#import "Decoder.h"
#import "Encoder.h"
#import "WebKitSystemInterface.h"
#import <WebCore/FileSystem.h>
#import <sys/stat.h>
#import <wtf/text/CString.h>
using namespace WebCore;
namespace WebKit {
SandboxExtension::Handle::Handle()
: m_sandboxExtension(0)
{
}
SandboxExtension::Handle::~Handle()
{
if (m_sandboxExtension) {
WKSandboxExtensionInvalidate(m_sandboxExtension);
WKSandboxExtensionDestroy(m_sandboxExtension);
}
}
void SandboxExtension::Handle::encode(IPC::Encoder& encoder) const
{
if (!m_sandboxExtension) {
encoder << IPC::DataReference();
return;
}
size_t length = 0;
const char *serializedFormat = WKSandboxExtensionGetSerializedFormat(m_sandboxExtension, &length);
ASSERT(serializedFormat);
encoder << IPC::DataReference(reinterpret_cast<const uint8_t*>(serializedFormat), length);
// Encoding will destroy the sandbox extension locally.
WKSandboxExtensionDestroy(m_sandboxExtension);
m_sandboxExtension = 0;
}
bool SandboxExtension::Handle::decode(IPC::Decoder& decoder, Handle& result)
{
ASSERT(!result.m_sandboxExtension);
IPC::DataReference dataReference;
if (!decoder.decode(dataReference))
return false;
if (dataReference.isEmpty())
return true;
result.m_sandboxExtension = WKSandboxExtensionCreateFromSerializedFormat(reinterpret_cast<const char*>(dataReference.data()), dataReference.size());
return true;
}
SandboxExtension::HandleArray::HandleArray()
: m_size(0)
{
}
SandboxExtension::HandleArray::~HandleArray()
{
}
void SandboxExtension::HandleArray::allocate(size_t size)
{
if (!size)
return;
ASSERT(!m_data);
m_data = std::make_unique<SandboxExtension::Handle[]>(size);
m_size = size;
}
SandboxExtension::Handle& SandboxExtension::HandleArray::operator[](size_t i)
{
ASSERT_WITH_SECURITY_IMPLICATION(i < m_size);
return m_data[i];
}
const SandboxExtension::Handle& SandboxExtension::HandleArray::operator[](size_t i) const
{
ASSERT_WITH_SECURITY_IMPLICATION(i < m_size);
return m_data[i];
}
size_t SandboxExtension::HandleArray::size() const
{
return m_size;
}
void SandboxExtension::HandleArray::encode(IPC::Encoder& encoder) const
{
encoder << static_cast<uint64_t>(size());
for (size_t i = 0; i < m_size; ++i)
encoder << m_data[i];
}
bool SandboxExtension::HandleArray::decode(IPC::Decoder& decoder, SandboxExtension::HandleArray& handles)
{
uint64_t size;
if (!decoder.decode(size))
return false;
handles.allocate(size);
for (size_t i = 0; i < size; i++) {
if (!decoder.decode(handles[i]))
return false;
}
return true;
}
RefPtr<SandboxExtension> SandboxExtension::create(const Handle& handle)
{
if (!handle.m_sandboxExtension)
return nullptr;
return adoptRef(new SandboxExtension(handle));
}
static WKSandboxExtensionType wkSandboxExtensionType(SandboxExtension::Type type)
{
switch (type) {
case SandboxExtension::ReadOnly:
return WKSandboxExtensionTypeReadOnly;
case SandboxExtension::ReadWrite:
return WKSandboxExtensionTypeReadWrite;
case SandboxExtension::Generic:
return WKSandboxExtensionTypeGeneric;
}
CRASH();
}
static CString resolveSymlinksInPath(const CString& path)
{
struct stat statBuf;
// Check if this file exists.
if (!stat(path.data(), &statBuf)) {
char resolvedName[PATH_MAX];
return realpath(path.data(), resolvedName);
}
const char* slashPtr = strrchr(path.data(), '/');
if (slashPtr == path.data())
return path;
size_t parentDirectoryLength = slashPtr - path.data();
if (parentDirectoryLength >= PATH_MAX)
return CString();
// Get the parent directory.
char parentDirectory[PATH_MAX];
memcpy(parentDirectory, path.data(), parentDirectoryLength);
parentDirectory[parentDirectoryLength] = '\0';
// Resolve it.
CString resolvedParentDirectory = resolveSymlinksInPath(CString(parentDirectory));
if (resolvedParentDirectory.isNull())
return CString();
size_t lastPathComponentLength = path.length() - parentDirectoryLength;
size_t resolvedPathLength = resolvedParentDirectory.length() + lastPathComponentLength;
if (resolvedPathLength >= PATH_MAX)
return CString();
// Combine the resolved parent directory with the last path component.
char* resolvedPathBuffer;
CString resolvedPath = CString::newUninitialized(resolvedPathLength, resolvedPathBuffer);
memcpy(resolvedPathBuffer, resolvedParentDirectory.data(), resolvedParentDirectory.length());
memcpy(resolvedPathBuffer + resolvedParentDirectory.length(), slashPtr, lastPathComponentLength);
return resolvedPath;
}
String stringByResolvingSymlinksInPath(const String& path)
{
return String::fromUTF8(resolveSymlinksInPath(path.utf8()));
}
String resolveAndCreateReadWriteDirectoryForSandboxExtension(const String& path)
{
NSError *error = nil;
NSString *nsPath = path;
if (![[NSFileManager defaultManager] createDirectoryAtPath:nsPath withIntermediateDirectories:YES attributes:nil error:&error]) {
NSLog(@"could not create directory \"%@\" for future sandbox extension, error %@", nsPath, error);
return { };
}
return resolvePathForSandboxExtension(path);
}
String resolvePathForSandboxExtension(const String& path)
{
// FIXME: Do we need both resolveSymlinksInPath() and -stringByStandardizingPath?
CString fileSystemPath = fileSystemRepresentation([(NSString *)path stringByStandardizingPath]);
if (fileSystemPath.isNull()) {
LOG_ERROR("Could not create a valid file system representation for the string '%s' of length %lu", fileSystemPath.data(), fileSystemPath.length());
return { };
}
CString standardizedPath = resolveSymlinksInPath(fileSystemPath);
return String::fromUTF8(standardizedPath);
}
bool SandboxExtension::createHandleWithoutResolvingPath(const String& path, Type type, Handle& handle)
{
ASSERT(!handle.m_sandboxExtension);
handle.m_sandboxExtension = WKSandboxExtensionCreate(path.utf8().data(), wkSandboxExtensionType(type));
if (!handle.m_sandboxExtension) {
LOG_ERROR("Could not create a sandbox extension for '%s'", path.utf8().data());
return false;
}
return true;
}
bool SandboxExtension::createHandle(const String& path, Type type, Handle& handle)
{
ASSERT(!handle.m_sandboxExtension);
return createHandleWithoutResolvingPath(resolvePathForSandboxExtension(path), type, handle);
}
bool SandboxExtension::createHandleForReadWriteDirectory(const String& path, SandboxExtension::Handle& handle)
{
String resolvedPath = resolveAndCreateReadWriteDirectoryForSandboxExtension(path);
if (resolvedPath.isNull())
return false;
return SandboxExtension::createHandleWithoutResolvingPath(resolvedPath, SandboxExtension::ReadWrite, handle);
}
String SandboxExtension::createHandleForTemporaryFile(const String& prefix, Type type, Handle& handle)
{
ASSERT(!handle.m_sandboxExtension);
Vector<char> path(PATH_MAX);
if (!confstr(_CS_DARWIN_USER_TEMP_DIR, path.data(), path.size()))
return String();
// Shrink the vector.
path.shrink(strlen(path.data()));
// FIXME: Change to a runtime assertion that the path ends with a slash once <rdar://problem/23579077> is
// fixed in all iOS Simulator versions that we use.
if (path.last() != '/')
path.append('/');
// Append the file name.
path.append(prefix.utf8().data(), prefix.length());
path.append('\0');
handle.m_sandboxExtension = WKSandboxExtensionCreate(fileSystemRepresentation(path.data()).data(), wkSandboxExtensionType(type));
if (!handle.m_sandboxExtension) {
WTFLogAlways("Could not create a sandbox extension for temporary file '%s'", path.data());
return String();
}
return String(path.data());
}
bool SandboxExtension::createHandleForGenericExtension(const String& extensionClass, Handle& handle)
{
ASSERT(!handle.m_sandboxExtension);
handle.m_sandboxExtension = WKSandboxExtensionCreate(extensionClass.utf8().data(), wkSandboxExtensionType(Type::Generic));
if (!handle.m_sandboxExtension) {
WTFLogAlways("Could not create a '%s' sandbox extension", extensionClass.utf8().data());
return false;
}
return true;
}
SandboxExtension::SandboxExtension(const Handle& handle)
: m_sandboxExtension(handle.m_sandboxExtension)
, m_useCount(0)
{
handle.m_sandboxExtension = 0;
}
SandboxExtension::~SandboxExtension()
{
if (!m_sandboxExtension)
return;
ASSERT(!m_useCount);
WKSandboxExtensionDestroy(m_sandboxExtension);
}
bool SandboxExtension::revoke()
{
ASSERT(m_sandboxExtension);
ASSERT(m_useCount);
if (--m_useCount)
return true;
return WKSandboxExtensionInvalidate(m_sandboxExtension);
}
bool SandboxExtension::consume()
{
ASSERT(m_sandboxExtension);
if (m_useCount++)
return true;
return WKSandboxExtensionConsume(m_sandboxExtension);
}
bool SandboxExtension::consumePermanently()
{
ASSERT(m_sandboxExtension);
bool result = WKSandboxExtensionConsume(m_sandboxExtension);
// Destroy the extension without invalidating it.
WKSandboxExtensionDestroy(m_sandboxExtension);
m_sandboxExtension = 0;
return result;
}
bool SandboxExtension::consumePermanently(const Handle& handle)
{
if (!handle.m_sandboxExtension)
return false;
bool result = WKSandboxExtensionConsume(handle.m_sandboxExtension);
// Destroy the extension without invalidating it.
WKSandboxExtensionDestroy(handle.m_sandboxExtension);
handle.m_sandboxExtension = 0;
return result;
}
} // namespace WebKit
#endif // ENABLE(SANDBOX_EXTENSIONS)