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.
2075 lines
52 KiB
2075 lines
52 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1995.
|
|
//
|
|
// File: context.c
|
|
//
|
|
// Contents:
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 8-08-95 RichardW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "sslsspi.h"
|
|
|
|
#define VALID_REQUEST_FLAGS ( ISC_REQ_MUTUAL_AUTH | \
|
|
ISC_REQ_REPLAY_DETECT | \
|
|
ISC_REQ_SEQUENCE_DETECT | \
|
|
ISC_REQ_CONFIDENTIALITY | \
|
|
ISC_REQ_ALLOCATE_MEMORY | \
|
|
ISC_REQ_STREAM )
|
|
|
|
|
|
#include "md5.h"
|
|
|
|
|
|
#ifdef SSL_DOMESTIC
|
|
CSystems ValidSystems[] = { { 0x00010080, 0, 16, 128 },
|
|
{ 0x00020080, 11, 5, 128 },
|
|
} ;
|
|
#else
|
|
CSystems ValidSystems[] = { { 0x00020080, 11, 5, 128} };
|
|
#endif
|
|
|
|
PSslContext ServerSaveArray[256];
|
|
PSslContext SslLRU;
|
|
CRITICAL_SECTION SessionCritSec;
|
|
DWORD SslKeepAliveTime = 100 * 1000;
|
|
|
|
|
|
#if defined(TIMERS)
|
|
#define ENABLE_TIMERS 1
|
|
|
|
NTSYSAPI
|
|
long
|
|
NTAPI
|
|
NtQueryPerformanceCounter (
|
|
OUT PLARGE_INTEGER PerformanceCounter,
|
|
OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
|
|
);
|
|
|
|
#else
|
|
#define ENABLE_TIMERS 0
|
|
#endif
|
|
|
|
|
|
#if DBG
|
|
typedef struct _DbgMapCrypto {
|
|
DWORD C;
|
|
PSTR psz;
|
|
} DbgMapCrypto;
|
|
|
|
DbgMapCrypto DbgCryptoNames[] = { {CRYPTO_RC4_128, "RC4 128"},
|
|
{CRYPTO_RC4_40, "RC4 128 - 40"},
|
|
{CRYPTO_RC2_128, "RC2 128"},
|
|
{CRYPTO_RC2_40, "RC2 128 - 40"},
|
|
{CRYPTO_IDEA_128, "IDEA 128"},
|
|
{CRYPTO_NULL, "Null"},
|
|
{CRYPTO_DES_64, "DES 64"},
|
|
{CRYPTO_3DES_192, "3DES 192"}
|
|
};
|
|
|
|
PSTR DbgAlgNames[] = { "Basic RSA", "RSA with MD2", "RSA with MD5", "RC4 stream"};
|
|
#define AlgName(x) ((x < sizeof(DbgAlgNames) / sizeof(PSTR)) ? DbgAlgNames[x] : "Unknown")
|
|
|
|
PSTR
|
|
DbgGetNameOfCrypto(DWORD x)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sizeof(DbgCryptoNames) / sizeof(DbgMapCrypto) ; i++ )
|
|
{
|
|
if (x == DbgCryptoNames[i].C)
|
|
{
|
|
return(DbgCryptoNames[i].psz);
|
|
}
|
|
}
|
|
|
|
return("Unknown");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// These next few functions handle the "dead" session list. SSL provides
|
|
// for storing sessions for up to n (usually 100) seconds after they are
|
|
// closed, since the HTTP protocol is designed to do quite a few opens and
|
|
// closes.
|
|
//
|
|
|
|
BOOL
|
|
SslInitializeSessions( VOID )
|
|
{
|
|
ZeroMemory( ServerSaveArray, sizeof( ServerSaveArray ) );
|
|
InitializeCriticalSection( &SessionCritSec );
|
|
return( TRUE );
|
|
}
|
|
|
|
VOID
|
|
SslUnlinkContext( PSslContext pContext )
|
|
{
|
|
if (pContext->pPrev)
|
|
{
|
|
pContext->pPrev->pNext = pContext->pNext;
|
|
}
|
|
|
|
if (pContext->pNext)
|
|
{
|
|
pContext->pNext->pPrev = pContext->pPrev;
|
|
}
|
|
|
|
pContext->pNext = NULL;
|
|
pContext->pPrev = NULL;
|
|
|
|
if (pContext->pHashPrev)
|
|
{
|
|
pContext->pHashPrev->pNext = pContext->pHashNext;
|
|
}
|
|
else
|
|
{
|
|
ServerSaveArray[pContext->ServerSessionId.bSessionId[0]] = pContext->pHashNext;
|
|
}
|
|
|
|
if (pContext->pHashNext)
|
|
{
|
|
pContext->pHashNext->pHashPrev = pContext->pHashPrev;
|
|
}
|
|
|
|
pContext->pHashNext = NULL;
|
|
pContext->pHashPrev = NULL;
|
|
}
|
|
|
|
VOID
|
|
SslPurgeOldEntries( VOID )
|
|
{
|
|
PSslContext pContext;
|
|
PSslContext pNext;
|
|
DWORD Now;
|
|
|
|
Now = GetTickCount();
|
|
|
|
pContext = SslLRU;
|
|
while (pContext && (pContext->ExpiryTime < Now) )
|
|
{
|
|
pNext = pContext->pNext;
|
|
|
|
SslUnlinkContext( pContext );
|
|
|
|
SslDeleteContext( pContext );
|
|
|
|
DebugLog((DEB_TRACE_CACHE, "Deleted expired entry\n"));
|
|
|
|
pContext = pNext;
|
|
}
|
|
|
|
SslLRU = pContext;
|
|
|
|
}
|
|
|
|
BOOL
|
|
SslCacheSession(PSslContext pContext )
|
|
{
|
|
DWORD index;
|
|
|
|
pContext->ExpiryTime = GetTickCount() + SslKeepAliveTime;
|
|
|
|
DebugLog((DEB_TRACE_CACHE, "Saving session for %d seconds\n",
|
|
SslKeepAliveTime / 1000));
|
|
|
|
EnterCriticalSection( &SessionCritSec );
|
|
|
|
SslPurgeOldEntries();
|
|
|
|
if (SslLRU)
|
|
{
|
|
SslLRU->pPrev = pContext;
|
|
}
|
|
|
|
pContext->pNext = SslLRU;
|
|
|
|
SslLRU = pContext;
|
|
|
|
index = pContext->ServerSessionId.bSessionId[0];
|
|
|
|
if (ServerSaveArray[index])
|
|
{
|
|
ServerSaveArray[index]->pHashPrev = pContext;
|
|
}
|
|
|
|
pContext->pHashNext = ServerSaveArray[index];
|
|
|
|
ServerSaveArray[index] = pContext;
|
|
|
|
LeaveCriticalSection( &SessionCritSec );
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
PSslContext
|
|
SslFindSession(PSslSessionId pId)
|
|
{
|
|
DWORD index;
|
|
PSslContext pContext;
|
|
|
|
index = pId->bSessionId[0];
|
|
|
|
EnterCriticalSection( &SessionCritSec );
|
|
|
|
SslPurgeOldEntries();
|
|
|
|
pContext = ServerSaveArray[index];
|
|
|
|
while (pContext)
|
|
{
|
|
if (memcmp(pContext->ServerSessionId.bSessionId,
|
|
pId->bSessionId,
|
|
pId->cbSessionId) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pContext = pContext->pHashNext;
|
|
|
|
}
|
|
|
|
if (pContext)
|
|
{
|
|
SslUnlinkContext( pContext );
|
|
}
|
|
|
|
LeaveCriticalSection( &SessionCritSec );
|
|
|
|
return( pContext );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MakeSessionKeys
|
|
//
|
|
// Synopsis: Makes the session keys for the context...
|
|
//
|
|
// Arguments: [pContext] --
|
|
// [Bits] --
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 10-10-95 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
MakeSessionKeys(
|
|
PSslContext pContext,
|
|
DWORD Bits)
|
|
{
|
|
MD5_CTX KeyMaterial0;
|
|
MD5_CTX KeyMaterial1;
|
|
|
|
UCHAR Buffer[MASTER_KEY_SIZE + 1 +
|
|
SSL_MAX_CHALLENGE_LEN + SSL_MAX_CONNECTION_ID_LEN];
|
|
|
|
DWORD BufferLen;
|
|
|
|
|
|
if (Bits != 128)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
CopyMemory(Buffer, pContext->MasterKey, 16 );
|
|
|
|
BufferLen = 16;
|
|
|
|
Buffer[BufferLen++] = '0';
|
|
|
|
CopyMemory(&Buffer[17], pContext->Challenge, pContext->ChallengeLength );
|
|
|
|
BufferLen += pContext->ChallengeLength ;
|
|
|
|
CopyMemory( &Buffer[BufferLen],
|
|
pContext->ServerSessionId.bSessionId,
|
|
pContext->ServerSessionId.cbSessionId );
|
|
|
|
BufferLen += pContext->ServerSessionId.cbSessionId ;
|
|
|
|
MD5Init( &KeyMaterial0 );
|
|
|
|
MD5Update( &KeyMaterial0, Buffer, BufferLen );
|
|
|
|
MD5Final( &KeyMaterial0 );
|
|
|
|
Buffer[16] = '1';
|
|
|
|
MD5Init( &KeyMaterial1 );
|
|
|
|
MD5Update( &KeyMaterial1, Buffer, BufferLen );
|
|
|
|
MD5Final( &KeyMaterial1 );
|
|
|
|
pContext->KeySize = 16;
|
|
|
|
if (pContext->Flags & CONTEXT_FLAG_OUTBOUND)
|
|
{
|
|
CopyMemory(pContext->ReadKey, KeyMaterial0.digest, CONTEXT_KEY_SIZE);
|
|
CopyMemory(pContext->WriteKey, KeyMaterial1.digest, CONTEXT_KEY_SIZE);
|
|
}
|
|
else
|
|
{
|
|
CopyMemory(pContext->ReadKey, KeyMaterial1.digest, CONTEXT_KEY_SIZE);
|
|
CopyMemory(pContext->WriteKey, KeyMaterial0.digest, CONTEXT_KEY_SIZE);
|
|
}
|
|
|
|
if (pContext->pSystem->Initialize( pContext->ReadKey,
|
|
CONTEXT_KEY_SIZE,
|
|
&pContext->pReadState ) )
|
|
{
|
|
if (pContext->pSystem->Initialize( pContext->WriteKey,
|
|
CONTEXT_KEY_SIZE,
|
|
&pContext->pWriteState) )
|
|
{
|
|
if (pContext->pCheck->Initialize( 0, &pContext->pReadBuffer))
|
|
{
|
|
pContext->pCheck->Sum( pContext->pReadBuffer,
|
|
pContext->KeySize,
|
|
pContext->ReadKey);
|
|
|
|
if (pContext->pCheck->Initialize( 0, &pContext->pWriteBuffer))
|
|
{
|
|
pContext->pCheck->Sum( pContext->pWriteBuffer,
|
|
pContext->KeySize,
|
|
pContext->WriteKey );
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
pContext->pCheck->Finish( &pContext->pReadBuffer );
|
|
|
|
}
|
|
|
|
pContext->pSystem->Discard( &pContext->pWriteState );
|
|
|
|
}
|
|
|
|
pContext->pSystem->Discard( &pContext->pReadState );
|
|
|
|
}
|
|
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
PSTR
|
|
CopyString(
|
|
PSTR pszString)
|
|
{
|
|
PSTR pszNewString;
|
|
DWORD cchString;
|
|
|
|
cchString = strlen(pszString) + 1;
|
|
|
|
pszNewString = SslAlloc('ypoC', LMEM_FIXED, cchString);
|
|
|
|
if (pszNewString)
|
|
{
|
|
CopyMemory(pszNewString, pszString, cchString);
|
|
}
|
|
|
|
return(pszNewString);
|
|
}
|
|
|
|
|
|
PSslContext
|
|
SslCreateContext(
|
|
VOID)
|
|
{
|
|
|
|
PSslContext pContext;
|
|
|
|
pContext = SslAlloc('txtC', LMEM_FIXED | LMEM_ZEROINIT, sizeof(SslContext));
|
|
if (pContext)
|
|
{
|
|
pContext->Magic = SSL_CONTEXT_MAGIC;
|
|
}
|
|
|
|
return(pContext);
|
|
}
|
|
|
|
VOID
|
|
SslDeleteContext(
|
|
PSslContext pContext)
|
|
{
|
|
if (pContext->pszName)
|
|
{
|
|
SslFree(pContext->pszName);
|
|
}
|
|
|
|
if ( pContext->pCred )
|
|
{
|
|
SslDereferenceCredential( pContext->pCred );
|
|
}
|
|
|
|
if ( pContext->pSystem )
|
|
{
|
|
|
|
if ( pContext->pReadState )
|
|
{
|
|
pContext->pSystem->Discard( &pContext->pReadState );
|
|
}
|
|
|
|
if ( pContext->pWriteState )
|
|
{
|
|
pContext->pSystem->Discard( &pContext->pWriteState );
|
|
}
|
|
}
|
|
|
|
if ( pContext->pCheck )
|
|
{
|
|
|
|
if ( pContext->pReadBuffer )
|
|
{
|
|
pContext->pCheck->Finish( &pContext->pReadBuffer );
|
|
}
|
|
|
|
if ( pContext->pWriteBuffer )
|
|
{
|
|
pContext->pCheck->Finish( &pContext->pWriteBuffer );
|
|
}
|
|
}
|
|
|
|
if ( pContext->pCertificate )
|
|
{
|
|
SslFree( pContext->pCertificate );
|
|
}
|
|
|
|
ZeroMemory( pContext, sizeof( SslContext ) );
|
|
|
|
SslFree( pContext );
|
|
|
|
}
|
|
|
|
PSslContext
|
|
SslpValidateContextHandle(
|
|
PCtxtHandle phContext)
|
|
{
|
|
PSslContext pContext = NULL;
|
|
BOOL fReturn;
|
|
|
|
fReturn = FALSE;
|
|
if (phContext)
|
|
{
|
|
try
|
|
{
|
|
pContext = (PSslContext) phContext->dwUpper;
|
|
if (pContext->Magic == SSL_CONTEXT_MAGIC)
|
|
{
|
|
fReturn = TRUE;
|
|
}
|
|
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
pContext = NULL;
|
|
}
|
|
}
|
|
|
|
if (fReturn)
|
|
{
|
|
return(pContext);
|
|
}
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECURITY_STATUS
|
|
SslHandleServerHello(
|
|
PSslContext pContext,
|
|
PSecBufferDesc pInput,
|
|
PCtxtHandle phNewContext,
|
|
PSecBufferDesc pOutput,
|
|
unsigned long SEC_FAR * pfContextAttr,
|
|
PTimeStamp ptsExpiry
|
|
)
|
|
{
|
|
SECURITY_STATUS scRet;
|
|
PServer_Hello pHello;
|
|
Client_Master_Key Key;
|
|
DWORD i;
|
|
BOOL fPack;
|
|
PSecBuffer pBuffer;
|
|
PX509Certificate pCertificate;
|
|
PUCHAR pPortionToEncrypt;
|
|
DWORD BytesToEncrypt;
|
|
UCHAR Temp[16];
|
|
|
|
|
|
if (!pOutput)
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
if (pOutput->cBuffers < 1)
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
if (!UnpackServerHello(TRUE,
|
|
(PSsl_Server_Hello) pInput->pBuffers[0].pvBuffer,
|
|
pInput->pBuffers[0].cbBuffer,
|
|
&pHello))
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
#if DBG
|
|
DebugLog((DEB_TRACE, "Hello = %x\n", pHello));
|
|
DebugLog((DEB_TRACE, " Session ID hit \t%s\n", pHello->SessionIdHit ? "Yes" : "No"));
|
|
DebugLog((DEB_TRACE, " Certificate Type\t%d\n", pHello->CertificateType));
|
|
DebugLog((DEB_TRACE, " Certificate Len\t%d\n", pHello->CertificateLength));
|
|
DebugLog((DEB_TRACE, " cCipherSpecs \t%d\n", pHello->cCipherSpecs));
|
|
DebugLog((DEB_TRACE, " ConnectionId \t%d\n", pHello->Connection.cbSessionId));
|
|
for (i = 0 ; i < pHello->cCipherSpecs ; i++ )
|
|
{
|
|
DebugLog((DEB_TRACE, " Cipher[%i] = %06x (%s)\n", i, pHello->pCipherSpecs[i],
|
|
DbgGetNameOfCrypto(pHello->pCipherSpecs[i]) ));
|
|
}
|
|
#endif
|
|
|
|
switch (pHello->CertificateType)
|
|
{
|
|
case SSL_CT_X509_CERTIFICATE:
|
|
if (!CrackCertificate( pHello->pCertificate,
|
|
pHello->CertificateLength,
|
|
TRUE,
|
|
&pCertificate))
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return( SEC_E_INVALID_TOKEN ); // BUGBUG: Generate error msg
|
|
}
|
|
|
|
|
|
pContext->pCertificate = pCertificate;
|
|
|
|
#if DBG
|
|
DebugLog((DEB_TRACE, "Certificate = %x\n", pCertificate));
|
|
DebugLog((DEB_TRACE, " Version = %d\n", pCertificate->Version));
|
|
DebugLog((DEB_TRACE, " Serial Number = %d : %d\n",
|
|
pCertificate->SerialNumber[0], pCertificate->SerialNumber[1]));
|
|
DebugLog((DEB_TRACE, " AlgId = %d, %s\n", pCertificate->SignatureAlgorithm,
|
|
AlgName(pCertificate->SignatureAlgorithm)));
|
|
DebugLog((DEB_TRACE, " Issuer = %s\n", pCertificate->pszIssuer));
|
|
DebugLog((DEB_TRACE, " Subject = %s\n", pCertificate->pszSubject));
|
|
DebugLog((DEB_TRACE, " Public Key\n"));
|
|
DebugLog((DEB_TRACE, " keylen = %d\n", pCertificate->pPublicKey->keylen));
|
|
DebugLog((DEB_TRACE, " bitlen = %d\n", pCertificate->pPublicKey->bitlen));
|
|
DebugLog((DEB_TRACE, " datalen = %d\n", pCertificate->pPublicKey->datalen));
|
|
DebugLog((DEB_TRACE, " exponent = %d\n", pCertificate->pPublicKey->pubexp));
|
|
#endif
|
|
|
|
|
|
// BUGBUG: Need to correctly select alg and key
|
|
|
|
SslGenerateRandomBits(pContext->MasterKey, 16);
|
|
|
|
pContext->ServerSessionId = pHello->Connection;
|
|
|
|
pContext->pSystem = &csRC4;
|
|
pContext->pCheck = &ckMD5;
|
|
|
|
scRet = MakeSessionKeys(pContext, 16 * 8 );
|
|
|
|
Key.CipherKind = pHello->pCipherSpecs[0];
|
|
|
|
if (Key.CipherKind == CRYPTO_RC4_40)
|
|
{
|
|
Key.ClearKeyLen = 11;
|
|
CopyMemory(Key.ClearKey, pContext->MasterKey, 11);
|
|
|
|
pPortionToEncrypt = &pContext->MasterKey[11];
|
|
|
|
BytesToEncrypt = 5;
|
|
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
Key.ClearKeyLen = 0;
|
|
|
|
pPortionToEncrypt = pContext->MasterKey;
|
|
|
|
BytesToEncrypt = 16;
|
|
|
|
}
|
|
|
|
Key.KeyArgLen = 0;
|
|
|
|
pBuffer = pOutput->pBuffers;
|
|
|
|
if (pContext->ContextAttr & ISC_REQ_ALLOCATE_MEMORY)
|
|
{
|
|
pBuffer->pvBuffer = NULL;
|
|
pBuffer->cbBuffer = 0;
|
|
|
|
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
|
|
|
|
}
|
|
|
|
//
|
|
// ok, we have the master key. Now, encrypt it with the
|
|
// public key we got from our friends on the net...
|
|
//
|
|
|
|
Key.EncryptedKeyLen = ENCRYPTED_KEY_SIZE;
|
|
|
|
if (!PkcsPublicEncrypt( pPortionToEncrypt,
|
|
BytesToEncrypt,
|
|
pCertificate->pPublicKey,
|
|
NETWORK_ORDER,
|
|
Key.EncryptedKey,
|
|
&Key.EncryptedKeyLen))
|
|
{
|
|
//
|
|
// Clean Up
|
|
//
|
|
|
|
return( SEC_E_NO_AUTHENTICATING_AUTHORITY );
|
|
}
|
|
|
|
|
|
|
|
fPack = PackClientMasterKey(&Key,
|
|
(PSsl_Client_Master_Key *) &pBuffer->pvBuffer,
|
|
&pBuffer->cbBuffer);
|
|
|
|
pContext->WriteCounter++;
|
|
|
|
pBuffer->BufferType = SECBUFFER_TOKEN;
|
|
|
|
SslFree( pHello );
|
|
|
|
|
|
if (fPack)
|
|
{
|
|
pContext->Flags |= CONTEXT_FLAG_KEY;
|
|
|
|
return(SEC_I_CONTINUE_NEEDED);
|
|
}
|
|
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
SECURITY_STATUS
|
|
SslHandleServerVerify(
|
|
PSslContext pContext,
|
|
PSecBufferDesc pInput,
|
|
PCtxtHandle phNewContext,
|
|
PSecBufferDesc pOutput,
|
|
unsigned long SEC_FAR * pfContextAttr,
|
|
PTimeStamp ptsExpiry
|
|
)
|
|
{
|
|
|
|
SECURITY_STATUS scRet;
|
|
CtxtHandle hContext;
|
|
SecBuffer Buffers[3];
|
|
SecBufferDesc Desc;
|
|
PSsl_Server_Verify pVerify;
|
|
DWORD HeaderSize;
|
|
PSecBuffer pBuffer;
|
|
PSsl_Client_Finished pFinish;
|
|
|
|
*pfContextAttr = 0;
|
|
|
|
hContext.dwUpper = (DWORD) pContext;
|
|
hContext.dwLower = 0;
|
|
|
|
pContext->ReadCounter = 1;
|
|
pContext->WriteCounter = 2;
|
|
|
|
Desc.pBuffers = Buffers;
|
|
Desc.ulVersion = SECBUFFER_VERSION;
|
|
Desc.cBuffers = 3;
|
|
|
|
Buffers[0] = pInput->pBuffers[0];
|
|
Buffers[0].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
|
Buffers[2].BufferType = SECBUFFER_EMPTY;
|
|
|
|
scRet = SslUnsealMessage( &hContext,
|
|
&Desc,
|
|
0,
|
|
NULL );
|
|
|
|
if (FAILED( scRet ) )
|
|
{
|
|
return(scRet);
|
|
}
|
|
|
|
if (Buffers[1].BufferType != SECBUFFER_DATA)
|
|
{
|
|
return( SEC_E_INTERNAL_ERROR );
|
|
}
|
|
|
|
pVerify = Buffers[1].pvBuffer;
|
|
|
|
if ((pVerify->MessageId != SSL_MT_SERVER_VERIFY) ||
|
|
(Buffers[1].cbBuffer > sizeof(Ssl_Server_Verify) ) )
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
if (memcmp( pVerify->ChallengeData,
|
|
pContext->Challenge,
|
|
pContext->ChallengeLength) )
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
//
|
|
// Ok, the server checked out. Now, generate the reply message:
|
|
//
|
|
|
|
HeaderSize = (pContext->Flags & CONTEXT_FLAG_BLOCK) ?
|
|
sizeof(Ssl_Message_Header_Ex) :
|
|
sizeof(Ssl_Message_Header);
|
|
|
|
Buffers[0].BufferType = SECBUFFER_TOKEN;
|
|
Buffers[0].cbBuffer = HeaderSize;
|
|
Buffers[1].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].cbBuffer = sizeof(Ssl_Client_Finished);
|
|
|
|
pBuffer = pOutput->pBuffers;
|
|
|
|
if (pContext->ContextAttr & ISC_REQ_ALLOCATE_MEMORY)
|
|
{
|
|
pBuffer->cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer;
|
|
|
|
pBuffer->pvBuffer = SslExternalAlloc( pBuffer->cbBuffer );
|
|
|
|
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
|
|
|
|
if (!pBuffer->pvBuffer)
|
|
{
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (pBuffer->cbBuffer < Buffers[0].cbBuffer + Buffers[1].cbBuffer)
|
|
{
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
else
|
|
{
|
|
pBuffer->cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer;
|
|
}
|
|
}
|
|
|
|
Buffers[0].pvBuffer = pBuffer->pvBuffer;
|
|
Buffers[1].pvBuffer = (PUCHAR) Buffers[0].pvBuffer + Buffers[0].cbBuffer;
|
|
Desc.cBuffers = 2;
|
|
|
|
pFinish = (PSsl_Client_Finished) Buffers[1].pvBuffer;
|
|
pFinish->MessageId = SSL_MT_CLIENT_FINISHED_V2;
|
|
|
|
CopyMemory( pFinish->ConnectionId,
|
|
pContext->ServerSessionId.bSessionId,
|
|
pContext->ServerSessionId.cbSessionId );
|
|
|
|
|
|
scRet = SslSealMessage( &hContext,
|
|
0,
|
|
&Desc,
|
|
0 );
|
|
|
|
if (FAILED( scRet ) )
|
|
{
|
|
return( scRet );
|
|
}
|
|
|
|
pContext->Flags |= CONTEXT_FLAG_VERIFY;
|
|
|
|
*pfContextAttr |= ISC_RET_INTERMEDIATE_RETURN;
|
|
|
|
return( SEC_I_CONTINUE_NEEDED );
|
|
|
|
}
|
|
|
|
|
|
SECURITY_STATUS
|
|
SslHandleServerFinish(
|
|
PSslContext pContext,
|
|
PSecBufferDesc pInput,
|
|
PCtxtHandle phNewContext,
|
|
PSecBufferDesc pOutput,
|
|
unsigned long SEC_FAR * pfContextAttr,
|
|
PTimeStamp ptsExpiry
|
|
)
|
|
{
|
|
|
|
SECURITY_STATUS scRet;
|
|
CtxtHandle hContext;
|
|
SecBuffer Buffers[3];
|
|
SecBufferDesc Desc;
|
|
PSsl_Server_Finished pFinished;
|
|
DWORD HeaderSize;
|
|
PSecBuffer pBuffer;
|
|
|
|
hContext.dwUpper = (DWORD) pContext;
|
|
hContext.dwLower = 0;
|
|
|
|
Desc.pBuffers = Buffers;
|
|
Desc.ulVersion = SECBUFFER_VERSION;
|
|
Desc.cBuffers = 3;
|
|
|
|
Buffers[0] = pInput->pBuffers[0];
|
|
Buffers[0].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
|
Buffers[2].BufferType = SECBUFFER_EMPTY;
|
|
|
|
scRet = SslUnsealMessage( &hContext,
|
|
&Desc,
|
|
0,
|
|
NULL );
|
|
|
|
if (FAILED( scRet ) )
|
|
{
|
|
return(scRet);
|
|
}
|
|
|
|
if (Buffers[1].BufferType != SECBUFFER_DATA)
|
|
{
|
|
return( SEC_E_INTERNAL_ERROR );
|
|
}
|
|
|
|
pFinished = Buffers[1].pvBuffer;
|
|
if (pFinished->MessageId != SSL_MT_SERVER_FINISHED_V2)
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
CopyMemory( pContext->ServerSessionId.bSessionId,
|
|
pFinished->SessionId,
|
|
Buffers[1].cbBuffer - 1);
|
|
|
|
*pfContextAttr = ISC_RET_CONFIDENTIALITY | ISC_RET_STREAM ;
|
|
|
|
return( SEC_E_OK );
|
|
|
|
}
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslInitializeSecurityContextW(
|
|
PCredHandle phCredential, // Cred to base context
|
|
PCtxtHandle phContext, // Existing context (OPT)
|
|
SEC_WCHAR SEC_FAR * pszTargetName, // Name of target
|
|
unsigned long fContextReq, // Context Requirements
|
|
unsigned long Reserved1, // Reserved, MBZ
|
|
unsigned long TargetDataRep, // Data rep of target
|
|
PSecBufferDesc pInput, // Input Buffers
|
|
unsigned long Reserved2, // Reserved, MBZ
|
|
PCtxtHandle phNewContext, // (out) New Context handle
|
|
PSecBufferDesc pOutput, // (inout) Output Buffers
|
|
unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
|
|
PTimeStamp ptsExpiry // (out) Life span (OPT)
|
|
)
|
|
{
|
|
PCHAR pszAnsiTarget;
|
|
DWORD cchTarget;
|
|
SECURITY_STATUS scRet;
|
|
|
|
if (pszTargetName)
|
|
{
|
|
cchTarget = wcslen(pszTargetName) + 1;
|
|
pszAnsiTarget = SslAlloc('isnA', LMEM_FIXED, cchTarget * 2);
|
|
if (!pszAnsiTarget)
|
|
{
|
|
return(SEC_E_INSUFFICIENT_MEMORY);
|
|
}
|
|
|
|
WideCharToMultiByte(
|
|
CP_ACP, 0,
|
|
pszTargetName, cchTarget,
|
|
pszAnsiTarget, cchTarget * 2,
|
|
NULL, NULL );
|
|
|
|
}
|
|
else
|
|
{
|
|
pszAnsiTarget = NULL;
|
|
}
|
|
|
|
scRet = SslInitializeSecurityContextA(
|
|
phCredential,
|
|
phContext,
|
|
pszAnsiTarget,
|
|
fContextReq,
|
|
Reserved1,
|
|
TargetDataRep,
|
|
pInput,
|
|
Reserved2,
|
|
phNewContext,
|
|
pOutput,
|
|
pfContextAttr,
|
|
ptsExpiry );
|
|
|
|
if (pszAnsiTarget)
|
|
{
|
|
SslFree(pszAnsiTarget);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslInitializeSecurityContextA(
|
|
PCredHandle phCredential, // Cred to base context
|
|
PCtxtHandle phContext, // Existing context (OPT)
|
|
SEC_CHAR SEC_FAR * pszTargetName, // Name of target
|
|
unsigned long fContextReq, // Context Requirements
|
|
unsigned long Reserved1, // Reserved, MBZ
|
|
unsigned long TargetDataRep, // Data rep of target
|
|
PSecBufferDesc pInput, // Input Buffers
|
|
unsigned long Reserved2, // Reserved, MBZ
|
|
PCtxtHandle phNewContext, // (out) New Context handle
|
|
PSecBufferDesc pOutput, // (inout) Output Buffers
|
|
unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
|
|
PTimeStamp ptsExpiry // (out) Life span (OPT)
|
|
)
|
|
{
|
|
PSslContext pContext;
|
|
PSslCredential pCred;
|
|
Client_Hello HelloMessage;
|
|
PSecBuffer pBuffer;
|
|
BOOL fPack;
|
|
|
|
if (Reserved1 || Reserved2)
|
|
{
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
}
|
|
|
|
if (!CryptoOk)
|
|
{
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
}
|
|
|
|
pContext = SslpValidateContextHandle(phContext);
|
|
if (pContext)
|
|
{
|
|
if (pContext->Flags & CONTEXT_FLAG_VERIFY)
|
|
{
|
|
return SslHandleServerFinish(
|
|
pContext,
|
|
pInput,
|
|
phNewContext,
|
|
pOutput,
|
|
pfContextAttr,
|
|
ptsExpiry );
|
|
}
|
|
|
|
if (pContext->Flags & CONTEXT_FLAG_KEY)
|
|
{
|
|
return SslHandleServerVerify(
|
|
pContext,
|
|
pInput,
|
|
phNewContext,
|
|
pOutput,
|
|
pfContextAttr,
|
|
ptsExpiry );
|
|
}
|
|
|
|
return SslHandleServerHello(
|
|
pContext,
|
|
pInput,
|
|
phNewContext,
|
|
pOutput,
|
|
pfContextAttr,
|
|
ptsExpiry);
|
|
}
|
|
|
|
pCred = SslpValidateCredentialHandle(phCredential);
|
|
|
|
if (!pCred)
|
|
{
|
|
return(SEC_E_INVALID_HANDLE);
|
|
}
|
|
|
|
if (!pOutput)
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
if (pOutput->cBuffers < 1)
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
fContextReq &= VALID_REQUEST_FLAGS;
|
|
|
|
HelloMessage.pCipherSpecs = SslAvailableCiphers;
|
|
HelloMessage.cCipherSpecs = SslNumberAvailableCiphers;
|
|
HelloMessage.SessionId.cbSessionId = 0;
|
|
|
|
|
|
pBuffer = pOutput->pBuffers;
|
|
|
|
if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
|
|
{
|
|
pBuffer->pvBuffer = NULL;
|
|
pBuffer->cbBuffer = 0;
|
|
|
|
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
|
|
|
|
}
|
|
|
|
pContext = SslCreateContext();
|
|
|
|
if (!pContext)
|
|
{
|
|
return(SEC_E_INSUFFICIENT_MEMORY);
|
|
}
|
|
|
|
if (pszTargetName)
|
|
{
|
|
pContext->pszName = CopyString(pszTargetName);
|
|
}
|
|
|
|
pContext->ContextAttr = fContextReq;
|
|
pContext->pCred = pCred;
|
|
pContext->Flags = CONTEXT_FLAG_OUTBOUND;
|
|
SslReferenceCredential(pCred);
|
|
|
|
pContext->ChallengeLength = 16; // BUGBUG
|
|
|
|
SslGenerateRandomBits(
|
|
pContext->Challenge,
|
|
pContext->ChallengeLength );
|
|
|
|
HelloMessage.Challenge.cbChallenge = pContext->ChallengeLength;
|
|
|
|
RtlCopyMemory( HelloMessage.Challenge.bChallenge,
|
|
pContext->Challenge,
|
|
HelloMessage.Challenge.cbChallenge );
|
|
|
|
fPack = PackClientHello(&HelloMessage,
|
|
(PSsl_Client_Hello *) &pBuffer->pvBuffer,
|
|
&pBuffer->cbBuffer);
|
|
|
|
pBuffer->BufferType = SECBUFFER_TOKEN;
|
|
|
|
if (fPack)
|
|
{
|
|
phNewContext->dwUpper = (DWORD) pContext;
|
|
|
|
return(SEC_I_CONTINUE_NEEDED);
|
|
}
|
|
|
|
SslDeleteContext( pContext );
|
|
|
|
return(SEC_E_INSUFFICIENT_MEMORY);
|
|
}
|
|
|
|
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslHandleClientMasterKey(
|
|
PSslContext pContext,
|
|
PSecBufferDesc pInput,
|
|
PCtxtHandle phNewContext,
|
|
PSecBufferDesc pOutput,
|
|
unsigned long SEC_FAR * pfContextAttr,
|
|
PTimeStamp ptsExpiry
|
|
)
|
|
{
|
|
SECURITY_STATUS scRet;
|
|
PClient_Master_Key pMasterKey;
|
|
PCSystems pSystem;
|
|
DWORD i;
|
|
DWORD EncryptedLen;
|
|
|
|
BOOL fPack;
|
|
PSecBuffer pBuffer;
|
|
CtxtHandle hContext;
|
|
SecBuffer Buffers[3];
|
|
SecBufferDesc Desc;
|
|
DWORD HeaderSize;
|
|
PSsl_Server_Verify pVerify;
|
|
|
|
|
|
if (!pOutput)
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
if (pOutput->cBuffers < 1)
|
|
{
|
|
return(SEC_E_INVALID_TOKEN);
|
|
}
|
|
|
|
if (!UnpackClientMasterKey(
|
|
(PSsl_Client_Master_Key) pInput->pBuffers[0].pvBuffer,
|
|
pInput->pBuffers[0].cbBuffer,
|
|
&pMasterKey))
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
//
|
|
// Validate Client Response
|
|
//
|
|
|
|
pSystem = NULL;
|
|
|
|
for (i = 0; i < sizeof(ValidSystems) / sizeof(CSystems) ; i++ )
|
|
{
|
|
if (ValidSystems[i].CipherSpec == pMasterKey->CipherKind)
|
|
{
|
|
pSystem = &ValidSystems[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! pSystem)
|
|
{
|
|
SslFree( pMasterKey );
|
|
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
DebugLog((DEB_TRACE, "Selected system %#x: clear = %d, cipher=%d, bits=%d\n",
|
|
pSystem->CipherSpec, pSystem->ClearKeyLen,
|
|
pSystem->EncryptedKeyLen, pSystem->Bits ));
|
|
|
|
if (pMasterKey->ClearKeyLen != pSystem->ClearKeyLen)
|
|
{
|
|
SslFree( pMasterKey );
|
|
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
pContext->pCSystem = pSystem;
|
|
|
|
CopyMemory( pContext->MasterKey,
|
|
pMasterKey->ClearKey,
|
|
pMasterKey->ClearKeyLen);
|
|
|
|
|
|
if (!PkcsPrivateDecrypt(pMasterKey->EncryptedKey,
|
|
pMasterKey->EncryptedKeyLen,
|
|
pContext->pCred->pPrivateKey,
|
|
NETWORK_ORDER,
|
|
&pContext->MasterKey[pMasterKey->ClearKeyLen],
|
|
&EncryptedLen ) )
|
|
{
|
|
SslFree( pMasterKey );
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
SslFree( pMasterKey );
|
|
|
|
if (EncryptedLen != pSystem->EncryptedKeyLen)
|
|
{
|
|
DebugLog((DEB_TRACE, "Invalid Encrypted Key Length: %#x\n", EncryptedLen));
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
pContext->pSystem = &csRC4;
|
|
pContext->pCheck = &ckMD5;
|
|
|
|
MakeSessionKeys( pContext, pSystem->Bits );
|
|
|
|
|
|
hContext.dwUpper = (DWORD) pContext;
|
|
hContext.dwLower = 0;
|
|
|
|
pContext->WriteCounter = 1;
|
|
pContext->ReadCounter = 2;
|
|
|
|
|
|
Desc.pBuffers = Buffers;
|
|
Desc.ulVersion = SECBUFFER_VERSION;
|
|
Desc.cBuffers = 2;
|
|
|
|
HeaderSize = (pContext->Flags & CONTEXT_FLAG_BLOCK) ?
|
|
sizeof(Ssl_Message_Header_Ex) :
|
|
sizeof(Ssl_Message_Header);
|
|
|
|
Buffers[0].BufferType = SECBUFFER_TOKEN;
|
|
Buffers[0].cbBuffer = HeaderSize;
|
|
Buffers[1].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].cbBuffer = 1 + pContext->ChallengeLength;
|
|
|
|
pBuffer = pOutput->pBuffers;
|
|
|
|
if (pContext->ContextAttr & ISC_REQ_ALLOCATE_MEMORY)
|
|
{
|
|
pBuffer->cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer;
|
|
|
|
pBuffer->pvBuffer = SslExternalAlloc( pBuffer->cbBuffer );
|
|
|
|
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
|
|
|
|
if (!pBuffer->pvBuffer)
|
|
{
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (pBuffer->cbBuffer < Buffers[0].cbBuffer + Buffers[1].cbBuffer)
|
|
{
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
else
|
|
{
|
|
pBuffer->cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer;
|
|
}
|
|
}
|
|
|
|
Buffers[0].pvBuffer = pBuffer->pvBuffer;
|
|
Buffers[1].pvBuffer = (PUCHAR) Buffers[0].pvBuffer + Buffers[0].cbBuffer;
|
|
Desc.cBuffers = 2;
|
|
|
|
pVerify = Buffers[1].pvBuffer;
|
|
pVerify->MessageId = SSL_MT_SERVER_VERIFY;
|
|
|
|
CopyMemory( pVerify->ChallengeData,
|
|
pContext->Challenge,
|
|
pContext->ChallengeLength );
|
|
|
|
|
|
scRet = SslSealMessage( &hContext,
|
|
0,
|
|
&Desc,
|
|
0 );
|
|
|
|
if (FAILED( scRet ) )
|
|
{
|
|
return( scRet );
|
|
}
|
|
|
|
pContext->Flags |= CONTEXT_FLAG_VERIFY;
|
|
|
|
return( SEC_I_CONTINUE_NEEDED );
|
|
|
|
}
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslHandleClientFinished(
|
|
PSslContext pContext,
|
|
PSecBufferDesc pInput,
|
|
PCtxtHandle phNewContext,
|
|
PSecBufferDesc pOutput,
|
|
unsigned long SEC_FAR * pfContextAttr,
|
|
PTimeStamp ptsExpiry
|
|
)
|
|
{
|
|
SECURITY_STATUS scRet;
|
|
CtxtHandle hContext;
|
|
SecBuffer Buffers[3];
|
|
SecBufferDesc Desc;
|
|
PSsl_Client_Finished pFinished;
|
|
PSsl_Server_Finished pFinish;
|
|
DWORD HeaderSize;
|
|
PSecBuffer pBuffer;
|
|
|
|
*pfContextAttr = 0;
|
|
|
|
hContext.dwUpper = (DWORD) pContext;
|
|
hContext.dwLower = 0;
|
|
|
|
Desc.pBuffers = Buffers;
|
|
Desc.ulVersion = SECBUFFER_VERSION;
|
|
Desc.cBuffers = 3;
|
|
|
|
Buffers[0] = pInput->pBuffers[0];
|
|
Buffers[0].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
|
Buffers[2].BufferType = SECBUFFER_EMPTY;
|
|
|
|
scRet = SslUnsealMessage( &hContext,
|
|
&Desc,
|
|
0,
|
|
NULL );
|
|
|
|
if (FAILED( scRet ) )
|
|
{
|
|
return(scRet);
|
|
}
|
|
|
|
if (Buffers[1].BufferType != SECBUFFER_DATA)
|
|
{
|
|
return( SEC_E_INTERNAL_ERROR );
|
|
}
|
|
|
|
pFinished = Buffers[1].pvBuffer;
|
|
if (pFinished->MessageId != SSL_MT_CLIENT_FINISHED_V2)
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// had problems building using RtlEqualMemory, why?
|
|
//
|
|
|
|
if (!RtlEqualMemory(pFinished->ConnectionId,
|
|
pContext->ServerSessionId.bSessionId,
|
|
pContext->ServerSessionId.cbSessionId))
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
#else
|
|
if ( memcmp(pFinished->ConnectionId,
|
|
pContext->ServerSessionId.bSessionId,
|
|
pContext->ServerSessionId.cbSessionId))
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
#endif
|
|
|
|
hContext.dwUpper = (DWORD) pContext;
|
|
hContext.dwLower = 0;
|
|
|
|
Desc.pBuffers = Buffers;
|
|
Desc.ulVersion = SECBUFFER_VERSION;
|
|
Desc.cBuffers = 2;
|
|
|
|
HeaderSize = (pContext->Flags & CONTEXT_FLAG_BLOCK) ?
|
|
sizeof(Ssl_Message_Header_Ex) :
|
|
sizeof(Ssl_Message_Header);
|
|
|
|
Buffers[0].BufferType = SECBUFFER_TOKEN;
|
|
Buffers[0].cbBuffer = HeaderSize;
|
|
Buffers[1].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].cbBuffer = sizeof(Ssl_Server_Finished);
|
|
|
|
pBuffer = pOutput->pBuffers;
|
|
|
|
if (pContext->ContextAttr & ASC_REQ_ALLOCATE_MEMORY)
|
|
{
|
|
pBuffer->cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer;
|
|
|
|
pBuffer->pvBuffer = SslExternalAlloc( pBuffer->cbBuffer );
|
|
|
|
*pfContextAttr = ASC_RET_ALLOCATED_MEMORY;
|
|
|
|
if (!pBuffer->pvBuffer)
|
|
{
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (pBuffer->cbBuffer < Buffers[0].cbBuffer + Buffers[1].cbBuffer)
|
|
{
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
else
|
|
{
|
|
pBuffer->cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer;
|
|
}
|
|
}
|
|
|
|
Buffers[0].pvBuffer = pBuffer->pvBuffer;
|
|
Buffers[1].pvBuffer = (PUCHAR) Buffers[0].pvBuffer + Buffers[0].cbBuffer;
|
|
Desc.cBuffers = 2;
|
|
|
|
pFinish = Buffers[1].pvBuffer;
|
|
pFinish->MessageId = SSL_MT_SERVER_FINISHED_V2;
|
|
|
|
CopyMemory( pFinish->SessionId,
|
|
pContext->ServerSessionId.bSessionId,
|
|
pContext->ServerSessionId.cbSessionId );
|
|
|
|
|
|
scRet = SslSealMessage( &hContext,
|
|
0,
|
|
&Desc,
|
|
0 );
|
|
|
|
if (FAILED( scRet ) )
|
|
{
|
|
return( scRet );
|
|
}
|
|
|
|
|
|
*pfContextAttr |= ASC_RET_CONFIDENTIALITY | ASC_RET_STREAM;
|
|
|
|
pContext->Flags |= CONTEXT_FLAG_FINISH;
|
|
|
|
|
|
return( SEC_E_OK );
|
|
|
|
}
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslAcceptSecurityContext(
|
|
PCredHandle phCredential, // Cred to base context
|
|
PCtxtHandle phContext, // Existing context (OPT)
|
|
PSecBufferDesc pInput, // Input buffer
|
|
unsigned long fContextReq, // Context Requirements
|
|
unsigned long TargetDataRep, // Target Data Rep
|
|
PCtxtHandle phNewContext, // (out) New context handle
|
|
PSecBufferDesc pOutput, // (inout) Output buffers
|
|
unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes
|
|
PTimeStamp ptsExpiry // (out) Life span (OPT)
|
|
)
|
|
{
|
|
PSslContext pContext;
|
|
PSslCredential pCred;
|
|
PSecBuffer pBuffer;
|
|
Server_Hello Reply;
|
|
PClient_Hello pHello;
|
|
BOOL fPack;
|
|
BOOL fCached;
|
|
DWORD RequiredSpace;
|
|
CtxtHandle hContext;
|
|
SecBufferDesc Desc;
|
|
SecBuffer Buffers[3];
|
|
PSsl_Server_Verify pVerify;
|
|
SECURITY_STATUS scRet;
|
|
CipherSpec CommonCiphers[16];
|
|
DWORD cCommonCiphers;
|
|
DWORD ClientCipher;
|
|
DWORD ServerCipher;
|
|
#if DBG
|
|
DWORD i;
|
|
#endif
|
|
|
|
#if ENABLE_TIMERS
|
|
LONGLONG StartTime;
|
|
LONGLONG EndTime;
|
|
LONGLONG Freq;
|
|
|
|
NtQueryPerformanceCounter((PLARGE_INTEGER) &StartTime,
|
|
(PLARGE_INTEGER) &Freq);
|
|
|
|
#endif
|
|
|
|
if (!CryptoOk)
|
|
{
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
}
|
|
|
|
pContext = SslpValidateContextHandle(phContext);
|
|
|
|
if (pContext)
|
|
{
|
|
if (pContext->Flags & CONTEXT_FLAG_VERIFY)
|
|
{
|
|
scRet = SslHandleClientFinished(
|
|
pContext,
|
|
pInput,
|
|
phNewContext,
|
|
pOutput,
|
|
pfContextAttr,
|
|
ptsExpiry );
|
|
|
|
}
|
|
else if (pContext->Flags & CONTEXT_FLAG_HELLO)
|
|
{
|
|
scRet = SslHandleClientMasterKey(
|
|
pContext,
|
|
pInput,
|
|
phNewContext,
|
|
pOutput,
|
|
pfContextAttr,
|
|
ptsExpiry );
|
|
}
|
|
else
|
|
{
|
|
scRet = SEC_E_UNSUPPORTED_FUNCTION;
|
|
}
|
|
|
|
#if ENABLE_TIMERS
|
|
NtQueryPerformanceCounter((PLARGE_INTEGER) &EndTime, NULL);
|
|
DebugLog((DEB_TRACE_PERF, "Op took %d intervals of %d\n",
|
|
(LONG)(EndTime - StartTime),
|
|
(LONG)(Freq) ));
|
|
#endif
|
|
|
|
return( scRet );
|
|
}
|
|
|
|
pCred = SslpValidateCredentialHandle(phCredential);
|
|
|
|
if (!pCred)
|
|
{
|
|
return( SEC_E_INVALID_HANDLE );
|
|
}
|
|
|
|
if (!pOutput || !pOutput)
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
if (pOutput->cBuffers < 1)
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
|
|
if (pInput->cBuffers < 1)
|
|
{
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
fContextReq &= VALID_REQUEST_FLAGS;
|
|
|
|
pBuffer = &pInput->pBuffers[0]; // BUGBUG: Find DATA
|
|
|
|
if (!UnpackClientHello( TRUE,
|
|
(PSsl_Client_Hello) pBuffer->pvBuffer,
|
|
pBuffer->cbBuffer,
|
|
&pHello ) )
|
|
{
|
|
DebugLog((DEB_TRACE, "Invalid hello message\n"));
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
#if DBG
|
|
DebugLog((DEB_TRACE, "Client Hello at %x\n", pHello));
|
|
DebugLog((DEB_TRACE, " CipherSpecs %d\n", pHello->cCipherSpecs));
|
|
for (i = 0 ; i < pHello->cCipherSpecs ; i++ )
|
|
{
|
|
DebugLog((DEB_TRACE, " Cipher[%i] = %06x (%s)\n", i, pHello->pCipherSpecs[i],
|
|
DbgGetNameOfCrypto(pHello->pCipherSpecs[i]) ));
|
|
}
|
|
DebugLog((DEB_TRACE, " ChallengeLength = %d\n", pHello->Challenge.cbChallenge));
|
|
#endif
|
|
|
|
//
|
|
// Build server side context:
|
|
//
|
|
|
|
pBuffer = pOutput->pBuffers;
|
|
|
|
if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
|
|
{
|
|
pBuffer->pvBuffer = NULL;
|
|
pBuffer->cbBuffer = 0;
|
|
|
|
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
ZeroMemory( &Reply, sizeof( Reply ) );
|
|
|
|
//
|
|
// Now, see what we can find:
|
|
//
|
|
|
|
pContext = NULL;
|
|
fCached = FALSE;
|
|
|
|
if (pHello->SessionId.cbSessionId)
|
|
{
|
|
pContext = SslFindSession( &pHello->SessionId );
|
|
|
|
//
|
|
// BUGBUG: Until we can precisely determine how session id reuse
|
|
// works (as opposed to is spec'd), this is turned off:
|
|
//
|
|
|
|
pContext = NULL;
|
|
#if 0
|
|
if (pContext)
|
|
{
|
|
|
|
DebugLog((DEB_TRACE, "Found context in cache\n"));
|
|
|
|
pContext->Flags = CONTEXT_FLAG_HELLO | CONTEXT_FLAG_VERIFY;
|
|
|
|
Reply.SessionIdHit = 1;
|
|
Reply.Connection = pContext->ServerSessionId;
|
|
|
|
pContext->ReadCounter = 0;
|
|
pContext->WriteCounter = 1;
|
|
|
|
fCached = TRUE ;
|
|
|
|
MakeSessionKeys( pContext, pContext->pCSystem->Bits );
|
|
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// Calculate common ciphers:
|
|
//
|
|
|
|
cCommonCiphers = 0;
|
|
|
|
for (ServerCipher = 0;
|
|
ServerCipher < SslNumberAvailableCiphers ;
|
|
ServerCipher++)
|
|
{
|
|
for (ClientCipher = 0;
|
|
ClientCipher < pHello->cCipherSpecs ;
|
|
ClientCipher++ )
|
|
{
|
|
if ( SslAvailableCiphers[ServerCipher] ==
|
|
pHello->pCipherSpecs[ClientCipher] )
|
|
{
|
|
CommonCiphers[cCommonCiphers++] = SslAvailableCiphers[ServerCipher];
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// if cCommonCipers == 0, then we have none in common. At this point, we
|
|
// should generate an error response, but that is for later. For now,
|
|
// we will generate an invalid_token return, and bail out.
|
|
//
|
|
|
|
if (cCommonCiphers == 0)
|
|
{
|
|
SslFree( pHello );
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
|
|
if ( !pContext )
|
|
{
|
|
pContext = SslCreateContext();
|
|
|
|
if (!pContext)
|
|
{
|
|
SslFree( pHello );
|
|
return(SEC_E_INSUFFICIENT_MEMORY);
|
|
}
|
|
|
|
|
|
pContext->ContextAttr = fContextReq;
|
|
|
|
SslReferenceCredential(pCred);
|
|
|
|
pContext->pCred = pCred;
|
|
|
|
pContext->Flags = 0;
|
|
|
|
|
|
Reply.SessionIdHit = 0;
|
|
Reply.CertificateType = SSL_CT_X509_CERTIFICATE;
|
|
Reply.CertificateLength = pCred->cbCertificate;
|
|
|
|
Reply.cCipherSpecs = cCommonCiphers;
|
|
Reply.pCipherSpecs = CommonCiphers;
|
|
|
|
Reply.Connection.cbSessionId = 16;
|
|
SslGenerateRandomBits( Reply.Connection.bSessionId,
|
|
Reply.Connection.cbSessionId );
|
|
|
|
pContext->ServerSessionId = Reply.Connection;
|
|
|
|
Reply.pCertificate = pCred->pCertificate;
|
|
}
|
|
|
|
CopyMemory( pContext->Challenge,
|
|
pHello->Challenge.bChallenge,
|
|
pHello->Challenge.cbChallenge );
|
|
|
|
pContext->ChallengeLength = pHello->Challenge.cbChallenge;
|
|
|
|
if ( fCached )
|
|
{
|
|
fPack = PackServerHello( &Reply, NULL, &RequiredSpace );
|
|
|
|
RequiredSpace += pContext->ChallengeLength + 1;
|
|
RequiredSpace += sizeof( Ssl_Message_Header ) + 3;
|
|
|
|
pBuffer->pvBuffer = SslExternalAlloc( RequiredSpace );
|
|
if (pBuffer->pvBuffer)
|
|
{
|
|
pBuffer->cbBuffer= RequiredSpace;
|
|
|
|
fPack = PackServerHello(&Reply,
|
|
(PSsl_Server_Hello *) &pBuffer->pvBuffer,
|
|
&RequiredSpace );
|
|
|
|
|
|
|
|
//
|
|
// Ugly stuff. We actually jam the next message into the buffer
|
|
// as well.
|
|
//
|
|
|
|
Buffers[0].BufferType = SECBUFFER_TOKEN;
|
|
Buffers[0].cbBuffer = sizeof( Ssl_Message_Header );
|
|
Buffers[0].pvBuffer = (PVOID) ((PUCHAR) pBuffer->pvBuffer + RequiredSpace);
|
|
Buffers[1].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].cbBuffer = 1 + pContext->ChallengeLength;
|
|
Buffers[1].pvBuffer = (PVOID) ((PUCHAR) Buffers[0].pvBuffer + Buffers[0].cbBuffer);
|
|
|
|
hContext.dwUpper = (DWORD) pContext;
|
|
hContext.dwLower = 0;
|
|
|
|
Desc.ulVersion = SECBUFFER_VERSION;
|
|
Desc.pBuffers = Buffers;
|
|
Desc.cBuffers = 2;
|
|
|
|
pVerify = Buffers[1].pvBuffer;
|
|
pVerify->MessageId = SSL_MT_SERVER_VERIFY;
|
|
|
|
CopyMemory( pVerify->ChallengeData,
|
|
pContext->Challenge,
|
|
pContext->ChallengeLength );
|
|
|
|
|
|
scRet = SslSealMessage( &hContext,
|
|
0,
|
|
&Desc,
|
|
0 );
|
|
|
|
}
|
|
else
|
|
{
|
|
fPack = FALSE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
fPack = PackServerHello(&Reply,
|
|
(PSsl_Server_Hello *) &pBuffer->pvBuffer,
|
|
&pBuffer->cbBuffer);
|
|
|
|
}
|
|
|
|
pBuffer->BufferType = SECBUFFER_TOKEN;
|
|
|
|
SslFree( pHello );
|
|
|
|
#if ENABLE_TIMERS
|
|
NtQueryPerformanceCounter((PLARGE_INTEGER) &EndTime, NULL);
|
|
DebugLog((DEB_TRACE_PERF, "Op took %d intervals of %d\n",
|
|
(LONG)(EndTime - StartTime),
|
|
(LONG)(Freq) ));
|
|
#endif
|
|
|
|
if (fPack)
|
|
{
|
|
phNewContext->dwUpper = (DWORD) pContext;
|
|
|
|
pContext->Flags |= CONTEXT_FLAG_HELLO;
|
|
|
|
return(SEC_I_CONTINUE_NEEDED);
|
|
}
|
|
|
|
SslDeleteContext( pContext );
|
|
|
|
return(SEC_E_INSUFFICIENT_MEMORY);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslDeleteSecurityContext(
|
|
PCtxtHandle phContext // Context to delete
|
|
)
|
|
{
|
|
PSslContext pContext;
|
|
|
|
|
|
pContext = SslpValidateContextHandle(phContext);
|
|
|
|
if ( !pContext )
|
|
{
|
|
return( SEC_E_INVALID_HANDLE );
|
|
}
|
|
|
|
if ((pContext->Flags & CONTEXT_FLAG_OUTBOUND) ||
|
|
((pContext->Flags & CONTEXT_FLAG_FINISH) == 0) )
|
|
{
|
|
SslDeleteContext( pContext );
|
|
return( SEC_E_OK );
|
|
}
|
|
|
|
#ifdef SESSION_RETRY_DONE
|
|
|
|
if ( (pContext->pReadState) && (pContext->pSystem) )
|
|
{
|
|
pContext->pSystem->Discard( &pContext->pReadState );
|
|
}
|
|
|
|
if ( (pContext->pWriteState) && (pContext->pSystem) )
|
|
{
|
|
pContext->pSystem->Discard( &pContext->pWriteState );
|
|
}
|
|
|
|
if ( (pContext->pReadBuffer) && (pContext->pCheck) )
|
|
{
|
|
pContext->pCheck->Finish( &pContext->pReadBuffer );
|
|
}
|
|
|
|
if ( (pContext->pWriteBuffer) && (pContext->pCheck) )
|
|
{
|
|
pContext->pCheck->Finish( &pContext->pWriteBuffer );
|
|
}
|
|
|
|
SslCacheSession( pContext );
|
|
|
|
#else
|
|
|
|
SslDeleteContext( pContext );
|
|
|
|
#endif
|
|
|
|
|
|
return( SEC_E_OK );
|
|
|
|
}
|
|
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslQueryContextAttributesA(
|
|
PCtxtHandle phContext, // Context to query
|
|
unsigned long ulAttribute, // Attribute to query
|
|
void SEC_FAR * pBuffer // Buffer for attributes
|
|
)
|
|
{
|
|
PSslContext pContext;
|
|
PSecPkgContext_Sizes pSizes;
|
|
PSecPkgContext_StreamSizes pStreamSize;
|
|
PSecPkgContext_Lifespan pLifeSpan;
|
|
PSecPkgContext_Names pNames;
|
|
PSecPkgContext_Authority pAuthority;
|
|
PSecPkgContext_KeyInfo pKeyInfo;
|
|
PX509Certificate pCert;
|
|
|
|
pContext = SslpValidateContextHandle( phContext );
|
|
|
|
if ( !pContext )
|
|
{
|
|
return( SEC_E_INVALID_HANDLE );
|
|
}
|
|
|
|
switch ( ulAttribute )
|
|
{
|
|
case SECPKG_ATTR_SIZES:
|
|
if (!pContext->pSystem)
|
|
{
|
|
return( SEC_E_INVALID_HANDLE );
|
|
}
|
|
|
|
pSizes = (PSecPkgContext_Sizes) pBuffer;
|
|
pSizes->cbMaxToken = 3 * 1024;
|
|
pSizes->cbMaxSignature = (pContext->Flags & CONTEXT_FLAG_BLOCK) ?
|
|
sizeof( Ssl_Message_Header_Ex ) :
|
|
sizeof( Ssl_Message_Header );
|
|
|
|
pSizes->cbBlockSize = pContext->pSystem->BlockSize;
|
|
pSizes->cbSecurityTrailer = pSizes->cbMaxSignature;
|
|
|
|
return( SEC_E_OK );
|
|
|
|
case SECPKG_ATTR_STREAM_SIZES:
|
|
if ( !pContext->pSystem )
|
|
{
|
|
return( SEC_E_INVALID_HANDLE );
|
|
}
|
|
|
|
pStreamSize = (PSecPkgContext_StreamSizes) pBuffer;
|
|
|
|
pStreamSize->cbHeader = (pContext->Flags & CONTEXT_FLAG_BLOCK) ?
|
|
sizeof( Ssl_Message_Header_Ex ) :
|
|
sizeof( Ssl_Message_Header );
|
|
pStreamSize->cbTrailer = 0;
|
|
pStreamSize->cbMaximumMessage = 0x3ffe;
|
|
pStreamSize->cBuffers = 4;
|
|
pStreamSize->cbBlockSize = (pContext->Flags & CONTEXT_FLAG_BLOCK) ?
|
|
8 : 1;
|
|
|
|
return( SEC_E_OK );
|
|
|
|
|
|
case SECPKG_ATTR_NAMES:
|
|
if ( !pContext->pCertificate )
|
|
{
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
}
|
|
|
|
pCert = pContext->pCertificate;
|
|
pNames = (PSecPkgContext_Names) pBuffer;
|
|
|
|
if ( pCert->pszSubject )
|
|
{
|
|
pNames->sUserName = SslExternalAlloc( strlen(pCert->pszSubject) + 1);
|
|
if (pNames->sUserName)
|
|
{
|
|
strcpy( pNames->sUserName, pCert->pszSubject );
|
|
return( SEC_E_OK );
|
|
}
|
|
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
|
|
|
|
case SECPKG_ATTR_LIFESPAN:
|
|
if ( !pContext->pCertificate )
|
|
{
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
}
|
|
|
|
pCert = pContext->pCertificate;
|
|
pLifeSpan = (PSecPkgContext_Lifespan) pBuffer;
|
|
|
|
pLifeSpan->tsStart.QuadPart = *((LONGLONG *) &pCert->ValidFrom);
|
|
pLifeSpan->tsExpiry.QuadPart = *((LONGLONG*) &pCert->ValidUntil);
|
|
return( SEC_E_OK );
|
|
|
|
case SECPKG_ATTR_DCE_INFO:
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
|
|
case SECPKG_ATTR_KEY_INFO:
|
|
if ( (!pContext->pSystem) || (!pContext->pCSystem) )
|
|
{
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
}
|
|
|
|
pKeyInfo = (PSecPkgContext_KeyInfo) pBuffer;
|
|
pKeyInfo->KeySize = pContext->pCSystem->EncryptedKeyLen * 8;
|
|
pKeyInfo->sSignatureAlgorithmName = SslExternalAlloc(
|
|
strlen( pContext->pCheck->pszName ) + 1 );
|
|
if ( pKeyInfo->sSignatureAlgorithmName)
|
|
{
|
|
strcpy( pKeyInfo->sSignatureAlgorithmName,
|
|
pContext->pCheck->pszName );
|
|
}
|
|
else
|
|
{
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
pKeyInfo->sEncryptAlgorithmName = SslExternalAlloc(
|
|
strlen( pContext->pSystem->pszName ) + 1 );
|
|
|
|
if ( pKeyInfo->sEncryptAlgorithmName )
|
|
{
|
|
strcpy( pKeyInfo->sEncryptAlgorithmName,
|
|
pContext->pSystem->pszName );
|
|
}
|
|
else
|
|
{
|
|
SslExternalFree( pKeyInfo->sSignatureAlgorithmName );
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
return( SEC_E_OK );
|
|
|
|
case SECPKG_ATTR_AUTHORITY:
|
|
if ( !pContext->pCertificate )
|
|
{
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
}
|
|
|
|
pCert = (PX509Certificate) pContext->pCertificate;
|
|
pAuthority = (PSecPkgContext_Authority) pBuffer;
|
|
|
|
if ( pCert->pszSubject )
|
|
{
|
|
pAuthority->sAuthorityName = SslExternalAlloc( strlen(pCert->pszIssuer) + 1);
|
|
if (pAuthority->sAuthorityName )
|
|
{
|
|
strcpy( pAuthority->sAuthorityName, pCert->pszIssuer );
|
|
return( SEC_E_OK );
|
|
}
|
|
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
return( SEC_E_UNSUPPORTED_FUNCTION );
|
|
|
|
|
|
|
|
default:
|
|
return( SEC_E_INVALID_TOKEN );
|
|
|
|
|
|
}
|
|
|
|
return( SEC_E_INVALID_TOKEN );
|
|
}
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslQueryContextAttributesW(
|
|
PCtxtHandle phContext, // Context to query
|
|
unsigned long ulAttribute, // Attribute to query
|
|
void SEC_FAR * pBuffer // Buffer for attributes
|
|
)
|
|
{
|
|
return( SslQueryContextAttributesA( phContext, ulAttribute, pBuffer ) );
|
|
}
|
|
|
|
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslImpersonateSecurityContext(
|
|
PCtxtHandle phContext // Context to impersonate
|
|
)
|
|
{
|
|
PSslContext pContext;
|
|
|
|
pContext = SslpValidateContextHandle( phContext );
|
|
if ( pContext )
|
|
{
|
|
return( SEC_E_NO_IMPERSONATION );
|
|
}
|
|
|
|
return( SEC_E_INVALID_HANDLE );
|
|
}
|
|
|
|
|
|
|
|
|
|
SECURITY_STATUS SEC_ENTRY
|
|
SslRevertSecurityContext(
|
|
PCtxtHandle phContext // Context from which to re
|
|
)
|
|
{
|
|
PSslContext pContext;
|
|
|
|
pContext = SslpValidateContextHandle( phContext );
|
|
if ( pContext )
|
|
{
|
|
return( SEC_E_NO_IMPERSONATION );
|
|
}
|
|
|
|
return( SEC_E_INVALID_HANDLE );
|
|
}
|
|
|