/* * 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 USE(SECURITY_FRAMEWORK) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1060 #import "BlockingResponseMap.h" #import "SecKeychainItemRequestData.h" #import "SecKeychainItemResponseData.h" #import "WebProcess.h" #import "WebProcessProxyMessages.h" #import "WebProcessShim.h" #import #import namespace WebKit { // Methods to allow the shim to manage memory for its own AttributeList contents. static HashSet& managedAttributeLists() { AtomicallyInitializedStatic(HashSet&, managedAttributeLists = *new HashSet); return managedAttributeLists; } static Mutex& managedAttributeListsMutex() { AtomicallyInitializedStatic(Mutex&, managedAttributeListsMutex = *new Mutex); return managedAttributeListsMutex; } static void allocateAttributeListContents(const Vector& attributes, SecKeychainAttributeList* attrList) { if (!attrList) return; MutexLocker locker(managedAttributeListsMutex()); ASSERT(!managedAttributeLists().contains(attrList)); ASSERT(attributes.size() == attrList->count); managedAttributeLists().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(attrList->attr[i].data)); } } // Methods to allow the shim to manage memory for its own KeychainItem content data. static HashSet& managedKeychainItemContents() { AtomicallyInitializedStatic(HashSet&, managedKeychainItemContents = *new HashSet); return managedKeychainItemContents; } static Mutex& managedKeychainItemContentsMutex() { AtomicallyInitializedStatic(Mutex&, managedKeychainItemContentsMutex = *new Mutex); return managedKeychainItemContentsMutex; } static void allocateKeychainItemContentData(CFDataRef cfData, UInt32* length, void** data) { 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); MutexLocker locker(managedKeychainItemContentsMutex()); managedKeychainItemContents().add(*data); } static bool webFreeAttributeListContent(SecKeychainAttributeList* attrList) { MutexLocker locker(managedAttributeListsMutex()); if (!managedAttributeLists().contains(attrList)) return false; for (size_t i = 0; i < attrList->count; ++i) free(attrList->attr[i].data); managedAttributeLists().remove(attrList); return true; } static bool webFreeKeychainItemContent(void* data) { MutexLocker locker(managedKeychainItemContentsMutex()); HashSet::iterator it = managedKeychainItemContents().find(data); if (it == managedKeychainItemContents().end()) return false; managedKeychainItemContents().remove(it); return true; } static BlockingResponseMap& responseMap() { AtomicallyInitializedStatic(BlockingResponseMap&, responseMap = *new BlockingResponseMap); return responseMap; } static uint64_t generateSecKeychainItemRequestID() { static int64_t uniqueSecKeychainItemRequestID; return atomicIncrement(&uniqueSecKeychainItemRequestID); } void didReceiveSecKeychainItemResponse(uint64_t requestID, const SecKeychainItemResponseData& response) { responseMap().didReceiveResponse(requestID, adoptPtr(new SecKeychainItemResponseData(response))); } static PassOwnPtr sendSeqKeychainItemRequest(const SecKeychainItemRequestData& request) { uint64_t requestID = generateSecKeychainItemRequestID(); if (!WebProcess::shared().connection()->send(Messages::WebProcessProxy::SecKeychainItemRequest(requestID, request), 0)) return nullptr; return responseMap().waitForResponse(requestID); } static OSStatus webSecKeychainItemCopyContent(SecKeychainItemRef item, SecItemClass* itemClass, SecKeychainAttributeList* attrList, UInt32* length, void** outData) { SecKeychainItemRequestData request(SecKeychainItemRequestData::CopyContent, item, attrList); OwnPtr response = sendSeqKeychainItemRequest(request); if (!response) { ASSERT_NOT_REACHED(); return errSecInteractionNotAllowed; } if (itemClass) *itemClass = response->itemClass(); allocateAttributeListContents(response->attributes(), attrList); allocateKeychainItemContentData(response->data(), length, outData); // FIXME: should return response->resultCode(). Returning noErr is a workaround for ; // the authentication should fail anyway, since on error no data will be returned. return noErr; } static OSStatus webSecKeychainItemCreateFromContent(SecItemClass itemClass, SecKeychainAttributeList* attrList, UInt32 length, const void* data, SecKeychainItemRef *item) { SecKeychainItemRequestData request(SecKeychainItemRequestData::CreateFromContent, itemClass, attrList, length, data); OwnPtr response = sendSeqKeychainItemRequest(request); if (!response) { ASSERT_NOT_REACHED(); return errSecInteractionNotAllowed; } if (item) *item = RetainPtr(response->keychainItem()).leakRef(); return response->resultCode(); } static OSStatus webSecKeychainItemModifyContent(SecKeychainItemRef itemRef, const SecKeychainAttributeList* attrList, UInt32 length, const void* data) { SecKeychainItemRequestData request(SecKeychainItemRequestData::ModifyContent, itemRef, (SecKeychainAttributeList*)attrList, length, data); OwnPtr response = sendSeqKeychainItemRequest(request); if (!response) { ASSERT_NOT_REACHED(); return errSecInteractionNotAllowed; } return response->resultCode(); } void initializeKeychainItemShim() { const WebProcessKeychainItemShimCallbacks callbacks = { webSecKeychainItemCopyContent, webSecKeychainItemCreateFromContent, webSecKeychainItemModifyContent, webFreeAttributeListContent, webFreeKeychainItemContent }; WebProcessKeychainItemShimInitializeFunc initializeFunction = reinterpret_cast(dlsym(RTLD_DEFAULT, "WebKitWebProcessKeychainItemShimInitialize")); initializeFunction(callbacks); } } // namespace WebKit #endif // USE(SECURITY_FRAMEWORK) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1060