windows-nt/Source/XPSP1/NT/ds/security/protocols/schannel/spbase/ssl2srv.c
2020-09-26 16:20:57 +08:00

1044 lines
30 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: ssl2srv.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 8-08-95 RichardW Created
//
//----------------------------------------------------------------------------
#include <spbase.h>
#include <ssl2msg.h>
#include <ssl2prot.h>
SP_STATUS Ssl2SrvGenerateServerFinish(PSPContext pContext,
PSPBuffer pCommOutput);
SP_STATUS Ssl2SrvGenerateServerVerify(PSPContext pContext,
PSPBuffer pCommOutput);
SP_STATUS Ssl2SrvVerifyClientFinishMsg(PSPContext pContext,
PSPBuffer pCommInput);
#define SSL_OFFSET_OF(t, v) ((DWORD)(ULONG_PTR)&(((t)NULL)->v))
#define SSL2_CERT_TYPE_FROM_CAPI(s) X509_ASN_ENCODING
SP_STATUS WINAPI
Ssl2ServerProtocolHandler(
PSPContext pContext,
PSPBuffer pCommInput,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = 0;
DWORD cMessageType;
DWORD dwStateTransition;
BOOL fRaw = TRUE;
SPBuffer MsgInput;
DWORD cbMsg;
PUCHAR pb;
UCHAR bCT;
if (NULL != pCommOutput)
{
pCommOutput->cbData = 0;
}
MsgInput.pvBuffer = pCommInput->pvBuffer;
MsgInput.cbBuffer = pCommInput->cbBuffer;
MsgInput.cbData = pCommInput->cbData;
// In the following states, we should decrypt the message:
switch(pContext->State)
{
case SSL2_STATE_SERVER_VERIFY:
case SSL2_STATE_SERVER_RESTART:
pctRet = Ssl2DecryptMessage(pContext, pCommInput, &MsgInput);
cMessageType = ((PUCHAR) MsgInput.pvBuffer)[0];
fRaw = FALSE;
break;
case SP_STATE_SHUTDOWN:
case SP_STATE_SHUTDOWN_PENDING:
cMessageType = 0;
break;
case SP_STATE_CONNECTED:
// The server has attempted to initiate a reconnect.
return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION);
default:
if(pCommInput->cbData < 3)
{
return SP_LOG_RESULT(PCT_INT_INCOMPLETE_MSG);
}
cMessageType = ((PUCHAR) MsgInput.pvBuffer)[2];
break;
}
if (pctRet != PCT_ERR_OK)
{
// to handle incomplete message errors
return(pctRet);
}
dwStateTransition = pContext->State | (cMessageType<<16);
switch(dwStateTransition)
{
case SP_STATE_SHUTDOWN_PENDING:
// There's no CloseNotify in SSL2, so just transition to
// the shutdown state and leave the output buffer empty.
pContext->State = SP_STATE_SHUTDOWN;
break;
case SP_STATE_SHUTDOWN:
return PCT_INT_EXPIRED;
/* Server receives client hello */
case (SSL2_MT_CLIENT_HELLO << 16) | SP_STATE_NONE:
{
PSsl2_Client_Hello pSsl2Hello;
// Attempt to recognize and handle various versions of client
// hello, start by trying to unpickle the most recent version, and
// then next most recent, until one unpickles. Then run the handle
// code. We can also put unpickling and handling code in here for
// SSL messages.
pctRet = Ssl2UnpackClientHello(pCommInput, &pSsl2Hello);
if (PCT_ERR_OK == pctRet)
{
if (((pContext->Flags & CONTEXT_FLAG_NOCACHE) == 0) &&
(pSsl2Hello->cbSessionID) &&
(SPCacheRetrieveBySession(pContext,
pSsl2Hello->SessionID,
pSsl2Hello->cbSessionID,
&pContext->RipeZombie)))
{
DebugLog((DEB_TRACE, "Accept client's reconnect request.\n"));
pctRet = Ssl2SrvGenRestart(pContext,
pSsl2Hello,
pCommOutput);
if (PCT_ERR_OK == pctRet)
{
pContext->State = SSL2_STATE_SERVER_VERIFY;
}
}
else
{
// We're doing a full handshake, so allocate a cache entry.
if(!SPCacheRetrieveNew(TRUE,
pContext->pszTarget,
&pContext->RipeZombie))
{
pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
else
{
pContext->RipeZombie->fProtocol = pContext->dwProtocol;
pContext->RipeZombie->dwCF = pContext->dwRequestedCF;
pContext->RipeZombie->pServerCred = pContext->pCredGroup;
pctRet = Ssl2SrvHandleClientHello(pContext,
pCommInput,
pSsl2Hello,
pCommOutput);
if (PCT_ERR_OK == pctRet)
{
pContext->State = SSL2_STATE_SERVER_HELLO;
}
}
}
SPExternalFree(pSsl2Hello);
}
else if(pctRet != PCT_INT_INCOMPLETE_MSG)
{
pctRet |= PCT_INT_DROP_CONNECTION;
}
if (SP_FATAL(pctRet))
{
pContext->State = PCT1_STATE_ERROR;
}
break;
}
case (SSL2_MT_CLIENT_MASTER_KEY << 16) | SSL2_STATE_SERVER_HELLO:
pctRet = Ssl2SrvHandleCMKey(pContext, pCommInput, pCommOutput);
if (SP_FATAL(pctRet))
{
pContext->State = PCT1_STATE_ERROR;
}
else
{
if (PCT_ERR_OK == pctRet)
{
pContext->State = SSL2_STATE_SERVER_VERIFY;
}
// We received a non-fatal error, so the state doesn't change,
// giving the app time to deal with this.
}
break;
case (SSL2_MT_CLIENT_FINISHED_V2 << 16) | SSL2_STATE_SERVER_VERIFY:
pctRet = Ssl2SrvHandleClientFinish(
pContext,
&MsgInput,
pCommOutput);
if (SP_FATAL(pctRet))
{
pContext->State = PCT1_STATE_ERROR;
}
else
{
if (PCT_ERR_OK == pctRet)
{
pContext->State = SP_STATE_CONNECTED;
pContext->DecryptHandler = Ssl2DecryptHandler;
pContext->Encrypt = Ssl2EncryptMessage;
pContext->Decrypt = Ssl2DecryptMessage;
pContext->GetHeaderSize = Ssl2GetHeaderSize;
}
// We received a non-fatal error, so the state doesn't change,
// giving the app time to deal with this.
}
break;
default:
DebugLog((DEB_WARN, "Error in protocol, dwStateTransition is %lx\n", dwStateTransition));
pContext->State = PCT1_STATE_ERROR;
pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
break;
}
if (pctRet & PCT_INT_DROP_CONNECTION)
{
pContext->State &= ~SP_STATE_CONNECTED;
}
return(pctRet);
}
SP_STATUS
Ssl2SrvHandleClientHello(
PSPContext pContext,
PSPBuffer pCommInput,
PSsl2_Client_Hello pHello,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
PSPCredential pCred;
Ssl2_Server_Hello Reply;
DWORD cCommonCiphers;
DWORD CommonCiphers[MAX_UNI_CIPHERS];
PSessCacheItem pZombie;
BOOL fFound;
DWORD i,j;
SP_BEGIN("Ssl2SrvHandleClientHello");
pCommOutput->cbData = 0;
/* validate the buffer configuration */
if(NULL == pContext)
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
}
pZombie = pContext->RipeZombie;
// See if we have a cert that supports ssl2
pctRet = SPPickServerCertificate(pContext, SP_EXCH_RSA_PKCS1);
if(PCT_ERR_OK != pctRet)
{
SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
}
pCred = pZombie->pActiveServerCred;
if (!pCred)
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
}
do {
ZeroMemory(&Reply, sizeof(Reply));
//
// Calculate common ciphers:
//
cCommonCiphers = 0;
for(i = 0; i < UniNumCiphers; i++)
{
PCipherInfo pCipherInfo;
PHashInfo pHashInfo;
PKeyExchangeInfo pExchInfo;
// Is this an SSL2 cipher suite?
if(!(UniAvailableCiphers[i].fProt & pContext->RipeZombie->fProtocol))
{
continue;
}
pCipherInfo = GetCipherInfo(UniAvailableCiphers[i].aiCipher,
UniAvailableCiphers[i].dwStrength);
if(NULL == pCipherInfo)
{
continue;
}
if(!IsCipherSuiteAllowed(pContext,
pCipherInfo,
pZombie->fProtocol,
pZombie->dwCF,
UniAvailableCiphers[i].dwFlags))
{
continue;
}
pHashInfo = GetHashInfo(UniAvailableCiphers[i].aiHash);
if(NULL == pHashInfo)
{
continue;
}
if(!IsHashAllowed(pContext, pHashInfo, pZombie->fProtocol))
{
continue;
}
pExchInfo = GetKeyExchangeInfo(UniAvailableCiphers[i].KeyExch);
if(NULL == pExchInfo)
{
continue;
}
if(!IsExchAllowed(pContext, pExchInfo, pZombie->fProtocol))
{
continue;
}
// Is this cipher suite supported by the client?
for(fFound = FALSE, j = 0; j < pHello->cCipherSpecs; j++)
{
if(UniAvailableCiphers[i].CipherKind == pHello->CipherSpecs[j])
{
fFound = TRUE;
break;
}
}
if(!fFound)
{
continue;
}
// Does the CSP support this cipher suite?
if(!IsAlgSupportedCapi(pContext->RipeZombie->fProtocol,
UniAvailableCiphers + i,
pCred->pCapiAlgs,
pCred->cCapiAlgs))
{
continue;
}
// Add this cipher to list.
CommonCiphers[cCommonCiphers++] = UniAvailableCiphers[i].CipherKind;
}
//
// if cCommonCipers == 0, then we have none in common. At this point, we
// should generate an error response, but that is for later. For now,
// we will generate an invalid_token return, and bail out.
//
if (cCommonCiphers == 0)
{
pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
LogCipherMismatchEvent();
break;
}
Reply.cCipherSpecs = cCommonCiphers;
Reply.pCipherSpecs = CommonCiphers;
Reply.SessionIdHit = 0;
Reply.CertificateType = SSL2_CERT_TYPE_FROM_CAPI(pCred->pCert->dwCertEncodingType);
// Auto allocate the certificate. !We must free them when we're done....
Reply.pCertificate = NULL;
Reply.cbCertificate = 0;
pctRet = SPSerializeCertificate(SP_PROT_SSL2,
FALSE,
&Reply.pCertificate,
&Reply.cbCertificate,
pCred->pCert,
0);
if (PCT_ERR_OK != pctRet)
{
break;
}
/* Generate a conneciton id to use while establishing connection */
Reply.cbConnectionID = SSL2_GEN_CONNECTION_ID_LEN;
GenerateRandomBits( Reply.ConnectionID,
Reply.cbConnectionID );
CopyMemory(pContext->pConnectionID,
Reply.ConnectionID,
Reply.cbConnectionID);
pContext->cbConnectionID = Reply.cbConnectionID;
/* keep challenge around for later */
CopyMemory( pContext->pChallenge,
pHello->Challenge,
pHello->cbChallenge);
pContext->cbChallenge = pHello->cbChallenge;
pctRet = Ssl2PackServerHello(&Reply, pCommOutput);
if(Reply.pCertificate)
{
SPExternalFree(Reply.pCertificate);
}
if (PCT_ERR_OK != pctRet)
{
break;
}
pContext->WriteCounter = 1; /* received client hello */
pContext->ReadCounter = 1; /* Sending server hello */
SP_RETURN(PCT_ERR_OK);
} while (TRUE); /* end Polish Loop */
if((pContext->Flags & CONTEXT_FLAG_EXT_ERR) &&
(pctRet == PCT_ERR_SPECS_MISMATCH))
{
// Our SSL2 implementation does not do client auth,
// so there is only one error message, cipher error.
pCommOutput->cbData = 3; // MSG-ERROR + ERROR-CODE-MSB + ERROR-CODE-LSB
if(pCommOutput->pvBuffer == NULL)
{
pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
if (NULL == pCommOutput->pvBuffer)
{
SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
}
pCommOutput->cbBuffer = pCommOutput->cbData;
}
if(pCommOutput->cbData > pCommOutput->cbBuffer)
{
// Required buffer size returned in pCommOutput->cbData.
SP_RETURN(SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL));
}
((PUCHAR)pCommOutput->pvBuffer)[0] = SSL2_MT_ERROR;
((PUCHAR)pCommOutput->pvBuffer)[1] = MSBOF(SSL_PE_NO_CIPHER);
((PUCHAR)pCommOutput->pvBuffer)[2] = LSBOF(SSL_PE_NO_CIPHER);
}
SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
}
SP_STATUS
Ssl2SrvGenRestart(
PSPContext pContext,
PSsl2_Client_Hello pHello,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
SPBuffer SecondOutput;
Ssl2_Server_Hello Reply;
DWORD cbMessage, cbMsg, cPadding;
PSessCacheItem pZombie;
SP_BEGIN("Ssl2SrvGenRestart");
pCommOutput->cbData = 0;
/* validate the buffer configuration */
/* make sure we have the needed authentication data area */
if (NULL == pContext)
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
}
pZombie = pContext->RipeZombie;
do {
FillMemory( &Reply, sizeof( Reply ), 0 );
Reply.SessionIdHit = (DWORD)1;
Reply.cCipherSpecs = 0;
Reply.pCipherSpecs = NULL;
Reply.pCertificate = NULL;
Reply.cbCertificate = 0;
Reply.CertificateType = 0;
/* Note, we generate both a server hello, and a server verify in
* this handling routing. This is because netscape will not send
* us a client finish until the server verify is received
*/
// Load pending ciphers from cache
pctRet = ContextInitCiphersFromCache(pContext);
if(PCT_ERR_OK != pctRet)
{
break;
}
pctRet = ContextInitCiphers(pContext, TRUE, TRUE);
if(PCT_ERR_OK != pctRet)
{
break;
}
Reply.cbConnectionID = SSL2_GEN_CONNECTION_ID_LEN;
GenerateRandomBits( Reply.ConnectionID,
Reply.cbConnectionID );
CopyMemory(pContext->pConnectionID,
Reply.ConnectionID,
Reply.cbConnectionID);
pContext->cbConnectionID = Reply.cbConnectionID;
/* keep challenge around for later */
CopyMemory( pContext->pChallenge,
pHello->Challenge,
pHello->cbChallenge);
pContext->cbChallenge = pHello->cbChallenge;
// Make a new set of session keys.
pctRet = MakeSessionKeys(pContext,
pContext->RipeZombie->hMasterProv,
pContext->RipeZombie->hMasterKey);
if(pctRet != PCT_ERR_OK)
{
break;
}
// Activate session keys.
pContext->hReadKey = pContext->hPendingReadKey;
pContext->hWriteKey = pContext->hPendingWriteKey;
pContext->hPendingReadKey = 0;
pContext->hPendingWriteKey = 0;
/* calc size of the server hello (restart only) */
cbMessage = Reply.cbConnectionID +
Reply.cbCertificate +
Reply.cCipherSpecs * sizeof(Ssl2_Cipher_Tuple) +
SSL_OFFSET_OF(PSSL2_SERVER_HELLO, VariantData) -
sizeof(SSL2_MESSAGE_HEADER);
pCommOutput->cbData = cbMessage + 2;
/* calc size of server verify */
cbMsg = sizeof(UCHAR) + pContext->cbChallenge;
cPadding = ((cbMsg+pContext->pHashInfo->cbCheckSum) % pContext->pCipherInfo->dwBlockSize);
if(cPadding)
{
cPadding = pContext->pCipherInfo->dwBlockSize - cPadding;
}
pCommOutput->cbData += cbMsg +
pContext->pHashInfo->cbCheckSum +
cPadding +
(cPadding?3:2);
/* are we allocating our own memory? */
if(pCommOutput->pvBuffer == NULL) {
pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
if (NULL == pCommOutput->pvBuffer)
SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
pCommOutput->cbBuffer = pCommOutput->cbData;
}
if(pCommOutput->cbData > pCommOutput->cbBuffer)
{
// Required buffer size returned in pCommOutput->cbData.
SP_RETURN(PCT_INT_BUFF_TOO_SMALL);
}
pctRet = Ssl2PackServerHello(&Reply, pCommOutput);
if (PCT_ERR_OK != pctRet)
{
break;
}
pContext->WriteCounter = 1; /* received client hello */
pContext->ReadCounter = 1; /* Sending server hello */
/* Now pack the server verify message and encrypt it */
SecondOutput.pvBuffer = (PUCHAR)pCommOutput->pvBuffer+pCommOutput->cbData;
SecondOutput.cbBuffer = pCommOutput->cbBuffer-pCommOutput->cbData;
pctRet = Ssl2SrvGenerateServerVerify(pContext, &SecondOutput);
if (PCT_ERR_OK != pctRet)
{
break;
}
pCommOutput->cbData += SecondOutput.cbData;
SP_RETURN(PCT_ERR_OK);
} while (TRUE); /* end Polish Loop */
SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
}
SP_STATUS
Ssl2SrvHandleCMKey(
PSPContext pContext,
PSPBuffer pCommInput,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
PSsl2_Client_Master_Key pMasterKey = NULL;
DWORD dwKeyLen;
DWORD EncryptedLen;
DWORD i;
DWORD cbData;
PSessCacheItem pZombie;
SP_BEGIN("Ssl2SrvHandleCMKey");
pCommOutput->cbData = 0;
pZombie = pContext->RipeZombie;
do {
/* make sure we have the needed authentication data area */
cbData = pCommInput->cbData;
pctRet = Ssl2UnpackClientMasterKey(pCommInput, &pMasterKey);
if (PCT_ERR_OK != pctRet)
{
// If it's an incomplete message or something, just return;
if(!SP_FATAL(pctRet))
{
SP_RETURN(pctRet);
}
break;
}
pctRet = PCT_ERR_ILLEGAL_MESSAGE;
/* CMK sent cleartext, so we must auto-inc the read counter */
pContext->ReadCounter++;
pContext->pCipherInfo = NULL;
pContext->pHashInfo = NULL;
pContext->pKeyExchInfo = NULL;
// Pick a cipher suite
pctRet = PCT_ERR_SPECS_MISMATCH;
for(i = 0; i < UniNumCiphers; i++)
{
// Is this an SSL2 cipher suite?
if(!(UniAvailableCiphers[i].fProt & pContext->RipeZombie->fProtocol))
{
continue;
}
if(UniAvailableCiphers[i].CipherKind != pMasterKey->CipherKind)
{
continue;
}
pZombie->aiCipher = UniAvailableCiphers[i].aiCipher;
pZombie->dwStrength = UniAvailableCiphers[i].dwStrength;
pZombie->aiHash = UniAvailableCiphers[i].aiHash;
pZombie->SessExchSpec = UniAvailableCiphers[i].KeyExch;
pctRet = ContextInitCiphersFromCache(pContext);
if(pctRet != PCT_ERR_OK)
{
continue;
}
break;
}
pctRet = ContextInitCiphers(pContext, TRUE, TRUE);
if(pctRet != PCT_ERR_OK)
{
SP_LOG_RESULT(pctRet);
break;
}
/* Copy over the key args */
CopyMemory( pZombie->pKeyArgs,
pMasterKey->KeyArg,
pMasterKey->KeyArgLen );
pZombie->cbKeyArgs = pMasterKey->KeyArgLen;
// Store the clear key in the context structure.
CopyMemory( pZombie->pClearKey,
pMasterKey->ClearKey,
pMasterKey->ClearKeyLen);
pZombie->cbClearKey = pMasterKey->ClearKeyLen;
/* Decrypt the encrypted portion of the master key */
pctRet = pContext->pKeyExchInfo->System->GenerateServerMasterKey(
pContext,
pMasterKey->ClearKey,
pMasterKey->ClearKeyLen,
pMasterKey->pbEncryptedKey,
pMasterKey->EncryptedKeyLen);
if(PCT_ERR_OK != pctRet)
{
break;
}
SPExternalFree( pMasterKey );
pMasterKey = NULL;
// Update keys.
pContext->hReadKey = pContext->hPendingReadKey;
pContext->hWriteKey = pContext->hPendingWriteKey;
pContext->hPendingReadKey = 0;
pContext->hPendingWriteKey = 0;
pctRet = Ssl2SrvGenerateServerVerify(pContext, pCommOutput);
SP_RETURN(pctRet);
} while(TRUE);
if (pMasterKey)
{
SPExternalFree( pMasterKey );
}
if((pContext->Flags & CONTEXT_FLAG_EXT_ERR) &&
(pctRet == PCT_ERR_SPECS_MISMATCH))
{
// Our SSL2 implementation does not do client auth,
// so there is only one error message, cipher error.
pCommOutput->cbData = 3; // MSG-ERROR + ERROR-CODE-MSB + ERROR-CODE-LSB
/* are we allocating our own memory? */
if(pCommOutput->pvBuffer == NULL)
{
pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
if (NULL == pCommOutput->pvBuffer)
{
SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
}
pCommOutput->cbBuffer = pCommOutput->cbData;
}
if(pCommOutput->cbData <= pCommOutput->cbBuffer)
{
((PUCHAR)pCommOutput->pvBuffer)[0] = SSL2_MT_ERROR;
((PUCHAR)pCommOutput->pvBuffer)[1] = MSBOF(SSL_PE_NO_CIPHER);
((PUCHAR)pCommOutput->pvBuffer)[2] = LSBOF(SSL_PE_NO_CIPHER);
}
else
{
pCommOutput->cbData = 0;
}
}
SP_RETURN((PCT_INT_DROP_CONNECTION | pctRet));
}
SP_STATUS
Ssl2SrvVerifyClientFinishMsg(
PSPContext pContext,
PSPBuffer pCommInput)
{
SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
PSSL2_CLIENT_FINISHED pFinished;
SP_BEGIN("Ssl2SrvVerifyClientFinishMsg");
/* Note, there is no header in this message, as it has been pre-decrypted */
if (pCommInput->cbData != sizeof(UCHAR) + pContext->cbConnectionID)
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
}
pFinished = pCommInput->pvBuffer;
if (pFinished->MessageId != SSL2_MT_CLIENT_FINISHED_V2)
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
}
if ( memcmp(pFinished->ConnectionID,
pContext->pConnectionID,
pContext->cbConnectionID))
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
}
SP_RETURN(PCT_ERR_OK);
}
SP_STATUS
Ssl2SrvGenerateServerVerify(
PSPContext pContext,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
PSSL2_SERVER_VERIFY pVerify;
DWORD HeaderSize;
SPBuffer MsgOutput;
DWORD cPadding;
BOOL fAlloced = FALSE;
pCommOutput->cbData = 0;
SP_BEGIN("Ssl2SrvGenerateServerVerify");
do {
MsgOutput.cbData = sizeof(UCHAR) + pContext->cbChallenge;
cPadding = ((MsgOutput.cbData+pContext->pHashInfo->cbCheckSum) % pContext->pCipherInfo->dwBlockSize);
if(cPadding)
{
cPadding = pContext->pCipherInfo->dwBlockSize - cPadding;
}
HeaderSize = (cPadding?3:2);
pCommOutput->cbData = MsgOutput.cbData +
pContext->pHashInfo->cbCheckSum +
cPadding + HeaderSize;
/* are we allocating our own memory? */
if (pCommOutput->pvBuffer == NULL) {
pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
if (NULL == pCommOutput->pvBuffer)
{
SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
}
fAlloced = TRUE;
pCommOutput->cbBuffer = pCommOutput->cbData;
}
MsgOutput.pvBuffer= (PUCHAR)pCommOutput->pvBuffer +
HeaderSize+pContext->pHashInfo->cbCheckSum;
MsgOutput.cbBuffer= pCommOutput->cbBuffer -
HeaderSize-pContext->pHashInfo->cbCheckSum;
pVerify = (PSSL2_SERVER_VERIFY) MsgOutput.pvBuffer;
pVerify->MessageId = SSL2_MT_SERVER_VERIFY;
CopyMemory( pVerify->ChallengeData,
pContext->pChallenge,
pContext->cbChallenge );
pctRet = Ssl2EncryptMessage( pContext, &MsgOutput, pCommOutput);
if(PCT_ERR_OK != pctRet)
{
break;
}
SP_RETURN(PCT_ERR_OK);
} while(TRUE);
if(fAlloced && (NULL != pCommOutput->pvBuffer))
{
SPExternalFree(pCommOutput->pvBuffer);
pCommOutput->cbBuffer = 0;
pCommOutput->cbData = 0;
pCommOutput->pvBuffer = NULL;
}
SP_RETURN(PCT_INT_DROP_CONNECTION | pctRet);
}
SP_STATUS
Ssl2SrvGenerateServerFinish(
PSPContext pContext,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
PSSL2_SERVER_FINISHED pFinish;
DWORD HeaderSize;
SPBuffer MsgOutput;
DWORD cPadding;
BOOL fAlloced = FALSE;
pCommOutput->cbData = 0;
SP_BEGIN("Ssl2SrvGenerateServerFinish");
do {
/* Generate a session id to use during the session */
pContext->RipeZombie->cbSessionID = SSL2_SESSION_ID_LEN;
/* store this context in the cache */
/* note - we don't check error 'cause it's recoverable
* if we don't cache */
SPCacheAdd(pContext);
MsgOutput.cbData = sizeof(UCHAR) + pContext->RipeZombie->cbSessionID;
cPadding = ((MsgOutput.cbData+pContext->pHashInfo->cbCheckSum) % pContext->pCipherInfo->dwBlockSize);
if(cPadding)
{
cPadding = pContext->pCipherInfo->dwBlockSize - cPadding;
}
HeaderSize = (cPadding?3:2);
pCommOutput->cbData = MsgOutput.cbData +
pContext->pHashInfo->cbCheckSum +
cPadding +
HeaderSize;
/* are we allocating our own memory? */
if(pCommOutput->pvBuffer == NULL)
{
pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
if (NULL == pCommOutput->pvBuffer)
{
SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
}
fAlloced = TRUE;
pCommOutput->cbBuffer = pCommOutput->cbData;
}
if(pCommOutput->cbData > pCommOutput->cbBuffer)
{
// Required buffer size returned in pCommOutput->cbData.
SP_RETURN(SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL));
}
MsgOutput.pvBuffer= (PUCHAR)pCommOutput->pvBuffer + HeaderSize+pContext->pHashInfo->cbCheckSum;
MsgOutput.cbBuffer= pCommOutput->cbBuffer-HeaderSize-pContext->pHashInfo->cbCheckSum;
pFinish = (PSSL2_SERVER_FINISHED) MsgOutput.pvBuffer;
pFinish->MessageId = SSL2_MT_SERVER_FINISHED_V2;
CopyMemory( pFinish->SessionID,
pContext->RipeZombie->SessionID,
pContext->RipeZombie->cbSessionID );
/* Cache Context Here */
pctRet = Ssl2EncryptMessage( pContext, &MsgOutput, pCommOutput);
if(PCT_ERR_OK != pctRet)
{
break;
}
SP_RETURN(PCT_ERR_OK);
} while(TRUE);
if(fAlloced && (NULL != pCommOutput->pvBuffer))
{
SPExternalFree(pCommOutput->pvBuffer);
pCommOutput->cbBuffer = 0;
pCommOutput->cbData = 0;
pCommOutput->pvBuffer = NULL;
}
SP_RETURN(PCT_INT_DROP_CONNECTION | pctRet);
}
SP_STATUS
Ssl2SrvHandleClientFinish(
PSPContext pContext,
PSPBuffer pCommInput,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
SP_BEGIN("Ssl2SrvHandleClientFinish");
pCommOutput->cbData = 0;
pctRet = Ssl2SrvVerifyClientFinishMsg(pContext, pCommInput);
if (PCT_ERR_OK != pctRet)
{
SP_RETURN(pctRet);
}
pctRet = Ssl2SrvGenerateServerFinish(pContext, pCommOutput);
SP_RETURN(pctRet);
}