#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"
#if ST_KEYCHAIN_ENABLE
#include <Keychain.h>
#include <KeychainPriv.h>
#endif
#include <string.h>
#if ST_KEYCHAIN_ENABLE
static OSStatus
addCertData(
SSLContext *ctx,
KCItemRef kcItem,
CSSM_DATA_PTR certData,
Boolean *goodCert);
static Boolean
isItemACert(KCItemRef kcItem)
{
KCAttribute attr;
FourCharCode itemClass;
OSStatus ortn;
UInt32 len;
attr.tag = kClassKCItemAttr;
attr.length = sizeof(FourCharCode);
attr.data = &itemClass;
ortn = KCGetAttribute (kcItem, &attr, &len);
if (ortn == noErr) {
return((itemClass == kCertificateKCItemClass) ? true : false);
}
else {
errorLog1("isItemACert: KCGetAttribute returned %d\n", ortn);
return false;
}
}
#endif
#if (ST_SERVER_MODE_ENABLE || ST_CLIENT_AUTHENTICATION)
OSStatus
parseIncomingCerts(
SSLContext *ctx,
CFArrayRef certs,
SSLCertificate **destCert,
CSSM_KEY_PTR *pubKey,
CSSM_KEY_PTR *privKey,
CSSM_CSP_HANDLE *cspHand,
KCItemRef *privKeyRef)
{
CFIndex numCerts;
CFIndex cert;
SSLCertificate *certChain = NULL;
SSLCertificate *thisSslCert;
KCItemRef kcItem;
SSLBuffer *derSubjCert = NULL;
UInt32 certLen;
OSStatus ortn;
SSLErr srtn;
FromItemGetPrivateKeyParams keyParams = {NULL, NULL};
FromItemGetKeyInfoParams keyInfo = {NULL, NULL, 0};
CSSM_CSP_HANDLE dummyCsp;
CASSERT(ctx != NULL);
CASSERT(destCert != NULL);
CASSERT(pubKey != NULL);
CASSERT(privKey != NULL);
CASSERT(cspHand != NULL);
CASSERT(privKeyRef != 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;
}
for(cert=0; cert<numCerts; cert++) {
kcItem = (KCItemRef)CFArrayGetValueAtIndex(certs, cert);
if(kcItem == NULL) {
errorLog0("parseIncomingCerts: bad cert array\n");
return paramErr;
}
if(!isItemACert(kcItem)) {
return paramErr;
}
ortn = KCGetData(kcItem, 0, NULL, &certLen);
if(ortn != noErr) {
errorLog1("parseIncomingCerts: KCGetData(1) returned %d\n", ortn);
return ortn;
}
thisSslCert = sslMalloc(sizeof(SSLCertificate));
if(thisSslCert == NULL) {
return memFullErr;
}
if(SSLAllocBuffer(&thisSslCert->derCert, certLen, &ctx->sysCtx)) {
return memFullErr;
}
ortn = KCGetData (kcItem,
certLen,
thisSslCert->derCert.data,
&certLen);
if(ortn) {
errorLog1("parseIncomingCerts: KCGetData(2) returned %d\n", ortn);
SSLFreeBuffer(&thisSslCert->derCert, &ctx->sysCtx);
return ortn;
}
thisSslCert->next = certChain;
certChain = thisSslCert;
if(derSubjCert == NULL) {
derSubjCert = &thisSslCert->derCert;
}
}
srtn = sslVerifyCertChain(ctx, certChain);
if(srtn) {
ortn = sslErrToOsStatus(srtn);
goto errOut;
}
keyParams.item = (KCItemRef)CFArrayGetValueAtIndex(certs, 0);
ortn = KCDispatch(kKCFromItemGetPrivateKey, &keyParams);
if(ortn) {
errorLog1("KCDispatch(kKCFromItemGetPrivateKey) returned %d\n", ortn);
goto errOut;
}
keyInfo.item = keyParams.privateKeyItem;
ortn = KCDispatch(kKCFromItemGetKeyInfo, &keyInfo);
if(ortn) {
errorLog1("KCDispatch(kKCFromItemGetKeyInfo) returned %d\n", ortn);
goto errOut;
}
*privKey = (CSSM_KEY_PTR)keyInfo.keyPtr;
*cspHand = keyInfo.cspHandle;
*privKeyRef = keyParams.privateKeyItem;
srtn = sslPubKeyFromCert(ctx,
derSubjCert,
pubKey,
&dummyCsp);
if(srtn) {
errorLog1("sslPubKeyFromCert returned %d\n", srtn);
ortn = sslErrToOsStatus(srtn);
goto errOut;
}
*destCert = certChain;
return noErr;
errOut:
sslDeleteCertificateChain(certChain, ctx);
if(keyInfo.keyPtr != NULL) {
sslFreeKey(keyInfo.cspHandle, &keyInfo.keyPtr, NULL);
}
if(keyParams.privateKeyItem != NULL) {
KCReleaseItem(&keyParams.privateKeyItem);
}
return ortn;
}
#endif
OSStatus addBuiltInCerts (SSLContextRef ctx)
{
#if ST_KEYCHAIN_ENABLE
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
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