|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: pct1cli.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 09-23-97 jbanes LSA integration stuff.
//
//----------------------------------------------------------------------------
#include <spbase.h>
#include <pct1msg.h>
#include <pct1prot.h>
VOID Pct1ActivateSessionKeys(PSPContext pContext) { if(pContext->hReadKey) { if(!CryptDestroyKey(pContext->hReadKey)) { SP_LOG_RESULT(GetLastError()); } } pContext->hReadKey = pContext->hPendingReadKey;
if(pContext->hReadMAC) { if(!CryptDestroyKey(pContext->hReadMAC)) { SP_LOG_RESULT(GetLastError()); } } pContext->hReadMAC = pContext->hPendingReadMAC;
if(pContext->hWriteKey) { if(!CryptDestroyKey(pContext->hWriteKey)) { SP_LOG_RESULT(GetLastError()); } } pContext->hWriteKey = pContext->hPendingWriteKey;
if(pContext->hWriteMAC) { if(!CryptDestroyKey(pContext->hWriteMAC)) { SP_LOG_RESULT(GetLastError()); } } pContext->hWriteMAC = pContext->hPendingWriteMAC;
pContext->hPendingReadKey = 0; pContext->hPendingReadMAC = 0; pContext->hPendingWriteKey = 0; pContext->hPendingWriteMAC = 0; }
SP_STATUS WINAPI Pct1ClientProtocolHandler(PSPContext pContext, PSPBuffer pCommInput, PSPBuffer pCommOutput) { SP_STATUS pctRet= PCT_ERR_OK; DWORD dwStateTransition;
SP_BEGIN("Pct1ClientProtocolHandler");
if(pCommOutput) pCommOutput->cbData = 0;
/* Protocol handling steps should be listed in most common
* to least common in order to improve performance */
/* We are not connected, so we're doing
* protocol negotiation of some sort. All protocol * negotiation messages are sent in the clear */ /* There are no branches in the connecting protocol
* state transition diagram, besides connection and error, * which means that a simple case statement will do */
/* Do we have enough data to determine what kind of message we have */ /* Do we have enough data to determine what kind of message we have, or how much data we need*/
dwStateTransition = (pContext->State & 0xffff);
if(pCommInput->cbData < 3) { if(!(dwStateTransition == PCT1_STATE_RENEGOTIATE || dwStateTransition == SP_STATE_SHUTDOWN || dwStateTransition == SP_STATE_SHUTDOWN_PENDING)) { pctRet = PCT_INT_INCOMPLETE_MSG; } } else {
dwStateTransition = (((PUCHAR)pCommInput->pvBuffer)[2]<<16) | (pContext->State & 0xffff); }
if(pctRet == PCT_ERR_OK) { switch(dwStateTransition) { case SP_STATE_SHUTDOWN_PENDING: // There's no CloseNotify in PCT, 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; case PCT1_STATE_RENEGOTIATE: { SPBuffer In; SPBuffer Out; DWORD cbMessage; BOOL fAllocated = FALSE;
cbMessage = pContext->pHashInfo->cbCheckSum + pContext->pCipherInfo->dwBlockSize + sizeof(PCT1_MESSAGE_HEADER_EX) + PCT1_MAX_CLIENT_HELLO;
/* are we allocating our own memory? */ if(pCommOutput->pvBuffer == NULL) { pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if (NULL == pCommOutput->pvBuffer) { SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } fAllocated = TRUE; pCommOutput->cbBuffer = cbMessage; }
if(cbMessage > pCommOutput->cbBuffer) { if(fAllocated) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = NULL; SP_RETURN(PCT_INT_INTERNAL_ERROR); } pCommOutput->cbData = cbMessage; SP_RETURN(PCT_INT_BUFF_TOO_SMALL); }
In.pvBuffer = ((char *)pCommOutput->pvBuffer)+3; In.cbBuffer = pCommOutput->cbBuffer-3; In.cbData = 1; ((char *)In.pvBuffer)[0] = PCT1_ET_REDO_CONN;
// Build a Redo Request
pctRet = Pct1EncryptRaw(pContext, &In, pCommOutput, PCT1_ENCRYPT_ESCAPE); if(pctRet != PCT_ERR_OK) { if(fAllocated) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = NULL; } break; } Out.pvBuffer = (char *)pCommOutput->pvBuffer + pCommOutput->cbData; Out.cbBuffer = pCommOutput->cbBuffer - pCommOutput->cbData;
// Mark context as "unmapped" so that the new keys will get
// passed to the application process once the handshake is
// completed.
pContext->Flags &= ~CONTEXT_FLAG_MAPPED;
if(!SPCacheClone(&pContext->RipeZombie)) { if(fAllocated) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = NULL; } SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
pctRet = GeneratePct1StyleHello(pContext, &Out); pCommOutput->cbData += Out.cbData; break; }
/* Client receives Server hello */ case (PCT1_MSG_SERVER_HELLO << 16) | UNI_STATE_CLIENT_HELLO: case (PCT1_MSG_SERVER_HELLO << 16) | PCT1_STATE_CLIENT_HELLO: { PPct1_Server_Hello pHello; /* Attempt to recognize and handle various versions
* of Server hello, start by trying to unpickle the * oldest, and the next version, until * one unpickles. Then run the handle code. We can also put * unpickling and handling code in here for SSL messages */ if(PCT_ERR_OK == (pctRet = Pct1UnpackServerHello( pCommInput, &pHello))) { /* let's resurrect the zombie session */ if (pHello->RestartOk) { pctRet = Pct1CliRestart(pContext, pHello, pCommOutput); if(PCT_ERR_OK == pctRet) { pContext->State = SP_STATE_CONNECTED; pContext->DecryptHandler = Pct1DecryptHandler; pContext->Encrypt = Pct1EncryptMessage; pContext->Decrypt = Pct1DecryptMessage; pContext->GetHeaderSize = Pct1GetHeaderSize;
} } else { pContext->RipeZombie->fProtocol = SP_PROT_PCT1_CLIENT;
if(pContext->RipeZombie->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.
pContext->RipeZombie->ZombieJuju = FALSE; if(!SPCacheClone(&pContext->RipeZombie)) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); } }
if(pctRet == PCT_ERR_OK) { pctRet = Pct1CliHandleServerHello(pContext, pCommInput, pHello, pCommOutput); } if(PCT_ERR_OK == pctRet) { pContext->State = PCT1_STATE_CLIENT_MASTER_KEY; pContext->DecryptHandler = Pct1DecryptHandler; pContext->Encrypt = Pct1EncryptMessage; /* ?DCB? */ pContext->Decrypt = Pct1DecryptMessage; /* ?DCB? */ pContext->GetHeaderSize = Pct1GetHeaderSize;
}
} SPExternalFree(pHello);
} else if(pctRet != PCT_INT_INCOMPLETE_MSG) { pctRet |= PCT_INT_DROP_CONNECTION; }
if(SP_FATAL(pctRet)) { pContext->State = PCT1_STATE_ERROR; }
break; }
case (PCT1_MSG_SERVER_VERIFY << 16) | PCT1_STATE_CLIENT_MASTER_KEY: pctRet = Pct1CliHandleServerVerify(pContext, pCommInput, pCommOutput); if(SP_FATAL(pctRet)) { pContext->State = PCT1_STATE_ERROR; } else { if(PCT_ERR_OK == pctRet) { pContext->State = SP_STATE_CONNECTED; pContext->DecryptHandler = Pct1DecryptHandler; pContext->Encrypt = Pct1EncryptMessage; pContext->Decrypt = Pct1DecryptMessage; pContext->GetHeaderSize = Pct1GetHeaderSize;
} /* We received a non-fatal error, so the state doesn't
* change, giving the app time to deal with this */ } break;
default: pContext->State = PCT1_STATE_ERROR; { pctRet = PCT_INT_ILLEGAL_MSG; if(((PUCHAR)pCommInput->pvBuffer)[2] == PCT1_MSG_ERROR) { /* we received an error message, process it */ pctRet = Pct1HandleError(pContext, pCommInput, pCommOutput);
} else { /* we received an unknown error, generate a
* PCT_ERR_ILLEGAL_MESSAGE */ pctRet = Pct1GenerateError(pContext, pCommOutput, PCT_ERR_ILLEGAL_MESSAGE, NULL); } }
} } if(pctRet & PCT_INT_DROP_CONNECTION) { pContext->State &= ~SP_STATE_CONNECTED; } SP_RETURN(pctRet); }
//+---------------------------------------------------------------------------
//
// Function: Pct1CheckForExistingCred
//
// 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 Pct1CheckForExistingCred( 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) { pctRet = SPPickClientCertificate(pContext, SP_EXCH_RSA_PKCS1);
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); } }
SP_STATUS Pct1CliHandleServerHello(PSPContext pContext, PSPBuffer pCommInput, PPct1_Server_Hello pHello, PSPBuffer pCommOutput) { /* error to return to peer */ SP_STATUS pctRet=PCT_ERR_ILLEGAL_MESSAGE;
PSessCacheItem pZombie; PPct1_Client_Master_Key pCMKey = NULL; SPBuffer ErrData;
DWORD i, j; DWORD fMismatch = 0; DWORD cbClientCert = 0; PBYTE pbClientCert = NULL; BYTE MisData[PCT_NUM_MISMATCHES]; CertTypeMap LocalCertEncodingPref[5] ; DWORD cLocalCertEncodingPref = 0;
BOOL fClientAuth; PSigInfo pSigInfo = NULL;
DWORD ClientCertSpec = 0;
SP_BEGIN("Pct1CliHandleServerHello");
pCommOutput->cbData = 0;
/* validate the buffer configuration */ ErrData.cbData = 0; ErrData.pvBuffer = NULL; ErrData.cbBuffer = 0;
pZombie = pContext->RipeZombie;
#if DBG
DebugLog((DEB_TRACE, "Hello = %x\n", pHello)); DebugLog((DEB_TRACE, " Restart\t%s\n", pHello->RestartOk ? "Yes":"No")); DebugLog((DEB_TRACE, " ClientAuth\t%s\n", pHello->ClientAuthReq ? "Yes":"No")); DebugLog((DEB_TRACE, " Certificate Type\t%x\n", pHello->SrvCertSpec)); DebugLog((DEB_TRACE, " Hash Type\t%x\n", pHello->SrvHashSpec)); DebugLog((DEB_TRACE, " Cipher Type\t%x (%s)\n", pHello->SrvCipherSpec, DbgGetNameOfCrypto(pHello->SrvCipherSpec))); DebugLog((DEB_TRACE, " Certificate Len\t%ld\n", pHello->CertificateLen)); #endif
CopyMemory(pContext->pConnectionID, pHello->ConnectionID, pHello->cbConnectionID);
pContext->cbConnectionID = pHello->cbConnectionID;
fClientAuth = pHello->ClientAuthReq;
if(fClientAuth) { // If we're doing client auth, check to see if we have
// proper credentials.
/* Build a list of cert specs */ for(i=0; i < cPct1CertEncodingPref; i++) { for(j=0; j< pHello->cCertSpecs; j++) { // Does the client want this cipher type
if(aPct1CertEncodingPref[i].Spec == pHello->pClientCertSpecs[j]) { LocalCertEncodingPref[cLocalCertEncodingPref].Spec = aPct1CertEncodingPref[i].Spec; LocalCertEncodingPref[cLocalCertEncodingPref++].dwCertEncodingType = aPct1CertEncodingPref[i].dwCertEncodingType; break; } } }
// Decide on a signature algorithm.
for(i = 0; i < cPct1LocalSigKeyPref; i++) { for(j = 0; j < pHello->cSigSpecs; j++) { if(pHello->pClientSigSpecs[j] != aPct1LocalSigKeyPref[i].Spec) { continue; }
pSigInfo = GetSigInfo(pHello->pClientSigSpecs[j]); if(pSigInfo == NULL) continue; if((pSigInfo->fProtocol & SP_PROT_PCT1_CLIENT) == 0) { continue; } break; } if(pSigInfo) { break; } }
// Our PCT implementation only supports RSA client authentication.
pContext->Ssl3ClientCertTypes[0] = SSL3_CERTTYPE_RSA_SIGN; pContext->cSsl3ClientCertTypes = 1;
pctRet = Pct1CheckForExistingCred(pContext);
if(pctRet == SEC_E_INCOMPLETE_CREDENTIALS) { // It's okay to return here as we haven't done anything
// yet. We just need to return this error as a warning.
SP_RETURN(SEC_I_INCOMPLETE_CREDENTIALS); } else if(pctRet != PCT_ERR_OK) { // Attempt to carry on without a certificate, and hope
// the server doesn't shut us down.
fClientAuth = FALSE; pSigInfo = NULL; LogNoClientCertFoundEvent(); } else { // We are doing client auth with a certificate.
// Check to see if we're doing CHAIN based certificates
// by finding the first shared encoding type that matches
// our certificate type.
for(i=0; i < cLocalCertEncodingPref; i++) { if(LocalCertEncodingPref[i].dwCertEncodingType == pContext->pActiveClientCred->pCert->dwCertEncodingType) { ClientCertSpec = LocalCertEncodingPref[i].Spec; if(LocalCertEncodingPref[i].Spec == PCT1_CERT_X509_CHAIN) { pContext->fCertChainsAllowed = TRUE; } break; } }
// Get the client certificate chain.
pctRet = SPSerializeCertificate(SP_PROT_PCT1, pContext->fCertChainsAllowed, &pbClientCert, &cbClientCert, pContext->pActiveClientCred->pCert, CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL); if(pctRet != PCT_ERR_OK) { SP_RETURN(pctRet); } } }
for(i=0; i < Pct1NumCipher; i++) { if(Pct1CipherRank[i].Spec == pHello->SrvCipherSpec) { // Store this cipher identifier in the cache
pZombie->aiCipher = Pct1CipherRank[i].aiCipher; pZombie->dwStrength = Pct1CipherRank[i].dwStrength;
// Load the pending cipher structure.
pContext->pPendingCipherInfo = GetCipherInfo(pZombie->aiCipher, pZombie->dwStrength);
if(!IsCipherAllowed(pContext, pContext->pPendingCipherInfo, pZombie->fProtocol, pZombie->dwCF)) { pContext->pPendingCipherInfo = NULL; continue; } break;
} }
for(i=0; i < Pct1NumHash; i++) { if(Pct1HashRank[i].Spec == pHello->SrvHashSpec) { // Store this hash id in the cache
pZombie->aiHash = Pct1HashRank[i].aiHash;
// Load the pending hash sturcture
pContext->pPendingHashInfo = GetHashInfo(pZombie->aiHash); if(!IsHashAllowed(pContext, pContext->pPendingHashInfo, pZombie->fProtocol)) { pContext->pPendingHashInfo = NULL; continue; } break;
} } for(i=0; i < cPct1LocalExchKeyPref; i++) { if(aPct1LocalExchKeyPref[i].Spec == pHello->SrvExchSpec) { // Store the exch id in the cache.
pZombie->SessExchSpec = aPct1LocalExchKeyPref[i].Spec;
// load the exch info structure
pContext->pKeyExchInfo = GetKeyExchangeInfo(pZombie->SessExchSpec);
if(!IsExchAllowed(pContext, pContext->pKeyExchInfo, pZombie->fProtocol)) { pContext->pKeyExchInfo = NULL; continue; } break;
} }
if (pContext->pPendingCipherInfo == NULL) { fMismatch |= PCT_IMIS_CIPHER; pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; }
if (pContext->pPendingHashInfo == NULL) { fMismatch |= PCT_IMIS_HASH; pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; }
if (pContext->pKeyExchInfo == NULL) { fMismatch |= PCT_IMIS_EXCH; pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; }
// Determine the CSP to use, based on the key exchange algorithm.
if(pContext->pKeyExchInfo->Spec != SP_EXCH_RSA_PKCS1) { pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; } pContext->RipeZombie->hMasterProv = g_hRsaSchannel;
// Go ahead and move the pending ciphers to active, and init them.
pctRet = ContextInitCiphers(pContext, TRUE, TRUE);
if(PCT_ERR_OK != pctRet) { goto cleanup; }
/* we aren't restarting, so let's continue with the protocol. */
/* Crack the server certificate */ pctRet = SPLoadCertificate(pZombie->fProtocol, X509_ASN_ENCODING, pHello->pCertificate, pHello->CertificateLen, &pZombie->pRemoteCert); if(pctRet != PCT_ERR_OK) { goto cleanup; }
if(pContext->RipeZombie->pRemotePublic != NULL) { SPExternalFree(pContext->RipeZombie->pRemotePublic); pContext->RipeZombie->pRemotePublic = NULL; }
pctRet = SPPublicKeyFromCert(pZombie->pRemoteCert, &pZombie->pRemotePublic, NULL);
if(pctRet != PCT_ERR_OK) { goto cleanup; }
// Automatically validate server certificate if appropriate
// context flag is set.
pctRet = AutoVerifyServerCertificate(pContext); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; }
pZombie->pbServerCertificate = SPExternalAlloc(pHello->CertificateLen); pZombie->cbServerCertificate = pHello->CertificateLen; if(pZombie->pbServerCertificate == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } CopyMemory(pZombie->pbServerCertificate, pHello->pCertificate, pHello->CertificateLen);
/* Create the verify prelude hashes */ /* Which should look like */ /* Hash(CLIENT_MAC_KEY, Hash( "cvp", CLIENT_HELLO, SERVER_HELLO)) */ /* Here we just do the inner hash */
if(pContext->pClientHello == NULL) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); goto cleanup; }
pCMKey = (PPct1_Client_Master_Key)SPExternalAlloc(sizeof(Pct1_Client_Master_Key) + cbClientCert);
if (NULL == pCMKey) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; }
// Generate Key Args
if(pContext->pCipherInfo->dwBlockSize > 1) { pctRet = GenerateRandomBits(pZombie->pKeyArgs, pContext->pCipherInfo->dwBlockSize); if(!NT_SUCCESS(pctRet)) { goto cleanup; }
pZombie->cbKeyArgs = pCMKey->KeyArgLen = pContext->pCipherInfo->dwBlockSize;
/* Copy over the key args */ CopyMemory(pCMKey->KeyArg, pZombie->pKeyArgs, pZombie->cbKeyArgs ); } else { pCMKey->KeyArgLen = 0; }
pctRet = pContext->pKeyExchInfo->System->GenerateClientExchangeValue( pContext, pHello->Response, pHello->ResponseLen, pCMKey->ClearKey, &pCMKey->ClearKeyLen, NULL, &pCMKey->EncryptedKeyLen); if(PCT_ERR_OK != pctRet) { goto cleanup; }
pCMKey->pbEncryptedKey = SPExternalAlloc(pCMKey->EncryptedKeyLen); if(pCMKey->pbEncryptedKey == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; }
pctRet = pContext->pKeyExchInfo->System->GenerateClientExchangeValue( pContext, pHello->Response, pHello->ResponseLen, pCMKey->ClearKey, &pCMKey->ClearKeyLen, pCMKey->pbEncryptedKey, &pCMKey->EncryptedKeyLen); if(PCT_ERR_OK != pctRet) { goto cleanup; }
pctRet = Pct1BeginVerifyPrelude(pContext, pContext->pClientHello, pContext->cbClientHello, pCommInput->pvBuffer, pCommInput->cbData); if(PCT_ERR_OK != pctRet) { goto cleanup; }
// Activate session keys.
Pct1ActivateSessionKeys(pContext);
pCMKey->VerifyPreludeLen = sizeof(pCMKey->VerifyPrelude); pctRet = Pct1EndVerifyPrelude(pContext, pCMKey->VerifyPrelude, &pCMKey->VerifyPreludeLen);
if(PCT_ERR_OK != pctRet) { goto cleanup; }
/* Choose a client cert */ /* For each Cert the server understands, check to see if we */ /* have that type of cert */
pCMKey->ClientCertLen = 0; pCMKey->ClientCertSpec = 0; pCMKey->ClientSigSpec = 0; pCMKey->ResponseLen = 0;
if(fClientAuth && pSigInfo != NULL) {
// The client cert spec was already chosen
// Also, pContext->fCertChainsAllowed will be
// previously set if we're doing chains.
pCMKey->ClientCertSpec = ClientCertSpec; pCMKey->ClientSigSpec = pSigInfo->Spec;
pCMKey->pClientCert = (PUCHAR)(pCMKey+1); pCMKey->ClientCertLen = cbClientCert; memcpy(pCMKey->pClientCert, pbClientCert, cbClientCert);
// Allocate memory for signature.
pCMKey->ResponseLen = pContext->pActiveClientCred->pPublicKey->cbPublic; pCMKey->pbResponse = SPExternalAlloc(pCMKey->ResponseLen); if(pCMKey->pbResponse == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; }
DebugLog((DEB_TRACE, "Sign client response.\n"));
// Sign hash via a call to the application process.
pctRet = SignHashUsingCallback(pContext->pActiveClientCred->hRemoteProv, pContext->pActiveClientCred->dwKeySpec, pSigInfo->aiHash, pCMKey->VerifyPrelude, pCMKey->VerifyPreludeLen, pCMKey->pbResponse, &pCMKey->ResponseLen, TRUE); if(pctRet != PCT_ERR_OK) { goto cleanup; }
DebugLog((DEB_TRACE, "Client response signed successfully.\n"));
// Convert signature to big endian.
ReverseInPlace(pCMKey->pbResponse, pCMKey->ResponseLen); }
pctRet = PCT_ERR_ILLEGAL_MESSAGE; if(PCT_ERR_OK != (pctRet = Pct1PackClientMasterKey(pCMKey, pCommOutput))) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; }
pContext->WriteCounter++;
pctRet = PCT_ERR_OK;
cleanup:
if(pCMKey) { if(pCMKey->pbEncryptedKey) { SPExternalFree(pCMKey->pbEncryptedKey); } if(pCMKey->pbResponse) { SPExternalFree(pCMKey->pbResponse); } SPExternalFree(pCMKey); }
if(pbClientCert) { SPExternalFree(pbClientCert); }
if(pctRet != PCT_ERR_OK) { if(pctRet == PCT_ERR_SPECS_MISMATCH) { for(i=0;i<PCT_NUM_MISMATCHES;i++) { MisData[i] = (BYTE)(fMismatch & 1); fMismatch = fMismatch >> 1; }
ErrData.cbData = ErrData.cbBuffer = PCT_NUM_MISMATCHES; ErrData.pvBuffer = MisData; }
pctRet = Pct1GenerateError(pContext, pCommOutput, pctRet, &ErrData);
pctRet |= PCT_INT_DROP_CONNECTION; }
SP_RETURN(pctRet); }
SP_STATUS Pct1CliRestart(PSPContext pContext, PPct1_Server_Hello pHello, PSPBuffer pCommOutput) { SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE; UCHAR Response[RESPONSE_SIZE]; DWORD cbResponse; PPct1_Server_Hello pLocalHello = pHello; PSessCacheItem pZombie;
SP_BEGIN("Pct1CliRestart"); pZombie = pContext->RipeZombie;
/* if there's no zombie, the message is wrong. We can't restart. */ if(pZombie == NULL) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; }
if(!pZombie->hMasterKey) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; }
if(!pZombie->ZombieJuju) { DebugLog((DEB_WARN, "Session expired on client machine, but not on server.\n")); }
CopyMemory(pContext->pConnectionID, pHello->ConnectionID, pHello->cbConnectionID);
pContext->cbConnectionID = pHello->cbConnectionID;
//Init pending ciphers
pctRet = ContextInitCiphersFromCache(pContext);
if(PCT_ERR_OK != pctRet) { goto error; }
// We know what our ciphers are, so init the cipher system
pctRet = ContextInitCiphers(pContext, TRUE, TRUE);
if(PCT_ERR_OK != pctRet) { goto error; }
// Make a new set of session keys.
pctRet = MakeSessionKeys(pContext, pContext->RipeZombie->hMasterProv, pContext->RipeZombie->hMasterKey); if(PCT_ERR_OK != pctRet) { goto error; }
// Activate session keys.
Pct1ActivateSessionKeys(pContext);
pctRet = PCT_ERR_ILLEGAL_MESSAGE;
DebugLog((DEB_TRACE, "Session Keys Made\n")); /* let's check the response in the message */
/* check the length */ if (pLocalHello->ResponseLen != pContext->pHashInfo->cbCheckSum) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; }
/* calculate the correct response */ cbResponse = sizeof(Response); pctRet = Pct1ComputeResponse(pContext, pContext->pChallenge, pContext->cbChallenge, pContext->pConnectionID, pContext->cbConnectionID, pZombie->SessionID, pZombie->cbSessionID, Response, &cbResponse); if(pctRet != PCT_ERR_OK) { goto error; }
/* check it against the response in the message */ if (memcmp(Response, pLocalHello->Response, pLocalHello->ResponseLen)) { pctRet = SP_LOG_RESULT(PCT_ERR_SERVER_AUTH_FAILED); goto error; }
/* ok, we're done, so let's jettison the auth data */ pContext->ReadCounter = 1; pContext->WriteCounter = 1;
/* fini. */ SP_RETURN(PCT_ERR_OK);
error:
pctRet = Pct1GenerateError(pContext, pCommOutput, pctRet, NULL);
SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION); }
SP_STATUS Pct1CliHandleServerVerify( PSPContext pContext, PSPBuffer pCommInput, PSPBuffer pCommOutput) { SP_STATUS pctRet; PPct1_Server_Verify pVerify = NULL; SPBuffer ErrData; PSessCacheItem pZombie; UCHAR Response[RESPONSE_SIZE]; DWORD cbResponse;
SP_BEGIN("Pct1CliHandleServerVerify");
pZombie = pContext->RipeZombie; pContext->ReadCounter = 2; pContext->WriteCounter = 2;
pCommOutput->cbData = 0;
ErrData.cbData = 0; ErrData.pvBuffer = NULL; ErrData.cbBuffer = 0;
/* unpack the message */ pctRet = Pct1UnpackServerVerify(pCommInput, &pVerify); if (PCT_ERR_OK != pctRet) { // If it's an incomplete message or something, just return;
if(!SP_FATAL(pctRet)) { SP_RETURN(pctRet); } goto error; }
// compute the correct response
cbResponse = sizeof(Response); pctRet = Pct1ComputeResponse(pContext, pContext->pChallenge, pContext->cbChallenge, pContext->pConnectionID, pContext->cbConnectionID, pVerify->SessionIdData, PCT_SESSION_ID_SIZE, Response, &cbResponse); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto error; }
if(pVerify->ResponseLen != cbResponse || memcmp(pVerify->Response, Response, pVerify->ResponseLen)) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; }
CopyMemory(pZombie->SessionID, pVerify->SessionIdData, PCT_SESSION_ID_SIZE);
pZombie->cbSessionID = PCT_SESSION_ID_SIZE;
/* done with the verify data */ SPExternalFree(pVerify); pVerify = NULL;
/* set up the session in cache */ SPCacheAdd(pContext);
SP_RETURN( PCT_ERR_OK );
error:
if(pVerify) SPExternalFree(pVerify);
pctRet = Pct1GenerateError(pContext, pCommOutput, pctRet, NULL);
SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION); }
SP_STATUS WINAPI GeneratePct1StyleHello( PSPContext pContext, PSPBuffer pOutput) { Pct1_Client_Hello HelloMessage; PSessCacheItem pZombie; CipherSpec aCipherSpecs[10]; HashSpec aHashSpecs[10]; CertSpec aCertSpecs[10]; ExchSpec aExchSpecs[10]; DWORD i;
SP_STATUS pctRet = PCT_INT_INTERNAL_ERROR;
SP_BEGIN("Pct1CliInstigateHello");
HelloMessage.pCipherSpecs = aCipherSpecs; HelloMessage.pHashSpecs = aHashSpecs; HelloMessage.pCertSpecs = aCertSpecs; HelloMessage.pExchSpecs = aExchSpecs;
if(pContext == NULL) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
if (!pOutput) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
pZombie = pContext->RipeZombie;
pContext->Flags |= CONTEXT_FLAG_CLIENT;
pctRet = GenerateRandomBits(pContext->pChallenge, PCT1_CHALLENGE_SIZE); if(!NT_SUCCESS(pctRet)) { SP_RETURN(SP_LOG_RESULT(pctRet)); }
pContext->cbChallenge = PCT1_CHALLENGE_SIZE; /* Build the hello message. */
HelloMessage.cbChallenge = PCT1_CHALLENGE_SIZE; HelloMessage.pKeyArg = NULL; HelloMessage.cbKeyArgSize = 0;
HelloMessage.cCipherSpecs = 0; for(i=0; i < Pct1NumCipher; i++) { PCipherInfo pCipherInfo; pCipherInfo = GetCipherInfo(Pct1CipherRank[i].aiCipher, Pct1CipherRank[i].dwStrength); if(IsCipherAllowed(pContext, pCipherInfo, pContext->dwProtocol, pContext->dwRequestedCF)) { HelloMessage.pCipherSpecs[HelloMessage.cCipherSpecs++] = Pct1CipherRank[i].Spec; } }
HelloMessage.cHashSpecs = 0; for(i=0; i < Pct1NumHash; i++) { PHashInfo pHashInfo; pHashInfo = GetHashInfo(Pct1HashRank[i].aiHash); if(IsHashAllowed(pContext, pHashInfo, pContext->dwProtocol)) { HelloMessage.pHashSpecs[HelloMessage.cHashSpecs++] = Pct1HashRank[i].Spec; }
}
HelloMessage.cCertSpecs = 0; for(i=0; i < cPct1CertEncodingPref; i++) { PCertSysInfo pCertInfo = GetCertSysInfo(aPct1CertEncodingPref[i].dwCertEncodingType);
if(pCertInfo == NULL) { continue; } // Is this cert type enabled?
if(0 == (pCertInfo->fProtocol & SP_PROT_PCT1_CLIENT)) { continue; }
HelloMessage.pCertSpecs[HelloMessage.cCertSpecs++] = aPct1CertEncodingPref[i].Spec;
}
HelloMessage.cExchSpecs = 0; for(i=0; i < cPct1LocalExchKeyPref; i++) { PKeyExchangeInfo pExchInfo; pExchInfo = GetKeyExchangeInfo(aPct1LocalExchKeyPref[i].Spec); if(IsExchAllowed(pContext, pExchInfo, pContext->dwProtocol)) { HelloMessage.pExchSpecs[HelloMessage.cExchSpecs++] = aPct1LocalExchKeyPref[i].Spec; } }
if (pZombie->cbSessionID) { CopyMemory(HelloMessage.SessionID, pZombie->SessionID, pZombie->cbSessionID); HelloMessage.cbSessionID = pZombie->cbSessionID; } else { FillMemory(HelloMessage.SessionID, PCT_SESSION_ID_SIZE, 0); HelloMessage.cbSessionID = PCT_SESSION_ID_SIZE; }
CopyMemory( HelloMessage.Challenge, pContext->pChallenge, HelloMessage.cbChallenge ); HelloMessage.cbChallenge = pContext->cbChallenge;
pctRet = Pct1PackClientHello(&HelloMessage, pOutput);
if(PCT_ERR_OK != pctRet) { SP_RETURN(pctRet); }
// 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->pClientHello = SPExternalAlloc(pOutput->cbData); if(pContext->pClientHello == NULL) { SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } CopyMemory(pContext->pClientHello, pOutput->pvBuffer, pOutput->cbData); pContext->cbClientHello = pOutput->cbData; pContext->dwClientHelloProtocol = SP_PROT_PCT1_CLIENT;
/* We set this here to tell the protocol engine that we just send a client
* hello, and we're expecting a pct server hello */ pContext->State = PCT1_STATE_CLIENT_HELLO; SP_RETURN(PCT_ERR_OK); }
|