diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
commit | 20c8675ba46bda97330a4117c459a59a9f1c465e (patch) | |
tree | d888c714fb61947dd79dc44b64a4aaae2f70bfb7 /contrib/keychain-mcd/cert_data.c | |
parent | ffca24bed7a03d95585ad02278667abe75d8b272 (diff) |
New upstream version 2.4~beta1upstream/2.4_beta1
Diffstat (limited to 'contrib/keychain-mcd/cert_data.c')
-rw-r--r-- | contrib/keychain-mcd/cert_data.c | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/contrib/keychain-mcd/cert_data.c b/contrib/keychain-mcd/cert_data.c new file mode 100644 index 0000000..a04bf79 --- /dev/null +++ b/contrib/keychain-mcd/cert_data.c @@ -0,0 +1,734 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2010 Brian Raderman <brian@irregularexpression.org> + * Copyright (C) 2013-2015 Vasily Kulikov <segoon@openwall.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "cert_data.h" +#include <CommonCrypto/CommonDigest.h> +#include <openssl/ssl.h> + +#include "common_osx.h" +#include "crypto_osx.h" +#include <err.h> + +CFStringRef kCertDataSubjectName = CFSTR("subject"), + kCertDataIssuerName = CFSTR("issuer"), + kCertDataSha1Name = CFSTR("SHA1"), + kCertDataMd5Name = CFSTR("MD5"), + kCertDataSerialName = CFSTR("serial"), + kCertNameFwdSlash = CFSTR("/"), + kCertNameEquals = CFSTR("="); +CFStringRef kCertNameOrganization = CFSTR("o"), + kCertNameOrganizationalUnit = CFSTR("ou"), + kCertNameCountry = CFSTR("c"), + kCertNameLocality = CFSTR("l"), + kCertNameState = CFSTR("st"), + kCertNameCommonName = CFSTR("cn"), + kCertNameEmail = CFSTR("e"); +CFStringRef kStringSpace = CFSTR(" "), + kStringEmpty = CFSTR(""); + +typedef struct _CertName +{ + CFArrayRef countryName, organization, organizationalUnit, commonName, description, emailAddress, + stateName, localityName; +} CertName, *CertNameRef; + +typedef struct _DescData +{ + CFStringRef name, value; +} DescData, *DescDataRef; + +void destroyDescData(DescDataRef pData); + +CertNameRef createCertName() +{ + CertNameRef pCertName = (CertNameRef)malloc(sizeof(CertName)); + pCertName->countryName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pCertName->organization = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pCertName->organizationalUnit = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pCertName->commonName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pCertName->description = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pCertName->emailAddress = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pCertName->stateName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pCertName->localityName = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + return pCertName; +} + +void destroyCertName(CertNameRef pCertName) +{ + if (!pCertName) + return; + + CFRelease(pCertName->countryName); + CFRelease(pCertName->organization); + CFRelease(pCertName->organizationalUnit); + CFRelease(pCertName->commonName); + CFRelease(pCertName->description); + CFRelease(pCertName->emailAddress); + CFRelease(pCertName->stateName); + CFRelease(pCertName->localityName); + free(pCertName); +} + +bool CFStringRefCmpCString(CFStringRef cfstr, const char *str) +{ + CFStringRef tmp = CFStringCreateWithCStringNoCopy(NULL, str, kCFStringEncodingUTF8, kCFAllocatorNull); + CFComparisonResult cresult = CFStringCompare(cfstr, tmp, 0); + bool result = cresult == kCFCompareEqualTo; + CFRelease(tmp); + return result; +} + +CFDateRef GetDateFieldFromCertificate(SecCertificateRef certificate, CFTypeRef oid) +{ + const void *keys[] = { oid }; + CFDictionaryRef dict = NULL; + CFErrorRef error; + CFDateRef date = NULL; + + CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks); + dict = SecCertificateCopyValues(certificate, keySelection, &error); + if (dict == NULL) + { + printErrorMsg("GetDateFieldFromCertificate: SecCertificateCopyValues", error); + goto release_ks; + } + CFDictionaryRef vals = dict ? CFDictionaryGetValue(dict, oid) : NULL; + CFNumberRef vals2 = vals ? CFDictionaryGetValue(vals, kSecPropertyKeyValue) : NULL; + if (vals2 == NULL) + goto release_dict; + + CFAbsoluteTime validityNotBefore; + if (CFNumberGetValue(vals2, kCFNumberDoubleType, &validityNotBefore)) + date = CFDateCreate(kCFAllocatorDefault,validityNotBefore); + +release_dict: + CFRelease(dict); +release_ks: + CFRelease(keySelection); + return date; +} + +CFArrayRef GetFieldsFromCertificate(SecCertificateRef certificate, CFTypeRef oid) +{ + CFMutableArrayRef fields = CFArrayCreateMutable(NULL, 0, NULL); + CertNameRef pCertName = createCertName(); + const void* keys[] = { oid, }; + CFDictionaryRef dict; + CFErrorRef error; + + CFArrayRef keySelection = CFArrayCreate(NULL, keys , 1, NULL); + + dict = SecCertificateCopyValues(certificate, keySelection, &error); + if (dict == NULL) { + printErrorMsg("GetFieldsFromCertificate: SecCertificateCopyValues", error); + CFRelease(keySelection); + CFRelease(fields); + destroyCertName(pCertName); + return NULL; + } + CFDictionaryRef vals = CFDictionaryGetValue(dict, oid); + CFArrayRef vals2 = vals ? CFDictionaryGetValue(vals, kSecPropertyKeyValue) : NULL; + if (vals2) + { + for(int i = 0; i < CFArrayGetCount(vals2); i++) { + CFDictionaryRef subDict = CFArrayGetValueAtIndex(vals2, i); + CFStringRef label = CFDictionaryGetValue(subDict, kSecPropertyKeyLabel); + CFStringRef value = CFDictionaryGetValue(subDict, kSecPropertyKeyValue); + + if (CFStringCompare(label, kSecOIDEmailAddress, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->emailAddress, value); + else if (CFStringCompare(label, kSecOIDCountryName, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->countryName, value); + else if (CFStringCompare(label, kSecOIDOrganizationName, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->organization, value); + else if (CFStringCompare(label, kSecOIDOrganizationalUnitName, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->organizationalUnit, value); + else if (CFStringCompare(label, kSecOIDCommonName, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->commonName, value); + else if (CFStringCompare(label, kSecOIDDescription, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->description, value); + else if (CFStringCompare(label, kSecOIDStateProvinceName, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->stateName, value); + else if (CFStringCompare(label, kSecOIDLocalityName, 0) == kCFCompareEqualTo) + CFArrayAppendValue((CFMutableArrayRef)pCertName->localityName, value); + } + CFArrayAppendValue(fields, pCertName); + } + + CFRelease(dict); + CFRelease(keySelection); + return fields; +} + +CertDataRef createCertDataFromCertificate(SecCertificateRef certificate) +{ + CertDataRef pCertData = (CertDataRef)malloc(sizeof(CertData)); + pCertData->subject = GetFieldsFromCertificate(certificate, kSecOIDX509V1SubjectName); + pCertData->issuer = GetFieldsFromCertificate(certificate, kSecOIDX509V1IssuerName); + + CFDataRef data = SecCertificateCopyData(certificate); + if (data == NULL) + { + warnx("SecCertificateCopyData() returned NULL"); + destroyCertData(pCertData); + return NULL; + } + + unsigned char sha1[CC_SHA1_DIGEST_LENGTH]; + CC_SHA1(CFDataGetBytePtr(data), CFDataGetLength(data), sha1); + pCertData->sha1 = createHexString(sha1, CC_SHA1_DIGEST_LENGTH); + + unsigned char md5[CC_MD5_DIGEST_LENGTH]; + CC_MD5(CFDataGetBytePtr(data), CFDataGetLength(data), md5); + pCertData->md5 = createHexString((unsigned char*)md5, CC_MD5_DIGEST_LENGTH); + + CFDataRef serial = SecCertificateCopySerialNumber(certificate, NULL); + pCertData->serial = createHexString((unsigned char *)CFDataGetBytePtr(serial), CFDataGetLength(serial)); + CFRelease(serial); + + return pCertData; +} + +CFStringRef stringFromRange(const char *cstring, CFRange range) +{ + CFStringRef str = CFStringCreateWithBytes (NULL, (uint8*)&cstring[range.location], range.length, kCFStringEncodingUTF8, false); + CFMutableStringRef mutableStr = CFStringCreateMutableCopy(NULL, 0, str); + CFStringTrimWhitespace(mutableStr); + CFRelease(str); + return mutableStr; +} + +DescDataRef createDescData(const char *description, CFRange nameRange, CFRange valueRange) +{ + DescDataRef pRetVal = (DescDataRef)malloc(sizeof(DescData)); + + memset(pRetVal, 0, sizeof(DescData)); + + if (nameRange.length > 0) + pRetVal->name = stringFromRange(description, nameRange); + + if (valueRange.length > 0) + pRetVal->value = stringFromRange(description, valueRange); + +#if 0 + fprintf(stderr, "name = '%s', value = '%s'\n", + CFStringGetCStringPtr(pRetVal->name, kCFStringEncodingUTF8), + CFStringGetCStringPtr(pRetVal->value, kCFStringEncodingUTF8)); +#endif + return pRetVal; +} + +void destroyDescData(DescDataRef pData) +{ + if (pData->name) + CFRelease(pData->name); + + if (pData->value) + CFRelease(pData->value); + + free(pData); +} + +CFArrayRef createDescDataPairs(const char *description) +{ + int numChars = strlen(description); + CFRange nameRange, valueRange; + DescDataRef pData; + CFMutableArrayRef retVal = CFArrayCreateMutable(NULL, 0, NULL); + + int i = 0; + + nameRange = CFRangeMake(0, 0); + valueRange = CFRangeMake(0, 0); + bool bInValue = false; + + while(i < numChars) + { + if (!bInValue && (description[i] != ':')) + { + nameRange.length++; + } + else if (bInValue && (description[i] != ':')) + { + valueRange.length++; + } + else if(!bInValue) + { + bInValue = true; + valueRange.location = i + 1; + valueRange.length = 0; + } + else //(bInValue) + { + bInValue = false; + while(description[i] != ' ') + { + valueRange.length--; + i--; + } + + pData = createDescData(description, nameRange, valueRange); + CFArrayAppendValue(retVal, pData); + + nameRange.location = i + 1; + nameRange.length = 0; + } + + i++; + } + + pData = createDescData(description, nameRange, valueRange); + CFArrayAppendValue(retVal, pData); + return retVal; +} + +void arrayDestroyDescData(const void *val, void *context) +{ + DescDataRef pData = (DescDataRef) val; + destroyDescData(pData); +} + + +int parseNameComponent(CFStringRef dn, CFStringRef *pName, CFStringRef *pValue) +{ + CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, dn, kCertNameEquals); + + *pName = *pValue = NULL; + + if (CFArrayGetCount(nameStrings) != 2) + return 0; + + CFMutableStringRef str; + + str = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, 0)); + CFStringTrimWhitespace(str); + *pName = str; + + str = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, 1)); + CFStringTrimWhitespace(str); + *pValue = str; + + CFRelease(nameStrings); + return 1; +} + +int tryAppendSingleCertField(CertNameRef pCertName, CFArrayRef where, CFStringRef key, + CFStringRef name, CFStringRef value) +{ + if (CFStringCompareWithOptions(name, key, CFRangeMake(0, CFStringGetLength(name)), kCFCompareCaseInsensitive) + == kCFCompareEqualTo) { + CFArrayAppendValue((CFMutableArrayRef)where, value); + return 1; + } + return 0; +} + +int appendCertField(CertNameRef pCert, CFStringRef name, CFStringRef value) +{ + struct { + CFArrayRef field; + CFStringRef key; + } fields[] = { + { pCert->organization, kCertNameOrganization}, + { pCert->organizationalUnit, kCertNameOrganizationalUnit}, + { pCert->countryName, kCertNameCountry}, + { pCert->localityName, kCertNameLocality}, + { pCert->stateName, kCertNameState}, + { pCert->commonName, kCertNameCommonName}, + { pCert->emailAddress, kCertNameEmail}, + }; + int i; + int ret = 0; + + for (i=0; i<sizeof(fields)/sizeof(fields[0]); i++) + ret += tryAppendSingleCertField(pCert, fields[i].field, fields[i].key, name, value); + return ret; +} + +int parseCertName(CFStringRef nameDesc, CFMutableArrayRef names) +{ + CFArrayRef nameStrings = CFStringCreateArrayBySeparatingStrings(NULL, nameDesc, kCertNameFwdSlash); + int count = CFArrayGetCount(nameStrings); + int i; + int ret = 1; + + CertNameRef pCertName = createCertName(); + + for(i = 0;i < count;i++) + { + CFMutableStringRef dn = CFStringCreateMutableCopy(NULL, 0, CFArrayGetValueAtIndex(nameStrings, i)); + CFStringTrimWhitespace(dn); + + CFStringRef name, value; + + if (!parseNameComponent(dn, &name, &value)) + ret = 0; + + if (!name || !value) + { + if (name) + CFRelease(name); + + if (value) + CFRelease(value); + if (name && !value) + ret = 0; + + CFRelease(dn); + continue; + } + + if (!appendCertField(pCertName, name, value)) + ret = 0; + CFRelease(name); + CFRelease(value); + CFRelease(dn); + } + + CFArrayAppendValue(names, pCertName); + CFRelease(nameStrings); + return ret; +} + +int arrayParseDescDataPair(const void *val, void *context) +{ + DescDataRef pDescData = (DescDataRef)val; + CertDataRef pCertData = (CertDataRef)context; + int ret = 1; + + if (!pDescData->name || !pDescData->value) + return 0; + + if (CFStringCompareWithOptions(pDescData->name, kCertDataSubjectName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + ret = parseCertName(pDescData->value, (CFMutableArrayRef)pCertData->subject); + else if (CFStringCompareWithOptions(pDescData->name, kCertDataIssuerName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + ret = parseCertName(pDescData->value, (CFMutableArrayRef)pCertData->issuer); + else if (CFStringCompareWithOptions(pDescData->name, kCertDataSha1Name, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + pCertData->sha1 = CFRetain(pDescData->value); + else if (CFStringCompareWithOptions(pDescData->name, kCertDataMd5Name, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + pCertData->md5 = CFRetain(pDescData->value); + else if (CFStringCompareWithOptions(pDescData->name, kCertDataSerialName, CFRangeMake(0, CFStringGetLength(pDescData->name)), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + pCertData->serial = CFRetain(pDescData->value); + else + return 0; + + return ret; +} + +CertDataRef createCertDataFromString(const char *description) +{ + CertDataRef pCertData = (CertDataRef)malloc(sizeof(CertData)); + pCertData->subject = CFArrayCreateMutable(NULL, 0, NULL); + pCertData->issuer = CFArrayCreateMutable(NULL, 0, NULL); + pCertData->sha1 = NULL; + pCertData->md5 = NULL; + pCertData->serial = NULL; + + CFArrayRef pairs = createDescDataPairs(description); + for (int i=0; i<CFArrayGetCount(pairs); i++) + if (!arrayParseDescDataPair(CFArrayGetValueAtIndex(pairs, i), pCertData)) { + arrayDestroyDescData(pCertData, NULL); + CFArrayApplyFunction(pairs, CFRangeMake(0, CFArrayGetCount(pairs)), arrayDestroyDescData, NULL); + CFRelease(pairs); + return 0; + } + + CFArrayApplyFunction(pairs, CFRangeMake(0, CFArrayGetCount(pairs)), arrayDestroyDescData, NULL); + CFRelease(pairs); + return pCertData; +} + +void arrayDestroyCertName(const void *val, void *context) +{ + CertNameRef pCertName = (CertNameRef)val; + destroyCertName(pCertName); +} + +void destroyCertData(CertDataRef pCertData) +{ + if (pCertData->subject) + { + CFArrayApplyFunction(pCertData->subject, CFRangeMake(0, CFArrayGetCount(pCertData->subject)), arrayDestroyCertName, NULL); + CFRelease(pCertData->subject); + } + + if (pCertData->issuer) + { + CFArrayApplyFunction(pCertData->issuer, CFRangeMake(0, CFArrayGetCount(pCertData->issuer)), arrayDestroyCertName, NULL); + CFRelease(pCertData->issuer); + } + + if (pCertData->sha1) + CFRelease(pCertData->sha1); + + if (pCertData->md5) + CFRelease(pCertData->md5); + + if (pCertData->serial) + CFRelease(pCertData->serial); + + free(pCertData); +} + +bool stringArrayMatchesTemplate(CFArrayRef strings, CFArrayRef templateArray) +{ + int templateCount, stringCount, i; + + templateCount = CFArrayGetCount(templateArray); + + if (templateCount > 0) + { + stringCount = CFArrayGetCount(strings); + if (stringCount != templateCount) + return false; + + for(i = 0;i < stringCount;i++) + { + CFStringRef str, template; + + template = (CFStringRef)CFArrayGetValueAtIndex(templateArray, i); + str = (CFStringRef)CFArrayGetValueAtIndex(strings, i); + + if (CFStringCompareWithOptions(template, str, CFRangeMake(0, CFStringGetLength(template)), kCFCompareCaseInsensitive) != kCFCompareEqualTo) + return false; + } + } + + return true; + +} + +bool certNameMatchesTemplate(CertNameRef pCertName, CertNameRef pTemplate) +{ + if (!stringArrayMatchesTemplate(pCertName->countryName, pTemplate->countryName)) + return false; + else if (!stringArrayMatchesTemplate(pCertName->organization, pTemplate->organization)) + return false; + else if (!stringArrayMatchesTemplate(pCertName->organizationalUnit, pTemplate->organizationalUnit)) + return false; + else if (!stringArrayMatchesTemplate(pCertName->commonName, pTemplate->commonName)) + return false; + else if (!stringArrayMatchesTemplate(pCertName->emailAddress, pTemplate->emailAddress)) + return false; + else if (!stringArrayMatchesTemplate(pCertName->stateName, pTemplate->stateName)) + return false; + else if (!stringArrayMatchesTemplate(pCertName->localityName, pTemplate->localityName)) + return false; + else + return true; +} + +bool certNameArrayMatchesTemplate(CFArrayRef certNameArray, CFArrayRef templateArray) +{ + int templateCount, certCount, i; + + templateCount = CFArrayGetCount(templateArray); + + if (templateCount > 0) + { + certCount = CFArrayGetCount(certNameArray); + if (certCount != templateCount) + return false; + + for(i = 0;i < certCount;i++) + { + CertNameRef pName, pTemplateName; + + pTemplateName = (CertNameRef)CFArrayGetValueAtIndex(templateArray, i); + pName = (CertNameRef)CFArrayGetValueAtIndex(certNameArray, i); + + if (!certNameMatchesTemplate(pName, pTemplateName)) + return false; + } + } + + return true; +} + +bool hexStringMatchesTemplate(CFStringRef str, CFStringRef template) +{ + if (template) + { + if (!str) + return false; + + CFMutableStringRef strMutable, templateMutable; + + strMutable = CFStringCreateMutableCopy(NULL, 0, str); + templateMutable = CFStringCreateMutableCopy(NULL, 0, template); + + CFStringFindAndReplace(strMutable, kStringSpace, kStringEmpty, CFRangeMake(0, CFStringGetLength(strMutable)), 0); + CFStringFindAndReplace(templateMutable, kStringSpace, kStringEmpty, CFRangeMake(0, CFStringGetLength(templateMutable)), 0); + + CFComparisonResult result = CFStringCompareWithOptions(templateMutable, strMutable, CFRangeMake(0, CFStringGetLength(templateMutable)), kCFCompareCaseInsensitive); + + CFRelease(strMutable); + CFRelease(templateMutable); + + if (result != kCFCompareEqualTo) + return false; + } + + return true; +} + +bool certDataMatchesTemplate(CertDataRef pCertData, CertDataRef pTemplate) +{ + if (!certNameArrayMatchesTemplate(pCertData->subject, pTemplate->subject)) + return false; + + if (!certNameArrayMatchesTemplate(pCertData->issuer, pTemplate->issuer)) + return false; + + if (!hexStringMatchesTemplate(pCertData->sha1, pTemplate->sha1)) + return false; + + if (!hexStringMatchesTemplate(pCertData->md5, pTemplate->md5)) + return false; + + if (!hexStringMatchesTemplate(pCertData->serial, pTemplate->serial)) + return false; + + return true; +} + +bool certExpired(SecCertificateRef certificate) +{ + bool result; + CFDateRef notAfter = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotAfter); + CFDateRef notBefore = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotBefore); + CFDateRef now = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()); + + if (!notAfter || !notBefore || !now) + { + warnx("GetDateFieldFromCertificate() returned NULL"); + result = true; + } + else + { + if (CFDateCompare(notBefore, now, NULL) != kCFCompareLessThan || + CFDateCompare(now, notAfter, NULL) != kCFCompareLessThan) + result = true; + else + result = false; + } + + CFRelease(notAfter); + CFRelease(notBefore); + CFRelease(now); + return result; +} + +SecIdentityRef findIdentity(CertDataRef pCertDataTemplate) +{ + const void *keys[] = { + kSecClass, + kSecReturnRef, + kSecMatchLimit + }; + const void *values[] = { + kSecClassIdentity, + kCFBooleanTrue, + kSecMatchLimitAll + }; + CFArrayRef result = NULL; + + CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + OSStatus status = SecItemCopyMatching(query, (CFTypeRef*)&result); + CFRelease(query); + if (status != noErr) + { + warnx ("No identities in keychain found"); + return NULL; + } + + SecIdentityRef bestIdentity = NULL; + CFDateRef bestNotBeforeDate = NULL; + + for (int i=0; i<CFArrayGetCount(result); i++) + { + SecIdentityRef identity = (SecIdentityRef)CFArrayGetValueAtIndex(result, i); + if (identity == NULL) + { + warnx ("identity == NULL"); + continue; + } + + SecCertificateRef certificate = NULL; + SecIdentityCopyCertificate (identity, &certificate); + if (certificate == NULL) + { + warnx ("SecIdentityCopyCertificate() returned NULL"); + continue; + } + + CertDataRef pCertData2 = createCertDataFromCertificate(certificate); + if (pCertData2 == NULL) + { + warnx ("createCertDataFromCertificate() returned NULL"); + goto release_cert; + } + bool bMatches = certDataMatchesTemplate(pCertData2, pCertDataTemplate); + bool bExpired = certExpired(certificate); + destroyCertData(pCertData2); + + if (bMatches && !bExpired) + { + CFDateRef notBeforeDate = GetDateFieldFromCertificate(certificate, kSecOIDX509V1ValidityNotBefore); + if (!notBeforeDate) + { + warnx ("GetDateFieldFromCertificate() returned NULL"); + goto release_cert; + } + if (bestIdentity == NULL) + { + CFRetain(identity); + bestIdentity = identity; + + bestNotBeforeDate = notBeforeDate; + CFRetain(notBeforeDate); + } + else if (CFDateCompare(bestNotBeforeDate, notBeforeDate, NULL) == kCFCompareLessThan) + { + CFRelease(bestIdentity); + CFRetain(identity); + bestIdentity = identity; + + bestNotBeforeDate = notBeforeDate; + CFRetain(notBeforeDate); + } + CFRelease(notBeforeDate); + } + release_cert: + CFRelease(certificate); + } + CFRelease(result); + + return bestIdentity; +} |