IdentityCursor.cpp [plain text]
#include <security_keychain/IdentityCursor.h>
#include <security_keychain/Identity.h>
#include <security_keychain/Trust.h>
#include <security_keychain/Item.h>
#include <security_keychain/Certificate.h>
#include <security_keychain/KeyItem.h>
#include <security_keychain/Globals.h>
#include <security_cdsa_utilities/Schema.h>
#include <security_cdsa_utilities/KeySchema.h>
#include <Security/oidsalg.h>
#include <Security/SecKeychainItemPriv.h>
#include <security_utilities/simpleprefs.h>
#include <sys/param.h>
using namespace KeychainCore;
IdentityCursorPolicyAndID::IdentityCursorPolicyAndID(const StorageManager::KeychainList &searchList, CSSM_KEYUSE keyUsage, CFStringRef idString, SecPolicyRef policy, bool returnOnlyValidIdentities) :
IdentityCursor(searchList, keyUsage),
mPolicy(policy),
mIDString(idString),
mReturnOnlyValidIdentities(returnOnlyValidIdentities),
mPreferredIdentityChecked(false),
mPreferredIdentity(nil)
{
if (mPolicy) {
CFRetain(mPolicy);
}
if (mIDString) {
CFRetain(mIDString);
}
}
IdentityCursorPolicyAndID::~IdentityCursorPolicyAndID() _NOEXCEPT
{
if (mPolicy) {
CFRelease(mPolicy);
}
if (mIDString) {
CFRelease(mIDString);
}
}
void
IdentityCursorPolicyAndID::findPreferredIdentity()
{
char idUTF8[MAXPATHLEN];
if (!mIDString || !CFStringGetCString(mIDString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
idUTF8[0] = (char)'\0';
uint32_t iprfValue = 'iprf'; SecKeychainAttribute sAttrs[] = {
{ kSecTypeItemAttr, sizeof(uint32_t), &iprfValue },
{ kSecServiceItemAttr, (UInt32)strlen(idUTF8), (char *)idUTF8 }
};
SecKeychainAttributeList sAttrList = { sizeof(sAttrs) / sizeof(sAttrs[0]), sAttrs };
Item item;
KCCursor cursor(mSearchList , kSecGenericPasswordItemClass, &sAttrList);
if (!cursor->next(item))
return;
SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
item->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);
if (pItemRef)
CFRelease(pItemRef);
item->freeContent(&itemAttrList, NULL);
if (status || !certItemRef)
return;
Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
SecPointer<Identity> identity(new Identity(mSearchList , certificate));
mPreferredIdentity = identity;
if (certItemRef)
CFRelease(certItemRef);
}
bool
IdentityCursorPolicyAndID::next(SecPointer<Identity> &identity)
{
SecPointer<Identity> currIdentity;
Boolean identityOK = true;
if (!mPreferredIdentityChecked)
{
try
{
findPreferredIdentity();
}
catch(...) {}
mPreferredIdentityChecked = true;
if (mPreferredIdentity)
{
identity = mPreferredIdentity;
return true;
}
}
for (;;)
{
bool result = IdentityCursor::next(currIdentity); if ( result )
{
if (mPreferredIdentity && (currIdentity == mPreferredIdentity))
{
identityOK = false; continue;
}
if ( !mPolicy )
{
identityOK = true; break;
}
SecPointer<Certificate> certificate = currIdentity->certificate();
CFRef<SecCertificateRef> certRef(certificate->handle());
CFRef<CFMutableArrayRef> anchorsArray(CFArrayCreateMutable(NULL, 1, NULL));
CFRef<CFMutableArrayRef> certArray(CFArrayCreateMutable(NULL, 1, NULL));
if ( !certArray || !anchorsArray )
{
identityOK = false; continue;
}
CFArrayAppendValue(certArray, certRef);
SecPointer<Trust> trustLite = new Trust(certArray, mPolicy);
StorageManager::KeychainList emptyList;
trustLite->anchors(anchorsArray);
trustLite->searchLibs(emptyList);
trustLite->evaluate();
SecTrustResultType trustResult = trustLite->result();
if (trustResult == kSecTrustResultRecoverableTrustFailure ||
trustResult == kSecTrustResultFatalTrustFailure)
{
CFArrayRef certChain = NULL;
CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL, *evInfo = NULL;
trustLite->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
if (statusChain)
evInfo = &statusChain[0];
if (!evInfo || evInfo->NumStatusCodes > 0) trustResult = kSecTrustResultInvalid; if (certChain)
CFRelease(certChain);
}
if (trustResult == kSecTrustResultInvalid)
{
identityOK = false; continue;
}
if ( !mReturnOnlyValidIdentities )
{
identityOK = true; break;
}
SecPointer<Trust> trust = new Trust(certArray, mPolicy);
trust->evaluate();
trustResult = trust->result();
if (trustResult == kSecTrustResultInvalid ||
trustResult == kSecTrustResultRecoverableTrustFailure ||
trustResult == kSecTrustResultFatalTrustFailure)
{
identityOK = false; continue;
}
identityOK = true; break;
}
else
{
identityOK = false; break;
}
}
if ( identityOK )
{
identity = currIdentity; return true;
}
else
{
return false;
}
}
IdentityCursor::IdentityCursor(const StorageManager::KeychainList &searchList, CSSM_KEYUSE keyUsage) :
mSearchList(searchList),
mKeyCursor(mSearchList, (SecItemClass) CSSM_DL_DB_RECORD_PRIVATE_KEY, NULL),
mMutex(Mutex::recursive)
{
StLock<Mutex>_(mMutex);
if (keyUsage & CSSM_KEYUSE_ANY)
keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT
| CSSM_KEYUSE_DERIVE | CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER
| CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP;
if (keyUsage & CSSM_KEYUSE_ENCRYPT)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Encrypt, true);
if (keyUsage & CSSM_KEYUSE_DECRYPT)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Decrypt, true);
if (keyUsage & CSSM_KEYUSE_DERIVE)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Derive, true);
if (keyUsage & CSSM_KEYUSE_SIGN)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Sign, true);
if (keyUsage & CSSM_KEYUSE_VERIFY)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Verify, true);
if (keyUsage & CSSM_KEYUSE_SIGN_RECOVER)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::SignRecover, true);
if (keyUsage & CSSM_KEYUSE_VERIFY_RECOVER)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::VerifyRecover, true);
if (keyUsage & CSSM_KEYUSE_WRAP)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Wrap, true);
if (keyUsage & CSSM_KEYUSE_UNWRAP)
mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Unwrap, true);
}
IdentityCursor::~IdentityCursor() _NOEXCEPT
{
}
CFDataRef CF_RETURNS_RETAINED
IdentityCursor::pubKeyHashForSystemIdentity(CFStringRef domain)
{
StLock<Mutex>_(mMutex);
CFDataRef entryValue = nil;
unique_ptr<Dictionary> identDict;
Dictionary* d = Dictionary::CreateDictionary("com.apple.security.systemidentities", Dictionary::US_System);
if (d)
{
identDict.reset(d);
entryValue = identDict->getDataValue(domain);
if (entryValue == nil) {
if(!CFEqual(domain, kSecIdentityDomainDefault)) {
entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
}
}
}
if (entryValue) {
CFRetain(entryValue);
}
return entryValue;
}
bool
IdentityCursor::next(SecPointer<Identity> &identity)
{
StLock<Mutex>_(mMutex);
for (;;)
{
if (!mCertificateCursor)
{
Item key;
if (!mKeyCursor->next(key))
return false;
mCurrentKey = static_cast<KeyItem *>(key.get());
CssmClient::DbUniqueRecord uniqueId = mCurrentKey->dbUniqueRecord();
CssmClient::DbAttributes dbAttributes(uniqueId->database(), 1);
dbAttributes.add(KeySchema::Label);
uniqueId->get(&dbAttributes, NULL);
const CssmData &keyHash = dbAttributes[0];
mCertificateCursor = KCCursor(mSearchList, (SecItemClass) CSSM_DL_DB_RECORD_X509_CERTIFICATE, NULL);
mCertificateCursor->add(CSSM_DB_EQUAL, Schema::kX509CertificatePublicKeyHash, keyHash);
CFDataRef systemDefaultCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainDefault);
if (systemDefaultCertPubKeyHash) {
CssmData pkHash((void *)CFDataGetBytePtr(systemDefaultCertPubKeyHash), CFDataGetLength(systemDefaultCertPubKeyHash));
mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
CFRelease(systemDefaultCertPubKeyHash);
}
CFDataRef kerbKDCCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainKerberosKDC);
if (kerbKDCCertPubKeyHash) {
CssmData pkHash((void *)CFDataGetBytePtr(kerbKDCCertPubKeyHash), CFDataGetLength(kerbKDCCertPubKeyHash));
mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
CFRelease(kerbKDCCertPubKeyHash);
}
}
Item cert;
if (mCertificateCursor->next(cert))
{
SecPointer<Certificate> certificate(static_cast<Certificate *>(cert.get()));
identity = new Identity(mCurrentKey, certificate);
return true;
}
else
mCertificateCursor = KCCursor();
}
}