You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4042 lines
113 KiB
4042 lines
113 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|