mirror of https://github.com/lianthony/NT4.0
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.
1376 lines
45 KiB
1376 lines
45 KiB
/*Included Files------------------------------------------------------------*/
|
|
#include "ssldbg.h" //#include <assert.h>
|
|
#include <malloc.h>
|
|
#include "ssl.h"
|
|
#include "guts.h"
|
|
#include "table.h"
|
|
#include "hash.h"
|
|
#include "crypt.h"
|
|
#include "pkcs.h"
|
|
|
|
#ifdef BETTER_RANDOM
|
|
|
|
#include "..\..\crypto\crypto.h"
|
|
|
|
extern void BlockDaSkt(SECURE_SOCKET skt);
|
|
extern void UnblockDaSkt(SECURE_SOCKET skt);
|
|
|
|
/************************************************************************/
|
|
/* GenRandom generates a specified number of random bytes and places */
|
|
/* them into the specified buffer. */
|
|
/************************************************************************/
|
|
|
|
const char szBrowserIEMainKeyRoot[] = "Software\\Microsoft\\Internet Explorer\\Main";
|
|
const char szSSLInfoKey[] = "SSLInfo";
|
|
|
|
static BOOL FillRandStateFromRegistry( BYTE *buffer, int cbSize )
|
|
{
|
|
HKEY hkey;
|
|
DWORD dwType, dwSize = cbSize;
|
|
BOOL retval = FALSE;
|
|
|
|
if (RegOpenKey( HKEY_LOCAL_MACHINE, szBrowserIEMainKeyRoot, &hkey ) == ERROR_SUCCESS)
|
|
{
|
|
if (RegQueryValueEx( hkey, szSSLInfoKey, NULL, &dwType, buffer, &dwSize ) == ERROR_SUCCESS
|
|
&& dwType == REG_BINARY )
|
|
{
|
|
retval = TRUE;
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static BOOL SetRegistryFromRandState( BYTE *buffer, int cbSize )
|
|
{
|
|
HKEY hkey;
|
|
BOOL retval = FALSE;
|
|
|
|
if (RegOpenKey( HKEY_LOCAL_MACHINE, szBrowserIEMainKeyRoot, &hkey ) == ERROR_SUCCESS)
|
|
{
|
|
if (RegSetValueEx( hkey, szSSLInfoKey, (DWORD) NULL, REG_BINARY, buffer, cbSize ) == ERROR_SUCCESS)
|
|
{
|
|
retval = TRUE;
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
#define NUM_RANDSTATE_BYTES 60
|
|
|
|
void PASCAL FAR GenRandom (BYTE *pbBuffer, size_t dwLength)
|
|
{
|
|
static RC4_KEYSTRUCT RC4Struct;
|
|
static BOOL needSeeding = TRUE;
|
|
static BYTE RandState[NUM_RANDSTATE_BYTES];
|
|
|
|
if ( needSeeding )
|
|
{
|
|
BYTE Randoms[NUM_RANDSTATE_BYTES]; // Crypto note: uninitialized won't hurt, but doesn't help much either
|
|
int index=0;
|
|
SYSTEMTIME lpSysTime;
|
|
LARGE_INTEGER liPerfCount;
|
|
MEMORYSTATUS lpmstMemStat;
|
|
POINT Point;
|
|
DWORD ticksSinceBoot;
|
|
MD5_CTX md5_context;
|
|
|
|
needSeeding = FALSE;
|
|
|
|
//mouse
|
|
if (GetCursorPos(&Point))
|
|
{
|
|
Randoms[index++] = LOBYTE(Point.x) ^ HIBYTE(Point.x);
|
|
Randoms[index++] = LOBYTE(Point.y) ^ HIBYTE(Point.y);
|
|
}
|
|
|
|
//local time: seconds and milliseconds
|
|
GetLocalTime(&lpSysTime);
|
|
Randoms[index++] = LOBYTE(lpSysTime.wMilliseconds);
|
|
Randoms[index++] = HIBYTE(lpSysTime.wMilliseconds);
|
|
Randoms[index++] = LOBYTE(lpSysTime.wSecond) ^ LOBYTE(lpSysTime.wMinute);
|
|
|
|
//performance counter (hi res perf counter of machine time)
|
|
if (QueryPerformanceCounter(&liPerfCount))
|
|
{
|
|
Randoms[index++] = (LOBYTE(LOWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart)));
|
|
Randoms[index++] = (HIBYTE(LOWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart)));
|
|
Randoms[index++] = (LOBYTE(HIWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart)));
|
|
Randoms[index++] = (HIBYTE(HIWORD(liPerfCount.LowPart)) ^ LOBYTE(LOWORD(liPerfCount.HighPart)));
|
|
}
|
|
|
|
//memory status report: available resources
|
|
GlobalMemoryStatus(&lpmstMemStat);
|
|
//only use hiwords, since lowwords always zero
|
|
Randoms[index++] = LOBYTE(HIWORD(lpmstMemStat.dwAvailPhys));
|
|
Randoms[index++] = HIBYTE(HIWORD(lpmstMemStat.dwAvailPhys));
|
|
|
|
Randoms[index++] = LOBYTE(HIWORD(lpmstMemStat.dwAvailPageFile));
|
|
Randoms[index++] = HIBYTE(HIWORD(lpmstMemStat.dwAvailPageFile));
|
|
|
|
Randoms[index++] = LOBYTE(HIWORD(lpmstMemStat.dwAvailVirtual));
|
|
//high byte doesn't change much
|
|
|
|
//get ticks since boot
|
|
ticksSinceBoot = GetCurrentTime();
|
|
Randoms[index++] = LOBYTE(LOWORD(ticksSinceBoot));
|
|
Randoms[index++] = HIBYTE(LOWORD(ticksSinceBoot));
|
|
Randoms[index++] = LOBYTE(HIWORD(ticksSinceBoot));
|
|
Randoms[index++] = HIBYTE(HIWORD(ticksSinceBoot));
|
|
|
|
// Fill RandState with value from registry
|
|
FillRandStateFromRegistry( RandState, sizeof(RandState) );
|
|
|
|
// Hash noise bits
|
|
MD5Init( &md5_context );
|
|
MD5Update( &md5_context, RandState, sizeof(RandState) );
|
|
MD5Update( &md5_context, Randoms, sizeof(Randoms) );
|
|
MD5Final( &md5_context );
|
|
|
|
//now call RC4
|
|
rc4_key (&RC4Struct, sizeof(md5_context.digest), md5_context.digest);
|
|
|
|
// Stream out some bits for future sessions
|
|
rc4 (&RC4Struct, sizeof(Randoms), Randoms);
|
|
|
|
// Save those bits in the registry
|
|
SetRegistryFromRandState( Randoms, sizeof(Randoms) );
|
|
|
|
// scrub random work area
|
|
memset (Randoms, 0, sizeof(Randoms));
|
|
memset (Randoms, 0xFF, sizeof(Randoms));
|
|
memset (Randoms, 0, sizeof(Randoms));
|
|
}
|
|
rc4 (&RC4Struct, dwLength, pbBuffer);
|
|
}
|
|
|
|
#endif // BETTER_RANDOM
|
|
|
|
/*
|
|
* RANTING
|
|
* Use Assert()s to at least partially validate for important input parameters.
|
|
*/
|
|
|
|
/*
|
|
* RANTING
|
|
* Consider making producer functions validate their output before returning to
|
|
* a consumer. Turn assumptions into debug code where reasonable.
|
|
*/
|
|
|
|
/*More Hardcoded info-------------------------------------------------------*/
|
|
/*
|
|
Each element has same format as will be used in CLIENT-MASTER-KEY Message
|
|
*/
|
|
|
|
/* Use #defines a little more often to make magic numbers more readable. */
|
|
|
|
static CipherInfo rgCipherInfo[]={
|
|
#ifndef CRYPTO_EXPORTABLE
|
|
{{SSL_CK_RC4_128_WITH_MD5}, 0,16,0,WSSA_CRYPT_RC4},
|
|
#endif
|
|
{{SSL_CK_RC4_128_EXPORT40_WITH_MD5}, 11,16,0,WSSA_CRYPT_RC4},
|
|
#ifdef WSSA_BLOCK_CIPHERS
|
|
#ifndef CRYPTO_EXPORTABLE
|
|
{{SSL_CK_RC2_128_CBC_WITH_MD5}, 0,16,8,WSSA_CRYPT_RC2},
|
|
#endif
|
|
{{SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5},11,16,8,WSSA_CRYPT_RC2},
|
|
#ifndef CRYPTO_EXPORTABLE
|
|
{{SSL_CK_IDEA_128_CBC_WITH_MD5}, 0,16,8,WSSA_CRYPT_IDEA},
|
|
{{SSL_CK_DES_64_CBC_WITH_MD5}, 0,16,8,WSSA_CRYPT_DES},
|
|
{{SSL_CK_DES_192_EDE3_CBC_WITH_MD5}, 0,24,8,WSSA_CRYPT_3DES}
|
|
#endif
|
|
#endif
|
|
};
|
|
#define NUM_CIPHERS (sizeof(rgCipherInfo)/sizeof(CipherInfo))
|
|
|
|
/*Utils---------------------------------------------------------------------*/
|
|
SSI WSSAFNCT ConstructSSI(SECURE_SOCKET ss){
|
|
SSI ssi;
|
|
int z;
|
|
|
|
/* Don't tell readers what the code can tell them better. */
|
|
|
|
/*Allocate memory for structure*/
|
|
ssi = malloc(sizeof(SECURE_SOCKET_STRUCTURE_I));
|
|
ASSERT(ssi);
|
|
|
|
/*Did alloc work?*/
|
|
if (NULL!=ssi) {
|
|
/*Clear data structure*/
|
|
memset(ssi,0,sizeof(SECURE_SOCKET_STRUCTURE_I));
|
|
/*Set original cypher codes*/
|
|
ssi->ss = ss;
|
|
ssi->nCipherSpecSize = NUM_CIPHERS * 3;
|
|
ssi->pCipherSpecData = malloc(ssi->nCipherSpecSize);
|
|
ASSERT(ssi->pCipherSpecData);
|
|
if (NULL!=ssi->pCipherSpecData){
|
|
for (z=0;z<NUM_CIPHERS;++z){
|
|
memcpy(ssi->pCipherSpecData + 3*z, (const void *) &rgCipherInfo[z], 3);
|
|
}
|
|
WssaTablePutSSI(ss,ssi);
|
|
return ssi;
|
|
}
|
|
else Free(ssi);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void WSSAFNCT DestructSSI(SSI ssi){
|
|
ASSERT(ssi);
|
|
|
|
if (NULL != ssi){
|
|
if ( ssi->pCipherInfo != NULL )
|
|
Free(ssi->pCipherInfo);
|
|
Free(ssi->pCertificateData);
|
|
Free(ssi->pCipherSpecData);
|
|
WssaCryptUninit(&ssi->wciServer1);
|
|
WssaCryptUninit(&ssi->wciClient1);
|
|
Free(ssi->pFeedback);
|
|
if ( ssi->pszRecvBuf != NULL )
|
|
Free(ssi->pszRecvBuf);
|
|
if ( ssi->pszHostName != NULL )
|
|
Free(ssi->pszHostName);
|
|
Free(ssi);
|
|
}
|
|
}
|
|
|
|
SSI WSSAFNCT DuplicateAndInstallSSI(SECURE_SOCKET s, SSI ssiIn){
|
|
#ifdef WSSA_SERVER
|
|
SSI ssi = ConstructSSI(s);
|
|
|
|
ASSERT(ssi);
|
|
if (NULL != ssi){
|
|
memcpy(ssi, ssiIn, sizeof(*ssi));
|
|
ssi->pCertificateData = malloc(ssi->nCertificateSize);
|
|
if (NULL != ssi->pCertificateData){
|
|
ssi->pCipherSpecData = malloc(ssi->nCipherSpecSize);
|
|
ASSERT(ssi->pCipherSpecData);
|
|
if (NULL != ssi->pCipherSpecData){
|
|
/*this will be rebuilt in later code*/
|
|
ssi->pFeedback = 0;
|
|
memcpy(ssi->pCertificateData, ssiIn->pCertificateData, ssi->nCertificateSize);
|
|
memcpy(ssi->pCipherSpecData , ssiIn->pCipherSpecData , ssi->nCipherSpecSize);
|
|
return ssi;
|
|
}
|
|
else Free(ssi->pCertificateData);
|
|
}
|
|
WssaTableRemoveEntry(s);
|
|
DestructSSI(ssi);
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
Make the MAC = Message Authentication Code
|
|
MAC-DATA = HASH[ SECRET, ACTUAL-DATA, PADDING-DATA, SEQUENCE-NUMBER ]
|
|
*/
|
|
static char * N2RG(int n){
|
|
static char rgBuf[4];
|
|
rgBuf[0] = (n >> 24) & 0xff;
|
|
rgBuf[1] = (n >> 16) & 0xff;
|
|
rgBuf[2] = (n >> 8) & 0xff;
|
|
rgBuf[3] = (n >> 0) & 0xff;
|
|
return rgBuf;
|
|
}
|
|
|
|
static int SslMakeMac(char *pBufOut, char *pSecret, int nSecret, char* pActual, int nActual, char *pPadding, int nPadding, int nSeq){
|
|
WssaHashInfo whi;
|
|
|
|
/*still must init if not in debug mode*/
|
|
if (WSSA_OK!=WssaHashInit(&whi, WSSA_HASH_MD5))
|
|
{
|
|
ASSERT(0);
|
|
return WSSA_ERROR;
|
|
}
|
|
/*key*/
|
|
WssaHashUpdate(&whi, pSecret, nSecret);
|
|
/*actual content*/
|
|
WssaHashUpdate(&whi, pActual, nActual);
|
|
/*padding if any*/
|
|
if (nPadding) WssaHashUpdate(&whi, pPadding, nPadding);
|
|
/*sequence #*/
|
|
WssaHashUpdate(&whi, N2RG(nSeq), 4);
|
|
/*finish it up*/
|
|
WssaHashFinal(&whi);
|
|
memcpy(pBufOut,whi.pDigest,whi.nDigestLen);
|
|
return WSSA_OK;
|
|
}
|
|
|
|
/*************************************************---------------------------\
|
|
| Cool Send and Receive Calls |
|
|
| Handle Packing and Encrypting |
|
|
| And Getting the rights size buffer |
|
|
| And other interesting Stuff |
|
|
\-------------------------------------***************************************/
|
|
/*
|
|
This function takes as input a buffer that will be packaged in a form
|
|
that can be sent out the door. This will use all necessary encryption
|
|
checksuming, and other relevant information. Then it will proceed to
|
|
send the data out. As input, the data should start at
|
|
the 4th byte of the input buffer. The first 3 will be overwritten.
|
|
|
|
pBuf - Data to be sent. Starts on 4th byte
|
|
nSize - Size of the data. Does not include prepad
|
|
nPadding - Number of bytes that were used as padding. Non-zero when
|
|
data is not a multiple of the cipher block.
|
|
fSecurity - Security Escape?
|
|
fMagicPrepad - True if pBuf has an initial padding of length WSSA_MAGIC_PREPAD
|
|
whose contents can be overwritten
|
|
RETURN - SOCKET_ERROR upon failure
|
|
*/
|
|
//*******************************************************************************
|
|
// BUGBUGBUG !!!! BUG ALERT !!!
|
|
// Ssl Pack and Send assumes that it will always be able to always send all
|
|
// its bytes as once. If it fails to send some of the bytes during
|
|
// handshaking, it does not have a method for resending some of those
|
|
// bytes that failed to be sent the first time
|
|
// This may be a problem, under high traffic - low bandwidth situations.
|
|
//*******************************************************************************
|
|
int WSSAFNCT SslPackAndSend(SECURE_SOCKET ss, char *pBuf, int nSize, BOOL fSecurity, BOOL fMagicPrepad){
|
|
SSI ssi;
|
|
int nSizeOut;
|
|
int errno;
|
|
int nPadding;
|
|
char *pBufOut, *pBufHead;
|
|
|
|
DebugEntry(SslPackAndSend);
|
|
|
|
/*Entry point work*/
|
|
ssi = WssaTableGetSSI(ss);
|
|
/*Default to ok*/
|
|
errno = WSSA_OK;
|
|
/*Assign output buffer*/
|
|
pBufOut = (TRUE == fMagicPrepad) ? pBuf : malloc (nSize + WSSA_MAGIC_PREPAD);
|
|
ASSERT(pBufOut);
|
|
|
|
if (NULL != pBufOut){
|
|
/*copy data into this buffer*/
|
|
if (FALSE == fMagicPrepad) memcpy(pBufOut+19, pBuf, nSize);
|
|
else pBuf += WSSA_MAGIC_PREPAD;
|
|
/*padding?*/
|
|
nPadding = 0;
|
|
/*encrypt on?*/
|
|
if (ssi->dwSSLSystemFlags & WSSA_FLAG_ENCRYPTION_ON){
|
|
if (WSSA_ERROR != SslMakeMac(pBufOut+3, ssi->wciClient1.pKey,
|
|
ssi->wciClient1.pCipherInfo->nKeyLen, pBuf, nSize,
|
|
pBufOut,nPadding, ssi->nSeqSend)){
|
|
nSizeOut = nSize + 16;
|
|
pBufHead = pBufOut;
|
|
WssaCryptTransform(&ssi->wciClient1, pBufOut + 3, nSizeOut);
|
|
}
|
|
else
|
|
errno = WSSA_ERROR_DATA_TOO_LARGE;
|
|
}
|
|
else {
|
|
nSizeOut = nSize;
|
|
pBufHead = pBufOut + 16;
|
|
}
|
|
/*Which header should we make?*/
|
|
if (0==nPadding && !fSecurity){
|
|
/*Make 2 byte header*/
|
|
if (nSize<=SSL_MAX_RECORD_LENGTH_2_BYTE_HEADER){
|
|
/*Define Output*/
|
|
pBufHead++;
|
|
nSizeOut+= 2;
|
|
/*fill header
|
|
RECORD-LENGTH = ((byte[0] & 0x7f) << 8)) | byte[1];
|
|
*/
|
|
pBufHead[0] = ((nSizeOut-2) >> 8) | 0x80;
|
|
pBufHead[1] = (nSizeOut-2) & 0xFF;
|
|
}
|
|
/*Data too large to fit in packet*/
|
|
else
|
|
errno = WSSA_ERROR_DATA_TOO_LARGE;
|
|
}
|
|
else{
|
|
/*Make 3 byte header*/
|
|
if (nSize<=SSL_MAX_RECORD_LENGTH_3_BYTE_HEADER){
|
|
/*Define Output*/
|
|
nSizeOut += 3;
|
|
/*fill header
|
|
RECORD-LENGTH = ((byte[0] & 0x3f) << 8)) | byte[1];
|
|
IS-ESCAPE = (byte[0] & 0x40) != 0;
|
|
PADDING = byte[2];
|
|
*/
|
|
pBufHead[0] = ((nSizeOut-3) >> 8) | (fSecurity?0x40:0x00);
|
|
pBufHead[1] = (nSizeOut-3) & 0xFF;
|
|
pBufHead[2] = nPadding;
|
|
}
|
|
/*Data too large to fit in packet*/
|
|
else
|
|
errno = WSSA_ERROR_DATA_TOO_LARGE;
|
|
}
|
|
|
|
if (WSSA_OK == errno) {
|
|
errno = send(SS2S(ss),pBufHead, nSizeOut, 0);
|
|
/*Delete buffer if we allocated it here*/
|
|
if (FALSE == fMagicPrepad) Free(pBufOut);
|
|
if (SOCKET_ERROR == errno)
|
|
{
|
|
ASSERT(0);
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (errno < nSizeOut)
|
|
{
|
|
TRACE_OUT("Failed to send all in \"SslPackAndSend\", asked=%d, actual=%d",
|
|
nSizeOut, errno);
|
|
}
|
|
ssi->nSeqSend++;
|
|
return errno - (nSizeOut - nSize);
|
|
}
|
|
}
|
|
WSASetLastError(errno);
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
/*
|
|
This function receives a full buffer of data and returns it unpackaged
|
|
Data will be stored in ssi->pBPR
|
|
|
|
ssi - socket on which to send data
|
|
pfSecurityOut - Security Escape?
|
|
ppBufOut - Data will be pointer to start of out packet.
|
|
Error when NULL. Error code has been set.
|
|
This is an offset into ssi->pBPR
|
|
pSizeOut - Data wil be size of data out
|
|
flags - flags associated with a send
|
|
*/
|
|
|
|
static enum {
|
|
STATE_RECEIVE_START = 0,
|
|
STATE_RECEIVE_HEADER1,
|
|
STATE_RECEIVE_HEADER2,
|
|
STATE_RECEIVE_HEADER3,
|
|
STATE_RECEIVE_MAC,
|
|
STATE_RECEIVE_DATA
|
|
};
|
|
|
|
int WSSAFNCT SslReceiveAndUnPack(SECURE_SOCKET ss, BOOL *pfSecurityOut, char *pBuf, int *pnSize, int flags){
|
|
SSI ssi;
|
|
int nTemp, nLen, nPadding;
|
|
|
|
DebugEntry(SslReceiveAndUnPack);
|
|
|
|
/*Entry point work*/
|
|
ssi = WssaTableGetSSI(ss);
|
|
/*Grand old State Table*/
|
|
while (1){
|
|
switch(ssi->nStateReceive){
|
|
case STATE_RECEIVE_START:
|
|
/*note, packets are guaranteed to contain data*/
|
|
nTemp = recv(SS2S(ss),ssi->rgMAC,2,flags);
|
|
if (SOCKET_ERROR == nTemp)
|
|
{
|
|
/*
|
|
if (WSAEWOULDBLOCK == WSAGetLastError())
|
|
{
|
|
BlockDaSkt(ss);
|
|
nTemp = recv(SS2S(ss),ssi->rgMAC,2,flags);
|
|
UnblockDaSkt(ss);
|
|
if (nTemp != SOCKET_ERROR)
|
|
goto done_good;
|
|
TRACE_OUT("sockets hosed us!! errcode=%d", WSAGetLastError());
|
|
return SOCKET_ERROR;
|
|
}
|
|
ASSERT(0);
|
|
*/
|
|
return SOCKET_ERROR;
|
|
}
|
|
done_good:
|
|
if (0 == nTemp) return 0;
|
|
if (1 == nTemp) ssi->nStateReceive = STATE_RECEIVE_HEADER1;
|
|
if (2 == nTemp) ssi->nStateReceive = STATE_RECEIVE_HEADER2;
|
|
ssi->nSeqRecv++;
|
|
break;
|
|
case STATE_RECEIVE_HEADER1:
|
|
/*not sure if this is still a problem. it was when tried to read 3 bytes*/
|
|
/*some servers really really suck!!! and send < 3 bytes, wait for them*/
|
|
TRACE_OUT("\n+++++++++++++ LOSER SERVER MODE. DOESN'T SEND HEADER FAST +++++++\n");
|
|
/*if server was really lame, try to get extra byte again*/
|
|
nTemp = recv(SS2S(ss),ssi->rgMAC + 1,1,flags);
|
|
if (SOCKET_ERROR == nTemp)
|
|
{
|
|
return SOCKET_ERROR;
|
|
}
|
|
ssi->nStateReceive = STATE_RECEIVE_HEADER2;
|
|
break;
|
|
case STATE_RECEIVE_HEADER2:
|
|
/*get size*/
|
|
if (ssi->rgMAC[0]&0x80) ssi->nBTG = ((ssi->rgMAC[0]&0x7F) << 8) + ((unsigned char) ssi->rgMAC[1]);
|
|
else ssi->nBTG = ((ssi->rgMAC[0]&0x3F) << 8) + ((unsigned char) ssi->rgMAC[1]);
|
|
/*which header type, again*/
|
|
if (ssi->rgMAC[0]&0x80) {
|
|
/*2 byte header*/
|
|
nPadding = 0;
|
|
*pfSecurityOut = FALSE;
|
|
ssi->nStateReceive = STATE_RECEIVE_MAC;
|
|
}
|
|
else{
|
|
/*setup extra info that we already have*/
|
|
*pfSecurityOut = (ssi->rgMAC[0] & 0x40) ? TRUE : FALSE;
|
|
ssi->nStateReceive = STATE_RECEIVE_HEADER3;
|
|
}
|
|
break;
|
|
case STATE_RECEIVE_HEADER3:
|
|
/*grab 3rd byte of 2 byte header*/
|
|
nTemp = recv(SS2S(ss),ssi->rgMAC,1,flags);
|
|
if (SOCKET_ERROR == nTemp)
|
|
{
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (0 == nTemp)
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
nPadding = ssi->rgMAC[0];
|
|
ssi->nStateReceive = STATE_RECEIVE_MAC;
|
|
case STATE_RECEIVE_MAC:
|
|
if (ssi->dwSSLSystemFlags & WSSA_FLAG_ENCRYPTION_ON){
|
|
nTemp = recv(SS2S(ss),ssi->rgMAC+ssi->nMAC,16-ssi->nMAC,flags);
|
|
if (SOCKET_ERROR == nTemp)
|
|
{
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (0 == nTemp)
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
|
|
ssi->nMAC += nTemp;
|
|
/*Fake an unrecoverable error so that we will be called again*/
|
|
if (16 != ssi->nMAC) RETURN_SOCKET_ERROR(WSAEWOULDBLOCK);
|
|
ssi->nMAC = 0;
|
|
WssaCryptTransform(&ssi->wciServer1, ssi->rgMAC, 16);
|
|
ssi->nBTG -= 16;
|
|
/*Setup Hash*/
|
|
WssaHashInit(&ssi->whiInput, WSSA_HASH_MD5);
|
|
WssaHashUpdate(&ssi->whiInput, ssi->wciServer1.pKey, ssi->wciServer1.pCipherInfo->nKeyLen);
|
|
ssi->nStateReceive = STATE_RECEIVE_DATA;
|
|
}
|
|
else ssi->nStateReceive = STATE_RECEIVE_DATA;
|
|
break;
|
|
case STATE_RECEIVE_DATA:
|
|
/*Get size of read*/
|
|
nLen = (ssi->nBTG < *pnSize) ? ssi->nBTG : *pnSize;
|
|
/*get data*/
|
|
nTemp = recv(SS2S(ss),pBuf,nLen,flags);
|
|
/*Did I get information?*/
|
|
if (SOCKET_ERROR == nTemp)
|
|
{
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (nTemp > 0) {
|
|
if (ssi->dwSSLSystemFlags & WSSA_FLAG_ENCRYPTION_ON){
|
|
WssaCryptTransform(&ssi->wciServer1, pBuf, nTemp);
|
|
WssaHashUpdate( &ssi->whiInput, pBuf, nTemp);
|
|
}
|
|
ssi->nBTG -= nTemp;
|
|
// total number of bytes recv so far
|
|
// this is the total of ALL recvs not just this particaular call
|
|
*pnSize = nTemp + ssi->nBytesRecv;
|
|
if (0 == ssi->nBTG){
|
|
if (ssi->dwSSLSystemFlags & WSSA_FLAG_ENCRYPTION_ON){
|
|
/*sequence #*/
|
|
WssaHashUpdate(&ssi->whiInput, N2RG(ssi->nSeqRecv), 4);
|
|
/*finish it up*/
|
|
WssaHashFinal(&ssi->whiInput);
|
|
if (0 != memcmp(ssi->whiInput.pDigest, ssi->rgMAC, 16)){
|
|
#ifdef WSSA_DEBUG_VOCAL
|
|
printf("\nMAC DOESN'T MATCH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
|
#endif
|
|
/*no, then die*/
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
}
|
|
}
|
|
ssi->nStateReceive = STATE_RECEIVE_START;
|
|
}
|
|
WSASetLastError(WSAEWOULDBLOCK);
|
|
return nTemp;
|
|
}
|
|
else RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*Stuff used in both client and server=======================================*/
|
|
/*===========================================================================*/
|
|
/*===========================================================================*/
|
|
/*===========================================================================*/
|
|
|
|
|
|
/*************************************************---------------------------\
|
|
| Stuff To Handle Error MEssages |
|
|
\-------------------------------------***************************************/
|
|
|
|
/*
|
|
Check for an error packet.
|
|
pBuf - Input Buffer
|
|
RETURN - TRUE if error. FALSE if not. Error code will be set
|
|
|
|
*-Definition-*
|
|
ERROR (Sent clear or encrypted)
|
|
char MSG-ERROR
|
|
char ERROR-CODE-MSB
|
|
char ERROR-CODE-LSB
|
|
*/
|
|
static int SslErrorParse(char *pBuf){
|
|
if (SSL_MT_ERROR == pBuf[0]){
|
|
WSASetLastError((pBuf[1]<<8) | pBuf[2]);
|
|
return WSSA_ERROR;
|
|
}
|
|
else return WSSA_OK;
|
|
}
|
|
|
|
static int SslErrorMake(char *pBuf, int nSize, int nErr){
|
|
if (nSize < 3) return 0;
|
|
|
|
pBuf[0] = SSL_MT_ERROR;
|
|
pBuf[1] = SslByteHi(nErr);
|
|
pBuf[2] = SslByteLo(nErr);
|
|
return 3;
|
|
}
|
|
|
|
|
|
/*
|
|
Initialize the crypto info for specified type
|
|
*/
|
|
static int SslMakeKeyMaterial(SSI ssi, WssaHashInfo *pwhi, char *pMasterData, int nMasterSize, char *sz, BOOL fUseChar){
|
|
if (WSSA_OK==WssaHashInit(pwhi, WSSA_HASH_MD5)){
|
|
WssaHashUpdate(pwhi, pMasterData, nMasterSize);
|
|
if (TRUE==fUseChar) WssaHashUpdate(pwhi, sz, 1);
|
|
WssaHashUpdate(pwhi, ssi->rgChallengeData, ssi->nChallengeSize);
|
|
WssaHashUpdate(pwhi, ssi->rgConnectionIdData, ssi->nConnectionIdSize);
|
|
WssaHashFinal(pwhi);
|
|
return WSSA_OK;
|
|
}
|
|
return WSSA_ERROR;
|
|
}
|
|
static int SslMakeKeys(SSI ssi){
|
|
WssaHashInfo whi;
|
|
BOOL fOption;
|
|
|
|
/*Which mode for block ciphers*/
|
|
fOption = (ssi->dwSSLUserFlags & SO_SSL_SERVER) ? TRUE : FALSE;
|
|
/*Just checking... Could be screwed if didn't restore all of state info on reconnect*/
|
|
ASSERT(ssi->pCipherInfo);
|
|
/*setup for each type of encryption*/
|
|
switch (ssi->pCipherInfo->nCryptoType){
|
|
case WSSA_CRYPT_RC4:
|
|
#ifdef WSSA_BLOCK_CIPHERS
|
|
case WSSA_CRYPT_RC2:
|
|
case WSSA_CRYPT_IDEA:
|
|
#endif
|
|
SslMakeKeyMaterial(ssi, &whi, ssi->rgMaster, sizeof(ssi->rgMaster), "0", TRUE);
|
|
WssaCryptInit(&ssi->wciServer1, whi.pDigest, ssi->pFeedback, ssi->pCipherInfo, fOption);
|
|
SslMakeKeyMaterial(ssi, &whi, ssi->rgMaster, sizeof(ssi->rgMaster), "1", TRUE);
|
|
WssaCryptInit(&ssi->wciClient1, whi.pDigest, ssi->pFeedback, ssi->pCipherInfo, !fOption);
|
|
break;
|
|
#ifdef WSSA_BLOCK_CIPHERS
|
|
case WSSA_CRYPT_DES:
|
|
SslMakeKeyMaterial(ssi, &whi, ssi->rgMaster, sizeof(ssi->rgMaster), "0", FALSE);
|
|
WssaCryptInit(&ssi->wciServer1, whi.pDigest, ssi->pFeedback, ssi->pCipherInfo, fOption);
|
|
WssaCryptInit(&ssi->wciClient1, whi.pDigest + whi.nDigestLen/2, ssi->pFeedback, ssi->pCipherInfo, !fOption);
|
|
break;
|
|
case WSSA_CRYPT_3DES:
|
|
SslMakeKeyMaterial(ssi, &whi, ssi->rgMaster, sizeof(ssi->rgMaster), "0", TRUE);
|
|
WssaCryptInit(&ssi->wciServer1, whi.pDigest, ssi->pFeedback, ssi->pCipherInfo, fOption);
|
|
WssaCryptInit(&ssi->wciServer2, whi.pDigest + whi.nDigestLen/2, ssi->pFeedback, ssi->pCipherInfo, fOption);
|
|
SslMakeKeyMaterial(ssi, &whi, ssi->rgMaster, sizeof(ssi->rgMaster), "1", TRUE);
|
|
WssaCryptInit(&ssi->wciServer3, whi.pDigest, ssi->pFeedback, ssi->pCipherInfo, fOption);
|
|
WssaCryptInit(&ssi->wciClient1, whi.pDigest + whi.nDigestLen/2, ssi->pFeedback, ssi->pCipherInfo, !fOption);
|
|
SslMakeKeyMaterial(ssi, &whi, ssi->rgMaster, sizeof(ssi->rgMaster), "2", TRUE);
|
|
WssaCryptInit(&ssi->wciClient2, whi.pDigest, ssi->pFeedback, ssi->pCipherInfo, !fOption);
|
|
WssaCryptInit(&ssi->wciClient1, whi.pDigest + whi.nDigestLen/2, ssi->pFeedback, ssi->pCipherInfo, !fOption);
|
|
break;
|
|
#endif
|
|
default:
|
|
TRACE_OUT("bad nCryptoType in SslMakeKeys");
|
|
return WSSA_ERROR;
|
|
}
|
|
return WSSA_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*Stuff used in only clients=================================================*/
|
|
/*===========================================================================*/
|
|
/*===========================================================================*/
|
|
/*===========================================================================*/
|
|
|
|
/*************************************************---------------------------\
|
|
| Stuff To Make Client Messages |
|
|
\-------------------------------------***************************************/
|
|
#ifdef WSSA_CLIENT
|
|
/*
|
|
Make Client Hello Message
|
|
Does not package!
|
|
pBuf - Output Buffer
|
|
nSize - Size of Buffer
|
|
RETURN - 0 if error. nSizeOut if not. Error Code will be set
|
|
|
|
*-Definition-*
|
|
CLIENT-HELLO (Phase 1; Sent in the clear)
|
|
char MSG-CLIENT-HELLO
|
|
char CLIENT-VERSION-MSB
|
|
char CLIENT-VERSION-LSB
|
|
char CIPHER-SPECS-LENGTH-MSB
|
|
char CIPHER-SPECS-LENGTH-LSB
|
|
char SESSION-ID-LENGTH-MSB
|
|
char SESSION-ID-LENGTH-LSB
|
|
char CHALLENGE-LENGTH-MSB
|
|
char CHALLENGE-LENGTH-LSB
|
|
char CIPHER-SPECS-DATA[(MSB<<8)|LSB]
|
|
char SESSION-ID-DATA[(MSB<<8)|LSB]
|
|
char CHALLENGE-DATA[(MSB<<8)|LSB]
|
|
*/
|
|
static int SslClientHelloMake(SSI ssi, char *pBuf, int nSize){
|
|
int nOffset, nSizeOut;
|
|
|
|
/*Generate new info*/
|
|
nOffset = ssi->nChallengeSize = 16;// Should be = 16 + SslRandom(17);, but bozo servers don't follow spec
|
|
#ifdef BETTER_RANDOM
|
|
GenRandom(ssi->rgChallengeData, ssi->nChallengeSize);
|
|
#else
|
|
for (nOffset=0;nOffset<ssi->nChallengeSize;++nOffset)
|
|
ssi->rgChallengeData[nOffset] = SslRandom(256);
|
|
#endif BETTER_RANDOM
|
|
|
|
/*Is Packet Large Enough?*/
|
|
nSizeOut = 9 + ssi->nCipherSpecSize + ssi->nSessionIdSize + ssi->nChallengeSize;
|
|
if (nSize >= nSizeOut){
|
|
/*Construct Packet*/
|
|
pBuf[0] = SSL_MT_CLIENT_HELLO;
|
|
pBuf[1] = SslByteHi(SSL_CLIENT_VERSION);
|
|
pBuf[2] = SslByteLo(SSL_CLIENT_VERSION);
|
|
pBuf[3] = SslByteHi(ssi->nCipherSpecSize);
|
|
pBuf[4] = SslByteLo(ssi->nCipherSpecSize);
|
|
pBuf[5] = SslByteHi(ssi->nSessionIdSize);
|
|
pBuf[6] = SslByteLo(ssi->nSessionIdSize);
|
|
pBuf[7] = SslByteHi(ssi->nChallengeSize);
|
|
pBuf[8] = SslByteLo(ssi->nChallengeSize);
|
|
/*Copy boring info*/
|
|
nOffset = 9;
|
|
memcpy(pBuf+nOffset,ssi->pCipherSpecData,ssi->nCipherSpecSize);
|
|
nOffset += ssi->nCipherSpecSize;
|
|
memcpy(pBuf+nOffset,ssi->rgSessionIdData,ssi->nSessionIdSize);
|
|
nOffset += ssi->nSessionIdSize;
|
|
memcpy(pBuf+nOffset,ssi->rgChallengeData,ssi->nChallengeSize);
|
|
}
|
|
else {
|
|
WSASetLastError(WSSA_ERROR_UNRECOVERABLE);
|
|
nSizeOut = 0;
|
|
}
|
|
return nSizeOut;
|
|
}
|
|
|
|
/*
|
|
Make Client Finihed Message
|
|
Does not package!
|
|
pBuf - Output Buffer
|
|
nSize - Size of Buffer
|
|
RETURN - 0 if error. nSizeOut if not. Error Code will be set
|
|
|
|
CLIENT-FINISHED (Phase 2; Sent encrypted)
|
|
char MSG-CLIENT-FINISHED
|
|
char CONNECTION-ID[N-1]
|
|
*/
|
|
static int SslClientFinishedMake(SSI ssi, char *pBuf, int nSize){
|
|
int nSizeOut = 0;
|
|
|
|
if (nSize >= 1 + ssi->nConnectionIdSize){
|
|
pBuf[0] = SSL_MT_CLIENT_FINISHED;
|
|
memcpy(pBuf+1,ssi->rgConnectionIdData,ssi->nConnectionIdSize);
|
|
nSizeOut = 1 + ssi->nConnectionIdSize;
|
|
}
|
|
else WSASetLastError(WSSA_ERROR_UNRECOVERABLE);
|
|
return nSizeOut;
|
|
}
|
|
|
|
/*
|
|
Make Master Key Message
|
|
Does not package!
|
|
pBuf - Output Buffer
|
|
nSize - Size of Buffer
|
|
RETURN - 0 if error. nSizeOut if not. Error Code will be set
|
|
|
|
CLIENT-MASTER-KEY (Phase 1; Sent primarily in the clear)
|
|
char MSG-CLIENT-MASTER-KEY
|
|
char CIPHER-KIND[3]
|
|
char CLEAR-KEY-LENGTH-MSB
|
|
char CLEAR-KEY-LENGTH-LSB
|
|
char ENCRYPTED-KEY-LENGTH-MSB
|
|
char ENCRYPTED-KEY-LENGTH-LSB
|
|
char KEY-ARG-LENGTH-MSB
|
|
char KEY-ARG-LENGTH-LSB
|
|
char CLEAR-KEY-DATA[MSB<<8|LSB]
|
|
char ENCRYPTED-KEY-DATA[MSB<<8|LSB]
|
|
char KEY-ARG-DATA[MSB<<8|LSB]
|
|
*/
|
|
static int SslClientMasterKeyMake(SSI ssi, char *pBuf, int nSize, LPBSAFE_PUB_KEY pKey){
|
|
#ifdef BETTER_RANDOM
|
|
int nSizeOut = 0;
|
|
#else
|
|
int z, nSizeOut = 0;
|
|
#endif
|
|
char *pBufIn, *pBufOut;
|
|
WssaHashInfo whi;
|
|
|
|
/*make buffers*/
|
|
pBufIn = malloc(pKey->keylen*2);
|
|
ASSERT(pBufIn);
|
|
|
|
pBufOut = pBufIn + pKey->keylen;
|
|
if ( NULL != ssi->pCipherInfo
|
|
&& NULL != pBufIn
|
|
){
|
|
ssi->pCipherInfo = ssi->pCipherInfo;
|
|
/*calc size*/
|
|
nSizeOut = 10 + ssi->pCipherInfo->nClearKeyLen + pKey->bitlen/8 + ssi->pCipherInfo->nFeedback;
|
|
/*the second option check to see if MD5 is available*/
|
|
if (nSizeOut >= nSize) nSizeOut = 0;
|
|
if (nSizeOut && WSSA_OK == WssaHashInit(&whi, WSSA_HASH_MD5)){
|
|
/*mesage identifier*/
|
|
pBuf[0] = SSL_MT_CLIENT_MASTER_KEY;
|
|
/*cipher spec chosen*/
|
|
memcpy(pBuf+1,ssi->pCipherInfo->rgSig,3);
|
|
/*clear key len*/
|
|
pBuf[4] = SslByteHi(ssi->pCipherInfo->nClearKeyLen);
|
|
pBuf[5] = SslByteLo(ssi->pCipherInfo->nClearKeyLen);
|
|
/*encrypted data len*/
|
|
pBuf[6] = SslByteHi((unsigned char)(pKey->bitlen/8));
|
|
pBuf[7] = SslByteLo((unsigned char)(pKey->bitlen/8));
|
|
/*key arg len*/
|
|
pBuf[8] = SslByteHi(ssi->pCipherInfo->nFeedback);
|
|
pBuf[9] = SslByteLo(ssi->pCipherInfo->nFeedback);
|
|
/*Generate Master Key :: CAPI-->CryptGenRandom*/
|
|
#ifdef BETTER_RANDOM
|
|
GenRandom(ssi->rgMaster, sizeof(ssi->rgMaster));
|
|
#else
|
|
for (z=0;z<sizeof(ssi->rgMaster);++z) ssi->rgMaster[z] = SslRandom(256);
|
|
#endif BETTER_RANDOM
|
|
/*copy in clear key info*/
|
|
memcpy(pBuf+10, ssi->rgMaster, ssi->pCipherInfo->nClearKeyLen);
|
|
pBuf += 10 + ssi->pCipherInfo->nClearKeyLen;
|
|
/*make encrypted data packet*/
|
|
/*clear extra for debuggging*/
|
|
memset(pBufIn, 0, pKey->keylen*2);
|
|
/*enocode packet*/
|
|
PKCS1Encode(pBufIn, pKey->bitlen/8, ssi->rgMaster+ssi->pCipherInfo->nClearKeyLen, ssi->pCipherInfo->nKeyLen - ssi->pCipherInfo->nClearKeyLen, 2);
|
|
/*encrypt packet*/
|
|
pKey->datalen = pKey->bitlen/8 - 1;
|
|
if (TRUE == BSafeEncPublic(pKey, pBufIn, pBufOut)){
|
|
/*copy the encrypted data in*/
|
|
memcpy(pBuf, pBufOut, pKey->bitlen/8);
|
|
FlipBuf(pBuf, pKey->bitlen/8);
|
|
/*advance packet to key arg array*/
|
|
pBuf += pKey->bitlen/8;
|
|
}
|
|
else nSizeOut = 0;
|
|
if (ssi->pCipherInfo->nFeedback > 0){
|
|
ssi->pFeedback = malloc(ssi->pCipherInfo->nFeedback);
|
|
if (NULL != ssi->pFeedback)
|
|
#ifdef BETTER_RANDOM
|
|
{
|
|
GenRandom(ssi->pFeedback, ssi->pCipherInfo->nFeedback);
|
|
memcpy( pBuf, ssi->pFeedback, ssi->pCipherInfo->nFeedback );
|
|
}
|
|
#else
|
|
for (z=0;z<ssi->pCipherInfo->nFeedback;++z)
|
|
pBuf[z] = ssi->pFeedback[z] = SslRandom(256);
|
|
#endif BETTER_RANDOM
|
|
else {
|
|
WSASetLastError(WSSA_ERROR_UNRECOVERABLE);
|
|
nSizeOut = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else WSASetLastError(WSSA_ERROR_UNRECOVERABLE);
|
|
Free(pBufIn);
|
|
return nSizeOut;
|
|
}
|
|
|
|
/*************************************************---------------------------\
|
|
| Stuff To Parse Server Messages |
|
|
\-------------------------------------***************************************/
|
|
/*
|
|
Parse Server-Hello Message
|
|
*/
|
|
static int SslServerHelloParse(SSI ssi, char *pBuf, int nSize, LPBSAFE_PUB_KEY *ppKey){
|
|
int errno, z, x, nCipherSpecSize;
|
|
|
|
/*default error as lame error*/
|
|
errno = WSSA_ERROR_UNRECOVERABLE;
|
|
/*Did we get a valid SERVER-HELLO message?*/
|
|
ssi->nConnectionIdSize = SslRg2Int(pBuf+9);
|
|
if (SslRg2Int(pBuf + 7) <= ssi->nCipherSpecSize
|
|
&& ssi->nConnectionIdSize >= 16
|
|
&& ssi->nConnectionIdSize <= 32
|
|
&& SSL_MT_SERVER_HELLO == pBuf[0]
|
|
&& SSL_SERVER_VERSION <= SslRg2Int(3+pBuf)
|
|
){
|
|
/*get some size entries*/
|
|
ssi->nCertificateSize = (int) SslRg2Int(pBuf + 5);
|
|
nCipherSpecSize = SslRg2Int(pBuf + 7);
|
|
/*Did they find a session-ID*/
|
|
if (pBuf[1]){
|
|
/*yes, check other fields for errors*/
|
|
if (ssi->nSessionIdSize
|
|
&& (0 == nCipherSpecSize + ssi->nCertificateSize)
|
|
){
|
|
TRACE_OUT("We are usign the short handshake--------");
|
|
/*get relevant data*/
|
|
memcpy(ssi->rgConnectionIdData, (const void *) (pBuf + 11), ssi->nConnectionIdSize);
|
|
/*have system skip master key message, we have a short_handshake...*/
|
|
ssi->dwSSLSystemFlags |= WSSA_FLAG_SHORT_HANDSHAKE;
|
|
return WSSA_OK;
|
|
}
|
|
}
|
|
else{
|
|
/*no ssion id found, get other data*/
|
|
ssi->nCertificateType = pBuf[2];
|
|
if (SSL_CT_X509_CERTIFICATE == ssi->nCertificateType){
|
|
/*make structure to hold certificate*/
|
|
if (ssi->pCertificateData) free(ssi->pCertificateData);
|
|
ssi->pCertificateData = malloc(ssi->nCertificateSize);
|
|
if (ssi->pCertificateData){
|
|
/*alloc succeeded, advance to certificate*/
|
|
nSize = 11;
|
|
/*copy in certificate*/
|
|
memcpy(ssi->pCertificateData, (const void *) (pBuf + nSize), ssi->nCertificateSize);
|
|
/*advance to cipher list*/
|
|
nSize += ssi->nCertificateSize;
|
|
/*chose cipher spec according to preferntiual listing*/
|
|
ssi->pCipherInfo = NULL;
|
|
for (z=0;z<ssi->nCipherSpecSize;z+=3)
|
|
for (x=0;x<nCipherSpecSize;x+=3)
|
|
if (0==memcmp(&ssi->pCipherSpecData[z],pBuf + nSize + x,3)) goto SslServerHelloParseCuzImLazyGoto;
|
|
SslServerHelloParseCuzImLazyGoto:
|
|
/*If there is a match, it should be at z, and z should be less than the max index*/
|
|
if (z < ssi->nCipherSpecSize)
|
|
for (x=0;x<NUM_CIPHERS;++x)
|
|
if (0==memcmp(rgCipherInfo[x].rgSig,&ssi->pCipherSpecData[z],3))
|
|
ssi->pCipherInfo = &rgCipherInfo[x];
|
|
/*be vocal and tell the whole world what cipher we chose, this check is really inaccurate*/
|
|
#ifdef WSSA_DEBUG_VOCAL
|
|
switch (ssi->pCipherInfo->nCryptoType){
|
|
case WSSA_CRYPT_RC4:
|
|
if (0 == ssi->pCipherInfo->nClearKeyLen) puts("USING SSL_CK_RC4_128_WITH_MD5--------");
|
|
else puts("USING SSL_CK_RC4_128_EXPORT40_WITH_MD5--------");
|
|
break;
|
|
default:
|
|
printf("Unknown Cipher Choice-------------\n");
|
|
break;
|
|
}
|
|
#endif
|
|
/*did we get something valid?!?!?!*/
|
|
if (NULL == ssi->pCipherInfo){
|
|
/*no, there is a cipher error*/
|
|
errno = SSL_PE_NO_CIPHER;
|
|
}
|
|
else{
|
|
/*yes, advance to connection id data*/
|
|
nSize += nCipherSpecSize;
|
|
/*copy in connection id data*/
|
|
memcpy(ssi->rgConnectionIdData,(const void *) (pBuf + nSize), ssi->nConnectionIdSize);
|
|
/*verify signature on certificate*/
|
|
if (WSSA_OK==SslCertificateX509Check(ssi,ssi->pCertificateData,ssi->nCertificateSize, ppKey)){
|
|
return WSSA_OK;
|
|
}
|
|
else errno = SSL_PE_BAD_CERTIFICATE;
|
|
}}}}}
|
|
WSASetLastError(errno);
|
|
return WSSA_ERROR;
|
|
}
|
|
|
|
/*
|
|
Parse Server-Verify Message
|
|
|
|
SERVER-VERIFY (Phase 1; Sent encrypted)
|
|
char MSG-SERVER-VERIFY
|
|
char CHALLENGE-DATA[N-1]
|
|
*/
|
|
static int SslServerVerifyParse(SSI ssi, char *pBuf, int nSize){
|
|
if (SSL_MT_SERVER_VERIFY == pBuf[0]
|
|
&& nSize >= ssi->nChallengeSize + 1
|
|
&& 0 == memcmp(pBuf + 1, ssi->rgChallengeData, ssi->nChallengeSize)
|
|
){
|
|
return WSSA_OK;
|
|
}
|
|
WSASetLastError(WSSA_ERROR_UNRECOVERABLE);
|
|
return WSSA_ERROR;
|
|
}
|
|
|
|
/*
|
|
Parse Server-Finish Message
|
|
|
|
SERVER-FINISHED (Phase 2; Sent encrypted)
|
|
char MSG-SERVER-FINISHED
|
|
char SESSION-ID-DATA[N-1]
|
|
*/
|
|
static int SslServerFinishParse(SSI ssi, char *pBuf, int nSize){
|
|
if (SSL_MT_SERVER_FINISHED == *pBuf++
|
|
&& --nSize >= sizeof (ssi->rgSessionIdData) )
|
|
{
|
|
if ( nSize > sizeof (ssi->rgSessionIdData) )
|
|
nSize = sizeof (ssi->rgSessionIdData);
|
|
ssi->nSessionIdSize = nSize;
|
|
memcpy(ssi->rgSessionIdData, pBuf, nSize);
|
|
return WSSA_OK;
|
|
}
|
|
WSASetLastError(WSSA_ERROR_UNRECOVERABLE);
|
|
return WSSA_ERROR;
|
|
}
|
|
|
|
/*************************************************---------------------------\
|
|
| Doing the protocols |
|
|
\-------------------------------------***************************************/
|
|
|
|
/*
|
|
Enter the handshaking process as a Client.
|
|
|
|
client-hello C -> S: challenge, cipher_specs
|
|
server-hello S -> C: connection-id,server_certificate,cipher_specs
|
|
client-master-key C -> S: {master_key}server_public_key
|
|
client-finish C -> S: {connection-id}client_write_key
|
|
server-verify S -> C: {challenge}server_write_key
|
|
server-finish S -> C: {new_session_id}server_write_key
|
|
*/
|
|
/*The size of this buffer must be large enough to hold largest out message*/
|
|
#define WSSA_MAX_IO_BUF 4096
|
|
|
|
static enum {
|
|
STATE_HANDSHAKE_AS_CLIENT_START = 0,
|
|
STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_HELLO,
|
|
STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_HELLO,
|
|
STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_MASTER_KEY,
|
|
STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_VERIFY,
|
|
STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_FINISH,
|
|
STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_FINISH
|
|
};
|
|
|
|
int WSSAFNCT SslHandshakeAsClient(SECURE_SOCKET ss){
|
|
SSI ssi;
|
|
int nSizeOut; /*Receives size of Data in thing we either constructed or received*/
|
|
BOOL fSecurity; /*Did a security escape come in. Lets hope not, cuz none have been defined yet*/
|
|
char *rgBuf; /*Send buffer with magic prepad*/
|
|
char *pBuf; /*Buffer we pass to construction calls*/
|
|
int nBytesRecv;
|
|
|
|
DebugEntry(SslHandshakeAsClient);
|
|
|
|
/*Entry point work*/
|
|
ssi = WssaTableGetSSI(ss);
|
|
ASSERT(ssi);
|
|
|
|
/* Init our Buffer */
|
|
if ( ssi->pszRecvBuf == NULL )
|
|
{
|
|
ssi->pszRecvBuf = malloc(WSSA_MAX_IO_BUF+WSSA_MAGIC_PREPAD);
|
|
ASSERT(ssi->pszRecvBuf);
|
|
|
|
if ( ssi->pszRecvBuf == NULL )
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
ssi->nBytesRecv = 0;
|
|
}
|
|
// use this buffer so we can append to it as wait for recv(s)
|
|
// back in IE code.
|
|
rgBuf = ssi->pszRecvBuf;
|
|
/*Setup stuff for pre-padding*/
|
|
pBuf = rgBuf + WSSA_MAGIC_PREPAD;
|
|
/*Grand old State Table*/
|
|
while (1){
|
|
switch (ssi->nStateHandshake){
|
|
case STATE_HANDSHAKE_AS_CLIENT_START:
|
|
case STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_HELLO:
|
|
/* We have 0 bytes at init of the handshake */
|
|
ssi->nBytesRecv = 0;
|
|
/*reinitialize nonces, done here cuz we may have a re-handshake*/
|
|
ssi->nSeqSend = 0;
|
|
/*set to value before flip to zero cuz we pre-increment in receive and unpack*/
|
|
ssi->nSeqRecv = 0xFFFFFFFF;
|
|
/*turn off state about handshake*/
|
|
ssi->dwSSLSystemFlags &= ~(WSSA_FLAG_STATE_MASK);
|
|
/*Make and Send Client Hello Message*/
|
|
nSizeOut = SslClientHelloMake(ssi, pBuf, WSSA_MAX_IO_BUF);
|
|
if (0 != nSizeOut
|
|
&& SOCKET_ERROR != SslPackAndSend(ss, rgBuf, nSizeOut, FALSE, TRUE))
|
|
{
|
|
ssi->nStateHandshake = STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_HELLO;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT("SslPackAndSend() failed; nSizeOut=0x%x", nSizeOut);
|
|
DebugExitINT(SslHandshakeAsClient, 0);
|
|
return SOCKET_ERROR;
|
|
}
|
|
case STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_HELLO:
|
|
/*M2:Recv Server Hello*/
|
|
nSizeOut = WSSA_MAX_IO_BUF - ssi->nBytesRecv;
|
|
nBytesRecv = SslReceiveAndUnPack(ss, &fSecurity, (rgBuf+ssi->nBytesRecv), &nSizeOut,0);
|
|
if ( nBytesRecv != SOCKET_ERROR )
|
|
{
|
|
ssi->nBytesRecv += nBytesRecv;
|
|
|
|
if ( ssi->nBTG == 0 )
|
|
{
|
|
ssi->nBytesRecv = 0;
|
|
|
|
if ( WSSA_ERROR != SslErrorParse(rgBuf)
|
|
&& WSSA_ERROR != SslServerHelloParse(ssi, rgBuf, nSizeOut, &ssi->pKey))
|
|
{
|
|
TRACE_OUT("SslErrorParse & SslServerHelloParse suceeded");
|
|
ssi->nStateHandshake = STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_MASTER_KEY;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
DebugExitINT(SslHandshakeAsClient, 0);
|
|
return SOCKET_ERROR;
|
|
case STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_MASTER_KEY:
|
|
/*What handshake are we using*/
|
|
if (ssi->dwSSLSystemFlags & WSSA_FLAG_SHORT_HANDSHAKE){
|
|
TRACE_OUT(" short handshake");
|
|
/*short handshake*/
|
|
nSizeOut = 0;
|
|
ssi->nStateHandshake = STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_VERIFY;
|
|
Free(ssi->pKey);
|
|
break;
|
|
}
|
|
else{
|
|
TRACE_OUT(" long handshake");
|
|
/*long hndshake, make master key message*/
|
|
nSizeOut = SslClientMasterKeyMake(ssi, pBuf, WSSA_MAX_IO_BUF, ssi->pKey);
|
|
/*if necessary, send master key message, else continue*/
|
|
if ( 0 != nSizeOut
|
|
&& SOCKET_ERROR != SslPackAndSend(ss, rgBuf, nSizeOut, FALSE, TRUE)
|
|
){
|
|
/*turn on encryption*/
|
|
ssi->dwSSLSystemFlags |= WSSA_FLAG_ENCRYPTION_ON;
|
|
ssi->nStateHandshake = STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_VERIFY;
|
|
Free(ssi->pKey);
|
|
break;
|
|
}
|
|
}
|
|
DebugExitINT(SslHandshakeAsClient, SOCKET_ERROR);
|
|
return SOCKET_ERROR;
|
|
|
|
case STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_VERIFY:
|
|
/*M2:Recv Server Verify*/
|
|
if (WSSA_ERROR != SslMakeKeys(ssi) )
|
|
{
|
|
nSizeOut = WSSA_MAX_IO_BUF - ssi->nBytesRecv;
|
|
nBytesRecv = SslReceiveAndUnPack(ss, &fSecurity, (rgBuf+ssi->nBytesRecv), &nSizeOut,0);
|
|
if ( nBytesRecv != SOCKET_ERROR )
|
|
{
|
|
ssi->nBytesRecv += nBytesRecv;
|
|
if ( ssi->nBTG == 0 )
|
|
{
|
|
ssi->nBytesRecv = 0;
|
|
|
|
if( WSSA_ERROR != SslErrorParse(rgBuf)
|
|
&& WSSA_ERROR != SslServerVerifyParse(ssi,rgBuf, nSizeOut) )
|
|
{
|
|
/*Make and Send Client Finished Message*/
|
|
ssi->nStateHandshake = STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_FINISH;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DebugExitINT(SslHandshakeAsClient, SOCKET_ERROR);
|
|
return SOCKET_ERROR;
|
|
|
|
case STATE_HANDSHAKE_AS_CLIENT_SEND_CLIENT_FINISH:
|
|
nSizeOut = SslClientFinishedMake(ssi, pBuf, WSSA_MAX_IO_BUF);
|
|
if (0 != nSizeOut
|
|
&& SOCKET_ERROR != SslPackAndSend(ss, rgBuf, nSizeOut, FALSE, TRUE)
|
|
){
|
|
ssi->nStateHandshake = STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_FINISH;
|
|
break;
|
|
}
|
|
else {
|
|
DebugExitINT(SslHandshakeAsClient, SOCKET_ERROR);
|
|
return SOCKET_ERROR;
|
|
}
|
|
case STATE_HANDSHAKE_AS_CLIENT_RECV_SERVER_FINISH:
|
|
/*M2:Recv Server Finish*/
|
|
nSizeOut = WSSA_MAX_IO_BUF - ssi->nBytesRecv;
|
|
nBytesRecv = SslReceiveAndUnPack(ss, &fSecurity, (rgBuf+ssi->nBytesRecv), &nSizeOut,0);
|
|
if ( nBytesRecv != SOCKET_ERROR )
|
|
{
|
|
ssi->nBytesRecv += nBytesRecv;
|
|
|
|
if( ssi->nBTG == 0 )
|
|
{
|
|
ssi->nBytesRecv = 0;
|
|
|
|
if ( WSSA_ERROR != SslErrorParse(rgBuf)
|
|
&& WSSA_ERROR != SslServerFinishParse(ssi,rgBuf, nSizeOut))
|
|
{
|
|
/*we don't see if this message is valid until sending data*/
|
|
ssi->dwSSLSystemFlags |= WSSA_FLAG_HANDSHAKE_DONE;
|
|
return (!SOCKET_ERROR);
|
|
}
|
|
}
|
|
}
|
|
DebugExitINT(SslHandshakeAsClient, SOCKET_ERROR);
|
|
return SOCKET_ERROR;
|
|
default:
|
|
ASSERT(0);
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
int WSSAFNCT SslHandshakeAsClient(SECURE_SOCKET ss){
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/*Stuff used in only servers=================================================*/
|
|
/*===========================================================================*/
|
|
/*===========================================================================*/
|
|
/*===========================================================================*/
|
|
#ifdef WSSA_SERVER
|
|
|
|
static int SslClientHelloParse(SSI ssi, char *pBuf, int nSize){
|
|
int nCipherSpecSize, nSessionIdSize, z;
|
|
|
|
/*Did we get a valid SERVER-HELLO message? check the size*/
|
|
nCipherSpecSize = SslRg2Int(pBuf + 3);
|
|
nSessionIdSize = SslRg2Int(pBuf + 5);
|
|
ssi->nChallengeSize = SslRg2Int(pBuf + 7);
|
|
if ( (nCipherSpecSize + ssi->nSessionIdSize + ssi->nChallengeSize == nSize)
|
|
&&((nCipherSpecSize > 2) && (0 == (nCipherSpecSize % 3)))
|
|
&&(SSL_MT_CLIENT_HELLO == pBuf[0])
|
|
&&(SSL_CLIENT_VERSION <= SslRg2Int(1+pBuf))
|
|
){
|
|
ssi->pCipherSpecDataEdited = malloc(nCipherSpecSize);
|
|
if (NULL != ssi->pCipherSpecDataEdited){
|
|
/*Get to cipher spec info*/
|
|
pBuf += 9;
|
|
/*edit their list of cipher specs*/
|
|
do{
|
|
for (z=0;z<ssi->nCipherSpecSize;z+=3){
|
|
if (0==memcmp(ssi->pCipherSpecData+z,pBuf,3)){
|
|
memcpy(ssi->pCipherSpecDataEdited, pBuf+z,3);
|
|
ssi->nCipherSpecDataEdited += 3;
|
|
}
|
|
}
|
|
pBuf += 3;
|
|
} while (nCipherSpecSize);
|
|
/*after that editing, we should be at the start of the session id field*/
|
|
if (0 != ssi->nCipherSpecDataEdited) {
|
|
pBuf += nCipherSpecSize;
|
|
/*Should use callback to check this information*/
|
|
if ( (nSessionIdSize != ssi->nSessionIdSize)
|
|
|| (0 != memcmp(ssi->rgSessionIdData, pBuf, nSessionIdSize))
|
|
) ssi->nSessionIdSize = 0;
|
|
/*store challenge data*/
|
|
pBuf += ssi->nSessionIdSize;
|
|
memcpy(ssi->rgChallengeData, pBuf, ssi->nChallengeSize);
|
|
return WSSA_OK;
|
|
}
|
|
}
|
|
else{
|
|
ssi->nCipherSpecDataEdited = 0;
|
|
}
|
|
}
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
}
|
|
|
|
static enum {
|
|
STATE_HANDSHAKE_AS_SERVER_START = 0,
|
|
STATE_HANDSHAKE_AS_SERVER_RECV_CLIENT_HELLO,
|
|
STATE_HANDSHAKE_AS_SERVER_SEND_SERVER_HELLO,
|
|
STATE_HANDSHAKE_AS_SERVER_RECV_SERVER_MASTER_KEY,
|
|
STATE_HANDSHAKE_AS_SERVER_SEND_SERVER_VERIFY,
|
|
STATE_HANDSHAKE_AS_SERVER_RECV_SERVER_FINISH,
|
|
STATE_HANDSHAKE_AS_SERVER_SEND_SERVER_FINISH
|
|
};
|
|
|
|
int WSSAFNCT SslHandshakeAsServer(SECURE_SOCKET ss){
|
|
SSI ssi;
|
|
int nSizeOut; /*Receives size of Data in thing we either constructed or received*/
|
|
BOOL fSecurity; /*Did a security escape come in. Lets hope not, cuz none have been defined yet*/
|
|
char rgBuf[WSSA_MAX_IO_BUF+WSSA_MAGIC_PREPAD]; /*Send buffer with magic prepad*/
|
|
char *pBuf; /*Buffer we pass to construction calls*/
|
|
|
|
DebugEntry(SslHandshakeAsServer);
|
|
/*Entry point work*/
|
|
ssi = WssaTableGetSSI(ss);
|
|
ASSERT(ssi);
|
|
/*Setup stuff for pre-padding*/
|
|
pBuf = rgBuf + WSSA_MAGIC_PREPAD;
|
|
/*Grand old State Table*/
|
|
while (1){
|
|
switch (ssi->nStateHandshake){
|
|
case STATE_HANDSHAKE_AS_SERVER_START:
|
|
ssi->nGOT = 0;
|
|
case STATE_HANDSHAKE_AS_SERVER_RECV_CLIENT_HELLO:
|
|
/*reinitialize nonces, done here cuz we may have a re-handshake*/
|
|
ssi->nSeqSend = 0;
|
|
/*set to value before flip to zero cuz we pre-increment in receive and unpack*/
|
|
ssi->nSeqRecv = 0xFFFFFFFF;
|
|
/*turn off state about handshake*/
|
|
ssi->dwSSLSystemFlags &= ~(WSSA_FLAG_STATE_MASK);
|
|
/*Make and Send Client Hello Message*/
|
|
do{
|
|
/*Loop till we get the client hello, or a socket error*/
|
|
nSizeOut = WSSA_MAX_IO_BUF - ssi->nGOT;
|
|
if (SOCKET_ERROR == SslReceiveAndUnPack(ss, &fSecurity, rgBuf + ssi->nGOT, &nSizeOut,0)) return SOCKET_ERROR;
|
|
ssi->nGOT += nSizeOut;
|
|
} while (ssi->nBTG > 0);
|
|
/*parse it dude*/
|
|
if ( WSSA_ERROR == SslErrorParse(rgBuf)
|
|
|| WSSA_ERROR == SslClientHelloParse(ssi, rgBuf, ssi->nGOT)
|
|
){
|
|
/*closest ssl defined error message that could have happened*/
|
|
SslPackAndSend(ss, rgBuf, SslErrorMake(pBuf, WSSA_MAX_IO_BUF, SSL_PE_NO_CIPHER), FALSE, TRUE);
|
|
WssaCloseSocket(ss);
|
|
return SOCKET_ERROR;
|
|
}
|
|
/*success, drop to next state*/
|
|
ssi->nStateHandshake = STATE_HANDSHAKE_AS_SERVER_SEND_SERVER_HELLO;
|
|
ssi->nGOT = 0;
|
|
case STATE_HANDSHAKE_AS_SERVER_SEND_SERVER_HELLO:
|
|
ASSERT(0);
|
|
break;
|
|
case STATE_HANDSHAKE_AS_SERVER_RECV_SERVER_MASTER_KEY:
|
|
ASSERT(0);
|
|
break;
|
|
case STATE_HANDSHAKE_AS_SERVER_SEND_SERVER_VERIFY:
|
|
ASSERT(0);
|
|
break;
|
|
case STATE_HANDSHAKE_AS_SERVER_RECV_SERVER_FINISH:
|
|
ASSERT(0);
|
|
break;
|
|
case STATE_HANDSHAKE_AS_SERVER_SEND_SERVER_FINISH:
|
|
ASSERT(0);
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
SslPackAndSend(ss, rgBuf, SslErrorMake(pBuf, WSSA_MAX_IO_BUF, SSL_MT_ERROR), FALSE, TRUE);
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
int WSSAFNCT SslHandshakeAsServer(SECURE_SOCKET ss){
|
|
RETURN_SOCKET_ERROR(WSSA_ERROR_UNRECOVERABLE);
|
|
}
|
|
#endif
|