X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/SecIdentity.cpp diff --git a/Security/libsecurity_keychain/lib/SecIdentity.cpp b/Security/libsecurity_keychain/lib/SecIdentity.cpp deleted file mode 100644 index 5c16a1b3..00000000 --- a/Security/libsecurity_keychain/lib/SecIdentity.cpp +++ /dev/null @@ -1,1119 +0,0 @@ -/* - * Copyright (c) 2002-2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include - -#include "SecBridge.h" -#include -#include -#include -#include -#include -#include -#include -#include - - -/* private function declarations */ -OSStatus -SecIdentityFindPreferenceItemWithNameAndKeyUsage( - CFTypeRef keychainOrArray, - CFStringRef name, - int32_t keyUsage, - SecKeychainItemRef *itemRef); - -OSStatus SecIdentityDeletePreferenceItemWithNameAndKeyUsage( - CFTypeRef keychainOrArray, - CFStringRef name, - int32_t keyUsage); - - -CSSM_KEYUSE ConvertArrayToKeyUsage(CFArrayRef usage) -{ - CFIndex count = 0; - CSSM_KEYUSE result = (CSSM_KEYUSE) 0; - - if ((NULL == usage) || (0 == (count = CFArrayGetCount(usage)))) - { - return result; - } - - for (CFIndex iCnt = 0; iCnt < count; iCnt++) - { - CFStringRef keyUsageStr = NULL; - keyUsageStr = (CFStringRef)CFArrayGetValueAtIndex(usage,iCnt); - if (NULL != keyUsageStr) - { - if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanEncrypt, keyUsageStr, 0)) - { - result |= CSSM_KEYUSE_ENCRYPT; - } - else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanDecrypt, keyUsageStr, 0)) - { - result |= CSSM_KEYUSE_DECRYPT; - } - else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanDerive, keyUsageStr, 0)) - { - result |= CSSM_KEYUSE_DERIVE; - } - else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanSign, keyUsageStr, 0)) - { - result |= CSSM_KEYUSE_SIGN; - } - else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanVerify, keyUsageStr, 0)) - { - result |= CSSM_KEYUSE_VERIFY; - } - else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanWrap, keyUsageStr, 0)) - { - result |= CSSM_KEYUSE_WRAP; - } - else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanUnwrap, keyUsageStr, 0)) - { - result |= CSSM_KEYUSE_UNWRAP; - } - } - } - - return result; -} - - -CFTypeID -SecIdentityGetTypeID(void) -{ - BEGIN_SECAPI - - return gTypes().Identity.typeID; - - END_SECAPI1(_kCFRuntimeNotATypeID) -} - - -OSStatus -SecIdentityCopyCertificate( - SecIdentityRef identityRef, - SecCertificateRef *certificateRef) -{ - BEGIN_SECAPI - - SecPointer certificatePtr(Identity::required(identityRef)->certificate()); - Required(certificateRef) = certificatePtr->handle(); - - END_SECAPI -} - - -OSStatus -SecIdentityCopyPrivateKey( - SecIdentityRef identityRef, - SecKeyRef *privateKeyRef) -{ - BEGIN_SECAPI - - SecPointer keyItemPtr(Identity::required(identityRef)->privateKey()); - Required(privateKeyRef) = keyItemPtr->handle(); - - END_SECAPI -} - -OSStatus -SecIdentityCreateWithCertificate( - CFTypeRef keychainOrArray, - SecCertificateRef certificateRef, - SecIdentityRef *identityRef) -{ - BEGIN_SECAPI - - SecPointer certificatePtr(Certificate::required(certificateRef)); - StorageManager::KeychainList keychains; - globals().storageManager.optionalSearchList(keychainOrArray, keychains); - SecPointer identityPtr(new Identity(keychains, certificatePtr)); - Required(identityRef) = identityPtr->handle(); - - END_SECAPI -} - -SecIdentityRef -SecIdentityCreate( - CFAllocatorRef allocator, - SecCertificateRef certificate, - SecKeyRef privateKey) -{ - SecIdentityRef identityRef = NULL; - OSStatus __secapiresult; - try { - SecPointer certificatePtr(Certificate::required(certificate)); - SecPointer keyItemPtr(KeyItem::required(privateKey)); - SecPointer identityPtr(new Identity(keyItemPtr, certificatePtr)); - identityRef = identityPtr->handle(); - - __secapiresult=errSecSuccess; - } - catch (const MacOSError &err) { __secapiresult=err.osStatus(); } - catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); } - catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; } - catch (...) { __secapiresult=errSecInternalComponent; } - return identityRef; -} - -CFComparisonResult -SecIdentityCompare( - SecIdentityRef identity1, - SecIdentityRef identity2, - CFOptionFlags compareOptions) -{ - if (!identity1 || !identity2) - { - if (identity1 == identity2) - return kCFCompareEqualTo; - else if (identity1 < identity2) - return kCFCompareLessThan; - else - return kCFCompareGreaterThan; - } - - BEGIN_SECAPI - - SecPointer id1(Identity::required(identity1)); - SecPointer id2(Identity::required(identity2)); - - if (id1 == id2) - return kCFCompareEqualTo; - else if (id1 < id2) - return kCFCompareLessThan; - else - return kCFCompareGreaterThan; - - END_SECAPI1(kCFCompareGreaterThan); -} - -static -CFArrayRef _SecIdentityCopyPossiblePaths( - CFStringRef name) -{ - // utility function to build and return an array of possible paths for the given name. - // if name is not a URL, this returns a single-element array. - // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy. - - CFMutableArrayRef names = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (!name) { - return names; - } - CFIndex oldLength = CFStringGetLength(name); - CFArrayAppendValue(names, name); - - CFURLRef url = CFURLCreateWithString(NULL, name, NULL); - if (url) { - if (CFURLCanBeDecomposed(url)) { - // first, remove the query portion of this URL, if any - CFStringRef qs = CFURLCopyQueryString(url, NULL); - if (qs) { - CFMutableStringRef newName = CFStringCreateMutableCopy(NULL, oldLength, name); - if (newName) { - CFIndex qsLength = CFStringGetLength(qs) + 1; // include the '?' - CFStringDelete(newName, CFRangeMake(oldLength-qsLength, qsLength)); - CFRelease(url); - url = CFURLCreateWithString(NULL, newName, NULL); - CFArraySetValueAtIndex(names, 0, newName); - CFRelease(newName); - } - CFRelease(qs); - } - // now add an entry for each level of the path - while (url) { - CFURLRef parent = CFURLCreateCopyDeletingLastPathComponent(NULL, url); - if (parent) { - CFStringRef parentURLString = CFURLGetString(parent); - if (parentURLString) { - CFIndex newLength = CFStringGetLength(parentURLString); - // check that string length has decreased as expected; for file URLs, - // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../' - if ((newLength >= oldLength) || (!CFStringHasPrefix(name, parentURLString))) { - CFRelease(parent); - CFRelease(url); - break; - } - oldLength = newLength; - CFArrayAppendValue(names, parentURLString); - } - } - CFRelease(url); - url = parent; - } - } - else { - CFRelease(url); - } - } - // finally, add wildcard entries for each subdomain - url = CFURLCreateWithString(NULL, name, NULL); - if (url) { - if (CFURLCanBeDecomposed(url)) { - CFStringRef netLocString = CFURLCopyNetLocation(url); - if (netLocString) { - // first strip off port number, if present - CFStringRef tmpLocString = netLocString; - CFArrayRef hostnameArray = CFStringCreateArrayBySeparatingStrings(NULL, netLocString, CFSTR(":")); - tmpLocString = (CFStringRef)CFRetain((CFStringRef)CFArrayGetValueAtIndex(hostnameArray, 0)); - CFRelease(netLocString); - CFRelease(hostnameArray); - netLocString = tmpLocString; - // split remaining string into domain components - hostnameArray = CFStringCreateArrayBySeparatingStrings(NULL, netLocString, CFSTR(".")); - CFIndex subdomainCount = CFArrayGetCount(hostnameArray); - CFIndex i = 0; - while (++i < subdomainCount) { - CFIndex j = i; - CFMutableStringRef wildcardString = CFStringCreateMutable(NULL, 0); - if (wildcardString) { - CFStringAppendCString(wildcardString, "*", kCFStringEncodingUTF8); - while (j < subdomainCount) { - CFStringRef domainString = (CFStringRef)CFArrayGetValueAtIndex(hostnameArray, j++); - if (CFStringGetLength(domainString) > 0) { - CFStringAppendCString(wildcardString, ".", kCFStringEncodingUTF8); - CFStringAppend(wildcardString, domainString); - } - } - if (CFStringGetLength(wildcardString) > 1) { - CFArrayAppendValue(names, wildcardString); - } - CFRelease(wildcardString); - } - } - CFRelease(hostnameArray); - CFRelease(netLocString); - } - } - CFRelease(url); - } - - return names; -} - -static -OSStatus _SecIdentityCopyPreferenceMatchingName( - CFStringRef name, - CSSM_KEYUSE keyUsage, - CFArrayRef validIssuers, - SecIdentityRef *identity) -{ - // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here; - // caller must handle exceptions - - StorageManager::KeychainList keychains; - globals().storageManager.getSearchList(keychains); - KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); - - char idUTF8[MAXPATHLEN]; - Required(name); - if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8)) - idUTF8[0] = (char)'\0'; - CssmData service(const_cast(idUTF8), strlen(idUTF8)); - FourCharCode itemType = 'iprf'; - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service); - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType); - if (keyUsage) - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage); - - Item prefItem; - if (!cursor->next(prefItem)) - return errSecItemNotFound; - - // get persistent certificate reference - SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } }; - SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs }; - prefItem->getContent(NULL, &itemAttrList, NULL, NULL); - - // find certificate, given persistent reference data - CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull); - SecKeychainItemRef certItemRef = nil; - OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); //%%% need to make this a method of ItemImpl - prefItem->freeContent(&itemAttrList, NULL); - if (pItemRef) - CFRelease(pItemRef); - if (status) - return status; - - // filter on valid issuers, if provided - if (validIssuers) { - //%%%TBI - } - - // create identity reference, given certificate - Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef)); - SecPointer certificate(static_cast(certItem.get())); - SecPointer identity_ptr(new Identity(keychains, certificate)); - if (certItemRef) - CFRelease(certItemRef); - - Required(identity) = identity_ptr->handle(); - - return status; -} - -SecIdentityRef SecIdentityCopyPreferred(CFStringRef name, CFArrayRef keyUsage, CFArrayRef validIssuers) -{ - // This function will look for a matching preference in the following order: - // - matches the name and the supplied key use - // - matches the name and the special 'ANY' key use - // - matches the name with no key usage constraint - - SecIdentityRef identityRef = NULL; - CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage); - OSStatus status = SecIdentityCopyPreference(name, keyUse, validIssuers, &identityRef); - if (status != errSecSuccess && keyUse != CSSM_KEYUSE_ANY) - status = SecIdentityCopyPreference(name, CSSM_KEYUSE_ANY, validIssuers, &identityRef); - if (status != errSecSuccess && keyUse != 0) - status = SecIdentityCopyPreference(name, 0, validIssuers, &identityRef); - - return identityRef; -} - -OSStatus SecIdentityCopyPreference( - CFStringRef name, - CSSM_KEYUSE keyUsage, - CFArrayRef validIssuers, - SecIdentityRef *identity) -{ - // The original implementation of SecIdentityCopyPreference matches the exact string only. - // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above), - // and this function is a wrapper which calls it, so that existing clients will get the - // extended behavior of server domain matching for items that specify URLs. - // (Note that behavior is unchanged if the specified name is not a URL.) - - BEGIN_SECAPI - - CFTypeRef val = (CFTypeRef)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"), - CFSTR("com.apple.security"), - kCFPreferencesCurrentUser, - kCFPreferencesAnyHost); - Boolean logging = false; - if (val && CFGetTypeID(val) == CFBooleanGetTypeID()) { - logging = CFBooleanGetValue((CFBooleanRef)val); - CFRelease(val); - } - - OSStatus status = errSecItemNotFound; - CFArrayRef names = _SecIdentityCopyPossiblePaths(name); - if (!names) { - return status; - } - - CFIndex idx, total = CFArrayGetCount(names); - for (idx = 0; idx < total; idx++) { - CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, idx); - try { - status = _SecIdentityCopyPreferenceMatchingName(aName, keyUsage, validIssuers, identity); - } - catch (...) { status = errSecItemNotFound; } - - if (logging) { - // get identity label - CFStringRef labelString = NULL; - if (!status && identity && *identity) { - try { - SecPointer cert(Identity::required(*identity)->certificate()); - cert->inferLabel(false, &labelString); - } - catch (...) { labelString = NULL; }; - } - char *labelBuf = NULL; - CFIndex labelBufSize = (labelString) ? CFStringGetLength(labelString) * 4 : 4; - labelBuf = (char *)malloc(labelBufSize); - if (!labelString || !CFStringGetCString(labelString, labelBuf, labelBufSize, kCFStringEncodingUTF8)) { - labelBuf[0] = 0; - } - if (labelString) { - CFRelease(labelString); - } - - // get service name - char *serviceBuf = NULL; - CFIndex serviceBufSize = CFStringGetLength(aName) * 4; - serviceBuf = (char *)malloc(serviceBufSize); - if (!CFStringGetCString(aName, serviceBuf, serviceBufSize, kCFStringEncodingUTF8)) { - serviceBuf[0] = 0; - } - - syslog(LOG_NOTICE, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf, serviceBuf); - if (!status && name) { - char *nameBuf = NULL; - CFIndex nameBufSize = CFStringGetLength(name) * 4; - nameBuf = (char *)malloc(nameBufSize); - if (!CFStringGetCString(name, nameBuf, nameBufSize, kCFStringEncodingUTF8)) { - nameBuf[0] = 0; - } - syslog(LOG_NOTICE, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf, nameBuf); - free(nameBuf); - } - - free(labelBuf); - free(serviceBuf); - } - - if (status == errSecSuccess) { - break; // match found - } - } - - CFRelease(names); - return status; - - END_SECAPI -} - -OSStatus SecIdentitySetPreference( - SecIdentityRef identity, - CFStringRef name, - CSSM_KEYUSE keyUsage) -{ - if (!name) { - return errSecParam; - } - if (!identity) { - // treat NULL identity as a request to clear the preference - // (note: if keyUsage is 0, this clears all key usage prefs for name) - return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL, name, keyUsage); - } - - BEGIN_SECAPI - - SecPointer certificate(Identity::required(identity)->certificate()); - - // determine the account attribute - // - // This attribute must be synthesized from certificate label + pref item type + key usage, - // as only the account and service attributes can make a generic keychain item unique. - // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that - // we can save a certificate preference if an identity preference already exists for the - // given service name, and vice-versa. - // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. - // - CFStringRef labelStr = nil; - certificate->inferLabel(false, &labelStr); - if (!labelStr) { - MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed" - } - CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1; - const char *templateStr = "%s [key usage 0x%X]"; - const int keyUsageMaxStrLen = 8; - accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen; - char accountUTF8[accountUTF8Len]; - if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8)) - accountUTF8[0] = (char)'\0'; - if (keyUsage) - snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage); - snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8); - CssmData account(const_cast(accountUTF8), strlen(accountUTF8)); - CFRelease(labelStr); - - // service attribute (name provided by the caller) - CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name), kCFStringEncodingUTF8) + 1;; - char serviceUTF8[serviceUTF8Len]; - if (!CFStringGetCString(name, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8)) - serviceUTF8[0] = (char)'\0'; - CssmData service(const_cast(serviceUTF8), strlen(serviceUTF8)); - - // look for existing identity preference item, in case this is an update - StorageManager::KeychainList keychains; - globals().storageManager.getSearchList(keychains); - KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); - FourCharCode itemType = 'iprf'; - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service); - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType); - if (keyUsage) { - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage); - } - - Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false); - bool add = (!cursor->next(item)); - // at this point, we either have a new item to add or an existing item to update - - // set item attribute values - item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service); - item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), itemType); - item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account); - item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage); - item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service); - - // generic attribute (store persistent certificate reference) - CFDataRef pItemRef = nil; - certificate->copyPersistentReference(pItemRef); - if (!pItemRef) { - MacOSError::throwMe(errSecInvalidItemRef); - } - const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef); - CFIndex dataLen = CFDataGetLength(pItemRef); - CssmData pref(const_cast(reinterpret_cast(dataPtr)), dataLen); - item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref); - CFRelease(pItemRef); - - if (add) { - Keychain keychain = nil; - try { - keychain = globals().storageManager.defaultKeychain(); - if (!keychain->exists()) - MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time. - } - catch(...) { - keychain = globals().storageManager.defaultKeychainUI(item); - } - - try { - keychain->add(item); - } - catch (const MacOSError &err) { - if (err.osStatus() != errSecDuplicateItem) - throw; // if item already exists, fall through to update - } - } - item->update(); - - END_SECAPI -} - -OSStatus -SecIdentitySetPreferred(SecIdentityRef identity, CFStringRef name, CFArrayRef keyUsage) -{ - CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage); - return SecIdentitySetPreference(identity, name, keyUse); -} - -OSStatus -SecIdentityFindPreferenceItem( - CFTypeRef keychainOrArray, - CFStringRef idString, - SecKeychainItemRef *itemRef) -{ - BEGIN_SECAPI - - StorageManager::KeychainList keychains; - globals().storageManager.optionalSearchList(keychainOrArray, keychains); - KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); - - char idUTF8[MAXPATHLEN]; - idUTF8[0] = (char)'\0'; - if (idString) - { - if (!CFStringGetCString(idString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8)) - idUTF8[0] = (char)'\0'; - } - size_t idUTF8Len = strlen(idUTF8); - if (!idUTF8Len) - MacOSError::throwMe(errSecParam); - - CssmData service(const_cast(idUTF8), idUTF8Len); - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service); - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf'); - - Item item; - if (!cursor->next(item)) - MacOSError::throwMe(errSecItemNotFound); - - if (itemRef) - *itemRef=item->handle(); - - END_SECAPI -} - -OSStatus -SecIdentityFindPreferenceItemWithNameAndKeyUsage( - CFTypeRef keychainOrArray, - CFStringRef name, - int32_t keyUsage, - SecKeychainItemRef *itemRef) -{ - BEGIN_SECAPI - - StorageManager::KeychainList keychains; - globals().storageManager.optionalSearchList(keychainOrArray, keychains); - KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); - - char idUTF8[MAXPATHLEN]; - idUTF8[0] = (char)'\0'; - if (name) - { - if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8)) - idUTF8[0] = (char)'\0'; - } - size_t idUTF8Len = strlen(idUTF8); - if (!idUTF8Len) - MacOSError::throwMe(errSecParam); - - CssmData service(const_cast(idUTF8), idUTF8Len); - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service); - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf'); - if (keyUsage) - cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage); - - Item item; - if (!cursor->next(item)) - MacOSError::throwMe(errSecItemNotFound); - - if (itemRef) - *itemRef=item->handle(); - - END_SECAPI -} - -OSStatus SecIdentityDeletePreferenceItemWithNameAndKeyUsage( - CFTypeRef keychainOrArray, - CFStringRef name, - int32_t keyUsage) -{ - // when a specific key usage is passed, we'll only match & delete that pref; - // when a key usage of 0 is passed, all matching prefs should be deleted. - // maxUsages represents the most matches there could theoretically be, so - // cut things off at that point if we're still finding items (if they can't - // be deleted for some reason, we'd never break out of the loop.) - - OSStatus status; - SecKeychainItemRef item = NULL; - int count = 0, maxUsages = 12; - while (++count <= maxUsages && - (status = SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray, name, keyUsage, &item)) == errSecSuccess) { - status = SecKeychainItemDelete(item); - CFRelease(item); - item = NULL; - } - - // it's not an error if the item isn't found - return (status == errSecItemNotFound) ? errSecSuccess : status; -} - - -static -OSStatus _SecIdentityAddPreferenceItemWithName( - SecKeychainRef keychainRef, - SecIdentityRef identityRef, - CFStringRef idString, - SecKeychainItemRef *itemRef) -{ - // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here; - // caller must handle exceptions - - if (!identityRef || !idString) - return errSecParam; - SecPointer cert(Identity::required(identityRef)->certificate()); - Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false); - sint32 keyUsage = 0; - - // determine the account attribute - // - // This attribute must be synthesized from certificate label + pref item type + key usage, - // as only the account and service attributes can make a generic keychain item unique. - // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that - // we can save a certificate preference if an identity preference already exists for the - // given service name, and vice-versa. - // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. - // - CFStringRef labelStr = nil; - cert->inferLabel(false, &labelStr); - if (!labelStr) { - return errSecDataTooLarge; // data is "in a format which cannot be displayed" - } - CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1; - const char *templateStr = "%s [key usage 0x%X]"; - const int keyUsageMaxStrLen = 8; - accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen; - char accountUTF8[accountUTF8Len]; - if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8)) - accountUTF8[0] = (char)'\0'; - if (keyUsage) - snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage); - snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8); - CssmData account(const_cast(accountUTF8), strlen(accountUTF8)); - CFRelease(labelStr); - - // service attribute (name provided by the caller) - CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString), kCFStringEncodingUTF8) + 1;; - char serviceUTF8[serviceUTF8Len]; - if (!CFStringGetCString(idString, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8)) - serviceUTF8[0] = (char)'\0'; - CssmData service(const_cast(serviceUTF8), strlen(serviceUTF8)); - - // set item attribute values - item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service); - item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service); - item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf'); - item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account); - item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), keyUsage); - - // generic attribute (store persistent certificate reference) - CFDataRef pItemRef = nil; - OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert->handle(), &pItemRef); - if (!pItemRef) - status = errSecInvalidItemRef; - if (status) - return status; - const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef); - CFIndex dataLen = CFDataGetLength(pItemRef); - CssmData pref(const_cast(reinterpret_cast(dataPtr)), dataLen); - item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref); - CFRelease(pItemRef); - - Keychain keychain = nil; - try { - keychain = Keychain::optional(keychainRef); - if (!keychain->exists()) - MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time. - } - catch(...) { - keychain = globals().storageManager.defaultKeychainUI(item); - } - - try { - keychain->add(item); - } - catch (const MacOSError &err) { - if (err.osStatus() != errSecDuplicateItem) - throw; // if item already exists, fall through to update - } - - item->update(); - - if (itemRef) - *itemRef = item->handle(); - - return status; -} - -OSStatus SecIdentityAddPreferenceItem( - SecKeychainRef keychainRef, - SecIdentityRef identityRef, - CFStringRef idString, - SecKeychainItemRef *itemRef) -{ - // The original implementation of SecIdentityAddPreferenceItem adds the exact string only. - // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above), - // and this function is a wrapper which calls it, so that existing clients will get the - // extended behavior of server domain matching for items that specify URLs. - // (Note that behavior is unchanged if the specified idString is not a URL.) - - BEGIN_SECAPI - - OSStatus status = errSecInternalComponent; - CFArrayRef names = _SecIdentityCopyPossiblePaths(idString); - if (!names) { - return status; - } - - CFIndex total = CFArrayGetCount(names); - if (total > 0) { - // add item for name (first element in array) - CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, 0); - try { - status = _SecIdentityAddPreferenceItemWithName(keychainRef, identityRef, aName, itemRef); - } - catch (const MacOSError &err) { status=err.osStatus(); } - catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); } - catch (const std::bad_alloc &) { status=errSecAllocate; } - catch (...) { status=errSecInternalComponent; } - } - if (total > 2) { - Boolean setDomainDefaultIdentity = FALSE; - CFTypeRef val = (CFTypeRef)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"), - CFSTR("com.apple.security.identities"), - kCFPreferencesCurrentUser, - kCFPreferencesAnyHost); - if (val) { - if (CFGetTypeID(val) == CFBooleanGetTypeID()) - setDomainDefaultIdentity = CFBooleanGetValue((CFBooleanRef)val) ? TRUE : FALSE; - CFRelease(val); - } - if (setDomainDefaultIdentity) { - // add item for domain (second-to-last element in array, e.g. "*.apple.com") - OSStatus tmpStatus = errSecSuccess; - CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, total-2); - try { - tmpStatus = _SecIdentityAddPreferenceItemWithName(keychainRef, identityRef, aName, itemRef); - } - catch (const MacOSError &err) { tmpStatus=err.osStatus(); } - catch (const CommonError &err) { tmpStatus=SecKeychainErrFromOSStatus(err.osStatus()); } - catch (const std::bad_alloc &) { tmpStatus=errSecAllocate; } - catch (...) { tmpStatus=errSecInternalComponent; } - } - } - - CFRelease(names); - return status; - - END_SECAPI -} - -/* deprecated in 10.5 */ -OSStatus SecIdentityUpdatePreferenceItem( - SecKeychainItemRef itemRef, - SecIdentityRef identityRef) -{ - BEGIN_SECAPI - - if (!itemRef || !identityRef) - MacOSError::throwMe(errSecParam); - SecPointer certificate(Identity::required(identityRef)->certificate()); - Item prefItem = ItemImpl::required(itemRef); - - // get the current key usage value for this item - sint32 keyUsage = 0; - UInt32 actLen = 0; - SecKeychainAttribute attr = { kSecScriptCodeItemAttr, sizeof(sint32), &keyUsage }; - try { - prefItem->getAttribute(attr, &actLen); - } - catch(...) { - keyUsage = 0; - }; - - // set the account attribute - // - // This attribute must be synthesized from certificate label + pref item type + key usage, - // as only the account and service attributes can make a generic keychain item unique. - // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that - // we can save a certificate preference if an identity preference already exists for the - // given service name, and vice-versa. - // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. - // - CFStringRef labelStr = nil; - certificate->inferLabel(false, &labelStr); - if (!labelStr) { - MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed" - } - CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1; - const char *templateStr = "%s [key usage 0x%X]"; - const int keyUsageMaxStrLen = 8; - accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen; - char accountUTF8[accountUTF8Len]; - if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8)) - accountUTF8[0] = (char)'\0'; - if (keyUsage) - snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage); - snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8); - CssmData account(const_cast(accountUTF8), strlen(accountUTF8)); - prefItem->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account); - CFRelease(labelStr); - - // generic attribute (store persistent certificate reference) - CFDataRef pItemRef = nil; - OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)certificate->handle(), &pItemRef); - if (!pItemRef) - status = errSecInvalidItemRef; - if (status) - MacOSError::throwMe(status); - const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef); - CFIndex dataLen = CFDataGetLength(pItemRef); - CssmData pref(const_cast(reinterpret_cast(dataPtr)), dataLen); - prefItem->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref); - CFRelease(pItemRef); - - prefItem->update(); - - END_SECAPI -} - -OSStatus SecIdentityCopyFromPreferenceItem( - SecKeychainItemRef itemRef, - SecIdentityRef *identityRef) -{ - BEGIN_SECAPI - - if (!itemRef || !identityRef) - MacOSError::throwMe(errSecParam); - Item prefItem = ItemImpl::required(itemRef); - - // get persistent certificate reference - SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } }; - SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs }; - prefItem->getContent(NULL, &itemAttrList, NULL, NULL); - - // find certificate, given persistent reference data - CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull); - SecKeychainItemRef certItemRef = nil; - OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); //%%% need to make this a method of ItemImpl - prefItem->freeContent(&itemAttrList, NULL); - if (pItemRef) - CFRelease(pItemRef); - if (status) - return status; - - // create identity reference, given certificate - StorageManager::KeychainList keychains; - globals().storageManager.optionalSearchList((CFTypeRef)NULL, keychains); - Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef)); - SecPointer certificate(static_cast(certItem.get())); - SecPointer identity(new Identity(keychains, certificate)); - if (certItemRef) - CFRelease(certItemRef); - - Required(identityRef) = identity->handle(); - - END_SECAPI -} - -/* - * System Identity Support. - */ - -/* plist domain (in /Library/Preferences) */ -#define IDENTITY_DOMAIN "com.apple.security.systemidentities" - -/* - * Our plist is a dictionary whose entries have the following format: - * key = domain name as CFString - * value = public key hash as CFData - */ - -#define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName - -/* - * All accesses to system identities and its associated plist are - * protected by this lock. - */ -ModuleNexus systemIdentityLock; - -OSStatus SecIdentityCopySystemIdentity( - CFStringRef domain, - SecIdentityRef *idRef, - CFStringRef *actualDomain) /* optional */ -{ - BEGIN_SECAPI - - StLock _(systemIdentityLock()); - auto_ptr identDict; - - /* get top-level dictionary - if not present, we're done */ - Dictionary* d = Dictionary::CreateDictionary(IDENTITY_DOMAIN, Dictionary::US_System); - if (d == NULL) - { - return errSecNotAvailable; - } - - identDict.reset(d); - - /* see if there's an entry for specified domain */ - CFDataRef entryValue = identDict->getDataValue(domain); - if(entryValue == NULL) { - /* try for default entry if we're not already looking for default */ - if(!CFEqual(domain, kSecIdentityDomainDefault)) { - entryValue = identDict->getDataValue(kSecIdentityDomainDefault); - } - if(entryValue == NULL) { - /* no default identity */ - MacOSError::throwMe(errSecItemNotFound); - } - - /* remember that we're not fetching the requested domain */ - domain = kSecIdentityDomainDefault; - } - - /* open system keychain - error here is fatal */ - Keychain systemKc = globals().storageManager.make(SYSTEM_KEYCHAIN_PATH, false); - CFRef systemKcRef(systemKc->handle()); - StorageManager::KeychainList keychains; - globals().storageManager.optionalSearchList(systemKcRef, keychains); - - /* search for specified cert */ - SecKeychainAttributeList attrList; - SecKeychainAttribute attr; - attr.tag = kSecPublicKeyHashItemAttr; - attr.length = (UInt32)CFDataGetLength(entryValue); - attr.data = (void *)CFDataGetBytePtr(entryValue); - attrList.count = 1; - attrList.attr = &attr; - - KCCursor cursor(keychains, kSecCertificateItemClass, &attrList); - Item certItem; - if(!cursor->next(certItem)) { - MacOSError::throwMe(errSecItemNotFound); - } - - /* found the cert; try matching with key to cook up identity */ - SecPointer certificate(static_cast(certItem.get())); - SecPointer identity(new Identity(keychains, certificate)); - - Required(idRef) = identity->handle(); - if(actualDomain) { - *actualDomain = domain; - CFRetain(*actualDomain); - } - - END_SECAPI -} - -OSStatus SecIdentitySetSystemIdentity( - CFStringRef domain, - SecIdentityRef idRef) -{ - BEGIN_SECAPI - - StLock _(systemIdentityLock()); - if(geteuid() != 0) { - MacOSError::throwMe(errSecAuthFailed); - } - - auto_ptr identDict; - MutableDictionary *d = MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN, Dictionary::US_System); - if (d) - { - identDict.reset(d); - } - else - { - if(idRef == NULL) { - /* nothing there, nothing to set - done */ - return errSecSuccess; - } - identDict.reset(new MutableDictionary()); - } - - if(idRef == NULL) { - /* Just delete the possible entry for this domain */ - identDict->removeValue(domain); - } - else { - /* obtain public key hash of identity's cert */ - SecPointer identity(Identity::required(idRef)); - SecPointer cert = identity->certificate(); - const CssmData &pubKeyHash = cert->publicKeyHash(); - CFRef pubKeyHashData(CFDataCreate(NULL, pubKeyHash.Data, - pubKeyHash.Length)); - - /* add/replace to dictionary */ - identDict->setValue(domain, pubKeyHashData); - } - - /* flush to disk */ - if(!identDict->writePlistToPrefs(IDENTITY_DOMAIN, Dictionary::US_System)) { - MacOSError::throwMe(errSecIO); - } - - END_SECAPI -} - -const CFStringRef kSecIdentityDomainDefault = CFSTR("com.apple.systemdefault"); -const CFStringRef kSecIdentityDomainKerberosKDC = CFSTR("com.apple.kerberos.kdc"); -