//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995. // // File: ssl2srv.c // // Contents: // // Classes: // // Functions: // // History: 8-08-95 RichardW Created // //---------------------------------------------------------------------------- #include #include #include 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); }