#include "config.h"
#include <Security/SecCertificate.h>
#include <Security/SecPolicy.h>
#include <Security/SecTrust.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecIdentity.h>
#include <Security/SecItem.h>
#include <TargetConditionals.h>
#include <Security/SecItemPriv.h>
#ifndef HAVE_OPENSSL
#include <Security/SecTrustPriv.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecCertificatePriv.h>
#else
#include <Security/SecBase.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecCertificateOIDs.h>
#include <Security/SecKeyPriv.h>
#include <Security/oidsalg.h>
#include <Security/cssmapi.h>
#include <Security/SecPolicySearch.h>
#endif
#include <CoreFoundation/CoreFoundation.h>
#if !TARGET_OS_EMBEDDED
#include <Security/SecIdentitySearch.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecKeychainItemPriv.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#endif
#include "plog.h"
#include "debug.h"
#include "misc.h"
#include "oakley.h"
#include "gcmalloc.h"
#include "crypto_cssm.h"
static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef, SecKeyRef *publicKeyRef);
static SecPolicyRef
crypto_cssm_x509cert_get_SecPolicyRef (CFStringRef hostname)
{
SecPolicyRef policyRef = NULL;
CFDictionaryRef properties = NULL;
const void *key[] = { kSecPolicyName };
const void *value[] = { hostname };
if (hostname) {
properties = CFDictionaryCreate(NULL, key, value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (properties == NULL) {
plog(ASL_LEVEL_ERR,
"unable to create dictionary for policy properties.\n");
}
}
policyRef = SecPolicyCreateWithProperties(kSecPolicyAppleIPsec, properties);
if (properties)
CFRelease(properties);
return policyRef;
}
SecCertificateRef
crypto_cssm_x509cert_CreateSecCertificateRef (vchar_t *cert)
{
SecCertificateRef certRef = NULL;
CFDataRef cert_data = CFDataCreateWithBytesNoCopy(NULL, (uint8_t*)cert->v, cert->l, kCFAllocatorNull);
if (cert_data) {
certRef = SecCertificateCreateWithData(NULL, cert_data);
CFRelease(cert_data);
}
if (certRef == NULL) {
plog(ASL_LEVEL_ERR,
"unable to get a certifcate reference.\n");
}
return certRef;
}
extern CFDataRef SecCertificateCopySubjectSequence( SecCertificateRef certificate);
CFDataRef
crypto_cssm_CopySubjectSequence(SecCertificateRef certRef)
{
CFDataRef subject = NULL;
subject = SecCertificateCopySubjectSequence(certRef);
return subject;
}
cert_status_t
crypto_cssm_check_x509cert_dates (SecCertificateRef certificateRef)
{
cert_status_t certStatus = CERT_STATUS_OK;
#ifndef HAVE_OPENSSL
CFAbsoluteTime timeNow = 0;
CFAbsoluteTime notvalidbeforedate = 0;
CFAbsoluteTime notvalidafterdate = 0;
CFDateRef nowcfdatedata = NULL;
CFDateRef notvalidbeforedatedata = NULL;
CFDateRef notvalidafterdatedata = NULL;
CFArrayRef certProparray = NULL;
if ((timeNow = CFAbsoluteTimeGetCurrent())) {
if (SecCertificateIsValid(certificateRef, timeNow)) {
plog(ASL_LEVEL_NOTICE, "Certificate expiration date is OK\n");
certStatus = CERT_STATUS_OK;
} else {
nowcfdatedata = CFDateCreate( NULL, timeNow);
if ((notvalidbeforedate = SecCertificateNotValidBefore(certificateRef))) {
notvalidbeforedatedata = CFDateCreate(NULL, notvalidbeforedate);
}
if ((notvalidafterdate = SecCertificateNotValidAfter(certificateRef))) {
notvalidafterdatedata = CFDateCreate(NULL, notvalidafterdate);
}
int year = 0;
int month = 0;
int day = 0;
int hour = 0;
int minute = 0;
CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorDefault, kCFGregorianCalendar);
if (calendar)
{
if (notvalidbeforedate) {
CFCalendarDecomposeAbsoluteTime(calendar, notvalidbeforedate, "yMdHm", &year, &month, &day, &hour, &minute);
plog(ASL_LEVEL_DEBUG, "Certificate not valid before yr %d, mon %d, days %d, hours %d, min %d\n", year, month, day, hour, minute);
}
if (notvalidafterdate) {
CFCalendarDecomposeAbsoluteTime(calendar, notvalidafterdate, "yMdHm", &year, &month, &day, &hour, &minute);
plog(ASL_LEVEL_DEBUG, "Certificate not valid after yr %d, mon %d, days %d, hours %d, min %d\n", year, month, day, hour, minute);
}
CFRelease(calendar);
}
if (nowcfdatedata != NULL) {
if (notvalidbeforedatedata && CFDateCompare(nowcfdatedata, notvalidbeforedatedata, NULL) == kCFCompareLessThan){
plog(ASL_LEVEL_ERR,
"current time before valid time\n");
certStatus = CERT_STATUS_PREMATURE;
} else if (notvalidafterdatedata && (CFDateCompare( nowcfdatedata, notvalidafterdatedata, NULL ) == kCFCompareGreaterThan)){
plog(ASL_LEVEL_ERR,
"current time after valid time\n");
certStatus = CERT_STATUS_EXPIRED;
}
}
}
} else {
plog(ASL_LEVEL_ERR, "CFAbsoluteTimeGetCurrent() failed");
certStatus = CERT_STATUS_INVALID;
}
if (notvalidbeforedatedata)
CFRelease(notvalidbeforedatedata);
if (notvalidafterdatedata)
CFRelease(notvalidafterdatedata);
if (certProparray)
CFRelease(certProparray);
if (nowcfdatedata)
CFRelease(nowcfdatedata);
#endif
return certStatus;
}
int crypto_cssm_check_x509cert (cert_t *hostcert, cert_t *certchain, CFStringRef hostname, SecKeyRef *publicKeyRef)
{
cert_t *p;
cert_status_t certStatus = 0;
OSStatus status;
CFIndex certArrayRefNumValues = 0;
CFIndex n = 0;
int certArraySiz;
SecCertificateRef *certArrayRef = NULL;
SecPolicyRef policyRef = crypto_cssm_x509cert_get_SecPolicyRef(hostname);
if (!hostcert || !certchain) {
return -1;
}
for (p = certchain; p; p = p->chain, n++);
if (n> 1) {
plog(ASL_LEVEL_DEBUG,
"%s: checking chain of %d certificates.\n", __FUNCTION__, (int)n);
}
certArraySiz = n * sizeof(CFTypeRef);
certArrayRef = CFAllocatorAllocate(NULL, certArraySiz, 0);
if (!certArrayRef) {
return -1;
}
bzero(certArrayRef, certArraySiz);
if ((certArrayRef[certArrayRefNumValues] = crypto_cssm_x509cert_CreateSecCertificateRef(&hostcert->cert))) {
if (!hostcert->status) {
hostcert->status = crypto_cssm_check_x509cert_dates(certArrayRef[certArrayRefNumValues]);
if (hostcert->status) {
plog(ASL_LEVEL_ERR,
"host certificate failed date verification: %d.\n", hostcert->status);
certStatus = hostcert->status;
}
}
certArrayRefNumValues++;
}
for (p = certchain; p && certArrayRefNumValues < n; p = p->chain) {
if (p != hostcert) {
if ((certArrayRef[certArrayRefNumValues] = crypto_cssm_x509cert_CreateSecCertificateRef(&p->cert))) {
if (!p->status) {
p->status = crypto_cssm_check_x509cert_dates(certArrayRef[certArrayRefNumValues]);
if (p->status) {
plog(ASL_LEVEL_ERR,
"other certificate in chain failed date verification: %d.\n", p->status);
if (!certStatus) {
certStatus = p->status;
}
}
}
certArrayRefNumValues++;
}
}
}
status = EvaluateCert(certArrayRef, certArrayRefNumValues, policyRef, publicKeyRef);
while (certArrayRefNumValues) {
CFRelease(certArrayRef[--certArrayRefNumValues]);
}
CFAllocatorDeallocate(NULL, certArrayRef);
if (policyRef)
CFRelease(policyRef);
if (status != noErr && status != -1) {
plog(ASL_LEVEL_ERR,
"check_x509cert error %d %s.\n", (int)status, GetSecurityErrorString(status));
status = -1;
} else if (certStatus == CERT_STATUS_PREMATURE || certStatus == CERT_STATUS_EXPIRED) {
status = -1;
}
return status;
}
int crypto_cssm_verify_x509sign(SecKeyRef publicKeyRef, vchar_t *hash, vchar_t *signature, Boolean useSHA1)
{
return SecKeyRawVerify(publicKeyRef, useSHA1 ? kSecPaddingPKCS1SHA1 : kSecPaddingPKCS1, (uint8_t*)hash->v, hash->l, (uint8_t*)signature->v, signature->l);
}
vchar_t* crypto_cssm_getsign(CFDataRef persistentCertRef, vchar_t* hash)
{
OSStatus status = -1;
SecIdentityRef identityRef = NULL;
SecKeyRef privateKeyRef = NULL;
vchar_t *sig = NULL;
CFDictionaryRef persistFind = NULL;
const void *keys_persist[] = { kSecReturnRef, kSecValuePersistentRef, kSecClass,
#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
kSecUseSystemKeychain,
#endif
};
const void *values_persist[] = { kCFBooleanTrue, persistentCertRef, kSecClassIdentity,
#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
kCFBooleanTrue,
#endif
};
#define SIG_BUF_SIZE 1024
persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist,
(sizeof(keys_persist) / sizeof(*keys_persist)), NULL, NULL);
if (persistFind == NULL)
goto end;
status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef);
if (status != noErr)
goto end;
status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef);
if (status != noErr)
goto end;
sig = vmalloc(SIG_BUF_SIZE);
if (sig == NULL)
goto end;
status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1, (uint8_t*)hash->v,
hash->l, (uint8_t*)sig->v, &sig->l);
end:
if (identityRef)
CFRelease(identityRef);
if (privateKeyRef)
CFRelease(privateKeyRef);
if (persistFind)
CFRelease(persistFind);
if (status != noErr) {
if (sig) {
vfree(sig);
sig = NULL;
}
}
if (status != noErr && status != -1) {
plog(ASL_LEVEL_ERR,
"getsign error %d %s.\n", (int)status, GetSecurityErrorString(status));
status = -1;
}
return sig;
}
vchar_t* crypto_cssm_get_x509cert(CFDataRef persistentCertRef,
cert_status_t *certStatus)
{
OSStatus status = -1;
vchar_t *cert = NULL;
SecCertificateRef certificateRef = NULL;
CFDictionaryRef persistFind = NULL;
size_t dataLen;
CFDataRef certData = NULL;
SecIdentityRef identityRef = NULL;
const void *keys_persist[] = { kSecReturnRef, kSecValuePersistentRef, kSecClass,
#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
kSecUseSystemKeychain,
#endif
};
const void *values_persist[] = { kCFBooleanTrue, persistentCertRef, kSecClassIdentity,
#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
kCFBooleanTrue,
#endif
};
persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist,
(sizeof(keys_persist) / sizeof(*keys_persist)), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (persistFind == NULL)
goto end;
status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef);
if (status != noErr)
goto end;
status = SecIdentityCopyCertificate(identityRef, &certificateRef);
if (status != noErr)
goto end;
certData = SecCertificateCopyData(certificateRef);
if (certData == NULL)
goto end;
dataLen = CFDataGetLength(certData);
if (dataLen == 0)
goto end;
cert = vmalloc(dataLen);
if (cert == NULL)
goto end;
CFDataGetBytes(certData, CFRangeMake(0, dataLen), (uint8_t*)cert->v);
if (certStatus) {
*certStatus = crypto_cssm_check_x509cert_dates(certificateRef);
}
end:
if (identityRef)
CFRelease(identityRef);
if (certificateRef)
CFRelease(certificateRef);
if (persistFind)
CFRelease(persistFind);
if (certData)
CFRelease(certData);
if (status != noErr && status != -1) {
plog(ASL_LEVEL_ERR,
"get_x509cert error %d %s.\n", (int)status, GetSecurityErrorString(status));
status = -1;
}
return cert;
}
static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef, SecKeyRef *publicKeyRef)
{
OSStatus status;
SecTrustRef trustRef = 0;
SecTrustResultType evalResult;
CFArrayRef errorStrings;
CFArrayRef cfCertRef = CFArrayCreate((CFAllocatorRef) NULL, (void*)evalCertArray, evalCertArrayNumValues,
&kCFTypeArrayCallBacks);
if (!cfCertRef) {
plog(ASL_LEVEL_ERR,
"unable to create CFArray.\n");
return -1;
}
status = SecTrustCreateWithCertificates(cfCertRef, policyRef, &trustRef);
if (status != noErr)
goto end;
status = SecTrustEvaluate(trustRef, &evalResult);
if (status != noErr)
goto end;
if (evalResult != kSecTrustResultProceed && evalResult != kSecTrustResultUnspecified) {
plog(ASL_LEVEL_ERR, "Error evaluating certificate.\n");
switch (evalResult) {
case kSecTrustResultInvalid:
plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultInvalid.\n");
break;
case kSecTrustResultProceed:
plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultProceed.\n");
break;
case kSecTrustResultDeny:
plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultDeny.\n");
break;
case kSecTrustResultUnspecified:
plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultUnspecified.\n");
break;
case kSecTrustResultRecoverableTrustFailure:
plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultRecoverableTrustFailure.\n");
break;
case kSecTrustResultFatalTrustFailure:
plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultFatalTrustFailure.\n");
break;
case kSecTrustResultOtherError:
plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultOtherError.\n");
break;
default:
plog(ASL_LEVEL_DEBUG, "eval result unknown: value = %d.\n", (int)evalResult);
break;
}
errorStrings = SecTrustCopyProperties(trustRef);
if (errorStrings) {
CFDictionaryRef dict;
CFStringRef val;
const char *str;
CFIndex count, maxcount = CFArrayGetCount(errorStrings);
plog(ASL_LEVEL_ERR, "---------------Returned error strings: ---------------.\n");
for (count = 0; count < maxcount; count++) {
dict = CFArrayGetValueAtIndex(errorStrings, count);
if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
val = CFDictionaryGetValue(dict, kSecPropertyKeyType);
if (val && (CFGetTypeID(val) == CFStringGetTypeID())) {
str = CFStringGetCStringPtr(val, kCFStringEncodingMacRoman);
if (str)
plog(ASL_LEVEL_ERR, "type = %s.\n", str);
}
val = CFDictionaryGetValue(dict, kSecPropertyKeyValue);
if (val && (CFGetTypeID(val) == CFStringGetTypeID())) {
str = CFStringGetCStringPtr(val, kCFStringEncodingMacRoman);
if (str)
plog(ASL_LEVEL_ERR, "value = %s.\n", str);
}
}
}
plog(ASL_LEVEL_ERR, "-----------------------------------------------------.\n");
CFRelease(errorStrings);
}
status = -1;
goto end;
}
*publicKeyRef = SecTrustCopyPublicKey(trustRef);
end:
if (cfCertRef)
CFRelease(cfCertRef);
if (trustRef)
CFRelease(trustRef);
if (status != noErr && status != -1) {
plog(ASL_LEVEL_ERR,
"EvaluateCert error %d %s.\n", (int)status, GetSecurityErrorString(status));
status = -1;
}
return status;
}
const char *
GetSecurityErrorString(OSStatus err)
{
switch(err) {
case noErr:
return "noErr";
case errSecNotAvailable:
return "errSecNotAvailable";
#if !TARGET_OS_EMBEDDED
case memFullErr:
return "memFullErr";
case paramErr:
return "paramErr";
case unimpErr:
return "unimpErr";
#endif
#ifndef HAVE_OPENSSL
case errSecReadOnly:
return "errSecReadOnly";
case errSecAuthFailed:
return "errSecAuthFailed";
case errSecNoSuchKeychain:
return "errSecNoSuchKeychain";
case errSecInvalidKeychain:
return "errSecInvalidKeychain";
case errSecDuplicateKeychain:
return "errSecDuplicateKeychain";
case errSecDuplicateCallback:
return "errSecDuplicateCallback";
case errSecInvalidCallback:
return "errSecInvalidCallback";
case errSecBufferTooSmall:
return "errSecBufferTooSmall";
case errSecDataTooLarge:
return "errSecDataTooLarge";
case errSecNoSuchAttr:
return "errSecNoSuchAttr";
case errSecInvalidItemRef:
return "errSecInvalidItemRef";
case errSecInvalidSearchRef:
return "errSecInvalidSearchRef";
case errSecNoSuchClass:
return "errSecNoSuchClass";
case errSecNoDefaultKeychain:
return "errSecNoDefaultKeychain";
case errSecInteractionNotAllowed:
return "errSecInteractionNotAllowed";
case errSecReadOnlyAttr:
return "errSecReadOnlyAttr";
case errSecWrongSecVersion:
return "errSecWrongSecVersion";
case errSecKeySizeNotAllowed:
return "errSecKeySizeNotAllowed";
case errSecNoStorageModule:
return "errSecNoStorageModule";
case errSecNoCertificateModule:
return "errSecNoCertificateModule";
case errSecNoPolicyModule:
return "errSecNoPolicyModule";
case errSecInteractionRequired:
return "errSecInteractionRequired";
case errSecDataNotAvailable:
return "errSecDataNotAvailable";
case errSecDataNotModifiable:
return "errSecDataNotModifiable";
case errSecCreateChainFailed:
return "errSecCreateChainFailed";
case errSecACLNotSimple:
return "errSecACLNotSimple";
case errSecPolicyNotFound:
return "errSecPolicyNotFound";
case errSecInvalidTrustSetting:
return "errSecInvalidTrustSetting";
case errSecNoAccessForItem:
return "errSecNoAccessForItem";
case errSecInvalidOwnerEdit:
return "errSecInvalidOwnerEdit";
#endif
case errSecDuplicateItem:
return "errSecDuplicateItem";
case errSecItemNotFound:
return "errSecItemNotFound";
default:
return "<unknown>";
}
}