|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: ssl3.c
//
// Contents: Ssl3 protocol handling functions
//
// Classes:
//
// Functions:
//
// History: 8-08-95 Ramas Created
// 1-14-97 Ramas Rewritten
//
//----------------------------------------------------------------------------
#include <spbase.h>
#include <_ssl3cli.h>
#include <time.h>
DWORD g_Ssl3CertTypes[] = { SSL3_CERTTYPE_RSA_SIGN, SSL3_CERTTYPE_DSS_SIGN}; DWORD g_cSsl3CertTypes = sizeof(g_Ssl3CertTypes) / sizeof(DWORD);
SP_STATUS WINAPI Ssl3ClientProtocolHandler( PSPContext pContext, PSPBuffer pCommInput, PSPBuffer pCommOutput);
SP_STATUS UpdateAndDuplicateIssuerList( PSPCredentialGroup pCredGroup, PBYTE * ppbIssuerList, PDWORD pcbIssuerList);
SP_STATUS WINAPI Ssl3ProtocolHandler( PSPContext pContext, PSPBuffer pCommInput, PSPBuffer pCommOutput) { SPBuffer MsgInput; SP_STATUS pctRet; DWORD cbInputData = 0;
if(pContext->Flags & CONTEXT_FLAG_CONNECTION_MODE) { do { MsgInput.pvBuffer = (PUCHAR) pCommInput->pvBuffer + cbInputData; MsgInput.cbData = pCommInput->cbData - cbInputData; MsgInput.cbBuffer = pCommInput->cbBuffer - cbInputData;
pctRet = Ssl3ClientProtocolHandler(pContext, &MsgInput, pCommOutput); cbInputData += MsgInput.cbData;
if(SP_STATE_CONNECTED == pContext->State) { break; } if(PCT_ERR_OK != pctRet) { break; }
} while(pCommInput->cbData - cbInputData);
pCommInput->cbData = cbInputData; } else { pctRet = Ssl3ClientProtocolHandler(pContext, pCommInput, pCommOutput); }
return(pctRet); }
/*
*************************************************************************** * Ssl3ProtocolHandler * Main Entry point for handling ssl3 type handshake messages... **************************************************************************** */ SP_STATUS WINAPI Ssl3ClientProtocolHandler ( PSPContext pContext, // in; state changes and temp data stored
PSPBuffer pCommInput, // in: decrypted in-place...
PSPBuffer pCommOutput) // out
{ SP_STATUS pctRet = PCT_ERR_OK; DWORD dwState; DWORD cbMsg; BYTE bContentType; BOOL fServer = (pContext->dwProtocol & SP_PROT_SERVERS); BOOL fProcessMultiple = FALSE; PBYTE pbData; DWORD cbData; DWORD cbBytesProcessed = 0; DWORD dwVersion; DWORD cbDecryptedMsg;
if(NULL != pCommOutput) { pCommOutput->cbData = 0; }
dwState = (pContext->State & 0xffff);
if(FNoInputState(dwState)) { // Process no input cases...
goto GenResponse; }
if(pContext->State == UNI_STATE_RECVD_UNIHELLO) { // We've just received a unified client_hello message.
// This always consists of a single SSL2-format handshake
// message.
if(pCommInput->cbData < 3) { return(PCT_INT_INCOMPLETE_MSG); }
bContentType = UNI_STATE_RECVD_UNIHELLO;
pbData = pCommInput->pvBuffer; cbData = pCommInput->cbData; cbDecryptedMsg = cbData; cbMsg = cbData;
goto Process; }
//
// The input buffer should contain one or more SSL3-format
// messages.
//
if(pCommInput->cbData < CB_SSL3_HEADER_SIZE) { return (PCT_INT_INCOMPLETE_MSG); }
//
// If there are multiple messages in the input buffer, and
// these messages exactly fill the buffer, then we should
// process all of the messages during this call. If there
// are any fractions, then we should just process the first
// message.
//
pbData = pCommInput->pvBuffer; cbData = pCommInput->cbData;
while(TRUE) { if(cbData < CB_SSL3_HEADER_SIZE) { break; }
bContentType = pbData[0];
if(bContentType != SSL3_CT_CHANGE_CIPHER_SPEC && bContentType != SSL3_CT_ALERT && bContentType != SSL3_CT_HANDSHAKE && bContentType != SSL3_CT_APPLICATIONDATA) { break; }
dwVersion = COMBINEBYTES(pbData[1], pbData[2]);
if(dwVersion != SSL3_CLIENT_VERSION && dwVersion != TLS1_CLIENT_VERSION) { break; }
cbMsg = COMBINEBYTES(pbData[3], pbData[4]); cbDecryptedMsg = cbMsg;
if(CB_SSL3_HEADER_SIZE + cbMsg > cbData) { break; }
pbData += CB_SSL3_HEADER_SIZE + cbMsg; cbData -= CB_SSL3_HEADER_SIZE + cbMsg;
if(cbData == 0) { fProcessMultiple = TRUE; break; } }
//
// Step through the messages in the input buffer, processing
// each one in turn.
//
pbData = pCommInput->pvBuffer; cbData = pCommInput->cbData;
while(TRUE) { //
// Validate the message.
//
if(cbData < CB_SSL3_HEADER_SIZE) { return (PCT_INT_INCOMPLETE_MSG); }
bContentType = pbData[0];
if(bContentType != SSL3_CT_CHANGE_CIPHER_SPEC && bContentType != SSL3_CT_ALERT && bContentType != SSL3_CT_HANDSHAKE && bContentType != SSL3_CT_APPLICATIONDATA) { return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); }
cbMsg = COMBINEBYTES(pbData[3], pbData[4]); cbDecryptedMsg = cbMsg;
if(CB_SSL3_HEADER_SIZE + cbMsg > cbData) { return (PCT_INT_INCOMPLETE_MSG); }
cbBytesProcessed += CB_SSL3_HEADER_SIZE + cbMsg;
pCommInput->cbData = cbBytesProcessed;
//
// Decrypt the message.
//
if(FSsl3Cipher(fServer)) { SPBuffer Message;
Message.cbBuffer = CB_SSL3_HEADER_SIZE + cbMsg; Message.cbData = CB_SSL3_HEADER_SIZE + cbMsg; Message.pvBuffer = pbData;
// Decrypt the message.
pctRet = UnwrapSsl3Message(pContext, &Message);
// if we have to send ALERT messages to the peer, build it!
if(TLS1_STATE_ERROR == pContext->State) { goto GenResponse; }
if(pctRet != PCT_ERR_OK) { return pctRet; }
cbDecryptedMsg = COMBINEBYTES(pbData[3], pbData[4]); }
pbData += CB_SSL3_HEADER_SIZE; cbData -= CB_SSL3_HEADER_SIZE;
Process:
pctRet = SPProcessMessage(pContext, bContentType, pbData, cbDecryptedMsg) ; if(pctRet != PCT_ERR_OK) { return pctRet; }
pbData += cbMsg; cbData -= cbMsg;
// If a response is required at this state then break out of the
// message processing loop.
if(F_RESPONSE(pContext->State)) {
GenResponse:
if(pContext->State > SSL3_STATE_GEN_START) { pctRet = SPGenerateResponse(pContext, pCommOutput); }
return pctRet; }
// If the handshake is complete then stop processing messages.
// We don't want to accidentally process any application data
// messages.
if(pContext->State == SP_STATE_CONNECTED) { break; }
if(fProcessMultiple && cbData > 0) { continue; }
break; }
return pctRet; }
/*
*************************************************************************** * Ssl3HandleFinish * Handle the handshake finished message.. **************************************************************************** */
SP_STATUS Ssl3HandleFinish( PSPContext pContext, PBYTE pbMsg, // in
DWORD cbMsg, // in
BOOL fClient) // in
{ BYTE rgbDigest[CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN]; SP_STATUS pctRet = PCT_ERR_OK; PBYTE pb = pbMsg; DWORD dwSize; DWORD dwSizeExpect = CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN;
SP_BEGIN("Ssl3HandleFinish");
//is this the right message type
if(*pb != SSL3_HS_FINISHED) { pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); goto error; }
if(pContext->RipeZombie->fProtocol & SP_PROT_TLS1) { dwSizeExpect = CB_TLS1_VERIFYDATA; }
dwSize = ((INT)pb[1] << 16) + ((INT)pb[2] << 8) + (INT)pb[3]; pb += sizeof(SHSH);
// Is the payload the size that we expect?
if(dwSize != dwSizeExpect) { pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); goto error; }
// Is the overall message size what we expect? Don't allow buffer overrun!
if(sizeof(SHSH) + dwSize != cbMsg) { pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); goto error; }
// Build our end finish message to compare
if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3) { pctRet = Ssl3BuildFinishMessage(pContext, rgbDigest, &rgbDigest[CB_MD5_DIGEST_LEN], fClient); } else { pctRet = Tls1BuildFinishMessage(pContext, rgbDigest, sizeof(rgbDigest), fClient); } if(pctRet != PCT_ERR_OK) { goto error; }
// compare the two...
if (memcmp(rgbDigest, pb, dwSizeExpect)) { DebugLog((DEB_WARN, "Finished MAC didn't matchChecksum Invalid\n")); pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); goto error; } SP_RETURN(PCT_ERR_OK);
error:
SP_RETURN(pctRet); }
/*
*************************************************************************** * Ssl3PackClientHello **************************************************************************** */
SP_STATUS Ssl3PackClientHello( PSPContext pContext, PSsl2_Client_Hello pCanonical, PSPBuffer pCommOutput) { DWORD cbHandshake; DWORD cbMessage; PBYTE pbMessage = NULL; DWORD dwCipherSize; DWORD i; BOOL fAllocated = FALSE;
//
// opaque SessionID<0..32>;
//
// struct {
// ProtocolVersion client_version;
// Random random;
// SessionID session_id;
// CipherSuite cipher_suites<2..2^16-1>;
// CompressionMethod compression_methods<1..2^8-1>;
// } ClientHello;
//
SP_BEGIN("Ssl3PackClientHello");
if(pCanonical == NULL || pCommOutput == NULL) { SP_RETURN(PCT_INT_INTERNAL_ERROR); }
// Compute size of the ClientHello message.
cbHandshake = sizeof(SHSH) + 2 + CB_SSL3_RANDOM + 1 + pCanonical->cbSessionID + 2 + pCanonical->cCipherSpecs * sizeof(short) + 2; // Size of compression algorithm 1 + null (0)
// Compute size of encrypted ClientHello message.
cbMessage = Ssl3CiphertextLen(pContext, cbHandshake, TRUE);
if(pCommOutput->pvBuffer) { // Application has allocated memory.
if(pCommOutput->cbBuffer < cbMessage) { pCommOutput->cbData = cbMessage; return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); } fAllocated = TRUE; } else { // Schannel is to allocate memory.
pCommOutput->cbBuffer = cbMessage; pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if(pCommOutput->pvBuffer == NULL) { return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); } } pCommOutput->cbData = cbMessage;
// Initialize the member variables.
pbMessage = (PBYTE)pCommOutput->pvBuffer + sizeof(SWRAP) + sizeof(SHSH);
*pbMessage++ = MSBOF(pCanonical->dwVer); *pbMessage++ = LSBOF(pCanonical->dwVer);
CopyMemory(pbMessage, pCanonical->Challenge, CB_SSL3_RANDOM); pbMessage += CB_SSL3_RANDOM;
*pbMessage++ = (BYTE)pCanonical->cbSessionID; CopyMemory(pbMessage, pCanonical->SessionID, pCanonical->cbSessionID); pbMessage += pCanonical->cbSessionID;
dwCipherSize = pCanonical->cCipherSpecs * sizeof(short); *pbMessage++ = MSBOF(dwCipherSize); *pbMessage++ = LSBOF(dwCipherSize); for(i = 0; i < pCanonical->cCipherSpecs; i++) { *pbMessage++ = MSBOF(pCanonical->CipherSpecs[i]); *pbMessage++ = LSBOF(pCanonical->CipherSpecs[i]); }
*pbMessage++ = 1; // One compression method;
*pbMessage++ = 0x00; // NULL compression method.
// Fill in Handshake structure.
SetHandshake((PBYTE)pCommOutput->pvBuffer + sizeof(SWRAP), SSL3_HS_CLIENT_HELLO, NULL, (WORD)(cbHandshake - sizeof(SHSH)));
// Save the ClientHello message so we can hash it later, once
// we know what algorithm and CSP we're using.
if(pContext->pClientHello) { SPExternalFree(pContext->pClientHello); } pContext->cbClientHello = cbHandshake; pContext->pClientHello = SPExternalAlloc(pContext->cbClientHello); if(pContext->pClientHello == NULL) { SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } CopyMemory(pContext->pClientHello, (PBYTE)pCommOutput->pvBuffer + sizeof(SWRAP), pContext->cbClientHello); pContext->dwClientHelloProtocol = SP_PROT_SSL3_CLIENT;
// Fill in record header and encrypt the message.
SP_RETURN(SPSetWrap(pContext, pCommOutput->pvBuffer, SSL3_CT_HANDSHAKE, cbHandshake, TRUE, NULL)); }
//+---------------------------------------------------------------------------
//
// Function: Ssl3GenerateRandom
//
// Synopsis: Create a client_random or server_random value.
//
// Arguments: [pRandom] -- Output buffer.
//
// History: 04-03-2001 jbanes Created.
//
// Notes: struct {
// uint32 gmt_unix_time;
// opaque random_bytes[28];
// } Random;
//
// gmt_unix_time
// The current time and date in standard UNIX 32-bit format
// (seconds since the midnight starting Jan 1, 1970, GMT)
// according to the sender's internal clock. Clocks are not
// required to be set correctly by the basic TLS Protocol;
// higher level or application protocols may define
// additional requirements.
//
// random_bytes
// 28 bytes generated by a secure random number generator.
//
//----------------------------------------------------------------------------
NTSTATUS Ssl3GenerateRandom( PBYTE pRandom) { time_t UnixTime;
time(&UnixTime);
*(DWORD *)pRandom = htonl((DWORD)UnixTime);
return GenerateRandomBits(pRandom + sizeof(DWORD), CB_SSL3_RANDOM - sizeof(DWORD)); }
/*
*************************************************************************** * GenerateSsl3ClientHello * v3 client hello build it on pOutpu **************************************************************************** */
SP_STATUS WINAPI GenerateSsl3ClientHello( PSPContext pContext, PSPBuffer pOutput) { Ssl2_Client_Hello HelloMessage; SP_STATUS pctRet;
SP_BEGIN("GenerateSsl3ClientHello");
pctRet = Ssl3GenerateRandom(pContext->pChallenge); if(NT_SUCCESS(pctRet)) { pContext->cbChallenge = CB_SSL3_RANDOM; pctRet = GenerateUniHelloMessage(pContext, &HelloMessage, SP_PROT_SSL3_CLIENT); if(PCT_ERR_OK == pctRet) { pctRet = Ssl3PackClientHello(pContext, &HelloMessage, pOutput); } }
SP_RETURN(pctRet); }
SP_STATUS WINAPI GenerateTls1ClientHello( PSPContext pContext, PSPBuffer pOutput, DWORD dwProtocol) { Ssl2_Client_Hello HelloMessage; SP_STATUS pctRet;
SP_BEGIN("GenerateTls1ClientHello");
pctRet = Ssl3GenerateRandom(pContext->pChallenge);
if(NT_SUCCESS(pctRet)) { pContext->cbChallenge = CB_SSL3_RANDOM; pctRet = GenerateUniHelloMessage(pContext, &HelloMessage, dwProtocol); if(PCT_ERR_OK == pctRet) { pctRet = Ssl3PackClientHello(pContext, &HelloMessage, pOutput); } }
SP_RETURN(pctRet); }
/*
*************************************************************************** * ParseCertificateRequest * if server is requesting client-auth, server will send this message. * parse them and store it in pContext, for later use.... **************************************************************************** */
SP_STATUS ParseCertificateRequest( PSPContext pContext, PBYTE pb, DWORD dwcb) { SP_STATUS pctRet; UCHAR cbCertType; DWORD cbIssuerList; PBYTE pbNewIssuerList; DWORD cbNewIssuerList;
UCHAR i, j;
//
// enum {
// rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
// rsa_ephemeral_dh(5), dss_ephemeral_dh(6), fortezza_dms(20), (255)
// } ClientCertificateType;
//
// opaque DistinguishedName<1..2^16-1>;
//
// struct {
// ClientCertificateType certificate_types<1..2^8-1>;
// DistinguishedName certificate_authorities<3..2^16-1>;
// } CertificateRequest;
//
//
// Skip over handshake header.
//
if(dwcb < sizeof(SHSH)) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; } pb += sizeof(SHSH); dwcb -= sizeof(SHSH);
//
// Parse certificate type list.
//
if(dwcb < 1) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; }
cbCertType = pb[0];
pb += 1; dwcb -= 1;
if(cbCertType > dwcb) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; }
pContext->cSsl3ClientCertTypes = 0; for(i = 0; i < cbCertType; i++) { for(j = 0; j < g_cSsl3CertTypes; j++) { if(g_Ssl3CertTypes[j] == pb[i]) { pContext->Ssl3ClientCertTypes[pContext->cSsl3ClientCertTypes++] = g_Ssl3CertTypes[j]; } } }
pb += cbCertType; dwcb -= cbCertType;
//
// Parse issuer list.
//
if(dwcb < 2) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; }
cbIssuerList = COMBINEBYTES(pb[0], pb[1]);
pb += 2; dwcb -= 2;
if(dwcb < cbIssuerList) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; }
pctRet = FormatIssuerList(pb, cbIssuerList, NULL, &cbNewIssuerList); if(pctRet != PCT_ERR_OK) { goto cleanup; }
pbNewIssuerList = SPExternalAlloc(2 + cbNewIssuerList); if(pbNewIssuerList == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; }
pbNewIssuerList[0] = MSBOF(cbNewIssuerList); pbNewIssuerList[1] = LSBOF(cbNewIssuerList);
pctRet = FormatIssuerList(pb, cbIssuerList, pbNewIssuerList + 2, &cbNewIssuerList); if(pctRet != PCT_ERR_OK) { SPExternalFree(pbNewIssuerList); goto cleanup; }
//
// Store issuer list in context structure.
//
if(pContext->pbIssuerList) { SPExternalFree(pContext->pbIssuerList); } pContext->pbIssuerList = pbNewIssuerList; pContext->cbIssuerList = cbNewIssuerList + 2;
cleanup:
return (pctRet); }
/*
*************************************************************************** * BuildCertVerify * Build certificate Verify message. This is sent by client if sending * client certificate. **************************************************************************** */
SP_STATUS BuildCertVerify( PSPContext pContext, PBYTE pbCertVerify, DWORD *pcbCertVerify) { SP_STATUS pctRet; PBYTE pbSigned; DWORD cbSigned; BYTE rgbHashValue[CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN]; DWORD cbHashValue; ALG_ID aiHash; PBYTE pbMD5; PBYTE pbSHA; DWORD cbHeader; DWORD cbBytesRequired;
PSPCredential pCred;
if((pcbCertVerify == NULL) || (pContext == NULL) || (pContext->RipeZombie == NULL) || (pContext->pActiveClientCred == NULL)) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
pCred = pContext->pActiveClientCred;
//
// digitally-signed struct {
// opaque md5_hash[16];
// opaque sha_hash[20];
// } Signature;
//
// struct {
// Signature signature;
// } CertificateVerify;
//
// CertificateVerify.signature.md5_hash = MD5(master_secret + pad2 +
// MD5(handshake_messages + master_secret + pad1));
//
// CertificateVerify.signature.sha_hash = SHA(master_secret + pad2 +
// SHA(handshake_messages + master_secret + pad1));
//
cbHeader = sizeof(SHSH);
cbBytesRequired = cbHeader + 2 + pCred->pPublicKey->cbPublic; if(pbCertVerify == NULL) { *pcbCertVerify = cbBytesRequired; return PCT_ERR_OK; }
if(*pcbCertVerify < sizeof(SHSH)) { *pcbCertVerify = cbBytesRequired; return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); }
//
// Generate hash values
//
switch(pCred->pPublicKey->pPublic->aiKeyAlg) { case CALG_RSA_SIGN: case CALG_RSA_KEYX: aiHash = CALG_SSL3_SHAMD5; pbMD5 = rgbHashValue; pbSHA = rgbHashValue + CB_MD5_DIGEST_LEN; cbHashValue = CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN; break;
case CALG_DSS_SIGN: aiHash = CALG_SHA; pbMD5 = NULL; pbSHA = rgbHashValue; cbHashValue = CB_SHA_DIGEST_LEN; break;
default: return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
if(pContext->RipeZombie->fProtocol & SP_PROT_TLS1) { pctRet = Tls1ComputeCertVerifyHashes(pContext, pbMD5, pbSHA); } else { pctRet = Ssl3ComputeCertVerifyHashes(pContext, pbMD5, pbSHA); } if(pctRet != PCT_ERR_OK) { return pctRet; }
//
// Sign hash values.
//
pbSigned = pbCertVerify + sizeof(SHSH) + 2; cbSigned = cbBytesRequired - sizeof(SHSH) - 2;
DebugLog((DEB_TRACE, "Sign certificate_verify message.\n"));
pctRet = SignHashUsingCred(pCred, aiHash, rgbHashValue, cbHashValue, pbSigned, &cbSigned); if(pctRet != PCT_ERR_OK) { return pctRet; }
DebugLog((DEB_TRACE, "Certificate_verify message signed successfully.\n"));
//
// Fill in header.
//
pbCertVerify[cbHeader + 0] = MSBOF(cbSigned); pbCertVerify[cbHeader + 1] = LSBOF(cbSigned);
SetHandshake(pbCertVerify, SSL3_HS_CERTIFICATE_VERIFY, NULL, (WORD)(cbSigned + 2));
*pcbCertVerify = cbHeader + 2 + cbSigned;
return PCT_ERR_OK; }
SP_STATUS HandleCertVerify( PSPContext pContext, PBYTE pbMessage, DWORD cbMessage) { PBYTE pbSignature; DWORD cbSignature; BYTE rgbHashValue[CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN]; DWORD cbHashValue; HCRYPTPROV hProv; ALG_ID aiHash; PBYTE pbMD5; PBYTE pbSHA; SP_STATUS pctRet;
if((pContext == NULL) || (pContext->RipeZombie == NULL)) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
if(pContext->RipeZombie->pRemotePublic == NULL) { return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); }
if(cbMessage < sizeof(SHSH) + 2) { return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); }
pbSignature = pbMessage + sizeof(SHSH);
cbSignature = ((DWORD)pbSignature[0] << 8) + pbSignature[1]; pbSignature += 2;
if(sizeof(SHSH) + 2 + cbSignature > cbMessage) { return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); }
switch(pContext->RipeZombie->pRemotePublic->pPublic->aiKeyAlg) { case CALG_RSA_SIGN: case CALG_RSA_KEYX: hProv = g_hRsaSchannel; aiHash = CALG_SSL3_SHAMD5; pbMD5 = rgbHashValue; pbSHA = rgbHashValue + CB_MD5_DIGEST_LEN; cbHashValue = CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN; break;
case CALG_DSS_SIGN: hProv = g_hDhSchannelProv; aiHash = CALG_SHA; pbMD5 = NULL; pbSHA = rgbHashValue; cbHashValue = CB_SHA_DIGEST_LEN; break;
default: return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
if(pContext->RipeZombie->fProtocol & SP_PROT_TLS1) { pctRet = Tls1ComputeCertVerifyHashes(pContext, pbMD5, pbSHA); } else { pctRet = Ssl3ComputeCertVerifyHashes(pContext, pbMD5, pbSHA); } if(pctRet != PCT_ERR_OK) { return pctRet; }
// Verify signature.
DebugLog((DEB_TRACE, "Verify certificate_verify signature.\n")); pctRet = SPVerifySignature(hProv, pContext->RipeZombie->pRemotePublic, aiHash, rgbHashValue, cbHashValue, pbSignature, cbSignature, FALSE); if(pctRet != PCT_ERR_OK) { return SP_LOG_RESULT(pctRet); } DebugLog((DEB_TRACE, "Certificate_verify verified successfully.\n"));
return PCT_ERR_OK; }
SP_STATUS FormatIssuerList( PBYTE pbInput, DWORD cbInput, PBYTE pbIssuerList, DWORD * pcbIssuerList) { DWORD cbIssuerList = 0; PBYTE pbList = pbInput; DWORD cbList = cbInput; DWORD cbIssuer; DWORD cbTag;
while(cbList) { if(cbList < 2) { return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); }
cbIssuer = COMBINEBYTES(pbList[0], pbList[1]);
pbList += 2; cbList -= 2;
if(cbList < cbIssuer) { return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); }
if(pbIssuerList) { pbIssuerList[0] = MSBOF(cbIssuer); pbIssuerList[1] = LSBOF(cbIssuer); pbIssuerList += 2; } cbIssuerList += 2;
if(pbList[0] != SEQUENCE_TAG) { // An old version of Netscape Enterprise Server had a bug, in that
// the issuer names did not start off with a SEQUENCE tag. Patch
// the name appropriately before storing it into pContext.
cbTag = CbLenOfEncode(cbIssuer, pbIssuerList);
if(pbIssuerList) { pbIssuerList += cbTag; } cbIssuerList += cbTag; }
if(pbIssuerList) { memcpy(pbIssuerList, pbList, cbIssuer); pbIssuerList += cbIssuer; } cbIssuerList += cbIssuer;
pbList += cbIssuer; cbList -= cbIssuer; }
*pcbIssuerList = cbIssuerList;
return(PCT_ERR_OK); }
/*
*************************************************************************** * CbLenOfEncode * Retunrs the length of the ASN encoding, for certificate **************************************************************************** */
DWORD CbLenOfEncode(DWORD dw, PBYTE pbDst) { BYTE lenbuf[8]; DWORD length = sizeof(lenbuf) - 1; LONG lth;
if (0x80 > dw) { lenbuf[length] = (BYTE)dw; lth = 1; } else { while (0 < dw) { lenbuf[length] = (BYTE)(dw & 0xff); length -= 1; dw = (dw >> 8) & 0x00ffffff; } lth = sizeof(lenbuf) - length; lenbuf[length] = (BYTE)(0x80 | (lth - 1)); }
if(NULL != pbDst) { pbDst[0] = 0x30; memcpy(pbDst+1, &lenbuf[length], lth);
} return ++lth; //for 0x30
}
/*
*************************************************************************** * SPDigestServerHello * Parse the server hello from the server. **************************************************************************** */ SP_STATUS SPDigestServerHello( PSPContext pContext, PUCHAR pbSrvHello, DWORD cbSrvHello, PBOOL pfRestart) { SSH *pssh; SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE; SHORT wCipher, wCompression; BOOL fRestartServer = FALSE; PSessCacheItem pZombie; PSPCredentialGroup pCred; DWORD dwVersion;
UNREFERENCED_PARAMETER(cbSrvHello);
// We should have a zombie identity here
if(pContext->RipeZombie == NULL) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); } pZombie = pContext->RipeZombie;
pCred = pContext->pCredGroup; if(!pCred) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
SP_BEGIN("SPDigestServerHello");
// Pad the client random structure with zero's if the challenge is
// smaller than the normal SSL3 size (SSL2 v3 hello, Unihello, PCT1 wierdness if
// we add it)
FillMemory(pContext->rgbS3CRandom, CB_SSL3_RANDOM - pContext->cbChallenge, 0);
CopyMemory( pContext->rgbS3CRandom + CB_SSL3_RANDOM - pContext->cbChallenge, pContext->pChallenge, pContext->cbChallenge);
pssh = (SSH *)pbSrvHello;
if(pssh->cbSessionId > CB_SSL3_SESSION_ID) { SP_RETURN(PCT_ERR_ILLEGAL_MESSAGE); }
dwVersion = COMBINEBYTES(pssh->bMajor, pssh->bMinor);
if((dwVersion == SSL3_CLIENT_VERSION) && (pCred->grbitEnabledProtocols & SP_PROT_SSL3_CLIENT)) { // This appears to be an SSL3 server_hello.
pContext->dwProtocol = SP_PROT_SSL3_CLIENT; } else if((dwVersion == TLS1_CLIENT_VERSION) && (pCred->grbitEnabledProtocols & SP_PROT_TLS1_CLIENT)) { // This appears to be a TLS server_hello.
pContext->dwProtocol = SP_PROT_TLS1_CLIENT; } else { return SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH); }
DebugLog((DEB_TRACE, "**********Protocol***** %x\n", pContext->dwProtocol));
CopyMemory(pContext->rgbS3SRandom, pssh->rgbRandom, CB_SSL3_RANDOM); wCipher = (SHORT)COMBINEBYTES(pssh->rgbSessionId[pssh->cbSessionId], pssh->rgbSessionId[pssh->cbSessionId+1]); wCompression = pssh->rgbSessionId[pssh->cbSessionId+2];
if( wCompression != 0) { SP_RETURN(PCT_ERR_ILLEGAL_MESSAGE); } if(pZombie->hMasterKey && pZombie->cbSessionID && memcmp(pZombie->SessionID, PbSessionid(pssh), pssh->cbSessionId) == 0) { fRestartServer = TRUE;
if(!pZombie->ZombieJuju) { DebugLog((DEB_WARN, "Session expired on client machine, but not on server.\n")); } }
if(!fRestartServer) { if(pZombie->hMasterKey != 0) { // We've attempted to do a reconnect and the server has
// blown us off. In this case we must use a new and different
// cache entry.
pZombie->ZombieJuju = FALSE;
if(!SPCacheClone(&pContext->RipeZombie)) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); } pZombie = pContext->RipeZombie; }
pZombie->fProtocol = pContext->dwProtocol;
#if DBG
if(pssh->cbSessionId == 0) { DebugLog((DEB_WARN, "Server didn't give us a session id!\n")); } #endif
if(pssh->cbSessionId) { CopyMemory(pZombie->SessionID, PbSessionid(pssh), pssh->cbSessionId); pZombie->cbSessionID = pssh->cbSessionId; } }
pctRet = Ssl3SelectCipher(pContext, wCipher); if(pctRet != PCT_ERR_OK) { SP_RETURN(SP_LOG_RESULT(pctRet)); }
if(fRestartServer) { // Make a new set of session keys.
pctRet = MakeSessionKeys(pContext, pZombie->hMasterProv, pZombie->hMasterKey); if(PCT_ERR_OK != pctRet) { SP_RETURN(SP_LOG_RESULT(pctRet)); } }
// Initialize handshake hashes and hash ClientHello message. This
// must be done _after_ the ServerHello message is processed,
// so that the correct CSP context is used.
if(pContext->dwClientHelloProtocol == SP_PROT_PCT1_CLIENT || pContext->dwClientHelloProtocol == SP_PROT_SSL2_CLIENT) { // Skip over the 2 byte header.
pctRet = UpdateHandshakeHash(pContext, pContext->pClientHello + 2, pContext->cbClientHello - 2, TRUE); } else { pctRet = UpdateHandshakeHash(pContext, pContext->pClientHello, pContext->cbClientHello, TRUE); } SPExternalFree(pContext->pClientHello); pContext->pClientHello = NULL; pContext->cbClientHello = 0;
*pfRestart = fRestartServer;
SP_RETURN(pctRet); }
/*
*************************************************************************** * SpDigestRemoteCertificate * Process the Certificate message. This is common for server/client. **************************************************************************** */
SP_STATUS SpDigestRemoteCertificate ( PSPContext pContext, PUCHAR pb, DWORD cb) { SP_STATUS pctRet = PCT_ERR_OK; CERT *pcert; DWORD cbCert; DWORD dwSize;
SP_BEGIN("SpDigestRemoteCertificate");
if(pContext == NULL) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
if((pContext->RipeZombie->fProtocol & SP_PROT_SERVERS) && (pContext->fCertReq == FALSE)) { // We're a server and the client just sent us an
// unexpected certificate message.
SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG)); }
pcert = (CERT *)pb;
if(cb < CB_SSL3_CERT_VECTOR + sizeof(SHSH)) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INCOMPLETE_MSG)); } dwSize = ((INT)pcert->bcb24 << 16) + ((INT)pcert->bcbMSB << 8) + (INT)pcert->bcbLSB;
cbCert = COMBINEBYTES(pcert->bcbMSBClist, pcert->bcbLSBClist); cbCert |= (pcert->bcbClist24 << 16);
if(dwSize != cbCert + CB_SSL3_CERT_VECTOR) { SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG)); }
if(dwSize + sizeof(SHSH) > cb) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INCOMPLETE_MSG)); }
if(cbCert == 0) { if(pContext->RipeZombie->fProtocol & SP_PROT_CLIENTS) { // Error out if the server certificate is zero length
SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG)); } else { DebugLog((DEB_WARN, "Zero length client certificate received.\n")); } }
if(cbCert != 0) //for tls1, it could be zero length.
{ // The certificate type is derived from the key exchange method
// but most currently use X509_ASN_ENCODING
pctRet = SPLoadCertificate( SP_PROT_SSL3_CLIENT, X509_ASN_ENCODING, (PUCHAR)&pcert->bcbCert24, cbCert, &pContext->RipeZombie->pRemoteCert);
if(PCT_ERR_OK != pctRet) { SP_RETURN(pctRet); } if(pContext->RipeZombie->pRemotePublic != NULL) { SPExternalFree(pContext->RipeZombie->pRemotePublic); pContext->RipeZombie->pRemotePublic = NULL; }
pctRet = SPPublicKeyFromCert(pContext->RipeZombie->pRemoteCert, &pContext->RipeZombie->pRemotePublic, NULL);
if(PCT_ERR_OK != pctRet) { SP_RETURN(pctRet); } }
SP_RETURN(pctRet); }
/*
*************************************************************************** * SPDigestSrvKeyX * Digest the Server key exhcnage message. * this function encrypts the Pre-master secret with the public-key from this * message **************************************************************************** */
SP_STATUS SPDigestSrvKeyX( PSPContext pContext, PUCHAR pbServerExchangeValue, DWORD cbServerExchangeValue) { SP_STATUS pctRet;
if((pContext->pKeyExchInfo == NULL) || ((pContext->pKeyExchInfo->fProtocol & SP_PROT_SSL3TLS1_CLIENTS) == 0)) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
SP_ASSERT(NULL == pContext->pbEncryptedKey);
pctRet = pContext->pKeyExchInfo->System->GenerateClientExchangeValue( pContext, pbServerExchangeValue, cbServerExchangeValue, NULL, NULL, NULL, &pContext->cbEncryptedKey);
if(pctRet != PCT_ERR_OK) { return pctRet; }
pContext->pbEncryptedKey = SPExternalAlloc(pContext->cbEncryptedKey); if(pContext->pbEncryptedKey == NULL) { return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); }
pctRet = pContext->pKeyExchInfo->System->GenerateClientExchangeValue( pContext, pbServerExchangeValue, cbServerExchangeValue, NULL, NULL, pContext->pbEncryptedKey, &pContext->cbEncryptedKey);
return pctRet; }
//+---------------------------------------------------------------------------
//
// Function: Ssl3CheckForExistingCred
//
// Synopsis: Choose client certificate. Use one of the certificates
// attached to the credential handle if possible. If the
// credential handle is anonymous, then attempt to create
// a default credential.
//
// Notes: This routine is called by the client-side only.
//
// Returns: PCT_ERR_OK
// The function completed successfully. The
// pContext->pActiveClientCred field has been updated to
// point at a suitable client credential.
//
// SEC_E_INCOMPLETE_CREDENTIALS
// No suitable certificate has been found. Notify the
// application.
//
// SEC_I_INCOMPLETE_CREDENTIALS
// No suitable certificate has been found. Attempt an
// anonymous connection.
//
// <other>
// Fatal error.
//
//----------------------------------------------------------------------------
SP_STATUS Ssl3CheckForExistingCred(PSPContext pContext) { SP_STATUS pctRet;
//
// Examine the certificates attached to the credential group and see
// if any of them are suitable.
//
if(pContext->pCredGroup->CredCount != 0) { DWORD i, j;
for(i = 0; i < pContext->cSsl3ClientCertTypes; i++) { for(j = 0; j < g_cCertTypes; j++) { if(pContext->Ssl3ClientCertTypes[i] != g_CertTypes[j].dwCertType) { continue; }
pctRet = SPPickClientCertificate( pContext, g_CertTypes[j].dwExchSpec); if(pctRet == PCT_ERR_OK) { // We found one.
DebugLog((DEB_TRACE, "Application provided suitable client certificate.\n"));
return PCT_ERR_OK; } } }
// The credential group contained one or more certificates,
// but none were suitable. Don't even try to find a default
// certificate in this situation.
goto error; }
//
// Attempt to acquire a default credential.
//
if(pContext->pCredGroup->dwFlags & CRED_FLAG_NO_DEFAULT_CREDS) { // Look in credential manager only.
pctRet = AcquireDefaultClientCredential(pContext, TRUE); } else { // Look in both credential manager and MY certificate store.
pctRet = AcquireDefaultClientCredential(pContext, FALSE); }
if(pctRet == PCT_ERR_OK) { DebugLog((DEB_TRACE, "Default client certificate acquired.\n"));
return PCT_ERR_OK; }
error:
if(pContext->Flags & CONTEXT_FLAG_NO_INCOMPLETE_CRED_MSG) { return SP_LOG_RESULT(SEC_I_INCOMPLETE_CREDENTIALS); } else { return SP_LOG_RESULT(SEC_E_INCOMPLETE_CREDENTIALS); } }
/*
*************************************************************************** * SPGenerateSHResponse * This is the main function which outgoing message to the wire **************************************************************************** */
SP_STATUS SPGenerateSHResponse(PSPContext pContext, PSPBuffer pCommOutput) { PBYTE pbMessage = NULL; DWORD cbMessage; PBYTE pbHandshake = NULL; DWORD cbHandshake; PBYTE pbClientCert = NULL; DWORD cbClientCert = 0; DWORD cbDataOut; SP_STATUS pctRet; BOOL fAllocated = FALSE; BOOL fClientAuth; PSessCacheItem pZombie;
SP_BEGIN("SPGenerateSHResponse");
if((pContext == NULL) || (pCommOutput == NULL) || (pContext->RipeZombie == NULL) || (pContext->pKeyExchInfo == NULL)) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
fClientAuth = pContext->fCertReq; pZombie = pContext->RipeZombie;
//
// Estimate size of the ClientKeyExchange message group.
//
cbMessage = 0;
if(fClientAuth) { if(pContext->pActiveClientCred != NULL) { DWORD cbCertVerify;
pbClientCert = pContext->pActiveClientCred->pbSsl3SerializedChain; cbClientCert = pContext->pActiveClientCred->cbSsl3SerializedChain;
if(cbClientCert > 0x3fff) //Separate Wrappers after this
{ // is a BIG UNDONE...
pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; }
cbMessage += sizeof(SHSH) + // ClientCertificate
CB_SSL3_CERT_VECTOR + cbClientCert;
pctRet = BuildCertVerify(pContext, NULL, &cbCertVerify); if(pctRet != PCT_ERR_OK) { goto cleanup; }
cbMessage += cbCertVerify; // CertificateVerify
} else { LogNoClientCertFoundEvent();
//for ssl3, it's no_certificate alert
if((pContext->RipeZombie->fProtocol & SP_PROT_SSL3)) { cbMessage += sizeof(SWRAP) + // no_certificate Alert
CB_SSL3_ALERT_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE; } // for tls1, it's certificate of zero length.
else { cbMessage += sizeof(SHSH) + CB_SSL3_CERT_VECTOR; } } }
cbMessage += sizeof(SWRAP) + // ClientKeyExchange
sizeof(SHSH) + pContext->cbEncryptedKey + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
cbMessage += sizeof(SWRAP) + // ChangeCipherSpec
CB_SSL3_CHANGE_CIPHER_SPEC_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
cbMessage += sizeof(SWRAP) + // Finished
CB_SSL3_FINISHED_MSG_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
//
// Allocate memory for the ClientKeyExchange message group.
//
if(pCommOutput->pvBuffer) { // Application has allocated memory.
if(pCommOutput->cbBuffer < cbMessage) { pCommOutput->cbData = cbMessage; pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); goto cleanup; } fAllocated = TRUE; } else { // Schannel is to allocate memory.
pCommOutput->cbBuffer = cbMessage; pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if(pCommOutput->pvBuffer == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } } pCommOutput->cbData = 0;
// Build no_certificate alert (at the end of the output buffer).
if((pContext->RipeZombie->fProtocol & SP_PROT_SSL3) && fClientAuth && pbClientCert == NULL) { pbMessage = (PBYTE)pCommOutput->pvBuffer + pCommOutput->cbData;
// Build alert message.
BuildAlertMessage(pbMessage, SSL3_ALERT_WARNING, SSL3_ALERT_NO_CERTIFICATE);
// Build record header and encrypt message.
pctRet = SPSetWrap(pContext, pbMessage, SSL3_CT_ALERT, CB_SSL3_ALERT_ONLY, TRUE, &cbDataOut);
if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; }
// Update buffer length.
pCommOutput->cbData += cbDataOut;
SP_ASSERT(pCommOutput->cbData <= pCommOutput->cbBuffer); }
// Keep pointer to record structure. This will represent the
// ClientCertificate, ClientKeyExchange, and CertificateVerify messages.
pbMessage = (PBYTE)pCommOutput->pvBuffer + pCommOutput->cbData; cbMessage = 0;
pbHandshake = pbMessage + sizeof(SWRAP);
// Build ClientCertificate message.
if((pContext->RipeZombie->fProtocol & SP_PROT_TLS1) && fClientAuth && pbClientCert == NULL) { //Build a zero length certificate message for TLS1
pbMessage = (PBYTE)pCommOutput->pvBuffer + pCommOutput->cbData;
((CERT *)pbHandshake)->bcbClist24 = 0; ((CERT *)pbHandshake)->bcbMSBClist = 0; ((CERT *)pbHandshake)->bcbLSBClist = 0;
cbHandshake = sizeof(SHSH) + CB_SSL3_CERT_VECTOR;
// Fill in Handshake structure.
SetHandshake(pbHandshake, SSL3_HS_CERTIFICATE, NULL, CB_SSL3_CERT_VECTOR);
// Update handshake hash objects.
pctRet = UpdateHandshakeHash(pContext, pbHandshake, cbHandshake, FALSE); if(pctRet != PCT_ERR_OK) { goto cleanup; }
pbHandshake += cbHandshake; cbMessage += cbHandshake; }
if(fClientAuth && pbClientCert != NULL) { memcpy(&((CERT *)pbHandshake)->bcbCert24, pbClientCert, cbClientCert);
((CERT *)pbHandshake)->bcbClist24 = MS24BOF(cbClientCert); ((CERT *)pbHandshake)->bcbMSBClist = MSBOF(cbClientCert); ((CERT *)pbHandshake)->bcbLSBClist = LSBOF(cbClientCert);
cbHandshake = sizeof(SHSH) + CB_SSL3_CERT_VECTOR + cbClientCert;
// Fill in Handshake structure.
SetHandshake(pbHandshake, SSL3_HS_CERTIFICATE, NULL, cbHandshake - sizeof(SHSH));
// Update handshake hash objects.
pctRet = UpdateHandshakeHash(pContext, pbHandshake, cbHandshake, FALSE); if(pctRet != PCT_ERR_OK) { goto cleanup; }
pbHandshake += cbHandshake; cbMessage += cbHandshake; }
// Build ClientKeyExchange message.
{ SetHandshake(pbHandshake, SSL3_HS_CLIENT_KEY_EXCHANGE, pContext->pbEncryptedKey, pContext->cbEncryptedKey);
cbHandshake = sizeof(SHSH) + pContext->cbEncryptedKey;
SPExternalFree(pContext->pbEncryptedKey); pContext->pbEncryptedKey = NULL; pContext->cbEncryptedKey = 0;
// Update handshake hash objects.
pctRet = UpdateHandshakeHash(pContext, pbHandshake, cbHandshake, FALSE); if(pctRet != PCT_ERR_OK) { goto cleanup; }
pbHandshake += cbHandshake; cbMessage += cbHandshake; }
// Build CertificateVerify message.
if(fClientAuth && pbClientCert != NULL) { pctRet = BuildCertVerify(pContext, pbHandshake, &cbHandshake); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; }
// Update handshake hash objects.
pctRet = UpdateHandshakeHash(pContext, pbHandshake, cbHandshake, FALSE); if(pctRet != PCT_ERR_OK) { goto cleanup; }
pbHandshake += cbHandshake; cbMessage += cbHandshake; }
// Add record header and encrypt handshake messages.
pctRet = SPSetWrap(pContext, pbMessage, SSL3_CT_HANDSHAKE, cbMessage, TRUE, &cbDataOut);
if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; }
// Update buffer length.
pCommOutput->cbData += cbDataOut;
SP_ASSERT(pCommOutput->cbData <= pCommOutput->cbBuffer);
// Build the ChangeCipherSpec and Finished messages.
{ pctRet = BuildCCSAndFinishMessage(pContext, pCommOutput, TRUE); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; } }
// Advance state machine.
pContext->State = SSL3_STATE_CLIENT_FINISH;
pctRet = PCT_ERR_OK;
cleanup:
SP_RETURN(pctRet); }
SP_STATUS SPGenerateCloseNotify( PSPContext pContext, PSPBuffer pCommOutput) { PBYTE pbMessage = NULL; DWORD cbMessage; DWORD cbDataOut; SP_STATUS pctRet;
SP_BEGIN("SPGenerateCloseNotify");
if((pContext == NULL) || (pCommOutput == NULL)) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
//
// Estimate size of the message.
//
cbMessage = sizeof(SWRAP) + CB_SSL3_ALERT_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
//
// Allocate memory for the message.
//
if(pCommOutput->pvBuffer) { // Application has allocated memory.
if(pCommOutput->cbBuffer < cbMessage) { pCommOutput->cbData = cbMessage; return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); } } else { // Schannel is to allocate memory.
pCommOutput->cbBuffer = cbMessage; pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if(pCommOutput->pvBuffer == NULL) { SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } } pCommOutput->cbData = 0;
//
// Build alert message.
//
pbMessage = pCommOutput->pvBuffer;
// Build alert message.
BuildAlertMessage(pbMessage, SSL3_ALERT_WARNING, SSL3_ALERT_CLOSE_NOTIFY);
// Build record header and encrypt message.
pctRet = SPSetWrap( pContext, pbMessage, SSL3_CT_ALERT, CB_SSL3_ALERT_ONLY, TRUE, &cbDataOut);
if(pctRet != PCT_ERR_OK) { SP_RETURN(SP_LOG_RESULT(pctRet)); }
// Update buffer length.
pCommOutput->cbData += cbDataOut;
SP_ASSERT(pCommOutput->cbData <= pCommOutput->cbBuffer);
pContext->State = SP_STATE_SHUTDOWN;
SP_RETURN(PCT_ERR_OK); }
/*
*************************************************************************** * SPProcessMessage * This is the main function which parses and stores the incoming messages * from the wire. **************************************************************************** */
SP_STATUS SPProcessMessage( PSPContext pContext, BYTE bContentType, PBYTE pbMsg, DWORD cbMsg) { SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE; DWORD dwState = pContext->State;
// enum {
// change_cipher_spec(20), alert(21), handshake(22),
// application_data(23), (255)
// } ContentType;
switch(bContentType) { case SSL3_CT_ALERT: DebugLog((DEB_TRACE, "Alert Message:\n"));
pctRet = ParseAlertMessage(pContext, pbMsg, cbMsg);
break;
case SSL3_CT_CHANGE_CIPHER_SPEC: DebugLog((DEB_TRACE, "Change Cipher Spec:\n")); if(SSL3_STATE_RESTART_CCS == dwState || SSL3_STATE_CLIENT_FINISH == dwState) { pctRet = Ssl3HandleCCS( pContext, pbMsg, cbMsg);
if (PCT_ERR_OK == pctRet) { if(SSL3_STATE_RESTART_CCS == dwState) pContext->State = SSL3_STATE_RESTART_SERVER_FINISH; } } else if(SSL3_STATE_CLIENT_KEY_XCHANGE == dwState || SSL3_STATE_CERT_VERIFY == dwState || SSL3_STATE_RESTART_SER_HELLO == dwState) { pctRet = Ssl3HandleCCS( pContext, pbMsg, cbMsg);
if (PCT_ERR_OK == pctRet) { if(SSL3_STATE_RESTART_SER_HELLO == dwState) { pContext->State = SSL3_STATE_SER_RESTART_CHANGE_CIPHER_SPEC; } }
}
else { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); } break;
case UNI_STATE_RECVD_UNIHELLO: { DebugLog((DEB_TRACE, "Unified Hello:\n"));
pctRet = Ssl3SrvHandleUniHello(pContext, pbMsg, cbMsg); if (SP_FATAL(pctRet)) { pContext->State = PCT1_STATE_ERROR; } } break;
case SSL3_CT_HANDSHAKE: { DWORD dwcb;
if(pContext->State == SP_STATE_CONNECTED) { //We may be getting a REDO message
DebugLog((DEB_WARN, "May be a ReDO")); pContext->State = SSL3_STATE_CLIENT_HELLO; }
//Since multiple handshake can be put into on Record
//layer- we have to do this loop-here.
do { if(cbMsg < sizeof(SHSH)) break; dwcb = ((INT)pbMsg[1] << 16) + ((INT)pbMsg[2] << 8) + (INT)pbMsg[3]; if(sizeof(SHSH) + dwcb > cbMsg) break; pctRet = SPProcessHandshake(pContext, pbMsg, dwcb + sizeof(SHSH)); CHECK_PCT_RET_BREAK(pctRet); cbMsg -= dwcb + sizeof(SHSH); pbMsg += dwcb + sizeof(SHSH); } while(cbMsg > 0); }
break;
default: DebugLog((DEB_WARN, "Error in protocol, dwState is %lx\n", dwState)); pContext->State = PCT1_STATE_ERROR; break; } if (pctRet & PCT_INT_DROP_CONNECTION) { pContext->State &= ~ SP_STATE_CONNECTED; }
return(pctRet); }
/*
*************************************************************************** * Helper function to make connected state for ssl3 **************************************************************************** */ void Ssl3StateConnected(PSPContext pContext) { pContext->State = SP_STATE_CONNECTED; pContext->DecryptHandler = Ssl3DecryptHandler; pContext->Encrypt = Ssl3EncryptMessage; pContext->Decrypt = Ssl3DecryptMessage; pContext->GetHeaderSize = Ssl3GetHeaderSize; SPContextClean(pContext); }
/*
*************************************************************************** * SPProcessHandshake, Process all the handshake messages. **************************************************************************** */
SP_STATUS SPProcessHandshake( PSPContext pContext, PBYTE pb, DWORD dwcb) { SP_STATUS pctRet; SHSH * pshsh; DWORD cbHandshake;
//
// char HandshakeType;
// char Length24
// char Length16
// char Length08
// <actual handshake message>
//
SP_BEGIN("SPProcessHandshake");
if(pContext == NULL || pb == NULL) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
if(dwcb < sizeof(SHSH)) { SP_RETURN(PCT_INT_INCOMPLETE_MSG); }
pshsh = (SHSH *)pb;
DebugLog((DEB_TRACE, "Protocol:%x, Message:%x, State:%x\n", pContext->dwProtocol, pshsh->typHS, pContext->State));
cbHandshake = ((DWORD)pshsh->bcb24 << 16) + ((DWORD)pshsh->bcbMSB << 8) + (DWORD)pshsh->bcbLSB;
if(dwcb < sizeof(SHSH) + cbHandshake) { SP_RETURN(PCT_INT_INCOMPLETE_MSG); }
if(pContext->dwProtocol & SP_PROT_CLIENTS) { //
// Handle client-side handshake states.
//
switch((pshsh->typHS << 16) | (pContext->State & 0xffff) ) { case (SSL3_HS_SERVER_HELLO << 16) | SSL3_STATE_CLIENT_HELLO: case (SSL3_HS_SERVER_HELLO << 16) | UNI_STATE_CLIENT_HELLO: { BOOL fRestart;
DebugLog((DEB_TRACE, "Server Hello:\n"));
pctRet = SPDigestServerHello(pContext, (PUCHAR) pb, dwcb, &fRestart); if(PCT_ERR_OK != pctRet) { SP_LOG_RESULT(pctRet); break; }
if(fRestart) { pContext->State = SSL3_STATE_RESTART_CCS; } else { pContext->State = SSL3_STATE_SERVER_HELLO ; } pContext->fCertReq = FALSE;
break; }
case (SSL3_HS_CERTIFICATE << 16) | SSL3_STATE_SERVER_HELLO: { DebugLog((DEB_TRACE, "Server Certificate:\n"));
// Process server Certificate message.
pctRet = SpDigestRemoteCertificate(pContext, pb, dwcb); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); break; }
// Automatically validate server certificate if appropriate
// context flag is set.
pctRet = AutoVerifyServerCertificate(pContext); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); break; }
pContext->State = SSL3_STATE_SERVER_CERTIFICATE ; break;
}
case (SSL3_HS_SERVER_KEY_EXCHANGE << 16) | SSL3_STATE_SERVER_CERTIFICATE: { DebugLog((DEB_TRACE, "Key Exchange:\n"));
if((pContext->dwRequestedCF & pContext->RipeZombie->dwCF) != (pContext->dwRequestedCF)) { if((pContext->dwRequestedCF & CF_FASTSGC) != 0) { pContext->State = SSL3_HS_SERVER_KEY_EXCHANGE; pctRet = PCT_ERR_OK; break; } }
// Store the server key exchange value in the context. This
// will be processed later when the ServerHelloDone message
// is received. This is necessary because Fortezza needs to
// process the CertificateRequest message before building the
// ClientKeyExchange value.
pContext->cbServerKeyExchange = dwcb - sizeof(SHSH); pContext->pbServerKeyExchange = SPExternalAlloc(pContext->cbServerKeyExchange); if(NULL == pContext->pbServerKeyExchange) { SP_RETURN(SEC_E_INSUFFICIENT_MEMORY); } CopyMemory(pContext->pbServerKeyExchange, pb + sizeof(SHSH), pContext->cbServerKeyExchange);
pContext->State = SSL3_HS_SERVER_KEY_EXCHANGE ; pctRet = PCT_ERR_OK; break; }
case (SSL3_HS_CERTIFICATE_REQUEST << 16)| SSL3_HS_SERVER_KEY_EXCHANGE: case (SSL3_HS_CERTIFICATE_REQUEST << 16)| SSL3_STATE_SERVER_CERTIFICATE: { DebugLog((DEB_TRACE, "Certificate Request:\n"));
if((pContext->dwRequestedCF & pContext->RipeZombie->dwCF) != (pContext->dwRequestedCF)) { if((pContext->dwRequestedCF & CF_FASTSGC) != 0) { pContext->State = SSL3_STATE_SERVER_CERTREQ; pctRet = PCT_ERR_OK; break; } }
pctRet = ParseCertificateRequest(pContext, pb, dwcb); CHECK_PCT_RET_BREAK(pctRet);
pctRet = Ssl3CheckForExistingCred(pContext);
if(pctRet == SEC_E_INCOMPLETE_CREDENTIALS) { pContext->fInsufficientCred = TRUE;
// Process all the messages and then return the warning...
pctRet = PCT_ERR_OK; } if(pctRet == SEC_I_INCOMPLETE_CREDENTIALS) { // we didn't have a cert, so we proceed, expecting
// to send a no cert alert
pctRet = PCT_ERR_OK; } CHECK_PCT_RET_BREAK(pctRet);
pContext->fCertReq = TRUE; pContext->State = SSL3_STATE_SERVER_CERTREQ ; break; }
case (SSL3_HS_SERVER_HELLO_DONE << 16) | SSL3_HS_SERVER_KEY_EXCHANGE: case (SSL3_HS_SERVER_HELLO_DONE << 16) | SSL3_STATE_SERVER_CERTIFICATE: case (SSL3_HS_SERVER_HELLO_DONE << 16) | SSL3_STATE_SERVER_CERTREQ: { DebugLog((DEB_TRACE, "Server Hello Done:\n"));
if(dwcb > sizeof(SHSH)) { pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); break; }
if((pContext->dwRequestedCF & pContext->RipeZombie->dwCF) != (pContext->dwRequestedCF)) { if((pContext->dwRequestedCF & CF_FASTSGC) != 0) { pContext->State = SSL3_STATE_GEN_HELLO_REQUEST; pContext->RipeZombie->dwCF = pContext->dwRequestedCF; pctRet = PCT_ERR_OK; SPContextClean(pContext); break; } }
pctRet = SPDigestSrvKeyX(pContext, pContext->pbServerKeyExchange, pContext->cbServerKeyExchange);
CHECK_PCT_RET_BREAK(pctRet); if(pContext->pbServerKeyExchange) { SPExternalFree(pContext->pbServerKeyExchange); pContext->pbServerKeyExchange = NULL; }
pContext->State = SSL3_STATE_GEN_SERVER_HELLORESP;
if(TRUE == pContext->fInsufficientCred) { pctRet = SEC_I_INCOMPLETE_CREDENTIALS; } else { pctRet = PCT_ERR_OK; } break; }
case (SSL3_HS_FINISHED << 16) | SSL3_STATE_RESTART_SERVER_FINISH: DebugLog((DEB_TRACE, "ServerFinished (reconnect):\n"));
pctRet = Ssl3HandleFinish(pContext, pb, dwcb, FALSE); if(PCT_ERR_OK != pctRet) { break; }
pContext->State = SSL3_STATE_GEN_CLIENT_FINISH; break;
case (SSL3_HS_FINISHED << 16) | SSL3_STATE_CHANGE_CIPHER_SPEC_CLIENT: DebugLog((DEB_TRACE, "ServerFinished (full):\n"));
pctRet = Ssl3HandleFinish(pContext, pb, dwcb, FALSE); if(PCT_ERR_OK != pctRet) { break; }
// Initiate SGC renegotiation if appropriate.
if((pContext->dwRequestedCF & pContext->RipeZombie->dwCF) != (pContext->dwRequestedCF)) { if((pContext->dwRequestedCF & CF_FASTSGC) == 0) { SPContextClean(pContext); pContext->State = SSL3_STATE_GEN_HELLO_REQUEST; pContext->RipeZombie->dwCF = pContext->dwRequestedCF; pctRet = PCT_ERR_OK; break; } }
Ssl3StateConnected(pContext);
// We add to cache because this is where we are finishing
// a normal connect.
SPCacheAdd(pContext);
break;
default: DebugLog((DEB_TRACE, "***********ILLEGAL ********\n")); if(pContext->RipeZombie->fProtocol & SP_PROT_TLS1) { SetTls1Alert(pContext, TLS1_ALERT_FATAL, TLS1_ALERT_UNEXPECTED_MESSAGE); } pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); } } else {
//
// Handle server-side handshake states.
//
switch((pshsh->typHS << 16) | (pContext->State & 0xffff) ) { case (SSL3_HS_CLIENT_HELLO << 16) | SSL3_STATE_RENEGOTIATE: DebugLog((DEB_TRACE, "ClientHello After REDO:\n"));
#if 1
// We need to do a full handshake, so lose the cache entry.
SPCacheDereference(pContext->RipeZombie); pContext->RipeZombie = NULL;
pctRet = SPSsl3SrvHandleClientHello(pContext, pb, FALSE); pContext->Flags &= ~CONTEXT_FLAG_MAPPED; if(PCT_ERR_OK == pctRet) { pContext->State = SSL3_STATE_GEN_REDO; } #else
// Allow reconnects during redo, for testing purposes only
pctRet = SPSsl3SrvHandleClientHello(pContext, pb, TRUE); pContext->Flags &= ~CONTEXT_FLAG_MAPPED; #endif
break;
case (SSL3_HS_CLIENT_HELLO << 16) | SSL2_STATE_SERVER_HELLO: DebugLog((DEB_TRACE, "ClientHello after fast SGC accepted:\n"));
// We need to do a full handshake, so lose the cache entry.
SPCacheDereference(pContext->RipeZombie); pContext->RipeZombie = NULL;
pctRet = SPSsl3SrvHandleClientHello(pContext, pb, FALSE); break;
case (SSL3_HS_CLIENT_HELLO << 16): DebugLog((DEB_TRACE, "ClientHello:\n")); pctRet = SPSsl3SrvHandleClientHello(pContext, pb, TRUE); break;
case (SSL3_HS_CLIENT_KEY_EXCHANGE << 16) | SSL2_STATE_SERVER_HELLO: case (SSL3_HS_CLIENT_KEY_EXCHANGE << 16) | SSL3_STATE_NO_CERT_ALERT: case (SSL3_HS_CLIENT_KEY_EXCHANGE << 16) | SSL2_STATE_CLIENT_CERTIFICATE: DebugLog((DEB_TRACE, "Client Key Exchange:\n")); pctRet = ParseKeyExchgMsg(pContext, pb) ; CHECK_PCT_RET_BREAK(pctRet);
if(PCT_ERR_OK == pctRet) pContext->State = SSL3_STATE_CLIENT_KEY_XCHANGE; if(!pContext->fCertReq) pContext->State = SSL3_STATE_CLIENT_KEY_XCHANGE;
break;
case ( SSL3_HS_CERTIFICATE << 16) | SSL2_STATE_SERVER_HELLO: DebugLog((DEB_TRACE, "Client Certificate:\n"));
pctRet = SpDigestRemoteCertificate(pContext, pb, dwcb); CHECK_PCT_RET_BREAK(pctRet); if(PCT_ERR_OK == pctRet) pContext->State = SSL2_STATE_CLIENT_CERTIFICATE ; break;
case (SSL3_HS_CERTIFICATE_VERIFY << 16) | SSL3_STATE_CLIENT_KEY_XCHANGE: DebugLog((DEB_TRACE, "Certificate Verify :\n"));
pctRet = HandleCertVerify(pContext, pb, dwcb); if(pctRet != PCT_ERR_OK) { break; }
pctRet = SPContextDoMapping(pContext); pContext->State = SSL3_STATE_CERT_VERIFY; break;
case (SSL3_HS_FINISHED << 16) | SSL3_STATE_SER_RESTART_CHANGE_CIPHER_SPEC: DebugLog((DEB_TRACE, "Finished(client) Restart :\n"));
pctRet = Ssl3HandleFinish(pContext, pb, dwcb, TRUE); if(pctRet != PCT_ERR_OK) { break; }
Ssl3StateConnected(pContext);
break;
case (SSL3_HS_FINISHED << 16) | SSL3_STATE_CHANGE_CIPHER_SPEC_SERVER: DebugLog((DEB_TRACE, "Finished(Client):\n")); pctRet = Ssl3HandleFinish(pContext, pb, dwcb, TRUE); if(PCT_ERR_OK == pctRet) { pContext->State = SSL3_STATE_GEN_SERVER_FINISH; } break;
default: DebugLog((DEB_TRACE, "***********ILLEGAL ********\n")); if(pContext->dwProtocol & SP_PROT_TLS1) { SetTls1Alert(pContext, TLS1_ALERT_FATAL, TLS1_ALERT_UNEXPECTED_MESSAGE); } pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
break; } }
if(pctRet == PCT_ERR_OK || pctRet == SEC_I_INCOMPLETE_CREDENTIALS) { if(pContext->cbClientHello == 0) { if(UpdateHandshakeHash(pContext, pb, dwcb, FALSE) != PCT_ERR_OK) { pctRet = PCT_INT_INTERNAL_ERROR; } } }
SP_RETURN(pctRet); }
/*
*************************************************************************** * SPGenerateResponse, All the messages are built from this function. **************************************************************************** */ SP_STATUS SPGenerateResponse( PSPContext pContext, PSPBuffer pCommOutput) //Out
{ SP_STATUS pctRet = PCT_ERR_OK;
DebugLog((DEB_TRACE, "**********Protocol***** %x\n", pContext->RipeZombie->fProtocol)); switch(pContext->State) { case TLS1_STATE_ERROR: // TLS1 Alert message
DebugLog((DEB_TRACE, "GEN TLS1 Alert Message:\n")); pctRet = SPBuildTlsAlertMessage(pContext, pCommOutput); pContext->State = SP_STATE_SHUTDOWN; break;
case SP_STATE_SHUTDOWN_PENDING: DebugLog((DEB_TRACE, "GEN Close Notify:\n")); pctRet = SPGenerateCloseNotify(pContext, pCommOutput); break;
case SP_STATE_SHUTDOWN: return PCT_INT_EXPIRED;
case SSL3_STATE_GEN_SERVER_HELLORESP: DebugLog((DEB_TRACE, "GEN Server Hello Response:\n")); pctRet = SPGenerateSHResponse(pContext, pCommOutput); break;
case SSL3_STATE_GEN_HELLO_REQUEST: DebugLog((DEB_TRACE, "GEN Hello Request:\n")); //Temp Disabling Reconnect during REDO
if(!SPCacheClone(&pContext->RipeZombie)) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); break; } if(pContext->RipeZombie->fProtocol == SP_PROT_SSL3_CLIENT) { pctRet = GenerateSsl3ClientHello(pContext, pCommOutput); } else { pctRet = GenerateTls1ClientHello(pContext, pCommOutput, SP_PROT_TLS1_CLIENT); }
pContext->Flags &= ~CONTEXT_FLAG_MAPPED;
pContext->State = SSL3_STATE_CLIENT_HELLO;
break;
case SSL3_STATE_GEN_CLIENT_FINISH: { DebugLog((DEB_TRACE, "GEN Client Finish:\n"));
pctRet = SPBuildCCSAndFinish(pContext, pCommOutput); if(PCT_ERR_OK == pctRet) { Ssl3StateConnected(pContext); } } break;
/*-------------------------------------SERVER SIDE------------------------------------*/
case SSL3_STATE_GEN_SERVER_FINISH: DebugLog((DEB_TRACE, "GEN Server Finish:\n")); pctRet = SPBuildCCSAndFinish(pContext, pCommOutput); /* Cache Session Here */ if(pctRet == PCT_ERR_OK) { Ssl3StateConnected(pContext); SPCacheAdd(pContext); } break;
case SSL3_STATE_GEN_SERVER_HELLO: // Generate the response
DebugLog((DEB_TRACE, "GEN Server hello:\n")); pctRet = SPSsl3SrvGenServerHello(pContext, pCommOutput); break;
case SSL3_STATE_GEN_SERVER_HELLO_RESTART: pctRet = SPSsl3SrvGenRestart(pContext, pCommOutput); break;
case SP_STATE_CONNECTED: // We were called from a connected state, so the application
// must be requesting a redo.
if(pContext->RipeZombie->fProtocol & SP_PROT_SERVERS) { // Build a HelloRequest message.
DebugLog((DEB_TRACE, "GEN Hello Request:\n")); pctRet = SPBuildHelloRequest(pContext, pCommOutput); } else { // Build a ClientHello message.
DebugLog((DEB_TRACE, "GEN renegotiate Client Hello\n")); pctRet = GenerateHello(pContext, pCommOutput, TRUE);
// Clear flag, so that the context gets re-transferred when
// handshake completes.
pContext->Flags &= ~CONTEXT_FLAG_MAPPED; }
break;
case SSL3_STATE_GEN_REDO: DebugLog((DEB_TRACE, "GEN Server hello ( REDO ):\n")); // We processed a client hello from the decrypt handler,
// so generate a server hello.
pctRet = SPSsl3SrvGenServerHello(pContext, pCommOutput); break;
default: break; } if (pctRet & PCT_INT_DROP_CONNECTION) { pContext->State &= ~ SP_STATE_CONNECTED; }
return(pctRet); }
/*
*************************************************************************** * FNoInputState Are we in a state that all the inputs are handled and * waiting for Response generation RETURN TRUE if yes **************************************************************************** */ BOOL FNoInputState(DWORD dwState) { switch(dwState) { default: return(FALSE);
case SSL3_STATE_GEN_HELLO_REQUEST: case SSL3_STATE_GEN_SERVER_HELLORESP: case SSL3_STATE_GEN_SERVER_FINISH: case SSL3_STATE_GEN_REDO: case SP_STATE_CONNECTED: case TLS1_STATE_ERROR: case SP_STATE_SHUTDOWN_PENDING:
return(TRUE); } }
/*
*************************************************************************** * SPBuildHelloRequest * * Build hello-request message, this is done, when server sees a GET and the * GET object needs client-authentication. * this may be needed when the server thinks that the session is for a long * time or lots of bytes gon down the wire, to RENEGOTIATE the keys **************************************************************************** */
SP_STATUS SPBuildHelloRequest( PSPContext pContext, PSPBuffer pCommOutput) { PBYTE pbMessage = NULL; DWORD cbMessage; PBYTE pbHandshake = NULL; DWORD cbHandshake; BOOL fAllocated = FALSE; SP_STATUS pctRet; DWORD cbDataOut;
// Estimate size of HelloRequest message.
cbMessage = sizeof(SWRAP) + sizeof(SHSH) + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
if(pCommOutput->pvBuffer) { // Application has allocated memory.
if(pCommOutput->cbBuffer < cbMessage) { pCommOutput->cbData = cbMessage; return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); } fAllocated = TRUE; } else { // Schannel is to allocate memory.
pCommOutput->cbBuffer = cbMessage; pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if(pCommOutput->pvBuffer == NULL) { return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); } } pCommOutput->cbData = 0;
pbMessage = pCommOutput->pvBuffer;
pbHandshake = pbMessage + sizeof(SWRAP); cbHandshake = sizeof(SHSH);
// Fill in Handshake structure.
SetHandshake(pbHandshake, SSL3_HS_HELLO_REQUEST, NULL, 0);
// Update handshake hash objects.
pctRet = UpdateHandshakeHash(pContext, pbHandshake, cbHandshake, FALSE); if(pctRet != PCT_ERR_OK) { return(pctRet); }
// Add record header and encrypt handshake messages.
pctRet = SPSetWrap(pContext, pbMessage, SSL3_CT_HANDSHAKE, cbHandshake, FALSE, &cbDataOut);
// Update buffer length.
pCommOutput->cbData += cbDataOut;
SP_ASSERT(pCommOutput->cbData <= pCommOutput->cbBuffer);
return pctRet; }
/*
***************************************************************************
**************************************************************************** */
SP_STATUS SPSsl3SrvGenServerHello( PSPContext pContext, PSPBuffer pCommOutput) { SP_STATUS pctRet; PSPCredentialGroup pCred; BOOL fAllocated = FALSE;
PBYTE pbServerCert = NULL; DWORD cbServerCert = 0;
PBYTE pbIssuerList = NULL; DWORD cbIssuerList = 0;
PBYTE pbMessage = NULL; DWORD cbMessage; PBYTE pbHandshake = NULL; DWORD cbHandshake; DWORD cbDataOut;
DWORD cbServerExchange;
BOOL fClientAuth = ((pContext->Flags & CONTEXT_FLAG_MUTUAL_AUTH) != 0);
if(pCommOutput == NULL) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
// Get pointer to server certificate chain.
pCred = pContext->RipeZombie->pServerCred; pbServerCert = pContext->RipeZombie->pActiveServerCred->pbSsl3SerializedChain; cbServerCert = pContext->RipeZombie->pActiveServerCred->cbSsl3SerializedChain;
if(pbServerCert == NULL) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); goto cleanup; }
//
// Estimate size of the ServerHello message group, which includes the
// ServerHello, ServerCertificate, ServerKeyExchange, CertificateRequest,
// and ServerHelloDone messages.
//
cbMessage = sizeof(SWRAP) + // ServerHello (plus encryption goo)
sizeof(SSH) + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
cbMessage += sizeof(SHSH) + // ServerCertificate
CB_SSL3_CERT_VECTOR + cbServerCert;
cbMessage += sizeof(SHSH); // ServerHelloDone
// Get pointer to key exchange system.
if((pContext->pKeyExchInfo == NULL) || ((pContext->pKeyExchInfo->fProtocol & SP_PROT_SSL3TLS1_CLIENTS) == 0)) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); goto cleanup; }
pctRet = pContext->pKeyExchInfo->System->GenerateServerExchangeValue( pContext, NULL, &cbServerExchange); if(pctRet != PCT_ERR_OK) { goto cleanup; } if(pContext->fExchKey) { cbMessage += sizeof(SHSH) + cbServerExchange; }
// Add in length of CertificateRequest message.
if(fClientAuth) { if(g_fSendIssuerList) { UpdateAndDuplicateIssuerList(pCred, &pbIssuerList, &cbIssuerList); }
cbMessage += sizeof(SHSH) + 1 + SSL3_CERTTYPE_MAX_COUNT + 2 + cbIssuerList; }
pContext->fCertReq = fClientAuth;
DebugLog((DEB_TRACE, "Server hello message %x\n", cbMessage));
//
// Allocate memory for the ServerHello message group.
//
if(pCommOutput->pvBuffer) { // Application has allocated memory.
if(pCommOutput->cbBuffer < cbMessage) { pCommOutput->cbData = cbMessage; pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); goto cleanup; } fAllocated = TRUE; } else { // Schannel is to allocate memory.
pCommOutput->cbBuffer = cbMessage; pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if(pCommOutput->pvBuffer == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } } pCommOutput->cbData = 0;
// Keep pointer to record structure. This will represent all of the
// handshake messages.
pbMessage = (PBYTE)pCommOutput->pvBuffer + pCommOutput->cbData; cbMessage = 0;
pbHandshake = pbMessage + sizeof(SWRAP);
// Generate the session ID (actually previously generated)
pContext->RipeZombie->cbSessionID = CB_SSL3_SESSION_ID;
// Generate internal values to make server hello
pctRet = Ssl3GenerateRandom(pContext->rgbS3SRandom); if(!NT_SUCCESS(pctRet)) { goto cleanup; }
// Build ServerHello
Ssl3BuildServerHello(pContext, pbHandshake); pbHandshake += sizeof(SSH); cbMessage += sizeof(SSH);
// Build ServerCertificate
{ memcpy(&((CERT *)pbHandshake)->bcbCert24, pbServerCert, cbServerCert);
((CERT *)pbHandshake)->bcbClist24 = MS24BOF(cbServerCert); ((CERT *)pbHandshake)->bcbMSBClist = MSBOF(cbServerCert); ((CERT *)pbHandshake)->bcbLSBClist = LSBOF(cbServerCert);
cbHandshake = sizeof(SHSH) + CB_SSL3_CERT_VECTOR + cbServerCert;
// Fill in Handshake structure.
SetHandshake(pbHandshake, SSL3_HS_CERTIFICATE, NULL, cbHandshake - sizeof(SHSH));
pbHandshake += cbHandshake; cbMessage += cbHandshake; }
// Build ServerKeyExchange.
if(pContext->fExchKey) { pctRet = pContext->pKeyExchInfo->System->GenerateServerExchangeValue( pContext, pbHandshake + sizeof(SHSH), &cbServerExchange); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; } SetHandshake(pbHandshake, SSL3_HS_SERVER_KEY_EXCHANGE, NULL, (WORD)cbServerExchange);
pbHandshake += sizeof(SHSH) + cbServerExchange; cbMessage += sizeof(SHSH) + cbServerExchange; }
// Build CertificateRequest.
if(fClientAuth) { pctRet = Ssl3BuildCertificateRequest(pbIssuerList, cbIssuerList, pbHandshake, &cbHandshake); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; }
pbHandshake += cbHandshake; cbMessage += cbHandshake; }
// Build ServerHelloDone.
{ BuildServerHelloDone(pbHandshake);
pbHandshake += sizeof(SHSH); cbMessage += sizeof(SHSH); }
// Initialize handshake hashes and hash ClientHello message. This
// must be done _after_ the ServerKeyExchange message is built,
// so that the correct CSP context is used.
pctRet = UpdateHandshakeHash(pContext, pContext->pClientHello, pContext->cbClientHello, TRUE); SPExternalFree(pContext->pClientHello); pContext->pClientHello = NULL; pContext->cbClientHello = 0; if(pctRet != PCT_ERR_OK) { goto cleanup; }
// Update handshake hash objects.
pctRet = UpdateHandshakeHash(pContext, pbMessage + sizeof(SWRAP), cbMessage, FALSE); if(pctRet != PCT_ERR_OK) { goto cleanup; }
// Add record header and encrypt handshake messages.
pctRet = SPSetWrap(pContext, pbMessage, SSL3_CT_HANDSHAKE, cbMessage, FALSE, &cbDataOut);
if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; }
// Update buffer length.
pCommOutput->cbData += cbDataOut;
SP_ASSERT(pCommOutput->cbData <= pCommOutput->cbBuffer);
// Advance state machine.
pContext->State = SSL2_STATE_SERVER_HELLO;
pctRet = PCT_ERR_OK;
cleanup:
if(pctRet != PCT_ERR_OK && !fAllocated) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer =NULL; }
if(pbIssuerList) { SPExternalFree(pbIssuerList); }
return pctRet; }
/*
*************************************************************************** **************************************************************************** */
SP_STATUS SPSsl3SrvGenRestart( PSPContext pContext, PSPBuffer pCommOutput) { SP_STATUS pctRet; PBYTE pbMessage = NULL; DWORD cbMessage; DWORD cbDataOut; BOOL fAllocated = FALSE;
if(pCommOutput == NULL) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
//
// Estimate size of the restart ServerHello message group, which includes
// the ServerHello, ChangeCipherSpec, and Finished messages.
//
cbMessage = sizeof(SWRAP) + // ServerHello (plus encryption goo)
sizeof(SSH) + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
cbMessage += sizeof(SWRAP) + // ChangeCipherSpec
CB_SSL3_CHANGE_CIPHER_SPEC_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
cbMessage += sizeof(SWRAP) + // Finished
CB_SSL3_FINISHED_MSG_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
DebugLog((DEB_TRACE, "Server hello message %x\n", cbMessage));
if(pCommOutput->pvBuffer) { // Application has allocated memory.
if(pCommOutput->cbBuffer < cbMessage) { pCommOutput->cbData = cbMessage; return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); } fAllocated = TRUE; } else { // Schannel is to allocate memory.
pCommOutput->cbBuffer = cbMessage; pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if(pCommOutput->pvBuffer == NULL) { return (SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } } pCommOutput->cbData = 0;
pbMessage = (PBYTE)pCommOutput->pvBuffer + pCommOutput->cbData;
// Generate internal values to make server hello
Ssl3GenerateRandom(pContext->rgbS3SRandom);
// Make a new set of session keys.
pctRet = MakeSessionKeys(pContext, pContext->RipeZombie->hMasterProv, pContext->RipeZombie->hMasterKey); if(pctRet != PCT_ERR_OK) { return SP_LOG_RESULT(pctRet); }
// Initialize handshake hashes and hash ClientHello message.
pctRet = UpdateHandshakeHash(pContext, pContext->pClientHello, pContext->cbClientHello, TRUE); SPExternalFree(pContext->pClientHello); pContext->pClientHello = NULL; pContext->cbClientHello = 0;
if(pctRet != PCT_ERR_OK) { return(pctRet); }
// Build ServerHello message body.
Ssl3BuildServerHello(pContext, pbMessage + sizeof(SWRAP));
// Update handshake hash objects.
pctRet = UpdateHandshakeHash(pContext, pbMessage + sizeof(SWRAP), sizeof(SSH), FALSE); if(pctRet != PCT_ERR_OK) { return(pctRet); }
// Build record header and encrypt message.
pctRet = SPSetWrap(pContext, pbMessage, SSL3_CT_HANDSHAKE, sizeof(SSH), FALSE, &cbDataOut);
if(pctRet != PCT_ERR_OK) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = 0; return pctRet; }
// Update buffer length.
pCommOutput->cbData += cbDataOut;
SP_ASSERT(pCommOutput->cbData <= pCommOutput->cbBuffer);
pctRet = BuildCCSAndFinishMessage(pContext, pCommOutput, FALSE); if(pctRet != PCT_ERR_OK) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = 0; return pctRet; }
pContext->State = SSL3_STATE_RESTART_SER_HELLO;
return(PCT_ERR_OK); }
/*
*************************************************************************** * SPSsl3SrvHandleClientHello * Client-hello from ssl3 parsing the client hello **************************************************************************** */
SP_STATUS SPSsl3SrvHandleClientHello( PSPContext pContext, PBYTE pb, BOOL fAttemptReconnect) { BOOL fRestart = FALSE; DWORD dwHandshakeLen;
SP_BEGIN("SPSsl3SrvHandleClientHello");
// Validate handshake type
if(pb[0] != SSL3_HS_CLIENT_HELLO) { SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG)); }
dwHandshakeLen = ((INT)pb[1] << 16) + ((INT)pb[2] << 8) + (INT)pb[3];
// Save the ClientHello message so we can hash it later, once
// we know what algorithm and CSP we're using.
if(pContext->pClientHello) { SPExternalFree(pContext->pClientHello); } pContext->cbClientHello = sizeof(SHSH) + dwHandshakeLen; pContext->pClientHello = SPExternalAlloc(pContext->cbClientHello); if(pContext->pClientHello == NULL) { SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } CopyMemory(pContext->pClientHello, pb, pContext->cbClientHello); pContext->dwClientHelloProtocol = SP_PROT_SSL3_CLIENT;
pb += sizeof(SHSH);
if(!Ssl3ParseClientHello(pContext, pb, dwHandshakeLen, fAttemptReconnect, &fRestart)) { SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG)); }
if(fRestart) { pContext->State = SSL3_STATE_GEN_SERVER_HELLO_RESTART; } else { pContext->State = SSL3_STATE_GEN_SERVER_HELLO; }
SP_RETURN(PCT_ERR_OK); }
/*
*************************************************************************** * SPBuildCCSAndFinish * This is a common nroutine for client/server. it builds the change cipher * spec message and finished message. **************************************************************************** */
SP_STATUS SPBuildCCSAndFinish( PSPContext pContext, // in, out
PSPBuffer pCommOutput) // out
{ DWORD cbMessage; BOOL fClient; SP_STATUS pctRet; BOOL fAllocated = FALSE;
// Estimate size of the message group.
cbMessage = sizeof(SWRAP) + // ChangeCipherSpec
CB_SSL3_CHANGE_CIPHER_SPEC_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
cbMessage += sizeof(SWRAP) + // Finished
CB_SSL3_FINISHED_MSG_ONLY + SP_MAX_DIGEST_LEN + SP_MAX_BLOCKCIPHER_SIZE;
if(pCommOutput->pvBuffer) { // Application has allocated memory.
if(pCommOutput->cbBuffer < cbMessage) { pCommOutput->cbData = cbMessage; return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); } fAllocated = TRUE; } else { // Schannel is to allocate memory.
pCommOutput->cbBuffer = cbMessage; pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if(pCommOutput->pvBuffer == NULL) { return (SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } } pCommOutput->cbData = 0;
// Are we the client?
if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3TLS1_CLIENTS) { fClient = TRUE; } else { fClient = FALSE; }
// Build messages.
pctRet = BuildCCSAndFinishMessage(pContext, pCommOutput, fClient); if(pctRet != PCT_ERR_OK) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = NULL; } return pctRet;
}
/*
*************************************************************************** * Ssl3SrvHandleUniHello: * we can get an UniHello from client-side, parse and digest the info **************************************************************************** */
SP_STATUS Ssl3SrvHandleUniHello( PSPContext pContext, PBYTE pbMsg, DWORD cbMsg) { SP_STATUS pctRet; PSsl2_Client_Hello pHello = NULL; SPBuffer Input;
SP_BEGIN("Ssl3SrvHandleUniHello");
if(pContext == NULL) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
if(pContext->pCredGroup == NULL) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
//
// Decode the ClientHello message.
//
Input.pvBuffer = pbMsg; Input.cbData = cbMsg; Input.cbBuffer = cbMsg;
pctRet = Ssl2UnpackClientHello(&Input, &pHello);
if(PCT_ERR_OK != pctRet) { goto Ret; }
// Save the ClientHello message so we can hash it later, once
// we know what algorithm and CSP we're using.
if(pContext->pClientHello) { SPExternalFree(pContext->pClientHello); } pContext->cbClientHello = Input.cbData - sizeof(SSL2_MESSAGE_HEADER); pContext->pClientHello = SPExternalAlloc(pContext->cbClientHello); if(pContext->pClientHello == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto Ret; } CopyMemory(pContext->pClientHello, (PUCHAR)Input.pvBuffer + sizeof(SSL2_MESSAGE_HEADER), pContext->cbClientHello); pContext->dwClientHelloProtocol = SP_PROT_SSL2_CLIENT;
/* keep challenge around for later */ CopyMemory( pContext->pChallenge, pHello->Challenge, pHello->cbChallenge); pContext->cbChallenge = pHello->cbChallenge;
/* Initialize the "Client.random" from the challenge */ FillMemory(pContext->rgbS3CRandom, CB_SSL3_RANDOM - pContext->cbChallenge, 0);
CopyMemory( pContext->rgbS3CRandom + CB_SSL3_RANDOM - pContext->cbChallenge, pContext->pChallenge, pContext->cbChallenge);
//
// We know that this isn't a reconnect, so allocate a new cache entry.
//
if(!SPCacheRetrieveNew(TRUE, pContext->pszTarget, &pContext->RipeZombie)) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto Ret; }
pContext->RipeZombie->fProtocol = pContext->dwProtocol; pContext->RipeZombie->dwCF = pContext->dwRequestedCF;
SPCacheAssignNewServerCredential(pContext->RipeZombie, pContext->pCredGroup);
//
// Determine cipher suite to use.
//
pctRet = Ssl3SelectCipherEx(pContext, pHello->CipherSpecs, pHello->cCipherSpecs); if (pctRet != PCT_ERR_OK) { goto Ret; }
pContext->State = SSL3_STATE_GEN_SERVER_HELLO;
Ret: if(NULL != pHello) { SPExternalFree(pHello); }
SP_RETURN( pctRet ); }
/*
*************************************************************************** Build Server hello onto pb... we need to check the boundary condition with cb **************************************************************************** */ void Ssl3BuildServerHello(PSPContext pContext, PBYTE pb) { SSH *pssh = (SSH *) pb; WORD wT = sizeof(SSH) - sizeof(SHSH); DWORD dwCipher = UniAvailableCiphers[pContext->dwPendingCipherSuiteIndex].CipherKind;
FillMemory(pssh, sizeof(SSH), 0); pssh->typHS = SSL3_HS_SERVER_HELLO; pssh->bcbMSB = MSBOF(wT) ; pssh->bcbLSB = LSBOF(wT) ; pssh->bMajor = SSL3_CLIENT_VERSION_MSB; if(pContext->RipeZombie->fProtocol == SP_PROT_SSL3_SERVER) { pssh->bMinor = (UCHAR)SSL3_CLIENT_VERSION_LSB; } else { pssh->bMinor = (UCHAR)TLS1_CLIENT_VERSION_LSB; } pssh->wCipherSelectedMSB = MSBOF(dwCipher); pssh->wCipherSelectedLSB = LSBOF(dwCipher); pssh->cbSessionId = (char)pContext->RipeZombie->cbSessionID; CopyMemory(pssh->rgbSessionId, pContext->RipeZombie->SessionID, pContext->RipeZombie->cbSessionID) ; CopyMemory(pssh->rgbRandom, pContext->rgbS3SRandom, CB_SSL3_RANDOM); }
/*
*************************************************************************** Build Server Hello Done message **************************************************************************** */ void BuildServerHelloDone( PBYTE pb) { SHSH *pshsh = (SHSH *) pb ;
// struct { } ServerHelloDone;
SP_BEGIN("BuildServerHelloDone"); FillMemory(pshsh, sizeof(SHSH), 0); pshsh->typHS = SSL3_HS_SERVER_HELLO_DONE; SP_END(); }
//+---------------------------------------------------------------------------
//
// Function: ParseKeyExchgMsg
//
// Synopsis: Parse the ClientKeyExchange message.
//
// Arguments: [pContext] -- Schannel context.
// [pb] -- Pointer to message's 4-byte handshake
// header.
//
// History: 10-03-97 jbanes Server-side CAPI integration.
//
// Notes: This routine is called by the server-side only.
//
//----------------------------------------------------------------------------
SP_STATUS ParseKeyExchgMsg( PSPContext pContext, PBYTE pb) { SP_STATUS pctRet; DWORD cbEncryptedKey; PBYTE pbEncryptedKey;
// check for correct state
if(SSL2_STATE_SERVER_HELLO == pContext->State && pContext->fCertReq) { return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); }
// make sure we're a server
if(!(pContext->pKeyExchInfo->fProtocol & SP_PROT_SSL3TLS1_CLIENTS)) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
if(*pb != SSL3_HS_CLIENT_KEY_EXCHANGE) { return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); }
cbEncryptedKey = ((INT)pb[1] << 16) + ((INT)pb[2] << 8) + (INT)pb[3]; pbEncryptedKey = pb + (sizeof(SHSH)) ;
if(pContext->pKeyExchInfo == NULL) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
/* Decrypt the encrypted portion of the master key */ pctRet = pContext->pKeyExchInfo->System->GenerateServerMasterKey( pContext, NULL, 0, pbEncryptedKey, cbEncryptedKey); if(pctRet != PCT_ERR_OK) { return SP_LOG_RESULT(pctRet); }
pContext->State = SSL3_STATE_SERVER_KEY_XCHANGE;
return PCT_ERR_OK; }
SP_STATUS UpdateAndDuplicateIssuerList( PSPCredentialGroup pCredGroup, PBYTE * ppbIssuerList, PDWORD pcbIssuerList) { SP_STATUS pctRet;
LockCredentialExclusive(pCredGroup);
*ppbIssuerList = NULL; *pcbIssuerList = 0;
// Check for GP update from the domain controller.
SslCheckForGPEvent();
// Build list of trusted issuers.
if((pCredGroup->pbTrustedIssuers == NULL) || (pCredGroup->dwFlags & CRED_FLAG_UPDATE_ISSUER_LIST)) { pctRet = SPContextGetIssuers(pCredGroup); if(pctRet != PCT_ERR_OK) { UnlockCredential(pCredGroup); return SP_LOG_RESULT(pctRet); } }
// Allocate memory.
*ppbIssuerList = SPExternalAlloc(pCredGroup->cbTrustedIssuers); if(*ppbIssuerList == NULL) { UnlockCredential(pCredGroup); return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); }
// Copy issuer list.
memcpy(*ppbIssuerList, pCredGroup->pbTrustedIssuers, pCredGroup->cbTrustedIssuers); *pcbIssuerList = pCredGroup->cbTrustedIssuers;
UnlockCredential(pCredGroup);
return PCT_ERR_OK; }
/*
* ***************************************************************************** * Ssl3BuildCertificateRequest * * Build the CERTIFICATE_REQUEST handshake message. */ SP_STATUS Ssl3BuildCertificateRequest( PBYTE pbIssuerList, // in
DWORD cbIssuerList, // in
PBYTE pbMessage, // out
DWORD *pdwMessageLen) // out
{ PBYTE pbMessageStart = pbMessage; DWORD dwBodyLength;
// HandshakeType
pbMessage[0] = SSL3_HS_CERTIFICATE_REQUEST; pbMessage += 1;
// Skip message body length field (3 bytes)
pbMessage += 3;
//
// enum {
// rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
// rsa_ephemeral_dh(5), dss_ephemeral_dh(6), fortezza_dms(20), (255)
// } ClientCertificateType;
//
// opaque DistinguishedName<1..2^16-1>;
//
// struct {
// ClientCertificateType certificate_types<1..2^8-1>;
// DistinguishedName certificate_authorities<3..2^16-1>;
// } CertificateRequest;
//
// Certificate type
pbMessage[0] = 2; // certificate type vector length
pbMessage[1] = SSL3_CERTTYPE_RSA_SIGN; pbMessage[2] = SSL3_CERTTYPE_DSS_SIGN; pbMessage += 3;
// Trusted issuer list length
pbMessage[0] = MSBOF(cbIssuerList); pbMessage[1] = LSBOF(cbIssuerList); pbMessage += 2;
// Trusted issuer list
if(pbIssuerList != NULL) { CopyMemory(pbMessage, pbIssuerList, cbIssuerList); pbMessage += cbIssuerList; }
// Compute message body length (subtract 4 byte header)
dwBodyLength = (DWORD)(pbMessage - pbMessageStart) - 4;
// Fill in message body length field (3 bytes)
pbMessageStart[1] = (UCHAR) ((dwBodyLength & 0x00ff0000) >> 16); pbMessageStart[2] = MSBOF(dwBodyLength); pbMessageStart[3] = LSBOF(dwBodyLength);
*pdwMessageLen = dwBodyLength + 4;
return PCT_ERR_OK; }
/*
* ***************************************************************************** * Ssl3ParseClientHello * * This routine parses just the CLIENT_HELLO message itself. The * handshake crud has already been stripped off. */ BOOL Ssl3ParseClientHello( PSPContext pContext, PBYTE pbMessage, DWORD cbMessage, BOOL fAttemptReconnect, BOOL * pfReconnect) { DWORD dwVersion; PBYTE pbSessionId; DWORD cbSessionId; DWORD cbCipherSpecLen; DWORD cbCompMethodLen; INT iCompMethod; DWORD i; SP_STATUS pctRet = PCT_ERR_OK; DWORD dwProtocol = SP_PROT_SSL3_SERVER; Ssl2_Cipher_Kind CipherSpecs[MAX_UNI_CIPHERS]; DWORD cCipherSpecs; DWORD dwCacheCipher; BOOL fFound; BOOL fReconnect = FALSE;
//
// struct {
// ProtocolVersion client_version;
// Random random;
// SessinoID session_id;
// CipherSuite cipher_suites<2..2^16-1>
// CompressionMethod compression_methods<1..2^8-1>;
// } ClientHello;
//
*pfReconnect = FALSE;
//
// Parse the ClientHello message.
//
// ProtocolVersion = client_version;
if(cbMessage < 2) { return FALSE; }
dwVersion = ((DWORD)pbMessage[0] << 8) + pbMessage[1]; if(dwVersion < SSL3_CLIENT_VERSION) { return FALSE; }
//see if it's a TLS 1 version !
if(dwVersion >= TLS1_CLIENT_VERSION) dwProtocol = SP_PROT_TLS1_SERVER; pbMessage += 2; cbMessage -= 2;
// Random random
if(cbMessage < CB_SSL3_RANDOM) { return FALSE; }
CopyMemory(pContext->rgbS3CRandom, pbMessage, CB_SSL3_RANDOM); pContext->cbChallenge = CB_SSL3_RANDOM; pbMessage += CB_SSL3_RANDOM; cbMessage -= CB_SSL3_RANDOM;
// SessionID session_id; (length)
if(cbMessage < 1) { return FALSE; } cbSessionId = pbMessage[0]; if(cbSessionId > CB_SSL3_SESSION_ID) { return FALSE; } pbMessage += 1; cbMessage -= 1;
// SessionID session_id;
if(cbMessage < cbSessionId) { return FALSE; } pbSessionId = pbMessage; pbMessage += cbSessionId; cbMessage -= cbSessionId;
// CipherSuite cipher_suites<2..2^16-1>; (length)
if(cbMessage < 2) { return FALSE; } cbCipherSpecLen = ((INT)pbMessage[0] << 8) + pbMessage[1]; if(cbCipherSpecLen % 2) { return FALSE; } pbMessage += 2; cbMessage -= 2;
if(cbMessage < cbCipherSpecLen) { return FALSE; }
if(cbCipherSpecLen %2) { return FALSE; }
// CipherSuite cipher_suites<2..2^16-1>;
if(cbCipherSpecLen / 2 > MAX_UNI_CIPHERS) { cCipherSpecs = MAX_UNI_CIPHERS; } else { cCipherSpecs = cbCipherSpecLen / 2; }
// Build list of client cipher suites.
for(i = 0; i < cCipherSpecs; i++) { CipherSpecs[i] = COMBINEBYTES(pbMessage[i*2], pbMessage[(i*2)+1]); } pbMessage += cbCipherSpecLen; cbMessage -= cbCipherSpecLen;
// CompressionMethod compression_methods<1..2^8-1>; (length)
if(cbMessage < 1) { return FALSE; } cbCompMethodLen = pbMessage[0]; if(cbCompMethodLen < 1) { return FALSE; } pbMessage += 1; cbMessage -= 1;
if(cbMessage < cbCompMethodLen) { return FALSE; }
iCompMethod = -1; for(i = 0 ; i <cbCompMethodLen; i++) { if(pbMessage[i] == 0) { iCompMethod = 0; break; }
} pbMessage += cbCompMethodLen; cbMessage -= cbCompMethodLen;
if(iCompMethod != 0) { return FALSE; }
//
// Check to see if this is a reconnect.
//
if(((pContext->Flags & CONTEXT_FLAG_NOCACHE) == 0) && (cbSessionId > 0) && fAttemptReconnect) { fReconnect = SPCacheRetrieveBySession( pContext, pbSessionId, cbSessionId, &pContext->RipeZombie); if(fReconnect) { // Make sure client's cipher suite list includes one from cache.
fFound = FALSE; dwCacheCipher = UniAvailableCiphers[pContext->RipeZombie->dwCipherSuiteIndex].CipherKind; for(i = 0; i < cCipherSpecs; i++) { if(CipherSpecs[i] == dwCacheCipher) { fFound = TRUE; break; } }
if(fFound) { // Transfer information from the cache entry to the context element.
pctRet = ContextInitCiphersFromCache(pContext); }
if(!fFound || pctRet != PCT_ERR_OK) { // This cache entry is not suitable for some reason. We need
// to dump this cache entry and perform a full handshake.
// This is typically caused by a client-side implementation
// problem.
pContext->RipeZombie->ZombieJuju = FALSE; SPCacheDereference(pContext->RipeZombie); pContext->RipeZombie = NULL; fReconnect = FALSE; } } }
if(fReconnect) { // We're doing a reconnect.
DebugLog((DEB_TRACE, "Accept client's reconnect request.\n"));
*pfReconnect = TRUE;
} else { // We're doing a full handshake, so allocate a cache entry.
if(!SPCacheRetrieveNew(TRUE, pContext->pszTarget, &pContext->RipeZombie)) { SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); return FALSE; }
pContext->RipeZombie->fProtocol = pContext->dwProtocol; pContext->RipeZombie->dwCF = pContext->dwRequestedCF;
SPCacheAssignNewServerCredential(pContext->RipeZombie, pContext->pCredGroup);
//
// Select cipher suite to use.
//
pctRet = Ssl3SelectCipherEx(pContext, CipherSpecs, cCipherSpecs); if (pctRet != PCT_ERR_OK) { return FALSE; } }
return TRUE; }
|