KeychainItemShimMethods.mm [plain text]
/*
* Copyright (C) 2011 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 "KeychainItemShimMethods.h"
#if defined(BUILDING_ON_SNOW_LEOPARD)
#import "CoreIPCClientRunLoop.h"
#import "SecKeychainItemRequestData.h"
#import "SecKeychainItemResponseData.h"
#import "WebProcess.h"
#import "WebProcessProxyMessages.h"
#import "WebProcessShim.h"
#import <dlfcn.h>
namespace WebKit {
// Methods to allow the shim to manage memory for its own AttributeList contents.
static HashSet<SecKeychainAttributeList*>& shimManagedAttributeLists()
{
DEFINE_STATIC_LOCAL(HashSet<SecKeychainAttributeList*>, set, ());
return set;
}
static void freeAttributeListContents(SecKeychainAttributeList* attrList)
{
ASSERT(shimManagedAttributeLists().contains(attrList));
ASSERT(attrList);
for (size_t i = 0; i < attrList->count; ++i)
free(attrList->attr[i].data);
shimManagedAttributeLists().remove(attrList);
}
static void allocateAttributeListContents(const Vector<KeychainAttribute>& attributes, SecKeychainAttributeList* attrList)
{
ASSERT(isMainThread());
if (!attrList)
return;
ASSERT(!shimManagedAttributeLists().contains(attrList));
ASSERT(attributes.size() == attrList->count);
shimManagedAttributeLists().add(attrList);
for (size_t i = 0; i < attrList->count; ++i) {
ASSERT(attributes[i].tag == attrList->attr[i].tag);
CFDataRef cfData = attributes[i].data.get();
if (!cfData) {
attrList->attr[i].length = 0;
attrList->attr[i].data = 0;
continue;
}
CFIndex length = CFDataGetLength(cfData);
attrList->attr[i].length = length;
attrList->attr[i].data = malloc(length);
CFDataGetBytes(cfData, CFRangeMake(0, length), static_cast<UInt8*>(attrList->attr[i].data));
}
}
// Methods to allow the shim to manage memory for its own KeychainItem content data.
static HashSet<void*>& shimManagedKeychainItemContents()
{
DEFINE_STATIC_LOCAL(HashSet<void*>, set, ());
return set;
}
static void allocateKeychainItemContentData(CFDataRef cfData, UInt32* length, void** data)
{
ASSERT(isMainThread());
ASSERT((length && data) || (!length && !data));
if (!data)
return;
if (!cfData) {
*data = 0;
*length = 0;
return;
}
*length = CFDataGetLength(cfData);
*data = malloc(*length);
CFDataGetBytes(cfData, CFRangeMake(0, *length), (UInt8*)*data);
shimManagedKeychainItemContents().add(*data);
}
// FIXME (https://bugs.webkit.org/show_bug.cgi?id=60975) - Once CoreIPC supports sync messaging from a secondary thread,
// we can remove FreeAttributeListContext, FreeKeychainItemDataContext, KeychainItemAPIContext, and these 5 main-thread methods,
// and we can have the shim methods call out directly from whatever thread they're called on.
struct FreeAttributeListContext {
SecKeychainAttributeList* attrList;
bool freed;
};
static void webFreeAttributeListContentOnMainThread(void* voidContext)
{
FreeAttributeListContext* context = (FreeAttributeListContext*)voidContext;
if (!shimManagedAttributeLists().contains(context->attrList)) {
context->freed = false;
return;
}
freeAttributeListContents(context->attrList);
context->freed = true;
}
static bool webFreeAttributeListContent(SecKeychainAttributeList* attrList)
{
FreeAttributeListContext context;
context.attrList = attrList;
callOnCoreIPCClientRunLoopAndWait(webFreeAttributeListContentOnMainThread, &context);
return context.freed;
}
struct FreeKeychainItemDataContext {
void* data;
bool freed;
};
static void webFreeKeychainItemContentOnMainThread(void* voidContext)
{
FreeKeychainItemDataContext* context = (FreeKeychainItemDataContext*)voidContext;
if (!shimManagedKeychainItemContents().contains(context->data)) {
context->freed = false;
return;
}
shimManagedKeychainItemContents().remove(context->data);
free(context->data);
context->freed = true;
}
static bool webFreeKeychainItemContent(void* data)
{
FreeKeychainItemDataContext context;
context.data = data;
callOnCoreIPCClientRunLoopAndWait(webFreeKeychainItemContentOnMainThread, &context);
return context.freed;
}
struct SecKeychainItemContext {
SecKeychainItemRef item;
SecKeychainAttributeList* attributeList;
SecItemClass initialItemClass;
UInt32 length;
const void* data;
SecItemClass* resultItemClass;
UInt32* resultLength;
void** resultData;
OSStatus resultCode;
};
static void webSecKeychainItemCopyContentOnMainThread(void* voidContext)
{
SecKeychainItemContext* context = (SecKeychainItemContext*)voidContext;
SecKeychainItemRequestData requestData(context->item, context->attributeList);
SecKeychainItemResponseData response;
if (!WebProcess::shared().connection()->sendSync(Messages::WebProcessProxy::SecKeychainItemCopyContent(requestData), Messages::WebProcessProxy::SecKeychainItemCopyContent::Reply(response), 0)) {
context->resultCode = errSecInteractionNotAllowed;
ASSERT_NOT_REACHED();
return;
}
allocateAttributeListContents(response.attributes(), context->attributeList);
allocateKeychainItemContentData(response.data(), context->resultLength, context->resultData);
if (context->resultItemClass)
*context->resultItemClass = response.itemClass();
context->resultCode = response.resultCode();
}
static OSStatus webSecKeychainItemCopyContent(SecKeychainItemRef item, SecItemClass* itemClass, SecKeychainAttributeList* attrList, UInt32* length, void** outData)
{
SecKeychainItemContext context;
memset(&context, 0, sizeof(SecKeychainItemContext));
context.item = item;
context.resultItemClass = itemClass;
context.attributeList = attrList;
context.resultLength = length;
context.resultData = outData;
callOnCoreIPCClientRunLoopAndWait(webSecKeychainItemCopyContentOnMainThread, &context);
// FIXME: should return context.resultCode. Returning noErr is a workaround for <rdar://problem/9520886>;
// the authentication should fail anyway, since on error no data will be returned.
return noErr;
}
static void webSecKeychainItemCreateFromContentOnMainThread(void* voidContext)
{
SecKeychainItemContext* context = (SecKeychainItemContext*)voidContext;
SecKeychainItemRequestData requestData(context->initialItemClass, context->attributeList, context->length, context->data);
SecKeychainItemResponseData response;
if (!WebProcess::shared().connection()->sendSync(Messages::WebProcessProxy::SecKeychainItemCreateFromContent(requestData), Messages::WebProcessProxy::SecKeychainItemCreateFromContent::Reply(response), 0)) {
context->resultCode = errSecInteractionNotAllowed;
ASSERT_NOT_REACHED();
return;
}
if (response.keychainItem())
CFRetain(response.keychainItem());
context->item = response.keychainItem();
context->resultCode = response.resultCode();
}
static OSStatus webSecKeychainItemCreateFromContent(SecItemClass itemClass, SecKeychainAttributeList* attrList, UInt32 length, const void* data, SecKeychainItemRef *item)
{
SecKeychainItemContext context;
memset(&context, 0, sizeof(SecKeychainItemContext));
context.initialItemClass = itemClass;
context.attributeList = attrList;
context.length = length;
context.data = data;
callOnCoreIPCClientRunLoopAndWait(webSecKeychainItemCreateFromContentOnMainThread, &context);
if (item)
*item = context.item;
else
CFRelease(context.item);
return context.resultCode;
}
static void webSecKeychainItemModifyContentOnMainThread(void* voidContext)
{
SecKeychainItemContext* context = (SecKeychainItemContext*)voidContext;
SecKeychainItemRequestData requestData(context->item, context->attributeList, context->length, context->data);
SecKeychainItemResponseData response;
if (!WebProcess::shared().connection()->sendSync(Messages::WebProcessProxy::SecKeychainItemModifyContent(requestData), Messages::WebProcessProxy::SecKeychainItemModifyContent::Reply(response), 0)) {
context->resultCode = errSecInteractionNotAllowed;
ASSERT_NOT_REACHED();
return;
}
context->resultCode = response.resultCode();
}
static OSStatus webSecKeychainItemModifyContent(SecKeychainItemRef itemRef, const SecKeychainAttributeList* attrList, UInt32 length, const void* data)
{
SecKeychainItemContext context;
context.item = itemRef;
context.attributeList = (SecKeychainAttributeList*)attrList;
context.length = length;
context.data = data;
callOnCoreIPCClientRunLoopAndWait(webSecKeychainItemModifyContentOnMainThread, &context);
return context.resultCode;
}
void initializeKeychainItemShim()
{
const WebProcessKeychainItemShimCallbacks callbacks = {
webSecKeychainItemCopyContent,
webSecKeychainItemCreateFromContent,
webSecKeychainItemModifyContent,
webFreeAttributeListContent,
webFreeKeychainItemContent
};
WebProcessKeychainItemShimInitializeFunc initializeFunction = reinterpret_cast<WebProcessKeychainItemShimInitializeFunc>(dlsym(RTLD_DEFAULT, "WebKitWebProcessKeychainItemShimInitialize"));
initializeFunction(callbacks);
}
} // namespace WebKit
#endif // defined(BUILDING_ON_SNOW_LEOPARD)