#include "ssl.h"
#include "sslContext.h"
#include "sslMemory.h"
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include "sslDigests.h"
#include "sslDebug.h"
#include "appleCdsa.h"
#include "sslKeychain.h"
#include "sslUtils.h"
#include "cipherSpecs.h"
#include "appleSession.h"
#include <string.h>
#include <Security/SecCertificate.h>
#include <Security/SecTrust.h>
static void sslFreeDnList(
SSLContext *ctx)
{
DNListElem *dn, *nextDN;
dn = ctx->acceptableDNList;
while (dn)
{
SSLFreeBuffer(dn->derDN, ctx);
nextDN = dn->next;
sslFree(dn);
dn = nextDN;
}
ctx->acceptableDNList = NULL;
}
static OSStatus sslFreeTrustedRoots(
SSLContext *ctx)
{
unsigned i;
assert(ctx != NULL);
if((ctx->numTrustedCerts == 0) || (ctx->trustedCerts == NULL)) {
assert((ctx->numTrustedCerts == 0) && (ctx->trustedCerts == NULL));
}
else {
for(i=0; i<ctx->numTrustedCerts; i++) {
stFreeCssmData(&ctx->trustedCerts[i], CSSM_FALSE);
}
sslFree(ctx->trustedCerts);
}
ctx->numTrustedCerts = 0;
ctx->trustedCerts = NULL;
sslFreeDnList(ctx);
return noErr;
}
#define DEFAULT_MAX_VERSION TLS_Version_1_0
OSStatus
SSLNewContext (Boolean isServer,
SSLContextRef *contextPtr)
{
SSLContext *ctx;
OSStatus serr;
if(contextPtr == NULL) {
return paramErr;
}
*contextPtr = NULL;
ctx = (SSLContext *)sslMalloc(sizeof(SSLContext));
if(ctx == NULL) {
return memFullErr;
}
memset(ctx, 0, sizeof(SSLContext));
ctx->state = SSL_HdskStateUninit;
ctx->clientCertState = kSSLClientCertNone;
if(isServer) {
ctx->protocolSide = SSL_ServerSide;
ctx->reqProtocolVersion = DEFAULT_MAX_VERSION;
}
else {
ctx->protocolSide = SSL_ClientSide;
ctx->reqProtocolVersion = SSL_Version_Undetermined;
}
ctx->negProtocolVersion = SSL_Version_Undetermined;
ctx->maxProtocolVersion = DEFAULT_MAX_VERSION;
ctx->sslTslCalls = &Ssl3Callouts;
ctx->selectedCipherSpec = &SSL_NULL_WITH_NULL_NULL_CipherSpec;
ctx->selectedCipher = ctx->selectedCipherSpec->cipherSpec;
ctx->writeCipher.macRef = ctx->selectedCipherSpec->macAlgorithm;
ctx->readCipher.macRef = ctx->selectedCipherSpec->macAlgorithm;
ctx->readCipher.symCipher = ctx->selectedCipherSpec->cipher;
ctx->writeCipher.symCipher = ctx->selectedCipherSpec->cipher;
ctx->writeCipher.encrypting = 1;
ctx->writePending.encrypting = 1;
ctx->validCipherSpecs = NULL;
ctx->numValidCipherSpecs = 0;
ctx->peerDomainName = NULL;
ctx->peerDomainNameLen = 0;
serr = attachToAll(ctx);
if(serr) {
goto errOut;
}
ctx->enableCertVerify = true;
addBuiltInCerts(ctx);
*contextPtr = ctx;
return noErr;
errOut:
sslFree(ctx);
return serr;
}
OSStatus
SSLDisposeContext (SSLContext *ctx)
{
WaitingRecord *wait, *next;
SSLBuffer buf;
if(ctx == NULL) {
return paramErr;
}
sslDeleteCertificateChain(ctx->localCert, ctx);
sslDeleteCertificateChain(ctx->encryptCert, ctx);
sslDeleteCertificateChain(ctx->peerCert, ctx);
ctx->localCert = ctx->encryptCert = ctx->peerCert = NULL;
SSLFreeBuffer(ctx->partialReadBuffer, ctx);
wait = ctx->recordWriteQueue;
while (wait)
{ SSLFreeBuffer(wait->data, ctx);
next = wait->next;
buf.data = (uint8*)wait;
buf.length = sizeof(WaitingRecord);
SSLFreeBuffer(buf, ctx);
wait = next;
}
SSLFreeBuffer(ctx->dhPeerPublic, ctx);
SSLFreeBuffer(ctx->dhExchangePublic, ctx);
SSLFreeBuffer(ctx->dhPrivate, ctx);
CloseHash(SSLHashSHA1, ctx->shaState, ctx);
CloseHash(SSLHashMD5, ctx->md5State, ctx);
SSLFreeBuffer(ctx->sessionID, ctx);
SSLFreeBuffer(ctx->peerID, ctx);
SSLFreeBuffer(ctx->resumableSession, ctx);
SSLFreeBuffer(ctx->preMasterSecret, ctx);
SSLFreeBuffer(ctx->partialReadBuffer, ctx);
SSLFreeBuffer(ctx->fragmentedMessageCache, ctx);
SSLFreeBuffer(ctx->receivedDataBuffer, ctx);
if(ctx->peerDomainName) {
sslFree(ctx->peerDomainName);
ctx->peerDomainName = NULL;
ctx->peerDomainNameLen = 0;
}
SSLDisposeCipherSuite(&ctx->readCipher, ctx);
SSLDisposeCipherSuite(&ctx->writeCipher, ctx);
SSLDisposeCipherSuite(&ctx->readPending, ctx);
SSLDisposeCipherSuite(&ctx->writePending, ctx);
sslFree(ctx->validCipherSpecs);
ctx->validCipherSpecs = NULL;
ctx->numValidCipherSpecs = 0;
sslFreeKey(ctx->signingKeyCsp, &ctx->signingPubKey, NULL);
sslFreeKey(ctx->encryptKeyCsp, &ctx->encryptPubKey, NULL);
sslFreeKey(ctx->peerPubKeyCsp, &ctx->peerPubKey, NULL);
sslFreeTrustedRoots(ctx);
detachFromAll(ctx);
memset(ctx, 0, sizeof(SSLContext));
sslFree(ctx);
sslCleanupSession();
return noErr;
}
OSStatus
SSLGetSessionState (SSLContextRef context,
SSLSessionState *state)
{
SSLSessionState rtnState = kSSLIdle;
if(context == NULL) {
return paramErr;
}
*state = rtnState;
switch(context->state) {
case SSL_HdskStateUninit:
case SSL_HdskStateServerUninit:
case SSL_HdskStateClientUninit:
rtnState = kSSLIdle;
break;
case SSL_HdskStateGracefulClose:
rtnState = kSSLClosed;
break;
case SSL_HdskStateErrorClose:
case SSL_HdskStateNoNotifyClose:
rtnState = kSSLAborted;
break;
case SSL2_HdskStateServerReady:
case SSL2_HdskStateClientReady:
rtnState = kSSLConnected;
break;
default:
assert((context->state >= SSL_HdskStateServerHello) &&
(context->state <= SSL2_HdskStateServerFinished));
rtnState = kSSLHandshake;
break;
}
*state = rtnState;
return noErr;
}
OSStatus
SSLSetIOFuncs (SSLContextRef ctx,
SSLReadFunc read,
SSLWriteFunc write)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
ctx->ioCtx.read = read;
ctx->ioCtx.write = write;
return noErr;
}
OSStatus
SSLSetConnection (SSLContextRef ctx,
SSLConnectionRef connection)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
ctx->ioCtx.ioRef = connection;
return noErr;
}
OSStatus
SSLSetPeerDomainName (SSLContextRef ctx,
const char *peerName,
size_t peerNameLen)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
if(ctx->peerDomainName) {
sslFree(ctx->peerDomainName);
}
ctx->peerDomainName = (char *)sslMalloc(peerNameLen);
if(ctx->peerDomainName == NULL) {
return memFullErr;
}
memmove(ctx->peerDomainName, peerName, peerNameLen);
ctx->peerDomainNameLen = peerNameLen;
return noErr;
}
OSStatus
SSLGetPeerDomainNameLength (SSLContextRef ctx,
size_t *peerNameLen) {
if(ctx == NULL) {
return paramErr;
}
*peerNameLen = ctx->peerDomainNameLen;
return noErr;
}
OSStatus
SSLGetPeerDomainName (SSLContextRef ctx,
char *peerName, size_t *peerNameLen) {
if(ctx == NULL) {
return paramErr;
}
if(*peerNameLen < ctx->peerDomainNameLen) {
return errSSLBufferOverflow;
}
memmove(peerName, ctx->peerDomainName, ctx->peerDomainNameLen);
*peerNameLen = ctx->peerDomainNameLen;
return noErr;
}
OSStatus
SSLSetProtocolVersion (SSLContextRef ctx,
SSLProtocol version)
{
SSLProtocolVersion versInt;
SSLProtocolVersion versMax;
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
switch(version) {
case kSSLProtocolUnknown:
versInt = SSL_Version_Undetermined;
versMax = DEFAULT_MAX_VERSION;
break;
case kSSLProtocol2:
versInt = versMax = SSL_Version_2_0;
break;
case kSSLProtocol3:
versInt = SSL_Version_Undetermined;
versMax = SSL_Version_3_0;
break;
case kSSLProtocol3Only:
versInt = SSL_Version_3_0_Only;
versMax = SSL_Version_3_0;
break;
case kTLSProtocol1:
versInt = SSL_Version_Undetermined;
versMax = TLS_Version_1_0;
break;
case kTLSProtocol1Only:
versInt = TLS_Version_1_0_Only;
versMax = TLS_Version_1_0;
break;
default:
return paramErr;
}
ctx->reqProtocolVersion = ctx->negProtocolVersion = versInt;
ctx->maxProtocolVersion = versMax;
return noErr;
}
static SSLProtocol convertProtToExtern(SSLProtocolVersion prot)
{
switch(prot) {
case SSL_Version_Undetermined:
return kSSLProtocolUnknown;
case SSL_Version_3_0_Only:
return kSSLProtocol3Only;
case SSL_Version_2_0:
return kSSLProtocol2;
case SSL_Version_3_0:
return kSSLProtocol3;
case TLS_Version_1_0_Only:
return kTLSProtocol1Only;
case TLS_Version_1_0:
return kTLSProtocol1;
case SSL_Version_3_0_With_2_0_Hello:
return kSSLProtocolUnknown;
default:
sslErrorLog("convertProtToExtern: bad prot\n");
return kSSLProtocolUnknown;
}
return kSSLProtocolUnknown;
}
OSStatus
SSLGetProtocolVersion (SSLContextRef ctx,
SSLProtocol *protocol)
{
if(ctx == NULL) {
return paramErr;
}
*protocol = convertProtToExtern(ctx->reqProtocolVersion);
return noErr;
}
OSStatus
SSLGetNegotiatedProtocolVersion (SSLContextRef ctx,
SSLProtocol *protocol)
{
if(ctx == NULL) {
return paramErr;
}
*protocol = convertProtToExtern(ctx->negProtocolVersion);
return noErr;
}
OSStatus
SSLSetEnableCertVerify (SSLContextRef ctx,
Boolean enableVerify)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
ctx->enableCertVerify = enableVerify;
return noErr;
}
OSStatus
SSLGetEnableCertVerify (SSLContextRef ctx,
Boolean *enableVerify)
{
if(ctx == NULL) {
return paramErr;
}
*enableVerify = ctx->enableCertVerify;
return noErr;
}
OSStatus
SSLSetAllowsExpiredCerts(SSLContextRef ctx,
Boolean allowExpired)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
ctx->allowExpiredCerts = allowExpired;
return noErr;
}
OSStatus
SSLGetAllowsExpiredCerts (SSLContextRef ctx,
Boolean *allowExpired)
{
if(ctx == NULL) {
return paramErr;
}
*allowExpired = ctx->allowExpiredCerts;
return noErr;
}
OSStatus
SSLSetAllowsExpiredRoots(SSLContextRef ctx,
Boolean allowExpired)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
ctx->allowExpiredRoots = allowExpired;
return noErr;
}
OSStatus
SSLGetAllowsExpiredRoots (SSLContextRef ctx,
Boolean *allowExpired)
{
if(ctx == NULL) {
return paramErr;
}
*allowExpired = ctx->allowExpiredRoots;
return noErr;
}
OSStatus SSLSetAllowsAnyRoot(
SSLContextRef ctx,
Boolean anyRoot)
{
if(ctx == NULL) {
return paramErr;
}
ctx->allowAnyRoot = anyRoot;
return noErr;
}
OSStatus
SSLGetAllowsAnyRoot(
SSLContextRef ctx,
Boolean *anyRoot)
{
if(ctx == NULL) {
return paramErr;
}
*anyRoot = ctx->allowAnyRoot;
return noErr;
}
OSStatus
SSLSetTrustedRoots (SSLContextRef ctx,
CFArrayRef trustedRoots,
Boolean replaceExisting)
{
unsigned dex;
unsigned outDex;
unsigned numIncoming;
uint32 numCerts;
CSSM_DATA_PTR newRoots = NULL;
const CSSM_DATA *existAnchors = NULL;
uint32 numExistAnchors = 0;
OSStatus ortn = noErr;
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
numCerts = numIncoming = CFArrayGetCount(trustedRoots);
if(!replaceExisting) {
if(ctx->trustedCerts != NULL) {
existAnchors = ctx->trustedCerts;
numExistAnchors = ctx->numTrustedCerts;
}
else {
ortn = SecTrustGetCSSMAnchorCertificates(&existAnchors,
&numExistAnchors);
if(ortn) {
return ortn;
}
}
numCerts += numExistAnchors;
}
newRoots = (CSSM_DATA_PTR)sslMalloc(numCerts * sizeof(CSSM_DATA));
memset(newRoots, 0, numCerts * sizeof(CSSM_DATA));
for(dex=0, outDex=0; dex<numIncoming; dex++, outDex++) {
CSSM_DATA certData;
SecCertificateRef secCert = (SecCertificateRef)
CFArrayGetValueAtIndex(trustedRoots, dex);
if(CFGetTypeID(secCert) != SecCertificateGetTypeID()) {
ortn = paramErr;
goto abort;
}
ortn = SecCertificateGetData(secCert, &certData);
if(ortn) {
goto abort;
}
stSetUpCssmData(&newRoots[outDex], certData.Length);
memmove(newRoots[outDex].Data, certData.Data, certData.Length);
}
for(dex=0; dex<numExistAnchors; dex++, outDex++) {
stSetUpCssmData(&newRoots[outDex], existAnchors[dex].Length);
memmove(newRoots[outDex].Data, existAnchors[dex].Data,
existAnchors[dex].Length);
}
sslFreeTrustedRoots(ctx);
ctx->numTrustedCerts = numCerts;
ctx->trustedCerts = newRoots;
return noErr;
abort:
sslFree(newRoots);
return ortn;
}
OSStatus
SSLGetTrustedRoots (SSLContextRef ctx,
CFArrayRef *trustedRoots)
{
uint32 numCerts;
const CSSM_DATA *certs;
CFMutableArrayRef certArray;
unsigned dex;
SecCertificateRef secCert;
OSStatus ortn;
if(ctx == NULL) {
return paramErr;
}
if(ctx->trustedCerts != NULL) {
certs = ctx->trustedCerts;
numCerts = ctx->numTrustedCerts;
}
else {
OSStatus ortn = SecTrustGetCSSMAnchorCertificates(&certs,
&numCerts);
if(ortn) {
return ortn;
}
}
certArray = CFArrayCreateMutable(kCFAllocatorDefault,
(CFIndex)numCerts, &kCFTypeArrayCallBacks);
if(certArray == NULL) {
return memFullErr;
}
for(dex=0; dex<numCerts; dex++) {
ortn = SecCertificateCreateFromData(&certs[dex],
CSSM_CERT_X_509v3,
CSSM_CERT_ENCODING_DER,
&secCert);
if(ortn) {
CFRelease(certArray);
return ortn;
}
CFArrayAppendValue(certArray, secCert);
}
*trustedRoots = certArray;
return noErr;
}
OSStatus
SSLSetClientSideAuthenticate (SSLContext *ctx,
SSLAuthenticate auth)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
ctx->clientAuth = auth;
switch(auth) {
case kNeverAuthenticate:
ctx->tryClientAuth = false;
break;
case kAlwaysAuthenticate:
case kTryAuthenticate:
ctx->tryClientAuth = true;
break;
}
return noErr;
}
OSStatus
SSLGetClientCertificateState (SSLContextRef ctx,
SSLClientCertificateState *clientState)
{
if(ctx == NULL) {
return paramErr;
}
*clientState = ctx->clientCertState;
return noErr;
}
OSStatus
SSLSetCertificate (SSLContextRef ctx,
CFArrayRef certRefs)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
return parseIncomingCerts(ctx,
certRefs,
&ctx->localCert,
&ctx->signingPubKey,
&ctx->signingPrivKey,
&ctx->signingKeyCsp
#if ST_KC_KEYS_NEED_REF
,
&ctx->signingKeyRef
#else
);
#endif
}
OSStatus
SSLSetEncryptionCertificate (SSLContextRef ctx,
CFArrayRef certRefs)
{
if(ctx == NULL) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
return parseIncomingCerts(ctx,
certRefs,
&ctx->encryptCert,
&ctx->encryptPubKey,
&ctx->encryptPrivKey,
&ctx->encryptKeyCsp
#if ST_KC_KEYS_NEED_REF
,
&ctx->encryptKeyRef);
#else
);
#endif
}
#if ST_MANAGES_TRUSTED_ROOTS
OSStatus
SSLSetTrustedRootCertKC (SSLContextRef ctx,
KCRef keyChainRef,
Boolean deleteExisting)
{
if((ctx == NULL) || (keyChainRef == nil)) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
if(deleteExisting) {
sslFreeTrustedRoots(ctx);
}
return parseTrustedKeychain(ctx, keyChainRef);
}
OSStatus
SSLSetNewRootKC (SSLContextRef ctx,
KCRef keyChainRef,
void *accessCreds)
{
if((ctx == NULL) || (keyChainRef == nil)) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
if(ctx->newRootCertKc != NULL) {
return badReqErr;
}
ctx->newRootCertKc = keyChainRef;
ctx->accessCreds = accessCreds;
return noErr;
}
#endif
OSStatus
SSLSetPeerID (SSLContext *ctx,
const void *peerID,
size_t peerIDLen)
{
OSStatus serr;
if((ctx == NULL) ||
(peerID == NULL) ||
(peerIDLen == 0)) {
return paramErr;
}
if(sslIsSessionActive(ctx)) {
return badReqErr;
}
SSLFreeBuffer(ctx->peerID, ctx);
serr = SSLAllocBuffer(ctx->peerID, peerIDLen, ctx);
if(serr) {
return serr;
}
memmove(ctx->peerID.data, peerID, peerIDLen);
return noErr;
}
OSStatus
SSLGetPeerID (SSLContextRef ctx,
const void **peerID,
size_t *peerIDLen)
{
*peerID = ctx->peerID.data; *peerIDLen = ctx->peerID.length;
return noErr;
}
OSStatus
SSLGetNegotiatedCipher (SSLContextRef ctx,
SSLCipherSuite *cipherSuite)
{
if(ctx == NULL) {
return paramErr;
}
if(!sslIsSessionActive(ctx)) {
return badReqErr;
}
*cipherSuite = (SSLCipherSuite)ctx->selectedCipher;
return noErr;
}
OSStatus
SSLAddDistinguishedName(
SSLContextRef ctx,
const void *derDN,
size_t derDNLen)
{
DNListElem *dn;
OSStatus err;
dn = (DNListElem *)sslMalloc(sizeof(DNListElem));
if(dn == NULL) {
return memFullErr;
}
if ((err = SSLAllocBuffer(dn->derDN, derDNLen, ctx)) != 0)
return err;
memcpy(dn->derDN.data, derDN, derDNLen);
dn->next = ctx->acceptableDNList;
ctx->acceptableDNList = dn;
return noErr;
}
OSStatus
SSLGetPeerCertificates (SSLContextRef ctx,
CFArrayRef *certs)
{
uint32 numCerts;
CFMutableArrayRef ca;
CFIndex i;
SecCertificateRef cfd;
OSStatus ortn;
CSSM_DATA certData;
SSLCertificate *scert;
if(ctx == NULL) {
return paramErr;
}
*certs = NULL;
numCerts = SSLGetCertificateChainLength(ctx->peerCert);
if(numCerts == 0) {
return noErr;
}
ca = CFArrayCreateMutable(kCFAllocatorDefault,
(CFIndex)numCerts, &kCFTypeArrayCallBacks);
if(ca == NULL) {
return memFullErr;
}
scert = ctx->peerCert;
for(i=0; (unsigned)i<numCerts; i++) {
assert(scert != NULL);
SSLBUF_TO_CSSM(&scert->derCert, &certData);
ortn = SecCertificateCreateFromData(&certData,
CSSM_CERT_X_509v3,
CSSM_CERT_ENCODING_DER,
&cfd);
if(ortn) {
CFRelease(ca);
return ortn;
}
CFArrayInsertValueAtIndex(ca, 0, cfd);
scert = scert->next;
}
*certs = ca;
return noErr;
}
OSStatus SSLInternalMasterSecret(
SSLContextRef ctx,
void *secret, size_t *secretSize) {
if((ctx == NULL) || (secret == NULL) || (secretSize == NULL)) {
return paramErr;
}
if(*secretSize < SSL_MASTER_SECRET_SIZE) {
return paramErr;
}
memmove(secret, ctx->masterSecret, SSL_MASTER_SECRET_SIZE);
*secretSize = SSL_MASTER_SECRET_SIZE;
return noErr;
}
OSStatus SSLInternalServerRandom(
SSLContextRef ctx,
void *rand, size_t *randSize) {
if((ctx == NULL) || (rand == NULL) || (randSize == NULL)) {
return paramErr;
}
if(*randSize < SSL_CLIENT_SRVR_RAND_SIZE) {
return paramErr;
}
memmove(rand, ctx->serverRandom, SSL_CLIENT_SRVR_RAND_SIZE);
*randSize = SSL_CLIENT_SRVR_RAND_SIZE;
return noErr;
}
OSStatus SSLInternalClientRandom(
SSLContextRef ctx,
void *rand, size_t *randSize) {
if((ctx == NULL) || (rand == NULL) || (randSize == NULL)) {
return paramErr;
}
if(*randSize < SSL_CLIENT_SRVR_RAND_SIZE) {
return paramErr;
}
memmove(rand, ctx->clientRandom, SSL_CLIENT_SRVR_RAND_SIZE);
*randSize = SSL_CLIENT_SRVR_RAND_SIZE;
return noErr;
}