#include "sslContext.h"
#include "sslHandshake.h"
#include "sslMemory.h"
#include "sslAlertMessage.h"
#include "sslSession.h"
#include "sslUtils.h"
#include "sslDebug.h"
#include "sslCrypto.h"
#include "sslRand.h"
#include "sslDigests.h"
#include "sslCipherSpecs.h"
#include "cipherSpecs.h"
#include <utilities/SecIOFormat.h>
#include <AssertMacros.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#define REQUEST_CERT_CORRECT 0
#if __LP64__
#define PRIstatus "d"
#else
#define PRIstatus "ld"
#endif
uint8_t *
SSLEncodeHandshakeHeader(SSLContext *ctx, SSLRecord *rec, SSLHandshakeType type, size_t msglen)
{
uint8_t *charPtr;
charPtr = rec->contents.data;
*charPtr++ = type;
charPtr = SSLEncodeSize(charPtr, msglen, 3);
if(rec->protocolVersion == DTLS_Version_1_0) {
charPtr = SSLEncodeInt(charPtr, ctx->hdskMessageSeq, 2);
charPtr = SSLEncodeInt(charPtr, 0, 3);
charPtr = SSLEncodeSize(charPtr, msglen, 3);
}
return charPtr;
}
static OSStatus SSLProcessHandshakeMessage(SSLHandshakeMsg message, SSLContext *ctx);
static OSStatus
SSLUpdateHandshakeMacs(const SSLBuffer *messageData, SSLContext *ctx)
{
OSStatus err = errSSLInternal;
bool do_md5 = false;
bool do_sha1 = false;
bool do_sha256 = false;
bool do_sha384 = false;
if(ctx->negProtocolVersion == SSL_Version_Undetermined)
{
assert(ctx->protocolSide==kSSLClientSide);
do_md5 = do_sha1 = true;
if(ctx->isDTLS
? ctx->maxProtocolVersion < DTLS_Version_1_0
: ctx->maxProtocolVersion >= TLS_Version_1_2)
{
do_sha256 = do_sha384 = true;
}
} else {
if(sslVersionIsLikeTls12(ctx)) {
do_sha1 = do_sha256 = do_sha384 = true;
} else {
do_md5 = do_sha1 = true;
}
}
if (do_md5 &&
(err = SSLHashMD5.update(&ctx->md5State, messageData)) != 0)
goto done;
if (do_sha1 &&
(err = SSLHashSHA1.update(&ctx->shaState, messageData)) != 0)
goto done;
if (do_sha256 &&
(err = SSLHashSHA256.update(&ctx->sha256State, messageData)) != 0)
goto done;
if (do_sha384 &&
(err = SSLHashSHA384.update(&ctx->sha512State, messageData)) != 0)
goto done;
sslLogNegotiateDebug("%s protocol: %02X max: %02X cipher: %02X%s%s%s%s",
ctx->protocolSide == kSSLClientSide ? "client" : "server",
ctx->negProtocolVersion,
ctx->maxProtocolVersion,
ctx->selectedCipher,
do_md5 ? " md5" : "",
do_sha1 ? " sha1" : "",
do_sha256 ? " sha256" : "",
do_sha384 ? " sha384" : "");
done:
return err;
}
OSStatus
SSLProcessHandshakeRecord(SSLRecord rec, SSLContext *ctx)
{ OSStatus err;
size_t remaining;
UInt8 *p;
UInt8 *startingP; SSLHandshakeMsg message = {};
SSLBuffer messageData;
if (ctx->fragmentedMessageCache.data != 0)
{
size_t origLen = ctx->fragmentedMessageCache.length;
if ((err = SSLReallocBuffer(&ctx->fragmentedMessageCache,
ctx->fragmentedMessageCache.length + rec.contents.length)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
memcpy(ctx->fragmentedMessageCache.data + origLen,
rec.contents.data, rec.contents.length);
remaining = ctx->fragmentedMessageCache.length;
p = ctx->fragmentedMessageCache.data;
}
else
{ remaining = rec.contents.length;
p = rec.contents.data;
}
startingP = p;
size_t head = 4;
while (remaining > 0)
{
if (remaining < head)
break;
messageData.data = p;
message.type = (SSLHandshakeType)*p++;
message.contents.length = SSLDecodeSize(p, 3);
p += 3;
if ((message.contents.length + head) > remaining)
break;
message.contents.data = p;
p += message.contents.length;
messageData.length = head + message.contents.length;
assert(p == messageData.data + messageData.length);
remaining -= messageData.length;
if ((err = SSLProcessHandshakeMessage(message, ctx)) != 0)
return err;
if (message.type != SSL_HdskHelloRequest)
{ if ((err = SSLUpdateHandshakeMacs(&messageData, ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
}
if ((err = SSLAdvanceHandshake(message.type, ctx)) != 0)
return err;
}
if (remaining > 0)
{
if (ctx->fragmentedMessageCache.data == 0)
{ if ((err = SSLAllocBuffer(&ctx->fragmentedMessageCache, remaining)))
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
}
if (startingP != ctx->fragmentedMessageCache.data)
{ memcpy(ctx->fragmentedMessageCache.data, startingP, remaining);
ctx->fragmentedMessageCache.length = remaining;
}
}
else if (ctx->fragmentedMessageCache.data != 0)
{ if ((err = SSLFreeBuffer(&ctx->fragmentedMessageCache)))
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
}
return errSecSuccess;
}
OSStatus
DTLSProcessHandshakeRecord(SSLRecord rec, SSLContext *ctx)
{ OSStatus err = errSecParam;
size_t remaining;
UInt8 *p;
UInt8 *startingP;
const UInt32 head = 12;
assert(ctx->isDTLS);
remaining = rec.contents.length;
p = rec.contents.data;
startingP = p;
while (remaining > 0)
{
UInt8 msgtype;
UInt32 msglen;
UInt32 msgseq;
UInt32 fraglen;
UInt32 fragofs;
if (remaining < head) {
sslErrorLog("DTLSProcessHandshakeRecord: remaining too small (%lu out of %lu)\n", remaining, rec.contents.length);
assert(0); err = errSSLProtocol;
goto flushit;
}
msgtype = (SSLHandshakeType)*p++;
msglen = SSLDecodeInt(p, 3); p+=3;
msgseq = SSLDecodeInt(p, 2); p+=2;
fragofs = SSLDecodeInt(p, 3); p+=3;
fraglen = SSLDecodeInt(p, 3); p+=3;
remaining -= head;
SSLLogHdskMsg(msgtype, 0);
sslHdskMsgDebug("DTLS Hdsk Record: type=%u, len=%u, seq=%u (%u), f_ofs=%u, f_len=%u, remaining=%u",
msgtype, (int)msglen, (int)msgseq, (int)ctx->hdskMessageSeqNext, (int)fragofs, (int)fraglen, (int)remaining);
if(
((fraglen+fragofs) > msglen)
|| (fraglen > remaining)
|| (msgseq!=ctx->hdskMessageSeqNext)
|| (fragofs!=ctx->hdskMessageCurrentOfs)
|| (fragofs && (msgtype!=ctx->hdskMessageCurrent.type))
|| (fragofs && (msglen != ctx->hdskMessageCurrent.contents.length))
)
{
sslErrorLog("DTLSProcessHandshakeRecord: wrong fragment\n");
err = errSecSuccess;
goto flushit;
}
if(fragofs==0) {
sslHdskMsgDebug("Allocating hdsk buf for msg type %d", msgtype);
assert(ctx->hdskMessageCurrent.contents.data==NULL);
assert(ctx->hdskMessageCurrent.contents.length==0);
if((err=SSLAllocBuffer(&(ctx->hdskMessageCurrent.contents), msglen))) {
SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
ctx->hdskMessageCurrent.type = msgtype;
}
memcpy(ctx->hdskMessageCurrent.contents.data + ctx->hdskMessageCurrentOfs, p, fraglen);
ctx->hdskMessageCurrentOfs+=fraglen;
p+=fraglen;
remaining-=fraglen;
if(ctx->hdskMessageCurrentOfs == ctx->hdskMessageCurrent.contents.length) {
err = SSLProcessHandshakeMessage(ctx->hdskMessageCurrent, ctx);
if(err)
goto flushit;
if ((msgtype != SSL_HdskHelloRequest) && (msgtype != SSL_HdskHelloVerifyRequest))
{
uint8_t pseudo_header[head];
SSLBuffer header;
header.data=pseudo_header;
header.length=head;
pseudo_header[0]=msgtype;
SSLEncodeInt(pseudo_header+1, msglen, 3);
SSLEncodeInt(pseudo_header+4, msgseq, 2);
SSLEncodeInt(pseudo_header+6, 0, 3);
SSLEncodeInt(pseudo_header+9, msglen, 3);
if ((err = SSLHashSHA1.update(&ctx->shaState, &header)) != 0 ||
(err = SSLHashMD5.update(&ctx->md5State, &header)) != 0)
{
SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
goto flushit;
}
SSLBuffer *messageData=&ctx->hdskMessageCurrent.contents;
if ((err = SSLHashSHA1.update(&ctx->shaState, messageData)) != 0 ||
(err = SSLHashMD5.update(&ctx->md5State, messageData)) != 0)
{
SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
goto flushit;
}
sslHdskMsgDebug("Hashing %d bytes of msg seq %d\n", (int)messageData->length, (int)msgseq);
}
sslHdskMsgDebug("processed message of type %d", msgtype);
if ((err = SSLAdvanceHandshake(msgtype, ctx)) != 0)
{
sslErrorLog("AdvanceHandshake error: %" PRIdOSStatus "\n", err);
goto flushit;
}
SSLFreeBuffer(&(ctx->hdskMessageCurrent.contents));
ctx->hdskMessageCurrentOfs=0;
ctx->hdskMessageSeqNext++;
}
sslHdskMsgDebug("remaining = %ld", remaining);
}
return errSecSuccess;
flushit:
sslErrorLog("DTLSProcessHandshakeRecord: flusing record (err=%"PRIstatus")\n", err);
SSLFreeBuffer(&(ctx->hdskMessageCurrent.contents));
ctx->hdskMessageCurrentOfs=0;
return err;
}
OSStatus
DTLSRetransmit(SSLContext *ctx)
{
sslHdskMsgDebug("DTLSRetransmit in state %s. Last Sent = %d, Last Recv=%d, timeout=%f\n",
hdskStateToStr(ctx->state), ctx->hdskMessageSeq, ctx->hdskMessageSeqNext, ctx->timeout_duration);
if(ctx->hdskMessageRetryCount>10)
return errSSLConnectionRefused;
if(ctx->messageQueueContainsChangeCipherSpec) {
OSStatus err;
err = ctx->recFuncs->rollbackWriteCipher(ctx->recCtx);
if(err)
return err;
}
ctx->hdskMessageRetryCount++;
ctx->timeout_deadline = CFAbsoluteTimeGetCurrent()+((1<<ctx->hdskMessageRetryCount)*ctx->timeout_duration);
return SSLSendFlight(ctx);
}
static OSStatus
SSLProcessHandshakeMessage(SSLHandshakeMsg message, SSLContext *ctx)
{ OSStatus err;
err = errSecSuccess;
SSLLogHdskMsg(message.type, 0);
switch (message.type)
{ case SSL_HdskHelloRequest:
if (ctx->protocolSide != kSSLClientSide)
goto wrongMessage;
if (message.contents.length > 0)
err = errSSLProtocol;
break;
case SSL_HdskClientHello:
if (ctx->state != SSL_HdskStateServerUninit)
goto wrongMessage;
err = SSLProcessClientHello(message.contents, ctx);
break;
case SSL_HdskServerHello:
if (ctx->state != SSL_HdskStateServerHello)
goto wrongMessage;
err = SSLProcessServerHello(message.contents, ctx);
break;
#if ENABLE_DTLS
case SSL_HdskHelloVerifyRequest:
if (ctx->protocolSide != kSSLClientSide)
goto wrongMessage;
if(ctx->state != SSL_HdskStateServerHello)
goto wrongMessage;
err = SSLProcessServerHelloVerifyRequest(message.contents, ctx);
break;
#endif
case SSL_HdskCert:
if (ctx->state != SSL_HdskStateCert &&
ctx->state != SSL_HdskStateClientCert)
goto wrongMessage;
err = SSLProcessCertificate(message.contents, ctx);
break;
case SSL_HdskCertRequest:
if (((ctx->state != SSL_HdskStateHelloDone) &&
(ctx->state != SSL_HdskStateKeyExchange))
|| ctx->certRequested)
goto wrongMessage;
err = SSLProcessCertificateRequest(message.contents, ctx);
if (ctx->breakOnCertRequest)
ctx->signalCertRequest = true;
break;
case SSL_HdskServerKeyExchange:
switch(ctx->state) {
case SSL_HdskStateKeyExchange:
case SSL_HdskStateHelloDone:
break;
default:
goto wrongMessage;
}
err = SSLProcessServerKeyExchange(message.contents, ctx);
break;
case SSL_HdskServerHelloDone:
if (ctx->state != SSL_HdskStateHelloDone)
goto wrongMessage;
err = SSLProcessServerHelloDone(message.contents, ctx);
break;
case SSL_HdskCertVerify:
if (ctx->state != SSL_HdskStateClientCertVerify)
goto wrongMessage;
err = SSLProcessCertificateVerify(message.contents, ctx);
assert(ctx->protocolSide == kSSLServerSide);
if(err) {
ctx->clientCertState = kSSLClientCertRejected;
}
break;
case SSL_HdskClientKeyExchange:
if (ctx->state != SSL_HdskStateClientKeyExchange)
goto wrongMessage;
err = SSLProcessKeyExchange(message.contents, ctx);
break;
case SSL_HdskFinished:
if (ctx->state != SSL_HdskStateFinished)
goto wrongMessage;
err = SSLProcessFinished(message.contents, ctx);
break;
default:
goto wrongMessage;
break;
}
if (err && !ctx->sentFatalAlert)
{ if (err == errSSLProtocol)
SSLFatalSessionAlert(SSL_AlertIllegalParam, ctx);
else if (err == errSSLNegotiation)
SSLFatalSessionAlert(SSL_AlertHandshakeFail, ctx);
else if (err != errSSLWouldBlock &&
err != errSSLServerAuthCompleted &&
err != errSSLClientCertRequested)
SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
}
return err;
wrongMessage:
SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx);
return errSSLProtocol;
}
static OSStatus
SSLResumeServerSide(
SSLContext *ctx)
{
OSStatus err;
if ((err = SSLPrepareAndQueueMessage(SSLEncodeServerHello, ctx)) != 0)
return err;
if ((err = SSLInitPendingCiphers(ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
if ((err = SSLPrepareAndQueueMessage(SSLEncodeChangeCipherSpec,
ctx)) != 0)
return err;
if ((err = SSLPrepareAndQueueMessage(SSLEncodeFinishedMessage,
ctx)) != 0)
return err;
SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
return errSecSuccess;
}
OSStatus
SSLAdvanceHandshake(SSLHandshakeType processed, SSLContext *ctx)
{ OSStatus err;
SSLBuffer sessionIdentifier;
SSLResetFlight(ctx);
switch (processed)
{
#if ENABLE_DTLS
case SSL_HdskHelloVerifyRequest:
#endif
case SSL_HdskHelloRequest:
ctx->certRequested = 0;
ctx->certSent = 0;
ctx->certReceived = 0;
ctx->x509Requested = 0;
ctx->clientCertState = kSSLClientCertNone;
ctx->readCipher_ready = 0;
ctx->writeCipher_ready = 0;
if ((err = SSLPrepareAndQueueMessage(SSLEncodeClientHello, ctx)) != 0)
return err;
SSLChangeHdskState(ctx, SSL_HdskStateServerHello);
break;
case SSL_HdskClientHello:
assert(ctx->protocolSide == kSSLServerSide);
ctx->sessionMatch = 0;
if((ctx->negProtocolVersion==DTLS_Version_1_0) && (ctx->cookieVerified==false))
{
if((err=SSLPrepareAndQueueMessage(SSLEncodeServerHelloVerifyRequest, ctx)) !=0 )
return err;
break;
}
#if SSL_PAC_SERVER_ENABLE
if((ctx->sessionTicket.data != NULL) &&
(ctx->masterSecretCallback != NULL)) {
size_t secretLen = SSL_MASTER_SECRET_SIZE;
sslEapDebug("Server side resuming based on masterSecretCallback");
if ((err = SSLEncodeRandom(ctx->serverRandom, ctx)) != 0)
return err;
ctx->serverRandomValid = 1;
ctx->masterSecretCallback(ctx, ctx->masterSecretArg,
ctx->masterSecret, &secretLen);
ctx->sessionMatch = 1;
if ((err = FindCipherSpec(ctx)) != 0) {
return err;
}
if((err = SSLResumeServerSide(ctx)) != 0)
return err;
break;
}
#endif
if (ctx->sessionID.data != 0)
{ if (ctx->resumableSession.data != 0)
{
SSLProtocolVersion sessionProt;
if ((err = SSLRetrieveSessionID(ctx->resumableSession,
&sessionIdentifier, ctx)) != 0)
return err;
if ((err = SSLRetrieveSessionProtocolVersion(ctx->resumableSession,
&sessionProt, ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
if ((sessionIdentifier.length == ctx->sessionID.length) &&
(memcmp(sessionIdentifier.data, ctx->sessionID.data,
ctx->sessionID.length) == 0) &&
(sessionProt == ctx->negProtocolVersion))
{
sslLogResumSessDebug("===RESUMING SSL3 server-side session");
if ((err = SSLInstallSessionFromData(ctx->resumableSession,
ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
ctx->sessionMatch = 1;
SSLFreeBuffer(&sessionIdentifier);
if((err = SSLResumeServerSide(ctx)) != 0)
return err;
break;
}
else {
sslLogResumSessDebug(
"===FAILED TO RESUME SSL3 server-side session");
}
if ((err = SSLFreeBuffer(&sessionIdentifier)) != 0 ||
(err = SSLDeleteSessionData(ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
}
if ((err = SSLFreeBuffer(&ctx->sessionID)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
}
if (ctx->peerID.data != 0)
{
assert(ctx->sessionID.data == 0);
err = SSLAllocBuffer(&ctx->sessionID, SSL_SESSION_ID_LEN);
if (err == 0)
{
if((err = sslRand(&ctx->sessionID)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
}
}
if ((err = SSLPrepareAndQueueMessage(SSLEncodeServerHello, ctx)) != 0)
return err;
switch (ctx->selectedCipherSpecParams.keyExchangeMethod)
{ case SSL_NULL_auth:
#if APPLE_DH
case SSL_DH_anon:
case SSL_ECDH_anon:
if(ctx->clientAuth == kAlwaysAuthenticate) {
SSLFatalSessionAlert(SSL_AlertHandshakeFail, ctx);
return errSSLNegotiation;
}
ctx->tryClientAuth = false;
break;
#endif
case TLS_PSK:
break;
case SSL_RSA:
case SSL_DH_DSS:
case SSL_DH_RSA:
case SSL_DHE_DSS:
case SSL_DHE_RSA:
case SSL_ECDH_ECDSA:
case SSL_ECDHE_ECDSA:
case SSL_ECDH_RSA:
case SSL_ECDHE_RSA:
if(ctx->localCert == NULL) {
sslErrorLog("SSLAdvanceHandshake: No server key!\n");
return errSSLBadConfiguration;
}
if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificate,
ctx)) != 0)
return err;
break;
default:
sslErrorLog("SSLAdvanceHandshake: Unsupported KEM!\n");
return errSSLInternal;
}
{
bool doServerKeyExch = false;
switch(ctx->selectedCipherSpecParams.keyExchangeMethod) {
case SSL_RSA:
if(ctx->encryptPrivKeyRef != NULL) {
doServerKeyExch = true;
}
break;
case SSL_DH_anon:
case SSL_DHE_RSA:
case SSL_DHE_DSS:
doServerKeyExch = true;
break;
default:
break;
}
if(doServerKeyExch) {
err = SSLPrepareAndQueueMessage(SSLEncodeServerKeyExchange, ctx);
if(err) {
return err;
}
}
}
if (ctx->tryClientAuth)
{ if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificateRequest,
ctx)) != 0)
return err;
ctx->certRequested = 1;
ctx->clientCertState = kSSLClientCertRequested;
}
if ((err = SSLPrepareAndQueueMessage(SSLEncodeServerHelloDone, ctx)) != 0)
return err;
if (ctx->certRequested) {
SSLChangeHdskState(ctx, SSL_HdskStateClientCert);
}
else {
SSLChangeHdskState(ctx, SSL_HdskStateClientKeyExchange);
}
break;
case SSL_HdskServerHello:
ctx->sessionMatch = 0;
if (ctx->resumableSession.data != 0 && ctx->sessionID.data != 0)
{
SSLProtocolVersion sessionProt;
if ((err = SSLRetrieveSessionID(ctx->resumableSession,
&sessionIdentifier, ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
if ((err = SSLRetrieveSessionProtocolVersion(ctx->resumableSession,
&sessionProt, ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
if ((sessionIdentifier.length == ctx->sessionID.length) &&
(memcmp(sessionIdentifier.data, ctx->sessionID.data,
ctx->sessionID.length) == 0) &&
(sessionProt == ctx->negProtocolVersion))
{
sslLogResumSessDebug("===RESUMING SSL3 client-side session");
if ((err = SSLInstallSessionFromData(ctx->resumableSession,
ctx)) != 0 ||
(err = SSLInitPendingCiphers(ctx)) != 0 ||
(err = SSLFreeBuffer(&sessionIdentifier)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
ctx->sessionMatch = 1;
SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
break;
}
else {
sslLogResumSessDebug("===FAILED TO RESUME SSL3 client-side "
"session");
}
if ((err = SSLFreeBuffer(&sessionIdentifier)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
}
switch (ctx->selectedCipherSpecParams.keyExchangeMethod)
{
case SSL_NULL_auth:
case SSL_DH_anon:
SSLChangeHdskState(ctx, SSL_HdskStateKeyExchange);
break;
case SSL_RSA:
case SSL_DH_DSS:
case SSL_DH_RSA:
case SSL_RSA_EXPORT:
case SSL_DHE_DSS:
case SSL_DHE_RSA:
case SSL_Fortezza:
case SSL_ECDH_ECDSA:
case SSL_ECDHE_ECDSA:
case SSL_ECDH_RSA:
case SSL_ECDHE_RSA:
SSLChangeHdskState(ctx, SSL_HdskStateCert);
break;
case TLS_PSK:
SSLChangeHdskState(ctx, SSL_HdskStateHelloDone);
break;
default:
assert("Unknown key exchange method");
break;
}
break;
case SSL_HdskCert:
if (ctx->state == SSL_HdskStateCert)
switch (ctx->selectedCipherSpecParams.keyExchangeMethod)
{ case SSL_RSA:
case SSL_DH_DSS:
case SSL_DH_RSA:
case SSL_ECDH_ECDSA:
case SSL_ECDH_RSA:
SSLChangeHdskState(ctx, SSL_HdskStateHelloDone);
break;
case SSL_DHE_DSS:
case SSL_DHE_RSA:
case SSL_Fortezza:
case SSL_ECDHE_ECDSA:
case SSL_ECDHE_RSA:
SSLChangeHdskState(ctx, SSL_HdskStateKeyExchange);
break;
default:
assert("Unknown or unexpected key exchange method");
break;
}
else if (ctx->state == SSL_HdskStateClientCert)
{ SSLChangeHdskState(ctx, SSL_HdskStateClientKeyExchange);
if (ctx->peerCert != 0)
ctx->certReceived = 1;
}
break;
case SSL_HdskCertRequest:
if (ctx->peerCert == 0)
{ SSLFatalSessionAlert(SSL_AlertHandshakeFail, ctx);
return errSSLProtocol;
}
assert(ctx->protocolSide == kSSLClientSide);
ctx->certRequested = 1;
ctx->clientCertState = kSSLClientCertRequested;
break;
case SSL_HdskServerKeyExchange:
SSLChangeHdskState(ctx, SSL_HdskStateHelloDone);
break;
case SSL_HdskServerHelloDone:
if (ctx->state != SSL_HdskStateClientCert) {
if (ctx->signalServerAuth) {
ctx->signalServerAuth = false;
SSLChangeHdskState(ctx, SSL_HdskStateClientCert);
return errSSLServerAuthCompleted;
} else if (ctx->signalCertRequest) {
ctx->signalCertRequest = false;
SSLChangeHdskState(ctx, SSL_HdskStateClientCert);
return errSSLClientCertRequested;
} else if (ctx->signalClientAuth) {
ctx->signalClientAuth = false;
return errSSLClientAuthCompleted;
}
}
if (ctx->clientCertState == kSSLClientCertRequested) {
if (ctx->localCert != 0 && ctx->x509Requested) {
if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificate,
ctx)) != 0) {
return err;
}
}
else {
if(ctx->negProtocolVersion >= TLS_Version_1_0) {
if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificate,
ctx)) != 0) {
return err;
}
}
else {
if ((err = SSLSendAlert(SSL_AlertLevelWarning, SSL_AlertNoCert_RESERVED,
ctx)) != 0) {
return err;
}
}
}
}
if ((err = SSLPrepareAndQueueMessage(SSLEncodeKeyExchange, ctx)) != 0)
return err;
assert(ctx->sslTslCalls != NULL);
if ((err = ctx->sslTslCalls->generateMasterSecret(ctx)) != 0 ||
(err = SSLInitPendingCiphers(ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
memset(ctx->preMasterSecret.data, 0, ctx->preMasterSecret.length);
if ((err = SSLFreeBuffer(&ctx->preMasterSecret))) {
return err;
}
if (ctx->certSent) {
switch(ctx->negAuthType) {
case SSLClientAuth_RSASign:
case SSLClientAuth_ECDSASign:
if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificateVerify,
ctx)) != 0) {
return err;
}
break;
default:
break;
}
}
if ((err = SSLPrepareAndQueueMessage(SSLEncodeChangeCipherSpec,
ctx)) != 0) {
return err;
}
if ((err = SSLPrepareAndQueueMessage(SSLEncodeFinishedMessage, ctx)) != 0)
return err;
SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
break;
case SSL_HdskCertVerify:
SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
break;
case SSL_HdskClientKeyExchange:
assert(ctx->sslTslCalls != NULL);
if ((err = ctx->sslTslCalls->generateMasterSecret(ctx)) != 0 ||
(err = SSLInitPendingCiphers(ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
memset(ctx->preMasterSecret.data, 0, ctx->preMasterSecret.length);
if ((err = SSLFreeBuffer(&ctx->preMasterSecret)))
return err;
if (ctx->certReceived) {
SSLChangeHdskState(ctx, SSL_HdskStateClientCertVerify);
}
else {
SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
}
break;
case SSL_HdskFinished:
ctx->readCipher_ready = 1;
if (ctx->writePending_ready != 0)
{ if ((err = SSLPrepareAndQueueMessage(SSLEncodeChangeCipherSpec,
ctx)) != 0)
return err;
if ((err = SSLPrepareAndQueueMessage(SSLEncodeFinishedMessage,
ctx)) != 0)
return err;
}
if (ctx->protocolSide == kSSLServerSide) {
SSLChangeHdskState(ctx, SSL_HdskStateServerReady);
}
else {
SSLChangeHdskState(ctx, SSL_HdskStateClientReady);
}
if ((ctx->peerID.data != 0) && (ctx->sessionTicket.data == NULL)) {
SSLAddSessionData(ctx);
}
break;
default:
assert(0);
break;
}
ctx->hdskMessageRetryCount = 0;
ctx->timeout_deadline = CFAbsoluteTimeGetCurrent() + ctx->timeout_duration;
return SSLSendFlight(ctx);
}
OSStatus
SSLPrepareAndQueueMessage(EncodeMessageFunc msgFunc, SSLContext *ctx)
{ OSStatus err;
SSLRecord rec = {0, 0, {0, NULL}};
WaitingMessage *out;
WaitingMessage *queue;
if ((err = msgFunc(&rec, ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
goto fail;
}
if (rec.contentType == SSL_RecordTypeHandshake)
{
if ((err = SSLUpdateHandshakeMacs(&rec.contents, ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
goto fail;
}
SSLLogHdskMsg((SSLHandshakeType)rec.contents.data[0], 1);
ctx->hdskMessageSeq++;
}
err=errSSLInternal;
out = (WaitingMessage *)sslMalloc(sizeof(WaitingMessage));
if(out==NULL) goto fail;
out->next = NULL;
out->rec = rec;
queue=ctx->messageWriteQueue;
if (queue == NULL) {
sslHdskMsgDebug("Queuing first message in flight\n");
ctx->messageWriteQueue = out;
} else {
int n=1;
while (queue->next != 0) {
queue = queue->next;
n++;
}
sslHdskMsgDebug("Queuing message %d in flight\n", n);
queue->next = out;
}
return errSecSuccess;
fail:
SSLFreeBuffer(&rec.contents);
return err;
}
static
OSStatus SSLSendMessage(SSLRecord rec, SSLContext *ctx)
{
OSStatus err;
if ((err = SSLWriteRecord(rec, ctx)) != 0)
return err;
if(rec.contentType == SSL_RecordTypeChangeCipher) {
ctx->writeCipher_ready = 0;
ctx->wroteAppData = 0;
if ((err = ctx->recFuncs->advanceWriteCipher(ctx->recCtx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
ctx->writePending_ready = 0;
ctx->writeCipher_ready = 1;
}
return errSecSuccess;
}
static
OSStatus DTLSSendMessage(SSLRecord rec, SSLContext *ctx)
{
OSStatus err=errSecSuccess;
if(rec.contentType != SSL_RecordTypeHandshake) {
sslHdskMsgDebug("Not fragmenting message type=%d len=%d\n", (int)rec.contentType, (int)rec.contents.length);
if ((err = SSLWriteRecord(rec, ctx)) != 0)
return err;
if(rec.contentType == SSL_RecordTypeChangeCipher) {
ctx->writeCipher_ready = 0;
ctx->wroteAppData = 0;
if ((err = ctx->recFuncs->advanceWriteCipher(ctx->recCtx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
ctx->writePending_ready = 0;
ctx->writeCipher_ready = 1;
}
} else {
SSLRecord fragrec;
int msghead = 12;
size_t fraglen;
size_t len = rec.contents.length-msghead;
UInt32 seq = SSLDecodeInt(rec.contents.data+4, 2);
(void) seq; size_t ofs = 0;
sslHdskMsgDebug("Fragmenting msg seq %d (rl=%d, ml=%d)", (int)seq, (int)rec.contents.length,
SSLDecodeInt(rec.contents.data+1, 3));
SSLGetDatagramWriteSize(ctx, &fraglen);
fraglen -= msghead;
fragrec.contentType = rec.contentType;
fragrec.protocolVersion = rec.protocolVersion;
if((err=SSLAllocBuffer(&fragrec.contents, fraglen + msghead)))
return err;
memcpy(fragrec.contents.data,rec.contents.data, 6);
while(len>fraglen) {
sslHdskMsgDebug("Fragmenting msg seq %d (o=%d,l=%d)", (int)seq, (int)ofs, (int)fraglen);
SSLEncodeSize(fragrec.contents.data+6, ofs, 3);
SSLEncodeSize(fragrec.contents.data+9, fraglen, 3);
memcpy(fragrec.contents.data+msghead, rec.contents.data+msghead+ofs, fraglen);
if ((err = SSLWriteRecord(fragrec, ctx)) != 0)
goto cleanup;
len-=fraglen;
ofs+=fraglen;
}
sslHdskMsgDebug("Fragmenting msg seq %d - Last Fragment (o=%d,l=%d)", (int)seq, (int)ofs, (int)len);
SSLEncodeSize(fragrec.contents.data+6, ofs, 3);
SSLEncodeSize(fragrec.contents.data+9, len, 3);
memcpy(fragrec.contents.data+msghead, rec.contents.data+msghead+ofs, len);
fragrec.contents.length=len+msghead;
err = SSLWriteRecord(fragrec, ctx);
cleanup:
SSLFreeBuffer(&fragrec.contents);
}
return err;
}
OSStatus SSLResetFlight(SSLContext *ctx)
{
OSStatus err;
WaitingMessage *queue;
WaitingMessage *next;
int n=0;
queue=ctx->messageWriteQueue;
ctx->messageQueueContainsChangeCipherSpec=false;
while(queue) {
n++;
err = SSLFreeBuffer(&queue->rec.contents);
if (err != 0)
goto fail;
next=queue->next;
sslFree(queue);
queue=next;
}
ctx->messageWriteQueue=NULL;
return errSecSuccess;
fail:
check_noerr(err);
return err;
}
OSStatus SSLSendFlight(SSLContext *ctx)
{
OSStatus err;
WaitingMessage *queue;
int n=0;
queue=ctx->messageWriteQueue;
while(queue) {
if (ctx->isDTLS) {
err=DTLSSendMessage(queue->rec, ctx);
} else {
err=SSLSendMessage(queue->rec, ctx);
}
if (err != 0)
goto fail;
queue=queue->next;
n++;
}
return errSecSuccess;
fail:
check_noerr(err);
return err;
}
OSStatus
SSL3ReceiveSSL2ClientHello(SSLRecord rec, SSLContext *ctx)
{ OSStatus err;
if ((err = SSLInitMessageHashes(ctx)) != 0)
return err;
if ((err = SSLHashSHA1.update(&ctx->shaState, &rec.contents)) != 0 ||
(err = SSLHashMD5.update(&ctx->md5State, &rec.contents)) != 0)
{ SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
return err;
}
if ((err = SSLAdvanceHandshake(SSL_HdskClientHello, ctx)) != 0)
return err;
return errSecSuccess;
}
OSStatus sslGetMaxProtVersion(
SSLContext *ctx,
SSLProtocolVersion *version) {
if (ctx->maxProtocolVersion == SSL_Version_Undetermined)
return errSecBadReq;
*version = ctx->maxProtocolVersion;
return errSecSuccess;
}
#ifndef NDEBUG
#include <stdio.h>
char *hdskStateToStr(SSLHandshakeState state)
{
static char badStr[100];
switch(state) {
case SSL_HdskStateUninit:
return "Uninit";
case SSL_HdskStateServerUninit:
return "ServerUninit";
case SSL_HdskStateClientUninit:
return "ClientUninit";
case SSL_HdskStateGracefulClose:
return "GracefulClose";
case SSL_HdskStateErrorClose:
return "ErrorClose";
case SSL_HdskStateNoNotifyClose:
return "NoNotifyClose";
case SSL_HdskStateServerHello:
return "ServerHello";
case SSL_HdskStateKeyExchange:
return "KeyExchange";
case SSL_HdskStateCert:
return "Cert";
case SSL_HdskStateHelloDone:
return "HelloDone";
case SSL_HdskStateClientCert:
return "ClientCert";
case SSL_HdskStateClientKeyExchange:
return "ClientKeyExchange";
case SSL_HdskStateClientCertVerify:
return "ClientCertVerify";
case SSL_HdskStateChangeCipherSpec:
return "ChangeCipherSpec";
case SSL_HdskStateFinished:
return "Finished";
case SSL_HdskStateServerReady:
return "SSL_ServerReady";
case SSL_HdskStateClientReady:
return "SSL_ClientReady";
default:
sprintf(badStr, "Unknown state (%d(d)", state);
return badStr;
}
}
void SSLChangeHdskState(SSLContext *ctx, SSLHandshakeState newState)
{
sslHdskStateDebug("...hdskState = %s", hdskStateToStr(newState));
ctx->state = newState;
}
static char *hdskMsgToStr(SSLHandshakeType msg)
{
static char badStr[100];
switch(msg) {
case SSL_HdskHelloRequest:
return "SSL_HdskHelloRequest";
case SSL_HdskClientHello:
return "SSL_HdskClientHello";
case SSL_HdskServerHello:
return "SSL_HdskServerHello";
case SSL_HdskHelloVerifyRequest:
return "SSL_HdskHelloVerifyRequest";
case SSL_HdskCert:
return "SSL_HdskCert";
case SSL_HdskServerKeyExchange:
return "SSL_HdskServerKeyExchange";
case SSL_HdskCertRequest:
return "SSL_HdskCertRequest";
case SSL_HdskServerHelloDone:
return "SSL_HdskServerHelloDone";
case SSL_HdskCertVerify:
return "SSL_HdskCertVerify";
case SSL_HdskClientKeyExchange:
return "SSL_HdskClientKeyExchange";
case SSL_HdskFinished:
return "SSL_HdskFinished";
default:
sprintf(badStr, "Unknown msg (%d(d))", msg);
return badStr;
}
}
void SSLLogHdskMsg(SSLHandshakeType msg, char sent)
{
sslHdskMsgDebug("---%s handshake msg %s",
hdskMsgToStr(msg), (sent ? "sent" : "recv"));
}
#endif