#include "ssl.h"
#include "sslctx.h"
#include "sslalloc.h"
#include "appleCdsa.h"
#include "appleGlue.h"
#include "sslerrs.h"
#include "sslDebug.h"
#include "sslKeychain.h"
#include "sslutil.h"
#include <string.h>
#include <assert.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/cssm.h>
#include <Security/SecCertificate.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecKeychain.h>
#include <Security/SecIdentity.h>
#include <Security/SecIdentitySearch.h>
#include <Security/SecKey.h>
#if ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS
static OSStatus
addCertData(
SSLContext *ctx,
KCItemRef kcItem,
CSSM_DATA_PTR certData,
Boolean *goodCert);
#endif
#if (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION)
#if ST_FAKE_KEYCHAIN
static OSStatus
findPrivateKeyInDb(
SSLContext *ctx,
CSSM_DL_DB_HANDLE dlDbHand,
CSSM_KEY_PTR *privKey, CSSM_DATA *printName) {
CSSM_QUERY query;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_HANDLE resultHand;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA theAttr;
CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &theAttr.Info;
CSSM_DATA theData = {0, NULL};
query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 0;
query.SelectionPredicate = NULL;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = CSSM_QUERY_RETURN_DATA;
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
recordAttrs.SemanticInformation = 0;
recordAttrs.NumberOfAttributes = 1;
recordAttrs.AttributeData = &theAttr;
attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attrInfo->Label.AttributeName = "PrintName";
attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
theAttr.NumberOfValues = 1;
theAttr.Value = NULL;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
&recordAttrs,
&theData,
&record);
if(crtn == CSSM_OK) {
CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
*privKey = (CSSM_KEY_PTR)theData.Data;
*printName = *theAttr.Value;
stAppFree(theAttr.Value, NULL);
return noErr;
}
else {
stPrintCdsaError("CSSM_DL_DataGetFirst", crtn);
errorLog0("findCertInDb: cert not found\n");
return errSSLBadCert;
}
}
static OSStatus
findCertInDb(
SSLContext *ctx,
CSSM_DL_DB_HANDLE dlDbHand,
const CSSM_DATA *printName, CSSM_DATA *certData) {
CSSM_QUERY query;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_HANDLE resultHand;
predicate.DbOperator = CSSM_DB_EQUAL;
predicate.Attribute.Info.AttributeNameFormat =
CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
predicate.Attribute.Info.Label.AttributeName = "PrintName";
predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
predicate.Attribute.Value = (CSSM_DATA_PTR)printName;
predicate.Attribute.NumberOfValues = 1;
query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 1;
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
NULL, certData,
&record);
if(crtn == CSSM_OK) {
CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
return noErr;
}
else {
stPrintCdsaError("CSSM_DL_DataGetFirst", crtn);
errorLog0("findCertInDb: cert not found\n");
return errSSLBadCert;
}
}
#endif
#if ST_FAKE_KEYCHAIN
OSStatus
parseIncomingCerts(
SSLContext *ctx,
CFArrayRef certs,
SSLCertificate **destCert,
CSSM_KEY_PTR *pubKey,
CSSM_KEY_PTR *privKey,
CSSM_CSP_HANDLE *cspHand
#if ST_KC_KEYS_NEED_REF
,
SecKeychainRef *privKeyRef)
#else
)
#endif
{
CSSM_DL_DB_HANDLE_PTR dlDbHand = NULL;
CFIndex numCerts;
CSSM_KEY_PTR lookupPriv = NULL;
CSSM_DATA lookupLabel = {0, NULL};
CSSM_DATA lookupCert = {0, NULL};
OSStatus ortn;
SSLCertificate *certChain = NULL;
SSLCertificate *thisSslCert;
SSLErr srtn;
CSSM_CSP_HANDLE dummyCsp;
assert(ctx != NULL);
assert(destCert != NULL);
assert(pubKey != NULL);
assert(privKey != NULL);
assert(cspHand != NULL);
sslDeleteCertificateChain(*destCert, ctx);
*destCert = NULL;
*pubKey = NULL;
*privKey = NULL;
*cspHand = 0;
if(certs == NULL) {
dprintf0("parseIncomingCerts: NULL incoming cert (DLDB) array\n");
return errSSLBadCert;
}
numCerts = CFArrayGetCount(certs);
if(numCerts != 1) {
dprintf0("parseIncomingCerts: empty incoming cert (DLDB) array\n");
return errSSLBadCert;
}
dlDbHand = (CSSM_DL_DB_HANDLE_PTR)CFArrayGetValueAtIndex(certs, 0);
if(dlDbHand == NULL) {
errorLog0("parseIncomingCerts: bad cert (DLDB) array\n");
return paramErr;
}
ortn = findPrivateKeyInDb(ctx, *dlDbHand, &lookupPriv, &lookupLabel);
if(ortn) {
errorLog0("parseIncomingCerts: no private key\n");
return ortn;
}
assert(lookupPriv->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE);
assert(lookupPriv->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);
ortn = findCertInDb(ctx, *dlDbHand, &lookupLabel, &lookupCert);
if(ortn) {
errorLog0("parseIncomingCerts: no cert\n");
return ortn;
}
sslFree(lookupLabel.Data);
assert(lookupCert.Length > 100);
thisSslCert = sslMalloc(sizeof(SSLCertificate));
if(thisSslCert == NULL) {
return memFullErr;
}
if(SSLAllocBuffer(&thisSslCert->derCert, lookupCert.Length, &ctx->sysCtx)) {
return memFullErr;
}
memmove(thisSslCert->derCert.data, lookupCert.Data, lookupCert.Length);
sslFree(lookupCert.Data);
thisSslCert->next = certChain;
certChain = thisSslCert;
srtn = sslPubKeyFromCert(ctx,
&certChain->derCert,
pubKey,
&dummyCsp);
if(srtn) {
errorLog1("sslPubKeyFromCert returned %d\n", srtn);
ortn = sslErrToOsStatus(srtn);
goto errOut;
}
assert((*pubKey)->KeyHeader.BlobType == CSSM_KEYBLOB_RAW);
assert((*pubKey)->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY);
assert((*pubKey)->KeyHeader.LogicalKeySizeInBits ==
((lookupPriv->KeyHeader.LogicalKeySizeInBits + 7) & ~7));
*destCert = certChain;
*privKey = lookupPriv;
assert(ctx->cspDlHand != 0);
*cspHand = ctx->cspDlHand;
*privKeyRef = NULL; return noErr;
errOut:
sslDeleteCertificateChain(certChain, ctx);
if(lookupPriv != NULL) {
sslFreeKey(ctx->cspDlHand, &lookupPriv, NULL);
}
return ortn;
}
#else
static OSStatus secCertToSslCert(
SSLContext *ctx,
SecCertificateRef certRef,
SSLCertificate **sslCert)
{
CSSM_DATA certData; OSStatus ortn;
SSLCertificate *thisSslCert = NULL;
ortn = SecCertificateGetData(certRef, &certData);
if(ortn) {
errorLog1("SecCertificateGetData() returned %d\n", (int)ortn);
return ortn;
}
thisSslCert = sslMalloc(sizeof(SSLCertificate));
if(thisSslCert == NULL) {
return memFullErr;
}
if(SSLAllocBuffer(&thisSslCert->derCert, certData.Length,
&ctx->sysCtx)) {
return memFullErr;
}
memcpy(thisSslCert->derCert.data, certData.Data, certData.Length);
thisSslCert->derCert.length = certData.Length;
*sslCert = thisSslCert;
return noErr;
}
OSStatus
parseIncomingCerts(
SSLContext *ctx,
CFArrayRef certs,
SSLCertificate **destCert,
CSSM_KEY_PTR *pubKey,
CSSM_KEY_PTR *privKey,
CSSM_CSP_HANDLE *cspHand
#if ST_KC_KEYS_NEED_REF
,
SecKeychainRef *privKeyRef)
#else
)
#endif
{
CFIndex numCerts;
CFIndex cert;
SSLCertificate *certChain = NULL;
SSLCertificate *thisSslCert;
SecKeychainRef kcRef;
OSStatus ortn;
SSLErr srtn;
SecIdentityRef identity;
SecCertificateRef certRef;
SecKeyRef keyRef;
CSSM_DATA certData;
CSSM_CL_HANDLE clHand; CSSM_RETURN crtn;
CASSERT(ctx != NULL);
CASSERT(destCert != NULL);
CASSERT(pubKey != NULL);
CASSERT(privKey != NULL);
CASSERT(cspHand != NULL);
sslDeleteCertificateChain(*destCert, ctx);
*destCert = NULL;
*pubKey = NULL;
*privKey = NULL;
*cspHand = 0;
if(certs == NULL) {
dprintf0("parseIncomingCerts: NULL incoming cert array\n");
return errSSLBadCert;
}
numCerts = CFArrayGetCount(certs);
if(numCerts == 0) {
dprintf0("parseIncomingCerts: empty incoming cert array\n");
return errSSLBadCert;
}
identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0);
if(identity == NULL) {
errorLog0("parseIncomingCerts: bad cert array (1)\n");
return paramErr;
}
if(CFGetTypeID(identity) != SecIdentityGetTypeID()) {
errorLog0("parseIncomingCerts: bad cert array (2)\n");
return paramErr;
}
ortn = SecIdentityCopyCertificate(identity, &certRef);
if(ortn) {
errorLog0("parseIncomingCerts: bad cert array (3)\n");
return ortn;
}
ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
if(ortn) {
errorLog0("parseIncomingCerts: bad cert array (4)\n");
return ortn;
}
thisSslCert->next = certChain;
certChain = thisSslCert;
ortn = SecIdentityCopyPrivateKey(identity, &keyRef);
if(ortn) {
errorLog1("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
(int)ortn);
return ortn;
}
ortn = SecKeyGetCSSMKey(keyRef, (const CSSM_KEY **)privKey);
if(ortn) {
errorLog1("parseIncomingCerts: SecKeyGetCSSMKey err %d\n",
(int)ortn);
return ortn;
}
ortn = SecCertificateGetCLHandle(certRef, &clHand);
if(ortn) {
errorLog1("parseIncomingCerts: SecCertificateGetCLHandle err %d\n",
(int)ortn);
return ortn;
}
certData.Data = thisSslCert->derCert.data;
certData.Length = thisSslCert->derCert.length;
crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, pubKey);
if(crtn) {
errorLog0("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n");
return (OSStatus)crtn;
}
#if ST_FAKE_GET_CSPDL_HANDLE
assert(ctx->cspDlHand != 0);
*cspHand = ctx->cspDlHand;
#else
ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
if(ortn) {
errorLog1("parseIncomingCerts: SecKeychainItemCopyKeychain err %d\n",
(int)ortn);
return ortn;
}
ortn = SecKeychainGetCSPHandle(kcRef, cspHand);
if(ortn) {
errorLog1("parseIncomingCerts: SecKeychainGetCSPHandle err %d\n",
(int)ortn);
return ortn;
}
#endif
for(cert=1; cert<numCerts; cert++) {
certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certs, cert);
if(certRef == NULL) {
errorLog0("parseIncomingCerts: bad cert array (5)\n");
return paramErr;
}
if(CFGetTypeID(certRef) != SecCertificateGetTypeID()) {
errorLog0("parseIncomingCerts: bad cert array (6)\n");
return paramErr;
}
ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
if(ortn) {
errorLog0("parseIncomingCerts: bad cert array (7)\n");
return ortn;
}
thisSslCert->next = certChain;
certChain = thisSslCert;
}
srtn = sslVerifyCertChain(ctx, certChain);
if(srtn) {
ortn = sslErrToOsStatus(srtn);
goto errOut;
}
*destCert = certChain;
return noErr;
errOut:
sslDeleteCertificateChain(certChain, ctx);
return ortn;
}
#endif
#endif
OSStatus addBuiltInCerts (SSLContextRef ctx)
{
#if ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS
OSStatus ortn;
KCRef kc = nil;
ortn = KCDispatch(kKCGetRootCertificateKeychain, &kc);
if(ortn) {
errorLog1("KCDispatch(kKCGetRootCertificateKeychain) returned %d\n",
ortn);
return ortn;
}
return parseTrustedKeychain(ctx, kc);
#else
return noErr;
#endif
}
#if ST_KEYCHAIN_ENABLE && ST_MANAGES_TRUSTED_ROOTS
OSStatus
parseTrustedKeychain (SSLContextRef ctx,
KCRef keyChainRef)
{
CFMutableArrayRef kcCerts = NULL;
uint32 numGoodCerts = 0;
CSSM_DATA_PTR certData = NULL;
CFIndex certDex;
CFIndex certsPerKc;
OSStatus ortn;
KCItemRef kcItem;
Boolean goodCert;
CASSERT(ctx != NULL);
if(keyChainRef == NULL) {
return paramErr;
}
ortn = KCFindX509Certificates(keyChainRef,
NULL, NULL, kCertSearchAny, &kcCerts); switch(ortn) {
case noErr:
break; case errKCItemNotFound:
return noErr; default:
errorLog1("parseTrustedKeychains: KCFindX509Certificates returned %d\n",
ortn);
return ortn;
}
if(kcCerts == NULL) {
dprintf0("parseTrustedKeychains: no certs in KC\n");
return noErr;
}
certsPerKc = CFArrayGetCount(kcCerts);
certData = sslMalloc(certsPerKc * sizeof(CSSM_DATA));
if(certData == NULL) {
ortn = memFullErr;
goto errOut;
}
memset(certData, 0, certsPerKc * sizeof(CSSM_DATA));
for(certDex=0; certDex<certsPerKc; certDex++) {
kcItem = (KCItemRef)CFArrayGetValueAtIndex(kcCerts, certDex);
if(kcItem == NULL) {
errorLog0("parseTrustedKeychains: CF error 1\n");
ortn = errSSLInternal;
goto errOut;
}
if(!KCIsRootCertificate(kcItem)) {
dprintf1("parseTrustedKeychains: cert %d NOT ROOT\n", certDex);
continue;
}
ortn = addCertData(ctx,
kcItem,
&certData[numGoodCerts],
&goodCert);
if(ortn) {
goto errOut;
}
if(goodCert) {
numGoodCerts++;
}
}
#if SSL_DEBUG
verifyTrustedRoots(ctx, certData, numGoodCerts);
#endif
ctx->trustedCerts = sslRealloc(ctx->trustedCerts,
ctx->numTrustedCerts * sizeof(CSSM_DATA),
(ctx->numTrustedCerts + numGoodCerts) * sizeof(CSSM_DATA));
if(ctx->trustedCerts == NULL) {
ortn = memFullErr;
goto errOut;
}
for(certDex=0; certDex<numGoodCerts; certDex++) {
ctx->trustedCerts[ctx->numTrustedCerts + certDex] = certData[certDex];
}
ctx->numTrustedCerts += numGoodCerts;
ortn = noErr;
#if SSL_DEBUG
verifyTrustedRoots(ctx, ctx->trustedCerts, ctx->numTrustedCerts);
#endif
errOut:
sslFree(certData);
if(kcCerts != NULL) {
CFRelease(kcCerts);
}
return ortn;
}
static OSStatus
addCertData(
SSLContext *ctx,
KCItemRef kcItem,
CSSM_DATA_PTR certData,
Boolean *goodCert)
{
UInt32 certSize;
OSStatus ortn;
SSLErr srtn;
CSSM_BOOL subjectExpired;
CSSM_DATA_PTR dnData;
CASSERT(ctx != NULL);
CASSERT(certData != NULL);
CASSERT(kcItem != NULL);
CASSERT(goodCert != NULL);
*goodCert = false;
ortn = KCGetData (kcItem, 0, NULL, &certSize);
if(ortn != noErr) {
errorLog1("addCertData: KCGetData(1) returned %d\n", ortn);
return ortn;
}
srtn = stSetUpCssmData(certData, certSize);
if(srtn) {
return sslErrToOsStatus(srtn);
}
ortn = KCGetData (kcItem, certSize, certData->Data, &certSize);
if(ortn) {
errorLog1("addCertData: KCGetData(2) returned %d\n", ortn);
stFreeCssmData(certData, CSSM_FALSE);
return ortn;
}
if(!sslVerifyCert(ctx,
certData,
certData,
ctx->cspHand,
&subjectExpired)) {
dprintf0("addCertData: cert does not self-verify!\n");
stFreeCssmData(certData, CSSM_FALSE);
return noErr;
}
dnData = sslGetCertSubjectName(ctx, certData);
if(dnData) {
DNListElem *dn = sslMalloc(sizeof(DNListElem));
if(dn == NULL) {
return memFullErr;
}
dn->next = ctx->acceptableDNList;
ctx->acceptableDNList = dn;
CSSM_TO_SSLBUF(dnData, &dn->derDN);
sslFree(dnData);
}
*goodCert = true;
return noErr;
}
SSLErr
sslAddNewRoot(
SSLContext *ctx,
const CSSM_DATA_PTR rootCert)
{
KCRef defaultKc;
Boolean bDefaultKcExists;
KCItemRef certRef = NULL;
OSStatus ortn;
CSSM_DATA_PTR newTrustee;
SSLErr serr;
CASSERT(ctx != NULL);
CASSERT(rootCert != NULL);
CASSERT(ctx->newRootCertKc != NULL);
ortn = KCGetDefaultKeychain(&defaultKc);
if(ortn) {
bDefaultKcExists = false;
}
else {
bDefaultKcExists = true;
}
ortn = KCSetDefaultKeychain(ctx->newRootCertKc);
if(ortn) {
errorLog1("sslAddNewRoot: KCSetDefaultKeychain returned %d\n", ortn);
return SSLUnknownRootCert;
}
ortn = KCAddX509Certificate(rootCert->Data, rootCert->Length, &certRef);
if(bDefaultKcExists) {
KCSetDefaultKeychain(defaultKc);
}
if(ortn) {
dprintf1("sslAddNewRoot: KCAddX509Certificate returned %d\n", ortn);
return SSLUnknownRootCert;
}
ctx->trustedCerts = (CSSM_DATA_PTR)sslRealloc(ctx->trustedCerts,
(ctx->numTrustedCerts * sizeof(CSSM_DATA)),
((ctx->numTrustedCerts + 1) * sizeof(CSSM_DATA)));
if(ctx->trustedCerts == NULL) {
return SSLMemoryErr;
}
newTrustee = &ctx->trustedCerts[ctx->numTrustedCerts];
newTrustee->Data = NULL;
newTrustee->Length = 0;
serr = stSetUpCssmData(newTrustee, rootCert->Length);
if(serr) {
return serr;
}
BlockMove(rootCert->Data, newTrustee->Data, rootCert->Length);
(ctx->numTrustedCerts)++;
return SSLNoErr;
}
#endif