#include <Security/SecIdentity.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecKeychainItemPriv.h>
#include <Security/SecItem.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecCertificatePriv.h>
#include "SecBridge.h"
#include <security_keychain/Certificate.h>
#include <security_keychain/Identity.h>
#include <security_keychain/KeyItem.h>
#include <security_keychain/KCCursor.h>
#include <security_cdsa_utilities/Schema.h>
#include <security_utilities/simpleprefs.h>
#include <sys/param.h>
#include <syslog.h>
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<Certificate> certificatePtr(Identity::required(identityRef)->certificate());
Required(certificateRef) = certificatePtr->handle();
#if SECTRUST_OSX
CssmData certData = certificatePtr->data();
CFDataRef data = NULL;
if (certData.Data && certData.Length) {
data = CFDataCreate(NULL, certData.Data, certData.Length);
}
if (!data) {
*certificateRef = NULL;
syslog(LOG_ERR, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)",
(long)certData.Length, (uintptr_t)certData.Data);
return errSecInternal;
}
SecCertificateRef tmpRef = *certificateRef;
*certificateRef = SecCertificateCreateWithKeychainItem(NULL, data, tmpRef);
if (data)
CFRelease(data);
if (tmpRef)
CFRelease(tmpRef);
#endif
END_SECAPI
}
OSStatus
SecIdentityCopyPrivateKey(
SecIdentityRef identityRef,
SecKeyRef *privateKeyRef)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItemPtr(Identity::required(identityRef)->privateKey());
Required(privateKeyRef) = keyItemPtr->handle();
END_SECAPI
}
OSStatus
SecIdentityCreateWithCertificate(
CFTypeRef keychainOrArray,
SecCertificateRef certificate,
SecIdentityRef *identityRef)
{
BEGIN_SECCERTAPI
SecPointer<Certificate> certificatePtr(Certificate::required(__itemImplRef));
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList(keychainOrArray, keychains);
SecPointer<Identity> identityPtr(new Identity(keychains, certificatePtr));
Required(identityRef) = identityPtr->handle();
END_SECCERTAPI
}
SecIdentityRef
SecIdentityCreate(
CFAllocatorRef allocator,
SecCertificateRef certificate,
SecKeyRef privateKey)
{
SecIdentityRef identityRef = NULL;
OSStatus __secapiresult;
SecCertificateRef __itemImplRef=SecCertificateCreateItemImplInstance(certificate);
try {
SecPointer<Certificate> certificatePtr(Certificate::required(__itemImplRef));
SecPointer<KeyItem> keyItemPtr(KeyItem::required(privateKey));
SecPointer<Identity> 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<Identity> id1(Identity::required(identity1));
SecPointer<Identity> 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)
{
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)) {
CFStringRef qs = CFURLCopyQueryString(url, NULL);
if (qs) {
CFMutableStringRef newName = CFStringCreateMutableCopy(NULL, oldLength, name);
if (newName) {
CFIndex qsLength = CFStringGetLength(qs) + 1; CFStringDelete(newName, CFRangeMake(oldLength-qsLength, qsLength));
CFRelease(url);
url = CFURLCreateWithString(NULL, newName, NULL);
CFArraySetValueAtIndex(names, 0, newName);
CFRelease(newName);
}
CFRelease(qs);
}
while (url) {
CFURLRef parent = CFURLCreateCopyDeletingLastPathComponent(NULL, url);
if (parent) {
CFStringRef parentURLString = CFURLGetString(parent);
if (parentURLString) {
CFIndex newLength = CFStringGetLength(parentURLString);
if ((newLength >= oldLength) || (!CFStringHasPrefix(name, parentURLString))) {
CFRelease(parent);
CFRelease(url);
break;
}
oldLength = newLength;
CFArrayAppendValue(names, parentURLString);
}
}
CFRelease(url);
url = parent;
}
}
else {
CFRelease(url);
}
}
url = CFURLCreateWithString(NULL, name, NULL);
if (url) {
if (CFURLCanBeDecomposed(url)) {
CFStringRef netLocString = CFURLCopyNetLocation(url);
if (netLocString) {
CFStringRef tmpLocString = netLocString;
CFArrayRef hostnameArray = CFStringCreateArrayBySeparatingStrings(NULL, netLocString, CFSTR(":"));
tmpLocString = (CFStringRef)CFRetain((CFStringRef)CFArrayGetValueAtIndex(hostnameArray, 0));
CFRelease(netLocString);
CFRelease(hostnameArray);
netLocString = tmpLocString;
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)
{
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<char *>(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;
SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
prefItem->getContent(NULL, &itemAttrList, NULL, NULL);
CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
SecKeychainItemRef certItemRef = nil;
OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); prefItem->freeContent(&itemAttrList, NULL);
if (pItemRef)
CFRelease(pItemRef);
if (status)
return status;
if (validIssuers) {
}
#if SECTRUST_OSX
status = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)certItemRef, identity);
#else
try {
Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
SecPointer<Identity> identity_ptr(new Identity(keychains, certificate));
if (certItemRef) {
CFRelease(certItemRef); }
Required(identity) = identity_ptr->handle();
}
catch (const MacOSError &err) { status=err.osStatus(); }
catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); }
catch (const std::bad_alloc &) { status=errSecAllocate; }
catch (...) { status=errSecInvalidItemRef; }
#endif
return status;
}
SecIdentityRef SecIdentityCopyPreferred(CFStringRef name, CFArrayRef keyUsage, CFArrayRef validIssuers)
{
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)
{
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) {
CFStringRef labelString = NULL;
if (!status && identity && *identity) {
try {
SecPointer<Certificate> 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);
}
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; }
}
CFRelease(names);
return status;
END_SECAPI
}
OSStatus SecIdentitySetPreference(
SecIdentityRef identity,
CFStringRef name,
CSSM_KEYUSE keyUsage)
{
if (!name) {
return errSecParam;
}
if (!identity) {
return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL, name, keyUsage);
}
BEGIN_SECAPI
SecPointer<Certificate> certificate(Identity::required(identity)->certificate());
CFStringRef labelStr = nil;
certificate->inferLabel(false, &labelStr);
if (!labelStr) {
MacOSError::throwMe(errSecDataTooLarge); }
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<char *>(accountUTF8), strlen(accountUTF8));
CFRelease(labelStr);
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<char *>(serviceUTF8), strlen(serviceUTF8));
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));
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);
CFDataRef pItemRef = nil;
certificate->copyPersistentReference(pItemRef);
if (!pItemRef) {
MacOSError::throwMe(errSecInvalidItemRef);
}
const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
CFIndex dataLen = CFDataGetLength(pItemRef);
CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(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); }
catch(...) {
keychain = globals().storageManager.defaultKeychainUI(item);
}
try {
keychain->add(item);
}
catch (const MacOSError &err) {
if (err.osStatus() != errSecDuplicateItem)
throw; }
}
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<char *>(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<char *>(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)
{
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;
}
return (status == errSecItemNotFound) ? errSecSuccess : status;
}
static
OSStatus _SecIdentityAddPreferenceItemWithName(
SecKeychainRef keychainRef,
SecIdentityRef identityRef,
CFStringRef idString,
SecKeychainItemRef *itemRef)
{
if (!identityRef || !idString)
return errSecParam;
SecPointer<Certificate> cert(Identity::required(identityRef)->certificate());
Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
sint32 keyUsage = 0;
CFStringRef labelStr = nil;
cert->inferLabel(false, &labelStr);
if (!labelStr) {
return errSecDataTooLarge; }
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<char *>(accountUTF8), strlen(accountUTF8));
CFRelease(labelStr);
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<char *>(serviceUTF8), strlen(serviceUTF8));
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);
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<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
CFRelease(pItemRef);
Keychain keychain = nil;
try {
keychain = Keychain::optional(keychainRef);
if (!keychain->exists())
MacOSError::throwMe(errSecNoSuchKeychain); }
catch(...) {
keychain = globals().storageManager.defaultKeychainUI(item);
}
try {
keychain->add(item);
}
catch (const MacOSError &err) {
if (err.osStatus() != errSecDuplicateItem)
throw; }
item->update();
if (itemRef)
*itemRef = item->handle();
return status;
}
OSStatus SecIdentityAddPreferenceItem(
SecKeychainRef keychainRef,
SecIdentityRef identityRef,
CFStringRef idString,
SecKeychainItemRef *itemRef)
{
BEGIN_SECAPI
OSStatus status = errSecInternalComponent;
CFArrayRef names = _SecIdentityCopyPossiblePaths(idString);
if (!names) {
return status;
}
CFIndex total = CFArrayGetCount(names);
if (total > 0) {
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) {
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
}
OSStatus SecIdentityUpdatePreferenceItem(
SecKeychainItemRef itemRef,
SecIdentityRef identityRef)
{
BEGIN_SECAPI
if (!itemRef || !identityRef)
MacOSError::throwMe(errSecParam);
SecPointer<Certificate> certificate(Identity::required(identityRef)->certificate());
Item prefItem = ItemImpl::required(itemRef);
sint32 keyUsage = 0;
UInt32 actLen = 0;
SecKeychainAttribute attr = { kSecScriptCodeItemAttr, sizeof(sint32), &keyUsage };
try {
prefItem->getAttribute(attr, &actLen);
}
catch(...) {
keyUsage = 0;
};
CFStringRef labelStr = nil;
certificate->inferLabel(false, &labelStr);
if (!labelStr) {
MacOSError::throwMe(errSecDataTooLarge); }
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<char *>(accountUTF8), strlen(accountUTF8));
prefItem->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
CFRelease(labelStr);
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<void *>(reinterpret_cast<const void *>(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);
SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
prefItem->getContent(NULL, &itemAttrList, NULL, NULL);
CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
SecKeychainItemRef certItemRef = nil;
OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); prefItem->freeContent(&itemAttrList, NULL);
if (pItemRef)
CFRelease(pItemRef);
if (status)
return status;
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList((CFTypeRef)NULL, keychains);
Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
SecPointer<Identity> identity(new Identity(keychains, certificate));
if (certItemRef)
CFRelease(certItemRef);
Required(identityRef) = identity->handle();
END_SECAPI
}
#define IDENTITY_DOMAIN "com.apple.security.systemidentities"
#define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
ModuleNexus<Mutex> systemIdentityLock;
OSStatus SecIdentityCopySystemIdentity(
CFStringRef domain,
SecIdentityRef *idRef,
CFStringRef *actualDomain)
{
BEGIN_SECAPI
StLock<Mutex> _(systemIdentityLock());
auto_ptr<Dictionary> identDict;
Dictionary* d = Dictionary::CreateDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
if (d == NULL)
{
return errSecNotAvailable;
}
identDict.reset(d);
CFDataRef entryValue = identDict->getDataValue(domain);
if(entryValue == NULL) {
if(!CFEqual(domain, kSecIdentityDomainDefault)) {
entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
}
if(entryValue == NULL) {
MacOSError::throwMe(errSecItemNotFound);
}
domain = kSecIdentityDomainDefault;
}
Keychain systemKc = globals().storageManager.make(SYSTEM_KEYCHAIN_PATH, false);
CFRef<SecKeychainRef> systemKcRef(systemKc->handle());
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList(systemKcRef, keychains);
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);
}
SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
SecPointer<Identity> 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<Mutex> _(systemIdentityLock());
if(geteuid() != 0) {
MacOSError::throwMe(errSecAuthFailed);
}
auto_ptr<MutableDictionary> identDict;
MutableDictionary *d = MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
if (d)
{
identDict.reset(d);
}
else
{
if(idRef == NULL) {
return errSecSuccess;
}
identDict.reset(new MutableDictionary());
}
if(idRef == NULL) {
identDict->removeValue(domain);
}
else {
SecPointer<Identity> identity(Identity::required(idRef));
SecPointer<Certificate> cert = identity->certificate();
const CssmData &pubKeyHash = cert->publicKeyHash();
CFRef<CFDataRef> pubKeyHashData(CFDataCreate(NULL, pubKeyHash.Data,
pubKeyHash.Length));
identDict->setValue(domain, pubKeyHashData);
}
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");