#include <Security/SecIdentity.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecKeychainItemPriv.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>
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();
END_SECAPI2("SecIdentityCopyCertificate")
}
OSStatus
SecIdentityCopyPrivateKey(
SecIdentityRef identityRef,
SecKeyRef *privateKeyRef)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItemPtr(Identity::required(identityRef)->privateKey());
Required(privateKeyRef) = keyItemPtr->handle();
END_SECAPI2("SecIdentityCopyPrivateKey")
}
OSStatus
SecIdentityCreateWithCertificate(
CFTypeRef keychainOrArray,
SecCertificateRef certificateRef,
SecIdentityRef *identityRef)
{
BEGIN_SECAPI
SecPointer<Certificate> certificatePtr(Certificate::required(certificateRef));
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList(keychainOrArray, keychains);
SecPointer<Identity> identityPtr(new Identity(keychains, certificatePtr));
Required(identityRef) = identityPtr->handle();
END_SECAPI2("SecIdentityCreateWithCertificate")
}
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);
}
OSStatus SecIdentityCopyPreference(
CFStringRef name,
CSSM_KEYUSE keyUsage,
CFArrayRef validIssuers,
SecIdentityRef *identity)
{
BEGIN_SECAPI
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));
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 prefItem;
if (!cursor->next(prefItem))
MacOSError::throwMe(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) {
}
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();
END_SECAPI2("SecIdentityCopyPreference")
}
OSStatus SecIdentitySetPreference(
SecIdentityRef identity,
CFStringRef name,
CSSM_KEYUSE keyUsage)
{
BEGIN_SECAPI
if (!identity || !name)
MacOSError::throwMe(paramErr);
SecPointer<Certificate> certificate(Identity::required(identity)->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<char *>(idUTF8), strlen(idUTF8));
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(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
bool add = (!cursor->next(item));
item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
CFStringRef labelString = nil;
certificate->inferLabel(false, &labelString);
if (!labelString || !CFStringGetCString(labelString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
MacOSError::throwMe(errSecDataTooLarge);
CssmData account(const_cast<void *>(reinterpret_cast<const void *>(idUTF8)), strlen(idUTF8));
item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
CFRelease(labelString);
if (keyUsage)
item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
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);
}
keychain->add(item);
}
item->update();
END_SECAPI2("SecIdentitySetPreference")
}
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];
if (idString)
{
if (!CFStringGetCString(idString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
idUTF8[0] = (char)'\0';
CssmData service(const_cast<char *>(idUTF8), strlen(idUTF8));
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_SECAPI2("SecIdentityFindPreferenceItem")
}
OSStatus SecIdentityAddPreferenceItem(
SecKeychainRef keychainRef,
SecIdentityRef identityRef,
CFStringRef idString,
SecKeychainItemRef *itemRef)
{
BEGIN_SECAPI
if (!identityRef || !idString)
MacOSError::throwMe(paramErr);
SecPointer<Certificate> cert(Identity::required(identityRef)->certificate());
Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
char idUTF8[MAXPATHLEN];
if (!CFStringGetCString(idString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
MacOSError::throwMe(errSecDataTooLarge);
CssmData service(const_cast<void *>(reinterpret_cast<const void *>(idUTF8)), strlen(idUTF8));
item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
CFStringRef labelString = nil;
cert->inferLabel(false, &labelString);
if (!labelString || !CFStringGetCString(labelString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
MacOSError::throwMe(errSecDataTooLarge);
CssmData account(const_cast<void *>(reinterpret_cast<const void *>(idUTF8)), strlen(idUTF8));
item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
CFRelease(labelString);
CFDataRef pItemRef = nil;
OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert->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);
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);
}
keychain->add(item);
item->update();
if (itemRef)
*itemRef = item->handle();
END_SECAPI2("SecIdentityAddPreferenceItem")
}
OSStatus SecIdentityUpdatePreferenceItem(
SecKeychainItemRef itemRef,
SecIdentityRef identityRef)
{
BEGIN_SECAPI
if (!itemRef || !identityRef)
MacOSError::throwMe(paramErr);
SecPointer<Certificate> cert(Identity::required(identityRef)->certificate());
Item prefItem = ItemImpl::required(itemRef);
char idUTF8[MAXPATHLEN];
CFStringRef labelString = nil;
cert->inferLabel(false, &labelString);
if (!labelString || !CFStringGetCString(labelString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
MacOSError::throwMe(errSecDataTooLarge);
CssmData account(const_cast<void *>(reinterpret_cast<const void *>(idUTF8)), strlen(idUTF8));
prefItem->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
CFRelease(labelString);
CFDataRef pItemRef = nil;
OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert->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_SECAPI2("SecIdentityUpdatePreferenceItem")
}
OSStatus SecIdentityCopyFromPreferenceItem(
SecKeychainItemRef itemRef,
SecIdentityRef *identityRef)
{
BEGIN_SECAPI
if (!itemRef || !identityRef)
MacOSError::throwMe(paramErr);
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_SECAPI2("SecIdentityCopyFromPreferenceItem")
}
#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;
try {
identDict.reset(new Dictionary(IDENTITY_DOMAIN, Dictionary::US_System));
}
catch(...) {
MacOSError::throwMe(errSecNotAvailable);
}
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 = 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_SECAPI2("SecIdentityCopySystemIdentity")
}
OSStatus SecIdentitySetSystemIdentity(
CFStringRef domain,
SecIdentityRef idRef)
{
BEGIN_SECAPI
StLock<Mutex> _(systemIdentityLock());
if(geteuid() != 0) {
MacOSError::throwMe(errSecAuthFailed);
}
auto_ptr<MutableDictionary> identDict;
try {
identDict.reset(new MutableDictionary(IDENTITY_DOMAIN, Dictionary::US_System));
}
catch(...) {
if(idRef == NULL) {
return noErr;
}
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(ioErr);
}
END_SECAPI2("SecIdentitySetSystemIdentity")
}
const CFStringRef kSecIdentityDomainDefault = CFSTR("com.apple.systemdefault");
const CFStringRef kSecIdentityDomainKerberosKDC = CFSTR("com.apple.kerberos.kdc");