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

992 lines
26 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: cliprot.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 09-23-97 jbanes LSA integration stuff.
//
//----------------------------------------------------------------------------
#include <spbase.h>
#include <pct1msg.h>
#include <pct1prot.h>
#include <ssl2msg.h>
#include <ssl3msg.h>
#include <ssl2prot.h>
UNICipherMap UniAvailableCiphers[] = {
// NULL cipher suite
{
// 0
SSL3_NULL_WITH_NULL_NULL
},
// PCT ciphers
{
// 1
UNI_CK_PCT,
SP_PROT_PCT1,
0,
0, 0,
SP_EXCH_UNKNOWN
},
{
// 2
SSL_MKFAST(PCT_SSL_CERT_TYPE, MSBOF(PCT1_CERT_X509_CHAIN), LSBOF(PCT1_CERT_X509_CHAIN)),
SP_PROT_PCT1,
0,
0, 0,
SP_EXCH_UNKNOWN
},
{
// 3
SSL_MKFAST(PCT_SSL_CERT_TYPE, MSBOF(PCT1_CERT_X509), LSBOF(PCT1_CERT_X509)),
SP_PROT_PCT1,
0,
0, 0,
SP_EXCH_UNKNOWN
},
{
// 4
SSL_MKFAST(PCT_SSL_HASH_TYPE, MSBOF(PCT1_HASH_MD5), LSBOF(PCT1_HASH_MD5)),
SP_PROT_PCT1,
CALG_MD5,
0, 0,
SP_EXCH_UNKNOWN
},
{
// 5
SSL_MKFAST(PCT_SSL_HASH_TYPE, MSBOF(PCT1_HASH_SHA), LSBOF(PCT1_HASH_SHA)),
SP_PROT_PCT1,
CALG_SHA,
0, 0,
SP_EXCH_UNKNOWN
},
{
// 6
SSL_MKFAST(PCT_SSL_EXCH_TYPE, MSBOF(SP_EXCH_RSA_PKCS1), LSBOF(SP_EXCH_RSA_PKCS1)),
SP_PROT_PCT1,
0,
0, 0,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX
},
// SSL3 Domestic ciphers
{
// 7
SSL3_RSA_WITH_RC4_128_MD5,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_MD5 ,
CALG_RC4 ,128 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 8
SSL3_RSA_WITH_RC4_128_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA ,
CALG_RC4 ,128 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 9
SSL3_RSA_WITH_3DES_EDE_CBC_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA,
CALG_3DES ,168 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 10
SSL3_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA,
CALG_3DES ,168 ,
SP_EXCH_DH_PKCS3, CALG_DH_EPHEM,
DOMESTIC_CIPHER_SUITE
},
// PCT Domestic ciphers
{
// 12
SSL_MKFAST(PCT_SSL_CIPHER_TYPE_1ST_HALF, MSBOF(PCT1_CIPHER_RC4>>16), LSBOF(PCT1_CIPHER_RC4>>16)),
SP_PROT_PCT1,
0,
CALG_RC4 ,128 ,
SP_EXCH_UNKNOWN, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 13
SSL_MKFAST(PCT_SSL_CIPHER_TYPE_2ND_HALF, MSBOF(PCT1_ENC_BITS_128), LSBOF(PCT1_MAC_BITS_128)),
SP_PROT_PCT1,
0,
CALG_RC4 ,128 ,
SP_EXCH_UNKNOWN, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
// SSL2 Domestic ciphers
{
// 14
SSL_CK_RC4_128_WITH_MD5,
SP_PROT_SSL2 ,
CALG_MD5 ,
CALG_RC4 ,128 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 15
SSL_CK_DES_192_EDE3_CBC_WITH_MD5,
SP_PROT_SSL2 ,
CALG_MD5 ,
CALG_3DES ,168 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 16
SSL_CK_RC2_128_CBC_WITH_MD5,
SP_PROT_SSL2 ,
CALG_MD5 ,
CALG_RC2 ,128 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
// SSL3 domestic DES ciphers
{
// 22
SSL3_RSA_WITH_DES_CBC_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA ,
CALG_DES , 56,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 23
SSL3_DHE_DSS_WITH_DES_CBC_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA ,
CALG_DES , 56 ,
SP_EXCH_DH_PKCS3, CALG_DH_EPHEM,
DOMESTIC_CIPHER_SUITE
},
// SSL2 domestic DES ciphers
{
// 24
SSL_CK_DES_64_CBC_WITH_MD5,
SP_PROT_SSL2,
CALG_MD5 ,
CALG_DES , 56 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
// SSL3 56-bit export ciphers
{
// 25
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA ,
CALG_RC4 ,56 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
EXPORT56_CIPHER_SUITE
},
{
// 26
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA ,
CALG_DES , 56,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
EXPORT56_CIPHER_SUITE
},
{
// 27
TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA ,
CALG_DES , 56 ,
SP_EXCH_DH_PKCS3, CALG_DH_EPHEM,
EXPORT56_CIPHER_SUITE
},
// SSL3 Export ciphers
{
// 28
SSL3_RSA_EXPORT_WITH_RC4_40_MD5,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_MD5 ,
CALG_RC4 ,40 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
EXPORT40_CIPHER_SUITE
},
{
// 29
SSL3_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_MD5 ,
CALG_RC2 ,40 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
EXPORT40_CIPHER_SUITE
},
// PCT Export ciphers
{
// 30
SSL_MKFAST(PCT_SSL_CIPHER_TYPE_1ST_HALF, MSBOF(PCT1_CIPHER_RC4>>16), LSBOF(PCT1_CIPHER_RC4>>16)),
SP_PROT_PCT1,
0,
CALG_RC4 ,40 ,
SP_EXCH_UNKNOWN, CALG_RSA_KEYX,
EXPORT40_CIPHER_SUITE
},
{
// 31
SSL_MKFAST(PCT_SSL_CIPHER_TYPE_2ND_HALF, MSBOF(PCT1_ENC_BITS_40), LSBOF(PCT1_MAC_BITS_128)),
SP_PROT_PCT1,
0,
CALG_RC4 ,40 ,
SP_EXCH_UNKNOWN, CALG_RSA_KEYX,
EXPORT40_CIPHER_SUITE
},
// SSL2 Export ciphers
{
// 32
SSL_CK_RC4_128_EXPORT40_WITH_MD5,
SP_PROT_SSL2 ,
CALG_MD5 ,
CALG_RC4 ,40 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
EXPORT40_CIPHER_SUITE
},
{
// 33
SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5,
SP_PROT_SSL2 ,
CALG_MD5 ,
CALG_RC2 ,40 ,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
EXPORT40_CIPHER_SUITE
},
// SSL3 Zero privacy ciphers
{
// 34
SSL3_RSA_WITH_NULL_MD5,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_MD5 ,
CALG_NULLCIPHER, 0,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
},
{
// 35
SSL3_RSA_WITH_NULL_SHA,
SP_PROT_SSL3 | SP_PROT_TLS1,
CALG_SHA ,
CALG_NULLCIPHER, 0,
SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX,
DOMESTIC_CIPHER_SUITE
}
};
DWORD UniNumCiphers = sizeof(UniAvailableCiphers)/sizeof(UNICipherMap);
SP_STATUS WINAPI
GenerateSsl2StyleHello(
PSPContext pContext,
PSPBuffer pOutput,
WORD fProtocol);
SP_STATUS
GetSupportedCapiAlgs(
HCRYPTPROV hProv,
DWORD dwCapiFlags,
PROV_ENUMALGS_EX ** ppAlgInfo,
DWORD * pcAlgInfo)
{
PROV_ENUMALGS_EX AlgInfo;
DWORD dwFlags;
DWORD cbData;
DWORD cAlgs;
DWORD i;
*ppAlgInfo = NULL;
*pcAlgInfo = 0;
// Count the algorithms.
dwFlags = CRYPT_FIRST;
for(cAlgs = 0; ; cAlgs++)
{
cbData = sizeof(PROV_ENUMALGS_EX);
if(!SchCryptGetProvParam(hProv,
PP_ENUMALGS_EX,
(PBYTE)&AlgInfo,
&cbData,
dwFlags,
dwCapiFlags))
{
if(GetLastError() != ERROR_NO_MORE_ITEMS)
{
SP_LOG_RESULT(GetLastError());
}
break;
}
dwFlags = 0;
}
if(cAlgs == 0)
{
return SP_LOG_RESULT(SEC_E_ALGORITHM_MISMATCH);
}
// Allocate memory.
*ppAlgInfo = SPExternalAlloc(sizeof(PROV_ENUMALGS_EX) * cAlgs);
if(*ppAlgInfo == NULL)
{
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
// Read the algorithms.
dwFlags = CRYPT_FIRST;
for(i = 0; i < cAlgs; i++)
{
cbData = sizeof(PROV_ENUMALGS_EX);
if(!SchCryptGetProvParam(hProv,
PP_ENUMALGS_EX,
(PBYTE)(*ppAlgInfo + i),
&cbData,
dwFlags,
dwCapiFlags))
{
if(GetLastError() != ERROR_NO_MORE_ITEMS)
{
SP_LOG_RESULT(GetLastError());
}
break;
}
dwFlags = 0;
}
if(i == 0)
{
SPExternalFree(*ppAlgInfo);
*ppAlgInfo = NULL;
LogNoCiphersSupportedEvent();
return SP_LOG_RESULT(SEC_E_ALGORITHM_MISMATCH);
}
*pcAlgInfo = i;
return PCT_ERR_OK;
}
SP_STATUS WINAPI
GenerateHello(
PSPContext pContext,
PSPBuffer pOutput,
BOOL fCache)
{
PSessCacheItem pZombie;
PSPCredentialGroup pCred;
BOOL fFound;
DWORD fProt;
if (!pOutput)
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
}
if(fCache)
{
// Look this id up in the cache
fFound = SPCacheRetrieveByName(pContext->pszTarget,
pContext->pCredGroup,
&pContext->RipeZombie);
}
else
{
fFound = FALSE;
}
if(!fFound)
{
// We're doing a full handshake, so allocate a cache entry.
if(!SPCacheRetrieveNew(FALSE,
pContext->pszTarget,
&pContext->RipeZombie))
{
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
}
pContext->RipeZombie->dwCF = pContext->dwRequestedCF;
}
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);
}
// Use protocol from cache unless it's a new cache element,
// in which case use the protocol from credential.
if(fFound)
{
fProt = pZombie->fProtocol;
}
else
{
fProt = pCred->grbitEnabledProtocols;
}
pContext->dwProtocol = fProt;
pContext->dwClientEnabledProtocols = fProt;
if(SP_PROT_UNI_CLIENT & fProt)
{
pContext->State = UNI_STATE_CLIENT_HELLO;
pContext->ProtocolHandler = ClientProtocolHandler;
return GenerateUniHello(pContext, pOutput, pCred->grbitEnabledProtocols);
}
else
if(SP_PROT_TLS1_CLIENT & fProt)
{
DWORD dwProtocol = SP_PROT_TLS1_CLIENT;
pContext->State = SSL3_STATE_CLIENT_HELLO;
pContext->ProtocolHandler = Ssl3ProtocolHandler;
if(!fFound)
{
pZombie->fProtocol = SP_PROT_TLS1_CLIENT;
}
if(SP_PROT_SSL3_CLIENT & fProt)
{
// Both TLS and SSL3 are enabled.
dwProtocol |= SP_PROT_SSL3_CLIENT;
}
return GenerateTls1ClientHello(pContext, pOutput, dwProtocol);
}
else
if(SP_PROT_SSL3_CLIENT & fProt)
{
pContext->State = SSL3_STATE_CLIENT_HELLO;
pContext->ProtocolHandler = Ssl3ProtocolHandler;
if(!fFound)
{
pZombie->fProtocol = SP_PROT_SSL3_CLIENT;
}
return GenerateSsl3ClientHello(pContext, pOutput);
}
else
if(SP_PROT_PCT1_CLIENT & fProt)
{
pContext->State = PCT1_STATE_CLIENT_HELLO;
pContext->ProtocolHandler = Pct1ClientProtocolHandler;
return GeneratePct1StyleHello(pContext, pOutput);
}
else
if(SP_PROT_SSL2_CLIENT & fProt)
{
pContext->State = SSL2_STATE_CLIENT_HELLO;
pContext->ProtocolHandler = Ssl2ClientProtocolHandler;
return GenerateUniHello(pContext, pOutput, SP_PROT_SSL2_CLIENT);
}
else
{
return SP_LOG_RESULT(SEC_E_ALGORITHM_MISMATCH);
}
}
//+---------------------------------------------------------------------------
//
// Function: ClientVetAlg
//
// Synopsis: Examine the cipher suite input, and decide if it is currently
// enabled. Take into account the enabled protocols and ciphers
// enabled in the schannel registry as well as the protocols and
// ciphers enabled by the application in the V3 credential.
// Return TRUE if the cipher suite is enabled.
//
// Arguments: [pContext] -- Schannel context.
//
// [dwProtocol] -- Client protocols to be included in the
// ClientHello message.
//
// [pCipherMap] -- Cipher suite to be examined.
//
// History: 10-29-97 jbanes Created
//
// Notes: This routine is called by the client-side only.
//
//----------------------------------------------------------------------------
BOOL
ClientVetAlg(
PSPContext pContext,
DWORD dwProtocol,
UNICipherMap * pCipherMap)
{
PCipherInfo pCipherInfo = NULL;
PHashInfo pHashInfo = NULL;
PKeyExchangeInfo pExchInfo = NULL;
if((pCipherMap->fProt & dwProtocol) == 0)
{
return FALSE;
}
// Is cipher supported?
if(pCipherMap->aiCipher != 0)
{
pCipherInfo = GetCipherInfo(pCipherMap->aiCipher,
pCipherMap->dwStrength);
if(!IsCipherSuiteAllowed(pContext,
pCipherInfo,
dwProtocol,
pContext->RipeZombie->dwCF,
pCipherMap->dwFlags))
{
return FALSE;
}
}
// Is hash supported?
if(pCipherMap->aiHash != 0)
{
pHashInfo = GetHashInfo(pCipherMap->aiHash);
if(!IsHashAllowed(pContext, pHashInfo, dwProtocol))
{
return FALSE;
}
}
// Is exchange alg supported?
if(pCipherMap->KeyExch != SP_EXCH_UNKNOWN)
{
pExchInfo = GetKeyExchangeInfo(pCipherMap->KeyExch);
if(!IsExchAllowed(pContext, pExchInfo, dwProtocol))
{
return FALSE;
}
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: ClientBuildAlgList
//
// Synopsis: Build a list of ciphers to be included in the ClientHello
// message. This routine is used by all protocols.
//
// Arguments: [pContext] -- Schannel context.
//
// [fProtocol] -- Protocol(s) to be included in the
// ClientHello message.
//
// [pCipherSpecs] -- (out) Array where cipher specs are
// placed.
//
// [pcCipherSpecs] -- (out) Size of cipher specs array.
//
// History: 10-29-97 jbanes Created
//
// Notes: This routine is called by the client-side only.
//
//----------------------------------------------------------------------------
SP_STATUS
ClientBuildAlgList(
PSPContext pContext,
DWORD dwProtocol,
Ssl2_Cipher_Kind * pCipherSpecs,
PDWORD pcCipherSpecs)
{
DWORD i;
DWORD cCipherSpecs = 0;
// Consider only the client protocols.
dwProtocol &= SP_PROT_CLIENTS;
//
// Handle the RSA case.
//
if(g_hRsaSchannel && g_pRsaSchannelAlgs)
{
for(i = 0; i < UniNumCiphers; i++)
{
if(UniAvailableCiphers[i].KeyExch != SP_EXCH_RSA_PKCS1 &&
UniAvailableCiphers[i].KeyExch != SP_EXCH_UNKNOWN)
{
continue;
}
if(!ClientVetAlg(pContext, dwProtocol, UniAvailableCiphers + i))
{
continue;
}
if(!IsAlgSupportedCapi(dwProtocol,
UniAvailableCiphers + i,
g_pRsaSchannelAlgs,
g_cRsaSchannelAlgs))
{
continue;
}
// this cipher is good to request
pCipherSpecs[cCipherSpecs++] = UniAvailableCiphers[i].CipherKind;
}
}
//
// Handle the DH case.
//
if(g_hDhSchannelProv)
{
for(i = 0; i < UniNumCiphers; i++)
{
if(UniAvailableCiphers[i].KeyExch != SP_EXCH_DH_PKCS3)
{
continue;
}
if(!ClientVetAlg(pContext, dwProtocol, UniAvailableCiphers + i))
{
continue;
}
if(!IsAlgSupportedCapi(dwProtocol,
UniAvailableCiphers + i,
g_pDhSchannelAlgs,
g_cDhSchannelAlgs))
{
continue;
}
// this cipher is good to request
pCipherSpecs[cCipherSpecs++] = UniAvailableCiphers[i].CipherKind;
}
}
if(cCipherSpecs == 0)
{
return SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH);
}
*pcCipherSpecs = cCipherSpecs;
return PCT_ERR_OK;
}
SP_STATUS WINAPI
GenerateUniHelloMessage(
PSPContext pContext,
Ssl2_Client_Hello * pHelloMessage,
DWORD fProtocol
)
{
SP_STATUS pctRet;
UCHAR bOffset = 2;
SP_BEGIN("GenerateUniHelloMessage");
if(!pHelloMessage)
{
SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
}
pContext->Flags |= CONTEXT_FLAG_CLIENT;
// Generate the cipher list
pHelloMessage->cCipherSpecs = MAX_UNI_CIPHERS;
pctRet = ClientBuildAlgList(pContext,
fProtocol,
pHelloMessage->CipherSpecs,
&pHelloMessage->cCipherSpecs);
if(pctRet != PCT_ERR_OK)
{
SP_RETURN(SP_LOG_RESULT(pctRet));
}
// We're minimally version 2
pHelloMessage->dwVer = SSL2_CLIENT_VERSION;
if(fProtocol & SP_PROT_TLS1_CLIENT)
{
pHelloMessage->dwVer = TLS1_CLIENT_VERSION;
}
else if(fProtocol & SP_PROT_SSL3_CLIENT)
{
pHelloMessage->dwVer = SSL3_CLIENT_VERSION;
}
/* Build the hello message. */
pHelloMessage->cbSessionID = 0;
if (pContext->RipeZombie && pContext->RipeZombie->cbSessionID)
{
KeyExchangeSystem *pKeyExchSys = NULL;
// Get pointer to key exchange system.
pKeyExchSys = KeyExchangeFromSpec(pContext->RipeZombie->SessExchSpec,
pContext->RipeZombie->fProtocol);
if(pKeyExchSys)
{
// Request a reconnect.
CopyMemory(pHelloMessage->SessionID,
pContext->RipeZombie->SessionID,
pContext->RipeZombie->cbSessionID);
pHelloMessage->cbSessionID = pContext->RipeZombie->cbSessionID;
}
else
{
DebugLog((DEB_WARN, "Abstaining from requesting reconnect\n"));
}
}
CopyMemory( pHelloMessage->Challenge,
pContext->pChallenge,
pContext->cbChallenge);
pHelloMessage->cbChallenge = pContext->cbChallenge;
SP_RETURN(PCT_ERR_OK);
}
SP_STATUS WINAPI
GenerateUniHello(
PSPContext pContext,
PSPBuffer pOutput,
DWORD fProtocol
)
{
SP_STATUS pctRet;
Ssl2_Client_Hello HelloMessage;
SP_BEGIN("GenerateUniHello");
GenerateRandomBits( pContext->pChallenge, SSL2_CHALLENGE_SIZE );
pContext->cbChallenge = SSL2_CHALLENGE_SIZE;
pctRet = GenerateUniHelloMessage(pContext, &HelloMessage, fProtocol);
pContext->ReadCounter = 0;
if(PCT_ERR_OK != pctRet)
{
SP_RETURN(pctRet);
}
if(PCT_ERR_OK != (pctRet = Ssl2PackClientHello(&HelloMessage, pOutput)))
{
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_SSL2_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->WriteCounter = 1;
pContext->ReadCounter = 0;
SP_RETURN(PCT_ERR_OK);
}
SP_STATUS WINAPI
ClientProtocolHandler(
PSPContext pContext,
PSPBuffer pCommInput,
PSPBuffer pCommOutput)
{
SP_STATUS pctRet = 0;
PUCHAR pb;
DWORD dwVersion;
PSPCredentialGroup pCred;
pCred = pContext->pCredGroup;
if(!pCred)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
/* PCTv1.0 Server Hello starts with
* RECORD_LENGTH_MSB (ignore)
* RECORD_LENGTH_LSB (ignore)
* PCT1_SERVER_HELLO (must be equal)
* SH_PAD
* PCT1_CLIENT_VERSION_MSB (must be pct1)
* PCT1_CLIENT_VERSION_LSB (must be pct1)
*
* ... PCT hello ...
*/
/* SSLv2 Hello starts with
* RECORD_LENGTH_MSB (ignore)
* RECORD_LENGTH_LSB (ignore)
* SSL2_SERVER_HELLO (must be equal)
* SESSION_ID_HIT
* CERTIFICATE_TYPE
* SSL2_CLIENT_VERSION_MSB (Must be ssl2)
* SSL2_CLIENT_VERSION_LSB (Must be ssl2)
*
* ... SSLv2 Hello ...
*/
/* SSLv3 Type 3 Server Hello starts with
* 0x15 Hex (HANDSHAKE MESSAGE)
* VERSION MSB
* VERSION LSB
* RECORD_LENGTH_MSB (ignore)
* RECORD_LENGTH_LSB (ignore)
* HS TYPE (SERVER_HELLO)
* 3 bytes HS record length
* HS Version
* HS Version
*/
// We need at least 12 bytes to determine what we have.
if (pCommInput->cbData < 12)
{
return(PCT_INT_INCOMPLETE_MSG);
}
pb = pCommInput->pvBuffer;
if(pb[0] == SSL3_CT_HANDSHAKE && pb[5] == SSL3_HS_SERVER_HELLO)
{
dwVersion = COMBINEBYTES(pb[9], pb[10]);
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_ILLEGAL_MSG);
}
pContext->ProtocolHandler = Ssl3ProtocolHandler;
pContext->DecryptHandler = Ssl3DecryptHandler;
return(Ssl3ProtocolHandler(pContext, pCommInput, pCommOutput));
}
if(pb[2] == SSL2_MT_SERVER_HELLO)
{
dwVersion = COMBINEBYTES(pb[5], pb[6]);
if(dwVersion == SSL2_CLIENT_VERSION)
{
if(!(SP_PROT_SSL2_CLIENT & pCred->grbitEnabledProtocols))
{
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}
// This appears to be an SSL2 server_hello.
pContext->dwProtocol = SP_PROT_SSL2_CLIENT;
pContext->ProtocolHandler = Ssl2ClientProtocolHandler;
pContext->DecryptHandler = Ssl2DecryptHandler;
return(Ssl2ClientProtocolHandler(pContext, pCommInput, pCommOutput));
}
}
if(pb[2] == PCT1_MSG_SERVER_HELLO)
{
DWORD i;
dwVersion = COMBINEBYTES(pb[4], pb[5]);
if(dwVersion ==PCT_VERSION_1)
{
if(!(SP_PROT_PCT1_CLIENT & pCred->grbitEnabledProtocols))
{
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}
// Convert challenge from 16 byte to 32 byte
for(i=0; i < pContext->cbChallenge; i++)
{
pContext->pChallenge[i + pContext->cbChallenge] = ~pContext->pChallenge[i];
}
pContext->cbChallenge = 2*pContext->cbChallenge;
// This appears to be a PCT server_hello.
pContext->dwProtocol = SP_PROT_PCT1_CLIENT;
pContext->ProtocolHandler = Pct1ClientProtocolHandler;
pContext->DecryptHandler = Pct1DecryptHandler;
return(Pct1ClientProtocolHandler(pContext, pCommInput, pCommOutput));
}
}
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}