X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/SecCertificate.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/SecCertificate.cpp b/Security/libsecurity_keychain/lib/SecCertificate.cpp new file mode 100644 index 00000000..4f204eed --- /dev/null +++ b/Security/libsecurity_keychain/lib/SecCertificate.cpp @@ -0,0 +1,1184 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "SecBridge.h" + +// %%% used by SecCertificate{Copy,Set}Preference +#include +#include +#include +#include +#include +#include "CertificateValues.h" +#include "SecCertificateP.h" +#include "SecCertificatePrivP.h" + +#include "AppleBaselineEscrowCertificates.h" + + +extern CSSM_KEYUSE ConvertArrayToKeyUsage(CFArrayRef usage); + +#define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v)); + +SEC_CONST_DECL (kSecCertificateProductionEscrowKey, "ProductionEscrowKey"); +SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey, "ProductionPCSEscrowKey"); +SEC_CONST_DECL (kSecCertificateEscrowFileName, "AppleESCertificates"); + + +using namespace CssmClient; + +CFTypeID +SecCertificateGetTypeID(void) +{ + BEGIN_SECAPI + + return gTypes().Certificate.typeID; + + END_SECAPI1(_kCFRuntimeNotATypeID) +} + + +OSStatus +SecCertificateCreateFromData(const CSSM_DATA *data, CSSM_CERT_TYPE type, CSSM_CERT_ENCODING encoding, SecCertificateRef *certificate) +{ + BEGIN_SECAPI + + SecPointer certificatePtr(new Certificate(Required(data), type, encoding)); + Required(certificate) = certificatePtr->handle(); + + END_SECAPI +} + +/* new in 10.6 */ +SecCertificateRef +SecCertificateCreateWithData(CFAllocatorRef allocator, CFDataRef data) +{ + SecCertificateRef certificate = NULL; + OSStatus __secapiresult; + try { + CSSM_DATA cssmCertData; + cssmCertData.Length = (data) ? (CSSM_SIZE)CFDataGetLength(data) : 0; + cssmCertData.Data = (data) ? (uint8 *)CFDataGetBytePtr(data) : NULL; + + //NOTE: there isn't yet a Certificate constructor which accepts a CFAllocatorRef + SecPointer certificatePtr(new Certificate(cssmCertData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER)); + certificate = certificatePtr->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 certificate; +} + +OSStatus +SecCertificateAddToKeychain(SecCertificateRef certificate, SecKeychainRef keychain) +{ + BEGIN_SECAPI + + Item item(Certificate::required(certificate)); + Keychain::optional(keychain)->add(item); + + END_SECAPI +} + +OSStatus +SecCertificateGetData(SecCertificateRef certificate, CSSM_DATA_PTR data) +{ + BEGIN_SECAPI + + Required(data) = Certificate::required(certificate)->data(); + + END_SECAPI +} + +/* new in 10.6 */ +CFDataRef +SecCertificateCopyData(SecCertificateRef certificate) +{ + CFDataRef data = NULL; + OSStatus __secapiresult = errSecSuccess; + try { + CssmData output = Certificate::required(certificate)->data(); + CFIndex length = (CFIndex)output.length(); + const UInt8 *bytes = (const UInt8 *)output.data(); + if (length && bytes) { + data = CFDataCreate(NULL, bytes, length); + } + } + 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 data; +} + +CFDataRef +SecCertificateGetSHA1Digest(SecCertificateRef certificate) +{ + CFDataRef data = NULL; + OSStatus __secapiresult = errSecSuccess; + try { + data = Certificate::required(certificate)->sha1Hash(); + } + 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 data; +} + +CFArrayRef +SecCertificateCopyDNSNames(SecCertificateRef certificate) +{ + CFArrayRef names = NULL; + OSStatus __secapiresult = errSecSuccess; + try { + names = Certificate::required(certificate)->copyDNSNames(); + } + 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 names; +} + +OSStatus +SecCertificateGetType(SecCertificateRef certificate, CSSM_CERT_TYPE *certificateType) +{ + BEGIN_SECAPI + + Required(certificateType) = Certificate::required(certificate)->type(); + + END_SECAPI +} + + +OSStatus +SecCertificateGetSubject(SecCertificateRef certificate, const CSSM_X509_NAME **subject) +{ + BEGIN_SECAPI + + Required(subject) = Certificate::required(certificate)->subjectName(); + + END_SECAPI +} + + +OSStatus +SecCertificateGetIssuer(SecCertificateRef certificate, const CSSM_X509_NAME **issuer) +{ + BEGIN_SECAPI + + Required(issuer) = Certificate::required(certificate)->issuerName(); + + END_SECAPI +} + + +OSStatus +SecCertificateGetCLHandle(SecCertificateRef certificate, CSSM_CL_HANDLE *clHandle) +{ + BEGIN_SECAPI + + Required(clHandle) = Certificate::required(certificate)->clHandle(); + + END_SECAPI +} + +/* + * Private API to infer a display name for a SecCertificateRef which + * may or may not be in a keychain. + */ +OSStatus +SecCertificateInferLabel(SecCertificateRef certificate, CFStringRef *label) +{ + BEGIN_SECAPI + + Certificate::required(certificate)->inferLabel(false, + &Required(label)); + + END_SECAPI +} + +OSStatus +SecCertificateCopyPublicKey(SecCertificateRef certificate, SecKeyRef *key) +{ + BEGIN_SECAPI + + Required(key) = Certificate::required(certificate)->publicKey()->handle(); + + END_SECAPI +} + +OSStatus +SecCertificateGetAlgorithmID(SecCertificateRef certificate, const CSSM_X509_ALGORITHM_IDENTIFIER **algid) +{ + BEGIN_SECAPI + + Required(algid) = Certificate::required(certificate)->algorithmID(); + + END_SECAPI +} + +OSStatus +SecCertificateCopyCommonName(SecCertificateRef certificate, CFStringRef *commonName) +{ + BEGIN_SECAPI + + Required(commonName) = Certificate::required(certificate)->commonName(); + + END_SECAPI +} + +/* new in 10.6 */ +CFStringRef +SecCertificateCopySubjectSummary(SecCertificateRef certificate) +{ + CFStringRef summary = NULL; + OSStatus __secapiresult; + try { + Certificate::required(certificate)->inferLabel(false, &summary); + + __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 summary; +} + +CFStringRef +SecCertificateCopyIssuerSummary(SecCertificateRef certificate) +{ + CFStringRef issuerStr = NULL; + SecCertificateRefP certP = NULL; + CFDataRef certData = SecCertificateCopyData(certificate); + if (certData) { + certP = SecCertificateCreateWithDataP(NULL, certData); + CFRelease(certData); + } + if (certP) { + issuerStr = SecCertificateCopyIssuerSummaryP(certP); + CFRelease(certP); + } + return issuerStr; +} + +OSStatus +SecCertificateCopySubjectComponent(SecCertificateRef certificate, const CSSM_OID *component, CFStringRef *result) +{ + BEGIN_SECAPI + + Required(result) = Certificate::required(certificate)->distinguishedName(&CSSMOID_X509V1SubjectNameCStruct, component); + + END_SECAPI +} + +OSStatus +SecCertificateGetCommonName(SecCertificateRef certificate, CFStringRef *commonName) +{ + // deprecated SPI signature; replaced by SecCertificateCopyCommonName + return SecCertificateCopyCommonName(certificate, commonName); +} + +OSStatus +SecCertificateGetEmailAddress(SecCertificateRef certificate, CFStringRef *emailAddress) +{ + BEGIN_SECAPI + + Required(emailAddress) = Certificate::required(certificate)->copyFirstEmailAddress(); + + END_SECAPI +} + +OSStatus +SecCertificateCopyEmailAddresses(SecCertificateRef certificate, CFArrayRef *emailAddresses) +{ + BEGIN_SECAPI + + Required(emailAddresses) = Certificate::required(certificate)->copyEmailAddresses(); + + END_SECAPI +} + +OSStatus +SecCertificateCopyFieldValues(SecCertificateRef certificate, const CSSM_OID *field, CSSM_DATA_PTR **fieldValues) +{ +/* Return a zero terminated list of CSSM_DATA_PTR's with the values of the field specified by field. Caller must call releaseFieldValues to free the storage allocated by this call. */ + BEGIN_SECAPI + + Required(fieldValues) = Certificate::required(certificate)->copyFieldValues(Required(field)); + + END_SECAPI +} + +OSStatus +SecCertificateReleaseFieldValues(SecCertificateRef certificate, const CSSM_OID *field, CSSM_DATA_PTR *fieldValues) +{ + BEGIN_SECAPI + + Certificate::required(certificate)->releaseFieldValues(Required(field), fieldValues); + + END_SECAPI +} + +OSStatus +SecCertificateCopyFirstFieldValue(SecCertificateRef certificate, const CSSM_OID *field, CSSM_DATA_PTR *fieldValue) +{ + BEGIN_SECAPI + + Required(fieldValue) = Certificate::required(certificate)->copyFirstFieldValue(Required(field)); + + END_SECAPI +} + +OSStatus +SecCertificateReleaseFirstFieldValue(SecCertificateRef certificate, const CSSM_OID *field, CSSM_DATA_PTR fieldValue) +{ + BEGIN_SECAPI + + Certificate::required(certificate)->releaseFieldValue(Required(field), fieldValue); + + END_SECAPI +} + +OSStatus +SecCertificateFindByIssuerAndSN(CFTypeRef keychainOrArray,const CSSM_DATA *issuer, + const CSSM_DATA *serialNumber, SecCertificateRef *certificate) +{ + BEGIN_SECAPI + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + Required(certificate) = Certificate::findByIssuerAndSN(keychains, CssmData::required(issuer), CssmData::required(serialNumber))->handle(); + + END_SECAPI +} + +OSStatus +SecCertificateFindBySubjectKeyID(CFTypeRef keychainOrArray, const CSSM_DATA *subjectKeyID, + SecCertificateRef *certificate) +{ + BEGIN_SECAPI + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + Required(certificate) = Certificate::findBySubjectKeyID(keychains, CssmData::required(subjectKeyID))->handle(); + + END_SECAPI +} + +OSStatus +SecCertificateFindByEmail(CFTypeRef keychainOrArray, const char *emailAddress, SecCertificateRef *certificate) +{ + BEGIN_SECAPI + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + Required(certificate) = Certificate::findByEmail(keychains, emailAddress)->handle(); + + END_SECAPI +} + +OSStatus +SecKeychainSearchCreateForCertificateByIssuerAndSN(CFTypeRef keychainOrArray, const CSSM_DATA *issuer, + const CSSM_DATA *serialNumber, SecKeychainSearchRef *searchRef) +{ + BEGIN_SECAPI + + Required(searchRef); + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + KCCursor cursor(Certificate::cursorForIssuerAndSN(keychains, CssmData::required(issuer), CssmData::required(serialNumber))); + *searchRef = cursor->handle(); + + END_SECAPI +} + +OSStatus +SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(CFTypeRef keychainOrArray, CFDataRef issuer, + CFDataRef serialNumber, SecKeychainSearchRef *searchRef) +{ + BEGIN_SECAPI + + Required(searchRef); + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + Required(issuer); + Required(serialNumber); + KCCursor cursor(Certificate::cursorForIssuerAndSN_CF(keychains, issuer, serialNumber)); + *searchRef = cursor->handle(); + + END_SECAPI +} + +OSStatus +SecKeychainSearchCreateForCertificateBySubjectKeyID(CFTypeRef keychainOrArray, const CSSM_DATA *subjectKeyID, + SecKeychainSearchRef *searchRef) +{ + BEGIN_SECAPI + + Required(searchRef); + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + KCCursor cursor(Certificate::cursorForSubjectKeyID(keychains, CssmData::required(subjectKeyID))); + *searchRef = cursor->handle(); + + END_SECAPI +} + +OSStatus +SecKeychainSearchCreateForCertificateByEmail(CFTypeRef keychainOrArray, const char *emailAddress, + SecKeychainSearchRef *searchRef) +{ + BEGIN_SECAPI + + Required(searchRef); + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + KCCursor cursor(Certificate::cursorForEmail(keychains, emailAddress)); + *searchRef = cursor->handle(); + + END_SECAPI +} + +/* NOT EXPORTED YET; copied from SecurityInterface but could be useful in the future. +CSSM_CSP_HANDLE +SecGetAppleCSPHandle() +{ + BEGIN_SECAPI + return CSP(gGuidAppleCSP)->handle(); + END_SECAPI1(NULL); +} + +CSSM_CL_HANDLE +SecGetAppleCLHandle() +{ + BEGIN_SECAPI + return CL(gGuidAppleX509CL)->handle(); + END_SECAPI1(NULL); +} +*/ + +CSSM_RETURN +SecDigestGetData (CSSM_ALGORITHMS alg, CSSM_DATA* digest, const CSSM_DATA* data) +{ + BEGIN_SECAPI + // sanity checking + if (!digest || !digest->Data || !digest->Length || !data || !data->Data || !data->Length) + return errSecParam; + + CSP csp(gGuidAppleCSP); + Digest context(csp, alg); + CssmData input(data->Data, data->Length); + CssmData output(digest->Data, digest->Length); + + context.digest(input, output); + digest->Length = output.length(); + + return CSSM_OK; + END_SECAPI1(1); +} + +/* determine whether a cert is self-signed */ +OSStatus SecCertificateIsSelfSigned( + SecCertificateRef certificate, + Boolean *isSelfSigned) /* RETURNED */ +{ + BEGIN_SECAPI + + *isSelfSigned = Certificate::required(certificate)->isSelfSigned(); + + END_SECAPI +} + +OSStatus +SecCertificateCopyPreference( + CFStringRef name, + CSSM_KEYUSE keyUsage, + SecCertificateRef *certificate) +{ + BEGIN_SECAPI + + Required(name); + Required(certificate); + StorageManager::KeychainList keychains; + globals().storageManager.getSearchList(keychains); + KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); + + char idUTF8[MAXPATHLEN]; + if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8)) + idUTF8[0] = (char)'\0'; + CssmData service(const_cast(idUTF8), strlen(idUTF8)); + FourCharCode itemType = 'cprf'; + 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)) + MacOSError::throwMe(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; + + *certificate = (SecCertificateRef)certItemRef; + + END_SECAPI +} + +SecCertificateRef +SecCertificateCopyPreferred( + CFStringRef name, + CFArrayRef keyUsage) +{ + // 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 + + SecCertificateRef certRef = NULL; + CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage); + OSStatus status = SecCertificateCopyPreference(name, keyUse, &certRef); + if (status != errSecSuccess && keyUse != CSSM_KEYUSE_ANY) + status = SecCertificateCopyPreference(name, CSSM_KEYUSE_ANY, &certRef); + if (status != errSecSuccess && keyUse != 0) + status = SecCertificateCopyPreference(name, 0, &certRef); + + return certRef; +} + +static OSStatus +SecCertificateFindPreferenceItemWithNameAndKeyUsage( + 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)'cprf'); + 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 +} + +static +OSStatus SecCertificateDeletePreferenceItemWithNameAndKeyUsage( + 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 = SecCertificateFindPreferenceItemWithNameAndKeyUsage(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; +} + +OSStatus SecCertificateSetPreference( + SecCertificateRef certificate, + CFStringRef name, + CSSM_KEYUSE keyUsage, + CFDateRef date) +{ + if (!name) { + return errSecParam; + } + if (!certificate) { + // treat NULL certificate as a request to clear the preference + // (note: if keyUsage is 0, this clears all key usage prefs for name) + return SecCertificateDeletePreferenceItemWithNameAndKeyUsage(NULL, name, keyUsage); + } + + BEGIN_SECAPI + + // 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::required(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); + 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 preference item, in case this is an update + StorageManager::KeychainList keychains; + globals().storageManager.getSearchList(keychains); + KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); + FourCharCode itemType = 'cprf'; + 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); + if (date) + ; // %%%TBI + + 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); + + // date + if (date) + ; // %%%TBI + + // generic attribute (store persistent certificate reference) + CFDataRef pItemRef = nil; + Certificate::required(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 SecCertificateSetPreferred( + SecCertificateRef certificate, + CFStringRef name, + CFArrayRef keyUsage) +{ + CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage); + return SecCertificateSetPreference(certificate, name, keyUse, NULL); +} + +CFDictionaryRef SecCertificateCopyValues(SecCertificateRef certificate, CFArrayRef keys, CFErrorRef *error) +{ + CFDictionaryRef result = NULL; + OSStatus __secapiresult; + try + { + CertificateValues cv(certificate); + result = cv.copyFieldValues(keys,error); + __secapiresult=0; + } + 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 result; +} + +CFStringRef SecCertificateCopyLongDescription(CFAllocatorRef alloc, SecCertificateRef certificate, CFErrorRef *error) +{ + return SecCertificateCopyShortDescription(alloc, certificate, error); +} + +CFStringRef SecCertificateCopyShortDescription(CFAllocatorRef alloc, SecCertificateRef certificate, CFErrorRef *error) +{ + CFStringRef result = NULL; + OSStatus __secapiresult; + try + { + __secapiresult = SecCertificateInferLabel(certificate, &result); + } + 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; } + if (error!=NULL && __secapiresult!=errSecSuccess) + { + *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, + __secapiresult ? __secapiresult : CSSM_ERRCODE_INTERNAL_ERROR, NULL); + } + return result; +} + +CFDataRef SecCertificateCopySerialNumber(SecCertificateRef certificate, CFErrorRef *error) +{ + CFDataRef result = NULL; + OSStatus __secapiresult; + try + { + CertificateValues cv(certificate); + result = cv.copySerialNumber(error); + __secapiresult=0; + } + 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 result; +} + +CFDataRef SecCertificateCopyNormalizedIssuerContent(SecCertificateRef certificate, CFErrorRef *error) +{ + CFDataRef result = NULL; + OSStatus __secapiresult; + try + { + CertificateValues cv(certificate); + result = cv.copyNormalizedIssuerContent(error); + __secapiresult=0; + } + 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 result; +} + +CFDataRef SecCertificateCopyNormalizedSubjectContent(SecCertificateRef certificate, CFErrorRef *error) +{ + CFDataRef result = NULL; + OSStatus __secapiresult; + try + { + CertificateValues cv(certificate); + result = cv.copyNormalizedSubjectContent(error); + __secapiresult=0; + } + 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 result; +} + +CFDataRef SecCertificateCopyIssuerSequence(SecCertificateRef certificate) +{ + CFDataRef result = NULL; + OSStatus __secapiresult; + try + { + CertificateValues cv(certificate); + result = cv.copyIssuerSequence(NULL); + __secapiresult=0; + } + 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 result; +} + +CFDataRef SecCertificateCopySubjectSequence(SecCertificateRef certificate) +{ + CFDataRef result = NULL; + OSStatus __secapiresult; + try + { + CertificateValues cv(certificate); + result = cv.copySubjectSequence(NULL); + __secapiresult=0; + } + 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 result; +} + +bool SecCertificateIsValid(SecCertificateRef certificate, CFAbsoluteTime verifyTime) +{ + bool result = NULL; + OSStatus __secapiresult; + try + { + CFErrorRef error = NULL; + CertificateValues cv(certificate); + result = cv.isValid(verifyTime, &error); + if (error) CFRelease(error); + __secapiresult=0; + } + 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 result; +} + +/* + * deprecated function name + */ +bool SecCertificateIsValidX(SecCertificateRef certificate, CFAbsoluteTime verifyTime) +{ + return SecCertificateIsValid(certificate, verifyTime); +} + + +CFAbsoluteTime SecCertificateNotValidBefore(SecCertificateRef certificate) +{ + CFAbsoluteTime result = 0; + OSStatus __secapiresult; + try + { + CFErrorRef error = NULL; + CertificateValues cv(certificate); + result = cv.notValidBefore(&error); + if (error) CFRelease(error); + __secapiresult=0; + } + 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 result; +} + +CFAbsoluteTime SecCertificateNotValidAfter(SecCertificateRef certificate) +{ + CFAbsoluteTime result = 0; + OSStatus __secapiresult; + try + { + CFErrorRef error = NULL; + CertificateValues cv(certificate); + result = cv.notValidAfter(&error); + if (error) CFRelease(error); + __secapiresult=0; + } + 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 result; +} + +/* new in 10.8 */ +SecCertificateRef SecCertificateCreateWithBytes(CFAllocatorRef allocator, + const UInt8 *bytes, CFIndex length) +{ + SecCertificateRef certificate = NULL; + OSStatus __secapiresult; + try { + CSSM_DATA cssmCertData = { (CSSM_SIZE)length, (uint8 *)bytes }; + + //NOTE: there isn't yet a Certificate constructor which accepts a CFAllocatorRef + SecPointer certificatePtr(new Certificate(cssmCertData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER)); + certificate = certificatePtr->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 certificate; +} + +/* new in 10.8 */ +CFIndex SecCertificateGetLength(SecCertificateRef certificate) +{ + CFIndex length = 0; + OSStatus __secapiresult; + try { + CssmData output = Certificate::required(certificate)->data(); + length = (CFIndex)output.length(); + __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 length; +} + +/* new in 10.8 */ +const UInt8 *SecCertificateGetBytePtr(SecCertificateRef certificate) +{ + const UInt8 *bytes = NULL; + OSStatus __secapiresult; + try { + CssmData output = Certificate::required(certificate)->data(); + bytes = (const UInt8 *)output.data(); + __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 bytes; +} + +static CFArrayRef CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType, CFErrorRef *error) +{ + // Return array of CFDataRef certificates. + CFArrayRef result = NULL; + int iCnt; + int numRoots = 0; + + // Get the hard coded set of production roots + // static struct RootRecord* kProductionEscrowRoots[] = {&kOldEscrowRootRecord, &kProductionEscrowRootRecord}; + + struct RootRecord** pEscrowRoots = NULL; + switch (escrowRootType) { + case kSecCertificateBaselineEscrowRoot: + numRoots = kNumberOfBaseLineEscrowRoots; + pEscrowRoots = kBaseLineEscrowRoots; + break; + case kSecCertificateProductionEscrowRoot: + numRoots = kNumberOfBaseLineEscrowRoots; //%%% currently, production == baseline on OS X + pEscrowRoots = kBaseLineEscrowRoots; + break; + case kSecCertificateBaselinePCSEscrowRoot: + numRoots = kNumberOfBaseLinePCSEscrowRoots; + pEscrowRoots = kBaseLinePCSEscrowRoots; + break; + case kSecCertificateProductionPCSEscrowRoot: + numRoots = kNumberOfBaseLinePCSEscrowRoots; //%%% currently, production == baseline on OS X + pEscrowRoots = kBaseLinePCSEscrowRoots; + break; + default: + break; + } + + CFDataRef productionCerts[numRoots]; + struct RootRecord* pRootRecord = NULL; + + for (iCnt = 0; pEscrowRoots != NULL && iCnt < numRoots; iCnt++) + { + pRootRecord = pEscrowRoots[iCnt]; + if (NULL != pRootRecord && pRootRecord->_length > 0 && NULL != pRootRecord->_bytes) + { + productionCerts[iCnt] = CFDataCreate(kCFAllocatorDefault, pRootRecord->_bytes, pRootRecord->_length); + } + } + result = CFArrayCreate(kCFAllocatorDefault, (const void **)productionCerts, numRoots, &kCFTypeArrayCallBacks); + for (iCnt = 0; iCnt < numRoots; iCnt++) + { + if (NULL != productionCerts[iCnt]) + { + CFRelease(productionCerts[iCnt]); + } + } + + return result; +} + +/* new in 10.9 */ +CFArrayRef SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType) +{ + CFArrayRef result = NULL; + int iCnt; + int numRoots = 0; + CFDataRef certData = NULL; + + // The request is for the base line certificates. + // Use the hard coded data to generate the return array + if (kSecCertificateBaselineEscrowRoot == escrowRootType) + { + // Get the hard coded set of roots + numRoots = kNumberOfBaseLineEscrowRoots; + SecCertificateRef baseLineCerts[numRoots]; + struct RootRecord* pRootRecord = NULL; + + for (iCnt = 0; iCnt < numRoots; iCnt++) + { + pRootRecord = kBaseLineEscrowRoots[iCnt]; + if (NULL != pRootRecord && pRootRecord->_length > 0 && NULL != pRootRecord->_bytes) + { + certData = CFDataCreate(kCFAllocatorDefault, pRootRecord->_bytes, pRootRecord->_length); + if (NULL != certData) + { + baseLineCerts[iCnt] = SecCertificateCreateWithData(kCFAllocatorDefault, certData); + CFRelease(certData); + } + } + } + result = CFArrayCreate(kCFAllocatorDefault, (const void **)baseLineCerts, numRoots, &kCFTypeArrayCallBacks); + for (iCnt = 0; iCnt < numRoots; iCnt++) + { + if (NULL != baseLineCerts[iCnt]) + { + CFRelease(baseLineCerts[iCnt]); + } + } + } + // The request is for the current certificates. + else + { + CFErrorRef error = NULL; + CFArrayRef cert_datas = CopyEscrowCertificates(escrowRootType, &error); + if (NULL != error || NULL == cert_datas || 0 == (numRoots = (int)CFArrayGetCount(cert_datas))) + { + if (NULL != error) + { + CFRelease(error); + } + + if (NULL != cert_datas) + { + CFRelease(cert_datas); + } + return result; + } + + SecCertificateRef assetCerts[numRoots]; + for (iCnt = 0; iCnt < numRoots; iCnt++) + { + certData = (CFDataRef)CFArrayGetValueAtIndex(cert_datas, iCnt); + if (NULL != certData) + { + SecCertificateRef aCertRef = SecCertificateCreateWithData(kCFAllocatorDefault, certData); + assetCerts[iCnt] = aCertRef; + } + else + { + assetCerts[iCnt] = NULL; + } + } + + if (numRoots > 0) + { + result = CFArrayCreate(kCFAllocatorDefault, (const void **)assetCerts, numRoots, &kCFTypeArrayCallBacks); + for (iCnt = 0; iCnt < numRoots; iCnt++) + { + if (NULL != assetCerts[iCnt]) + { + CFRelease(assetCerts[iCnt]); + } + } + } + CFRelease(cert_datas); + } + return result; +} +