Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2187 lines
61 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: context.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 8-08-95 RichardW Created
// 8-13-95 TerenceS PCTized
//
//----------------------------------------------------------------------------
// TBD: finish client authentication.
// TBD: include another exchange system?
// TBD: add redo capability?
#include "pctsspi.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_CONNECTION )
#include "md5.h"
// supported cipher type arrays
CipherSelect PctAvailableCiphers[] = {
{ PCT_CIPHER_RC4 | PCT_ENC_BITS_128 | PCT_MAC_BITS_128, &csRC4 },
{ PCT_CIPHER_RC4 | PCT_ENC_BITS_40 | PCT_MAC_BITS_128, &csRC4 },
};
// available ciphers, in order of preference
CipherSpec PctCipherRank[] = {
PCT_CIPHER_RC4 | PCT_ENC_BITS_128 | PCT_MAC_BITS_128,
PCT_CIPHER_RC4 | PCT_ENC_BITS_40 | PCT_MAC_BITS_128
};
DWORD PctNumAvailableCiphers = 2;
HashSelect PctAvailableHashes[] = {
{ PCT_HASH_MD5, &ckMD5 }
};
// available hashes, in order of preference
HashSpec PctHashRank[] = {
PCT_HASH_MD5
};
DWORD PctNumAvailableHashes = 1;
CertSpec PctAvailableCerts[] = {
PCT_CERT_X509
};
DWORD PctNumAvailableCerts = 1;
SigSpec PctAvailableSigs[] = {
PCT_SIG_NONE
};
DWORD PctNumAvailableSigs = 1;
ExchSpec PctAvailableExch[] = {
PCT_EXCH_RSA_PKCS1
};
DWORD PctNumAvailableExch = 1;
#if DBG
typedef struct _DbgMapCrypto {
DWORD C;
PSTR psz;
} DbgMapCrypto;
DbgMapCrypto DbgCryptoNames[] = { {PCT_CIPHER_RC4, "RC4 "},
};
CHAR DbgNameSpace[100];
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 & PCT_CIPHER_ALG) == DbgCryptoNames[i].C)
{
sprintf(DbgNameSpace, "%s %d / %d MACbits",
(DbgCryptoNames[i].psz),
(x & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS,
(x & PCT_CIPHER_MAC));
return DbgNameSpace;
}
}
return("Unknown");
}
#endif
// allow quick initialization of hashes
void InitHashBuf(HashBuf Buf,
PPctContext pContext
)
{
CloneHashBuf(Buf, pContext->InitMACState, pContext->pCheck);
return;
}
// session key computation
BOOL
PctComputeKey(PCheckSumBuffer *ppHash,
PPctContext pContext,
UCHAR *Buffer,
UCHAR *pConst,
DWORD dwCLen,
DWORD fFlags)
{
DWORD BufferLen;
PPctAuthContext pAuth;
#if DBG
DWORD di;
CHAR BufDispBuf[16 * 3 + 5];
#endif
pAuth = pContext->pAuthData;
BufferLen = 0;
Buffer[BufferLen] = 1;
BufferLen += 1;
if (!(fFlags & PCT_MAKE_MAC))
{
// add the first constant
CopyMemory(Buffer+BufferLen, pConst, dwCLen);
BufferLen += dwCLen;
}
// add the master key
CopyMemory(Buffer+BufferLen, pAuth->MasterKey, MASTER_KEY_SIZE );
BufferLen += MASTER_KEY_SIZE;
// repeat the constant
CopyMemory(Buffer+BufferLen, pConst, dwCLen);
BufferLen += dwCLen;
// add the connection id
CopyMemory(Buffer+BufferLen, pAuth->ConnectionId.bSessionId,
PCT_SESSION_ID_SIZE);
BufferLen += PCT_SESSION_ID_SIZE;
// repeat the constant
CopyMemory(Buffer+BufferLen, pConst, dwCLen);
BufferLen += dwCLen;
if (fFlags & PCT_USE_CERT)
{
// add in the certificate
CopyMemory(Buffer+BufferLen, (UCHAR *)pAuth->pRawCert,
pAuth->CertLen);
BufferLen += pAuth->CertLen;
// repeat the constant
CopyMemory(Buffer+BufferLen, pConst, dwCLen);
BufferLen += dwCLen;
}
// add the challenge
CopyMemory(Buffer+BufferLen, (UCHAR *)pAuth->Challenge,
PCT_CHALLENGE_SIZE);
BufferLen += PCT_CHALLENGE_SIZE;
// again, repeat the constant
CopyMemory(Buffer+BufferLen, pConst, dwCLen);
BufferLen += dwCLen;
#if DBG
#if DBG_WATCH_KEYS
DebugLog((DEB_TRACE, "Buffer:\n"));
BufDispBuf[0] = 0;
for(di=0;di<BufferLen;di++)
{
sprintf(BufDispBuf+((di % 16)*3), "%2.2x ", Buffer[di]);
if ((di & 15) == 15)
{
DebugLog((DEB_TRACE, "\t%s\n", BufDispBuf));
BufDispBuf[0] = 0;
}
}
DebugLog((DEB_TRACE, "\t%s\n", BufDispBuf));
#endif
#endif
// hash the buffer
pContext->pCheck->Sum( *ppHash, BufferLen, Buffer );
return TRUE;
}
BOOL
PctMakeSessionKeys(PPctContext pContext, UCHAR *pClearKey, DWORD dwClearLen)
{
PCheckSumBuffer CWriteHash, SWriteHash, CMacHash, SMacHash;
PCheckSumBuffer CExportKey, SExportKey;
PPctAuthContext pAuth;
UCHAR pWriteKey[MASTER_KEY_SIZE], pReadKey[MASTER_KEY_SIZE];
UCHAR Buffer[DERIVATION_BUFFER_SIZE];
DWORD BufferLen, MaxBufferLen, dwKeyLen;
HashBuf CWriteHB, SWriteHB, CMACHB, SMACHB, CExpHB, SExpHB;
#if DBG
DWORD i;
CHAR KeyDispBuf[MASTER_KEY_SIZE*2+1];
#endif
if (!pContext->InitMACState)
return FALSE;
InitHashBuf(CWriteHB, pContext);
InitHashBuf(SWriteHB, pContext);
InitHashBuf(CMACHB, pContext);
InitHashBuf(SMACHB, pContext);
InitHashBuf(CExpHB, pContext);
InitHashBuf(SExpHB, pContext);
CWriteHash = (PCheckSumBuffer)CWriteHB;
SWriteHash = (PCheckSumBuffer)SWriteHB;
CMacHash = (PCheckSumBuffer)CMACHB;
SMacHash = (PCheckSumBuffer)SMACHB;
CExportKey = (PCheckSumBuffer)CExpHB;
SExportKey = (PCheckSumBuffer)SExpHB;
pAuth = pContext->pAuthData;
#if DBG
DebugLog((DEB_TRACE, "Making session keys\n", KeyDispBuf));
for(i=0;i<MASTER_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x",
pAuth->ConnectionId.bSessionId[i]);
DebugLog((DEB_TRACE, " ConnId\t%s\n", KeyDispBuf));
for(i=0;i<MASTER_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x",
((UCHAR *)(pAuth->pRawCert))[i]);
DebugLog((DEB_TRACE, " Cert\t%s\n", KeyDispBuf));
for(i=0;i<MASTER_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x", (UCHAR *)pAuth->Challenge[i]);
DebugLog((DEB_TRACE, " Challenge \t%s\n", KeyDispBuf));
#endif
MaxBufferLen = 1 + // initial number
PCT_MAX_NUM_SEP * PCT_MAX_SEP_LEN +
MASTER_KEY_SIZE +
PCT_SESSION_ID_SIZE +
PCT_SESSION_ID_SIZE +
pAuth->CertLen;
if (MaxBufferLen > DERIVATION_BUFFER_SIZE)
return FALSE;
// compute the ClientWriteKey
PctComputeKey( &CWriteHash, pContext, Buffer, PCT_CONST_CWK,
PCT_CONST_CWK_LEN, PCT_USE_CERT);
// compute the ServerWriteKey
PctComputeKey( &SWriteHash, pContext, Buffer, PCT_CONST_SWK,
PCT_CONST_SWK_LEN, 0);
// compute the ClientMacKey
PctComputeKey( &CMacHash, pContext, Buffer, PCT_CONST_CMK,
PCT_CONST_CMK_LEN, PCT_USE_CERT | PCT_MAKE_MAC);
// compute the ServerMacKey
PctComputeKey( &SMacHash, pContext, Buffer, PCT_CONST_SMK,
PCT_CONST_SMK_LEN, PCT_MAKE_MAC);
// find bit strength of cipher
dwKeyLen = (pAuth->CipherType & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS;
// convert to bytes
dwKeyLen = dwKeyLen / 8;
if (dwKeyLen < (pContext->pCheck->cbCheckSum))
{
// chop the encryption keys down to selected length
#if DBG
DebugLog((DEB_TRACE, "Chopping down client write keys\n", KeyDispBuf));
#endif
// compute the standard length clientwritekey
BufferLen = 0;
Buffer[BufferLen] = 1;
BufferLen += 1;
CopyMemory(Buffer+BufferLen, "sl", 2);
BufferLen += 2;
pContext->pCheck->Finalize( CWriteHash, Buffer+BufferLen );
BufferLen += dwKeyLen;
CopyMemory(Buffer+BufferLen, "sl", 2);
BufferLen += 2;
CopyMemory(Buffer+BufferLen, pClearKey, dwClearLen);
BufferLen += dwClearLen;
InitHashBuf(CWriteHB, pContext);
pContext->pCheck->Sum( CWriteHash, BufferLen, Buffer );
// compute the standard length serverwritekey
#if DBG
DebugLog((DEB_TRACE, "Chopping down server write keys\n", KeyDispBuf));
#endif
BufferLen = 0;
Buffer[BufferLen] = 1;
BufferLen += 1;
CopyMemory(Buffer+BufferLen, PCT_CONST_SLK, PCT_CONST_SLK_LEN);
BufferLen += PCT_CONST_SLK_LEN;
pContext->pCheck->Finalize( SWriteHash, Buffer+BufferLen );
BufferLen += dwKeyLen;
CopyMemory(Buffer+BufferLen, PCT_CONST_SLK, PCT_CONST_SLK_LEN);
BufferLen += PCT_CONST_SLK_LEN;
CopyMemory(Buffer+BufferLen, pClearKey, dwClearLen);
BufferLen += dwClearLen;
InitHashBuf(SWriteHB, pContext);
pContext->pCheck->Sum( SWriteHash, BufferLen, Buffer );
}
pContext->KeySize = MASTER_KEY_SIZE;
if (pContext->Flags & CONTEXT_FLAG_OUTBOUND)
{
pContext->pCheck->Finalize( SWriteHash, pReadKey );
pContext->pCheck->Finalize( CWriteHash, pWriteKey );
pContext->pCheck->Finalize( SMacHash, pContext->ReadMACKey );
pContext->pCheck->Finalize( CMacHash, pContext->WriteMACKey );
}
else
{
pContext->pCheck->Finalize( SWriteHash, pWriteKey );
pContext->pCheck->Finalize( CWriteHash, pReadKey );
pContext->pCheck->Finalize( SMacHash, pContext->WriteMACKey );
pContext->pCheck->Finalize( CMacHash, pContext->ReadMACKey );
}
pContext->pCheck->Initialize(0, &(pContext->ReadMACState));
pContext->pCheck->Sum( pContext->ReadMACState, pContext->KeySize,
pContext->ReadMACKey);
pContext->pCheck->Initialize(0, &(pContext->WriteMACState));
pContext->pCheck->Sum( pContext->WriteMACState, pContext->KeySize,
pContext->WriteMACKey);
#if DBG
for(i=0;i<MASTER_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x", pAuth->MasterKey[i]);
DebugLog((DEB_TRACE, " MasterKey \t%s\n", KeyDispBuf));
for(i=0;i<CONTEXT_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x", pReadKey[i]);
DebugLog((DEB_TRACE, " ReadKey\t%s\n", KeyDispBuf));
for(i=0;i<CONTEXT_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x", pContext->ReadMACKey[i]);
DebugLog((DEB_TRACE, " MACKey\t%s\n", KeyDispBuf));
for(i=0;i<CONTEXT_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x", pWriteKey[i]);
DebugLog((DEB_TRACE, " WriteKey\t%s\n", KeyDispBuf));
for(i=0;i<CONTEXT_KEY_SIZE;i++)
sprintf(KeyDispBuf+(i*2), "%2.2x", pContext->WriteMACKey[i]);
DebugLog((DEB_TRACE, " MACKey\t%s\n", KeyDispBuf));
#endif
if (pContext->pSystem->Initialize( pReadKey,
CONTEXT_KEY_SIZE,
&pContext->pReadState ) )
{
if (pContext->pSystem->Initialize( pWriteKey,
CONTEXT_KEY_SIZE,
&pContext->pWriteState) )
{
return (TRUE);
}
pContext->pSystem->Discard( &pContext->pReadState );
}
return( FALSE );
}
PSTR
CopyString(
PSTR pszString)
{
PSTR pszNewString;
DWORD cchString;
cchString = strlen(pszString) + 1;
pszNewString = LocalAlloc(LMEM_FIXED, cchString);
if (pszNewString)
{
CopyMemory(pszNewString, pszString, cchString);
}
return(pszNewString);
}
PPctContext
PctCreateContext(
VOID)
{
PPctContext pContext;
pContext = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(PctContext));
if (pContext)
{
if ((pContext->pAuthData = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
sizeof(PctAuthContext)))
== NULL)
{
LocalFree(pContext);
return NULL;
}
}
pContext->Magic = PCT_CONTEXT_MAGIC;
pContext->Flags = 0;
return(pContext);
}
VOID
PctDeleteAuthContext(
PPctAuthContext pAuth)
{
if (pAuth)
{
if (pAuth->pCred)
PctDereferenceCredential( pAuth->pCred );
if (pAuth->pCertificate)
LocalFree( pAuth->pCertificate );
if (pAuth->pClHello)
LocalFree( pAuth->pClHello );
ZeroMemory( pAuth, sizeof(PctAuthContext) );
LocalFree(pAuth);
}
}
VOID
PctDeleteContext(PPctContext pContext)
{
if (pContext->pszName)
LocalFree(pContext->pszName);
if (pContext->ReadMACState)
pContext->pCheck->Finish( &(pContext->ReadMACState) );
if (pContext->WriteMACState)
pContext->pCheck->Finish( &(pContext->WriteMACState) );
if (pContext->InitMACState)
pContext->pCheck->Finish( &(pContext->InitMACState) );
if (pContext->pReadState)
pContext->pSystem->Discard( &pContext->pReadState );
if (pContext->pWriteState)
pContext->pSystem->Discard( &pContext->pWriteState );
PctDeleteAuthContext( pContext->pAuthData );
ZeroMemory( pContext, sizeof( PctContext ) );
LocalFree( pContext );
}
PPctContext
PctpValidateContextHandle(PCtxtHandle phContext)
{
PPctContext pContext = NULL;
BOOL fReturn;
fReturn = FALSE;
if (phContext)
{
try
{
pContext = (PPctContext) phContext->dwUpper;
if (pContext->Magic == PCT_CONTEXT_MAGIC)
{
fReturn = TRUE;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
pContext = NULL;
}
}
if (fReturn)
{
return(pContext);
}
return(NULL);
}
SECURITY_STATUS
PctHandleServerHello(
PPctContext pContext,
PSecBufferDesc pInput,
PCtxtHandle phNewContext,
PSecBufferDesc pOutput,
unsigned long SEC_FAR * pfContextAttr,
PTimeStamp ptsExpiry
)
{
SECURITY_STATUS scRet;
PPctAuthContext pAuth;
PServer_Hello pHello;
PCheckSumBuffer pSum, pSubSum;
Client_Master_Key Key;
PctError XmitError;
ExportKeyInfo *pExportKey;
DWORD i, dwKeyLen, ExchOK, dwError;
BOOL fPack;
PSecBuffer pBuffer;
PX509Certificate pCertificate;
PUCHAR pPortionToEncrypt;
DWORD BytesToEncrypt;
UCHAR Temp[16];
UCHAR TmpDigest[RESPONSE_SIZE];
HashBuf SumBuf, SubSumBuf;
// validate the buffer configuration
if ((!pOutput) ||
(pOutput->cBuffers < 1) ||
(pInput->pBuffers[0].cbBuffer < sizeof(Pct_Record_Header)))
{
return(SEC_E_INVALID_TOKEN);
}
// make sure we have the needed authentication data area
if (pContext->pAuthData == NULL)
{
return (SEC_E_INVALID_TOKEN);
}
pAuth = pContext->pAuthData;
pBuffer = pOutput->pBuffers;
if (pContext->ContextAttr & ISC_REQ_ALLOCATE_MEMORY)
{
pBuffer->pvBuffer = NULL;
pBuffer->cbBuffer = 0;
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
}
if (!UnpackServerHello(TRUE,
&dwError,
(PPct_Server_Hello) pInput->pBuffers[0].pvBuffer,
pInput->pBuffers[0].cbBuffer,
&pHello))
{
if (dwError)
{
// process error from client here....
}
return(SEC_E_INVALID_TOKEN);
}
#if DBG
DebugLog((DEB_TRACE, "Hello = %x\n", pHello));
DebugLog((DEB_TRACE, " Restart\t%s\n", pHello->RestartOk ? "Yes":"No"));
DebugLog((DEB_TRACE, " ClientAuth\t%s\n",
pHello->ClientAuthReq ? "Yes":"No"));
DebugLog((DEB_TRACE, " Certificate Type\t%x\n", pHello->SrvCertSpec));
DebugLog((DEB_TRACE, " Hash Type\t%x\n", pHello->SrvHashSpec));
DebugLog((DEB_TRACE, " Cipher Type\t%x (%s)\n", pHello->SrvCipherSpec,
DbgGetNameOfCrypto(pHello->SrvCipherSpec)));
DebugLog((DEB_TRACE, " Certificate Len\t%x\n", pHello->CertificateLen));
#endif
pAuth->CipherType = pHello->SrvCipherSpec;
pAuth->HashType = pHello->SrvHashSpec;
pAuth->ExchType = pHello->SrvExchSpec;
pAuth->ConnectionId = pHello->Connection;
ExchOK = 0;
for(i=0;i<PctNumAvailableExch;i++)
if (PctAvailableExch[i] == pHello->SrvExchSpec)
{
// init the system
ExchOK = 1;
}
if (!ExchOK)
{
PctExternalFree(pHello);
return (SEC_E_INVALID_TOKEN);
}
pContext->pSystem = NULL;
for(i=0;i<PctNumAvailableCiphers;i++)
if (PctAvailableCiphers[i].Spec == pHello->SrvCipherSpec)
pContext->pSystem = PctAvailableCiphers[i].System;
if (pContext->pSystem == NULL)
{
PctExternalFree(pHello);
return (SEC_E_INVALID_TOKEN);
}
pContext->pCheck = NULL;
for(i=0;i<PctNumAvailableHashes;i++)
if (PctAvailableHashes[i].Spec == pHello->SrvHashSpec)
{
pContext->pCheck = PctAvailableHashes[i].System;
pContext->pCheck->Initialize(0, &(pContext->InitMACState));
}
if (pContext->pCheck == NULL)
{
PctExternalFree(pHello);
return (SEC_E_INVALID_TOKEN);
}
// let's resurrect the zombie session
if (pHello->RestartOk)
{
// if there's no zombie, the message is wrong. We can't restart.
if ((pAuth->ZombieJuju) == PCT_DEAD_ZOMBIE)
{
PctExternalFree(pHello);
return (SEC_E_INVALID_TOKEN);
}
// get all the needed goo from the zombie data
pAuth->SessionId = pAuth->ZombieSession.Session;
CopyMemory(pAuth->MasterKey, pAuth->ZombieSession.MasterKey,
MASTER_KEY_SIZE);
memcpy(pAuth->pRawCert, pAuth->ZombieSession.CertData,
pAuth->ZombieSession.SessCertLen);
pAuth->CertLen = pAuth->ZombieSession.SessCertLen;
if ((pAuth->ZombieSession.SessCiphSpec & PCT_CIPHER_STRENGTH) <
PCT_ENC_BITS_128)
{
pExportKey = pAuth->ZombieSession.ClearData;
scRet = PctMakeSessionKeys(pContext,
pExportKey->ClearKey,
pExportKey->dwClearLen);
}
else
{
scRet = PctMakeSessionKeys(pContext, NULL, 0);
}
// let's check the response in the message
// check the length
if (pHello->ResponseLen != pContext->pCheck->cbCheckSum)
return (SEC_E_INVALID_TOKEN);
// calculate the correct response
// set up the two hashes
InitHashBuf(SubSumBuf, pContext);
CloneHashBuf(SumBuf, pContext->ReadMACState, pContext->pCheck);
pSum = (PCheckSumBuffer)SumBuf;
pSubSum = (PCheckSumBuffer)SubSumBuf;
// create the server response
pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP);
pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE, pAuth->Challenge);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pAuth->ConnectionId.bSessionId);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pAuth->SessionId.bSessionId);
pContext->pCheck->Finalize(pSubSum, TmpDigest);
pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum,
TmpDigest);
pContext->pCheck->Finalize(pSum, TmpDigest);
// check it against the response in the message
if (memcmp(TmpDigest, pHello->Response, pHello->ResponseLen))
{
PctExternalFree(pHello);
XmitError.Error = PCT_ERR_SERVER_AUTH_FAILED;
XmitError.ErrInfoLen = 0;
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
if (!fPack)
{
return (SEC_E_INVALID_TOKEN);
}
else
{
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_INVALID_TOKEN;
return (SEC_I_CONTINUE_NEEDED);
}
}
// do we need to authenticate ourselves?
if (pHello->ClientAuthReq)
{
// add the verify prelude
InitHashBuf(SumBuf, pContext);
pSum = (PCheckSumBuffer)SumBuf;
pContext->pCheck->Sum(pSum, MASTER_KEY_SIZE,
pContext->WriteMACKey);
pContext->pCheck->Finalize(pAuth->pVerifyPrelude,
Key.VerifyPrelude);
pContext->pCheck->Finish(&(pAuth->pVerifyPrelude));
pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum,
Key.VerifyPrelude);
pContext->pCheck->Finalize(pSum, Key.VerifyPrelude);
Key.VerifyPreludeLen = pContext->pCheck->cbCheckSum;
Key.ResponseLen = PCT_SIGNATURE_SIZE;
PctExternalFree(pHello);
if (!PctSign(pContext, Key.VerifyPrelude, Key.VerifyPreludeLen,
Key.Response, &Key.ResponseLen))
{
return (SEC_E_INVALID_TOKEN);
}
return (SEC_I_CONTINUE_NEEDED);
}
// ok, we're done, so let's jettison the auth data
pContext->pAuthData = NULL;
PctDeleteAuthContext(pAuth);
PctExternalFree(pHello);
pContext->ReadCounter = 1;
pContext->WriteCounter = 1;
// fini.
return (SEC_E_OK);
}
// we aren't restarting, so let's continue with the protocol.
CopyMemory(pAuth->pRawCert, pHello->pCertificate,
pHello->CertificateLen);
pAuth->CertLen = pHello->CertificateLen;
switch(pHello->SrvCertSpec)
{
case PCT_CERT_X509:
if (!CrackCertificate(pHello->pCertificate, pHello->CertificateLen,
&pCertificate))
{
XmitError.Error = PCT_ERR_BAD_CERTIFICATE;
XmitError.ErrInfoLen = 0;
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
PctExternalFree(pHello);
if (!fPack)
{
return (SEC_E_INVALID_TOKEN);
}
else
{
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_INVALID_TOKEN;
return (SEC_I_CONTINUE_NEEDED);
}
}
break;
default:
XmitError.Error = PCT_ERR_BAD_CERTIFICATE;
XmitError.ErrInfoLen = 0;
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
PctExternalFree(pHello);
if (!fPack)
{
return (SEC_E_INVALID_TOKEN);
}
else
{
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_INVALID_TOKEN;
return (SEC_I_CONTINUE_NEEDED);
}
}
pAuth->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
GenerateRandomBits(pAuth->MasterKey, MASTER_KEY_SIZE);
// take care of generating the needed clearkey if we are not
// using a full strength cipher.
dwKeyLen = ((pAuth->CipherType & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS) / 8;
if (dwKeyLen < MASTER_KEY_SIZE)
{
pAuth->dwClearLen = MASTER_KEY_SIZE - dwKeyLen;
GenerateRandomBits(pAuth->ClearKey, pAuth->dwClearLen);
}
else
{
pAuth->dwClearLen = 0;
}
pContext->pCheck->Initialize(0, &(pAuth->pVerifyPrelude));
pContext->pCheck->Sum(pAuth->pVerifyPrelude, PCT_CONST_VP_LEN,
PCT_CONST_VP);
pContext->pCheck->Sum(pAuth->pVerifyPrelude, pAuth->cbClHello,
pAuth->pClHello);
pContext->pCheck->Sum(pAuth->pVerifyPrelude,
pInput->pBuffers[0].cbBuffer,
pInput->pBuffers[0].pvBuffer);
// don't need the verify goo anymore
LocalFree(pAuth->pClHello);
pAuth->pClHello = NULL;
// don't need the server hello data anymore
PctExternalFree(pHello);
scRet = PctMakeSessionKeys(pContext, pAuth->ClearKey, pAuth->dwClearLen);
// prepare the clientmasterkey message
Key.ClientCertLen = 0;
// add the verify prelude
InitHashBuf(SumBuf, pContext);
pSum = (PCheckSumBuffer)SumBuf;
pContext->pCheck->Sum(pSum, MASTER_KEY_SIZE, pContext->WriteMACKey);
pContext->pCheck->Finalize(pAuth->pVerifyPrelude, Key.VerifyPrelude);
pContext->pCheck->Finish(&pAuth->pVerifyPrelude);
pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum,
Key.VerifyPrelude);
pContext->pCheck->Finalize(pSum, Key.VerifyPrelude);
Key.VerifyPreludeLen = pContext->pCheck->cbCheckSum;
Key.ClearKeyLen = pAuth->dwClearLen;
if (pAuth->dwClearLen)
CopyMemory(Key.ClearKey, pAuth->MasterKey, pAuth->dwClearLen);
Key.KeyArgLen = 0;
//
// 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( pAuth->MasterKey,
MASTER_KEY_SIZE,
pCertificate->pPublicKey,
NETWORK_ORDER,
Key.EncryptedKey,
&Key.EncryptedKeyLen))
{
//
// Clean Up
//
XmitError.Error = PCT_ERR_BAD_CERTIFICATE;
XmitError.ErrInfoLen = 0;
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
if (!fPack)
{
return (SEC_E_NO_AUTHENTICATING_AUTHORITY);
}
else
{
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_NO_AUTHENTICATING_AUTHORITY;
return (SEC_I_CONTINUE_NEEDED);
}
}
Key.ResponseLen = 0;
fPack = PackClientMasterKey(&Key,
(PPct_Client_Master_Key *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
pContext->WriteCounter++;
pBuffer->BufferType = SECBUFFER_TOKEN;
if (fPack)
{
pContext->Flags |= CONTEXT_FLAG_KEY;
return(SEC_I_CONTINUE_NEEDED);
}
return(SEC_E_INVALID_TOKEN);
}
SECURITY_STATUS
PctHandleServerVerify(
PPctContext pContext,
PSecBufferDesc pInput,
PCtxtHandle phNewContext,
PSecBufferDesc pOutput,
unsigned long SEC_FAR * pfContextAttr,
PTimeStamp ptsExpiry
)
{
SECURITY_STATUS scRet;
PPctAuthContext pAuth;
PCheckSumBuffer pSum, pSubSum;
PServer_Verify pVerify;
DWORD HeaderSize, dwError;
PSecBuffer pBuffer;
SEC_CHAR *Target;
SessCacheItem CachedSession;
HashBuf SumBuf, SubSumBuf;
pContext->ReadCounter = 2;
pContext->WriteCounter = 2;
// add to the verify prelude context, skipping the header.
if (pInput->pBuffers[0].cbBuffer < sizeof(Pct_Record_Header))
{
return (SEC_E_INVALID_TOKEN);
}
// insure that we have the authentication data around
if (pContext->pAuthData == NULL)
{
return (SEC_E_INVALID_TOKEN);
}
pAuth = pContext->pAuthData;
// unpack the message
if (!UnpackServerVerify(TRUE,
&dwError,
(PPct_Server_Verify) pInput->pBuffers[0].pvBuffer,
pInput->pBuffers[0].cbBuffer,
&pVerify))
{
if (dwError)
{
// process client error return here.
}
return(SEC_E_INVALID_TOKEN);
}
// construct the response
CloneHashBuf(SumBuf, pContext->ReadMACState, pContext->pCheck);
InitHashBuf(SubSumBuf, pContext);
pSum = (PCheckSumBuffer)SumBuf;
pSubSum = (PCheckSumBuffer)SubSumBuf;
pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP);
pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE, pAuth->Challenge);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pAuth->ConnectionId.bSessionId);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pVerify->SessionIdData);
// we don't need the challenge anymore, so finish the compare hash
// out into the challenge data memory.
pContext->pCheck->Finalize(pSubSum, pAuth->Challenge);
pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum,
pAuth->Challenge);
pContext->pCheck->Finalize(pSum, pAuth->Challenge);
if ((pVerify->ResponseLen != pContext->pCheck->cbCheckSum) ||
(memcmp(pVerify->Response, pAuth->Challenge, pVerify->ResponseLen)))
{
PctExternalFree(pVerify);
return( SEC_E_INVALID_TOKEN );
}
CopyMemory(pAuth->SessionId.bSessionId, pVerify->SessionIdData,
PCT_SESSION_ID_SIZE);
// done with the verify data
PctExternalFree(pVerify);
if ((pContext->pszName) &&
((Target = PctExternalAlloc(strlen(pContext->pszName)+1)) == NULL))
{
// we can't cache, but we can still function....
return (SEC_E_OK);
}
strcpy(Target, pContext->pszName);
CachedSession.TargetName = Target;
CachedSession.Session = pAuth->SessionId;
CachedSession.SessCiphSpec = pAuth->CipherType;
CachedSession.SessHashSpec = pAuth->HashType;
CachedSession.SessExchSpec = pAuth->ExchType;
CachedSession.SessCertLen = pAuth->CertLen;
CopyMemory(CachedSession.MasterKey, pAuth->MasterKey, MASTER_KEY_SIZE);
CopyMemory(CachedSession.CertData, pAuth->pRawCert, pAuth->CertLen);
if (!PctAddToCache(&CachedSession))
{
// again, no error return because we can still work with cache
// failures...
PctExternalFree(Target);
}
return( SEC_E_OK );
}
SECURITY_STATUS SEC_ENTRY
PctInitializeSecurityContextW(
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 = LocalAlloc(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 = PctInitializeSecurityContextA(phCredential,
phContext,
pszAnsiTarget,
fContextReq,
Reserved1,
TargetDataRep,
pInput,
Reserved2,
phNewContext,
pOutput,
pfContextAttr,
ptsExpiry );
if (pszAnsiTarget)
{
LocalFree(pszAnsiTarget);
}
}
SECURITY_STATUS SEC_ENTRY
PctInitializeSecurityContextA(
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)
)
{
PPctContext pContext;
PPctAuthContext pAuth;
PPctCredential pCred;
Client_Hello HelloMessage;
PSecBuffer pBuffer;
BOOL fPack;
SessCacheItem CachedSession;
if (Reserved2)
{
return(SEC_E_UNSUPPORTED_FUNCTION);
}
pContext = PctpValidateContextHandle(phContext);
if (pContext)
{
if (pContext->Flags & CONTEXT_FLAG_ERRMODE)
return pContext->Error;
if (pContext->Flags & CONTEXT_FLAG_KEY)
{
return PctHandleServerVerify(
pContext,
pInput,
phNewContext,
pOutput,
pfContextAttr,
ptsExpiry);
}
return PctHandleServerHello(
pContext,
pInput,
phNewContext,
pOutput,
pfContextAttr,
ptsExpiry);
}
pCred = PctpValidateCredentialHandle(phCredential);
if (!pCred)
{
return(SEC_E_INVALID_HANDLE);
}
if ((!pOutput) ||
(pOutput->cBuffers < 1))
{
return(SEC_E_INVALID_TOKEN);
}
fContextReq &= VALID_REQUEST_FLAGS;
pBuffer = pOutput->pBuffers;
if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
{
pBuffer->pvBuffer = NULL;
pBuffer->cbBuffer = 0;
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
}
pContext = PctCreateContext();
if (!pContext)
{
return(SEC_E_INSUFFICIENT_MEMORY);
}
if (pszTargetName)
{
pContext->pszName = CopyString(pszTargetName);
}
pContext->ContextAttr = fContextReq;
pAuth = pContext->pAuthData;
pAuth->pCred = pCred;
pContext->Flags = CONTEXT_FLAG_OUTBOUND;
PctReferenceCredential(pCred);
GenerateRandomBits( pAuth->Challenge, PCT_CHALLENGE_SIZE );
// Build the hello message.
HelloMessage.pKeyArg = NULL;
HelloMessage.cbKeyArgSize = 0;
HelloMessage.pCipherSpecs = PctCipherRank;
HelloMessage.cCipherSpecs = PctNumAvailableCiphers;
HelloMessage.pHashSpecs = PctHashRank;
HelloMessage.cHashSpecs = PctNumAvailableHashes;
HelloMessage.pCertSpecs = PctAvailableCerts;
HelloMessage.cCertSpecs = PctNumAvailableCerts;
HelloMessage.pExchSpecs = PctAvailableExch;
HelloMessage.cExchSpecs = PctNumAvailableExch;
// check if we can find the target in the cache.
// (defeat the cache if reserved1 is set to PCT_TEST_NOCACHE)
if ((!(Reserved1 & PCT_TEST_NOCACHE)) &&
(PctFindTargetInCache(pContext->pszName, &CachedSession)))
{
#if DBG
DebugLog((DEB_TRACE, "Attempting to restart session.\n"));
#endif
HelloMessage.SessionId = CachedSession.Session;
pAuth->ZombieSession = CachedSession;
pAuth->ZombieJuju = PCT_LIVE_ZOMBIE;
}
else
{
pAuth->ZombieJuju = PCT_DEAD_ZOMBIE;
RtlZeroMemory(HelloMessage.SessionId.bSessionId, PCT_SESSION_ID_SIZE);
}
RtlCopyMemory( HelloMessage.Challenge.bChallenge,
pAuth->Challenge,
PCT_CHALLENGE_SIZE );
fPack = PackClientHello(&HelloMessage,
(PPct_Client_Hello *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
pBuffer->BufferType = SECBUFFER_TOKEN;
if (fPack)
{
// we need to keep the actual message around, since we need to
// hash it later, and we don't know the hash algorithm to use
// yet.
if ((pAuth->pClHello = LocalAlloc(LMEM_FIXED, pBuffer->cbBuffer))
== NULL)
{
return (SEC_E_INSUFFICIENT_MEMORY);
}
CopyMemory(pAuth->pClHello, pBuffer->pvBuffer, pBuffer->cbBuffer);
pAuth->cbClHello = pBuffer->cbBuffer;
phNewContext->dwUpper = (DWORD) pContext;
return(SEC_I_CONTINUE_NEEDED);
}
PctDeleteContext( pContext );
return(SEC_E_INSUFFICIENT_MEMORY);
}
SECURITY_STATUS SEC_ENTRY
PctHandleClientMasterKey(
PPctContext pContext,
PSecBufferDesc pInput,
PCtxtHandle phNewContext,
PSecBufferDesc pOutput,
unsigned long SEC_FAR * pfContextAttr,
PTimeStamp ptsExpiry
)
{
SECURITY_STATUS scRet;
PPctAuthContext pAuth;
PClient_Master_Key pMasterKey;
PctError XmitError;
PCryptoSystem pSystem;
DWORD i, dwKeyLen, dwError;
DWORD EncryptedLen;
PCheckSumBuffer pSum, pSubSum;
HashBuf SumBuf, SubSumBuf;
BOOL fPack;
PSecBuffer pBuffer;
CtxtHandle hContext;
SecBuffer Buffers[3];
SecBufferDesc Desc;
DWORD HeaderSize;
Server_Verify Verify;
SessCacheItem CachedSession;
if ((!pOutput) ||
(pOutput->cBuffers < 1) ||
(pInput->pBuffers[0].cbBuffer < sizeof(Pct_Record_Header)))
{
return(SEC_E_INVALID_TOKEN);
}
// make sure we have the needed authentication data area
if (pContext->pAuthData == NULL)
{
return (SEC_E_INVALID_TOKEN);
}
pAuth = pContext->pAuthData;
pBuffer = pOutput->pBuffers;
if (pContext->ContextAttr & ISC_REQ_ALLOCATE_MEMORY)
{
pBuffer->pvBuffer = NULL;
pBuffer->cbBuffer = 0;
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
}
if (!UnpackClientMasterKey(&dwError,
(PPct_Client_Master_Key) pInput->pBuffers[0].pvBuffer,
pInput->pBuffers[0].cbBuffer,
&pMasterKey))
{
if (dwError)
{
// process client error here....
}
return (SEC_E_INVALID_TOKEN);
}
//
// Validate Client Response
//
EncryptedLen = MASTER_KEY_SIZE;
if ((!PkcsPrivateDecrypt(pMasterKey->EncryptedKey,
pMasterKey->EncryptedKeyLen,
pAuth->pCred->pPrivateKey,
NETWORK_ORDER,
pAuth->MasterKey,
&EncryptedLen )) ||
(EncryptedLen != MASTER_KEY_SIZE))
{
XmitError.Error = PCT_ERR_ILLEGAL_MESSAGE;
XmitError.ErrInfoLen = 0;
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
// clean up.
PctExternalFree(pMasterKey);
if (!fPack)
{
return (SEC_E_INVALID_TOKEN);
}
else
{
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_INVALID_TOKEN;
return (SEC_I_CONTINUE_NEEDED);
}
}
dwKeyLen = ((pAuth->CipherType & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS) / 8;
if ((dwKeyLen == MASTER_KEY_SIZE) && (pMasterKey->ClearKeyLen))
{
XmitError.Error = PCT_ERR_ILLEGAL_MESSAGE;
XmitError.ErrInfoLen = 0;
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
PctExternalFree(pMasterKey);
if (!fPack)
{
return (SEC_E_INVALID_TOKEN);
}
else
{
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_INVALID_TOKEN;
return (SEC_I_CONTINUE_NEEDED);
}
}
pAuth->dwClearLen = pMasterKey->ClearKeyLen;
memcpy(pAuth->ClearKey, pMasterKey->ClearKey, pAuth->dwClearLen);
PctMakeSessionKeys( pContext, pMasterKey->ClearKey,
pMasterKey->ClearKeyLen );
// don't need master key info anymore
PctExternalFree(pMasterKey);
pContext->WriteCounter = 2;
pContext->ReadCounter = 2;
GenerateRandomBits( pAuth->SessionId.bSessionId, PCT_SESSION_ID_SIZE );
CopyMemory( Verify.SessionIdData, pAuth->SessionId.bSessionId,
PCT_SESSION_ID_SIZE);
// compute the response
CloneHashBuf(SumBuf, pContext->WriteMACState, pContext->pCheck);
InitHashBuf(SubSumBuf, pContext);
pSum = (PCheckSumBuffer)SumBuf;
pSubSum = (PCheckSumBuffer)SubSumBuf;
pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP);
pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE, pAuth->Challenge);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pAuth->ConnectionId.bSessionId);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pAuth->SessionId.bSessionId);
// we don't need the challenge anymore, so finish the compare hash
// out into the challenge data memory.
pContext->pCheck->Finalize(pSubSum, pAuth->Challenge);
pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum,
pAuth->Challenge);
pContext->pCheck->Finalize(pSum, pAuth->Challenge);
CopyMemory( Verify.Response, pAuth->Challenge,
pContext->pCheck->cbCheckSum );
Verify.ResponseLen = pContext->pCheck->cbCheckSum;
fPack = PackServerVerify(&Verify,
(PPct_Server_Verify *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
pBuffer->BufferType = SECBUFFER_TOKEN;
if (fPack)
{
// let's add this to the cache.
// try to allocate the clear key data, if needed
if ((pAuth->CipherType & PCT_CIPHER_STRENGTH) < PCT_ENC_BITS_128)
{
if ((CachedSession.ClearData =
LocalAlloc(LMEM_FIXED, sizeof(ExportKeyInfo))) == NULL)
{
pContext->pAuthData = NULL;
PctDeleteAuthContext(pAuth);
// can't cache, but continue anyway.
return (SEC_E_OK);
}
memcpy(CachedSession.ClearData->ClearKey, pAuth->ClearKey,
pAuth->dwClearLen);
CachedSession.ClearData->dwClearLen = pAuth->dwClearLen;
}
else
CachedSession.ClearData = NULL;
CachedSession.Session = pAuth->SessionId;
CachedSession.SessCiphSpec = pAuth->CipherType;
CachedSession.SessHashSpec = pAuth->HashType;
CachedSession.SessExchSpec = pAuth->ExchType;
CachedSession.SessCertLen = pAuth->CertLen;
CachedSession.TargetName = NULL;
memcpy(CachedSession.CertData, pAuth->pRawCert,
pAuth->CertLen);
memcpy(CachedSession.MasterKey, pAuth->MasterKey,
MASTER_KEY_SIZE);
PctAddToCache(&CachedSession);
pContext->pAuthData = NULL;
PctDeleteAuthContext(pAuth);
return(SEC_E_OK);
}
PctDeleteContext( pContext );
return(SEC_E_INSUFFICIENT_MEMORY);
}
SECURITY_STATUS SEC_ENTRY
PctAcceptSecurityContext(
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)
)
{
PPctContext pContext;
PPctAuthContext pAuth;
PPctCredential pCred;
PSecBuffer pBuffer;
PctError XmitError;
ExportKeyInfo *pExportKey;
Server_Hello Reply;
PClient_Hello pHello;
BOOL fPack, fRealSessId;
PCheckSumBuffer pSum, pSubSum;
HashBuf SumBuf, SubSumBuf;
SessCacheItem CachedSession;
DWORD i, j, ExchOK, fMismatch, ErrorDetail;
BYTE MisData[PCT_NUM_MISMATCHES];
#if DBG
DWORD di;
#endif
pContext = PctpValidateContextHandle(phContext);
if (pContext)
{
if (pContext->Flags & CONTEXT_FLAG_ERRMODE)
{
PctDeleteContext(pContext);
return pContext->Error;
}
if (pContext->Flags & CONTEXT_FLAG_HELLO)
{
return PctHandleClientMasterKey(pContext,
pInput,
phNewContext,
pOutput,
pfContextAttr,
ptsExpiry );
}
return( SEC_E_UNSUPPORTED_FUNCTION );
}
pCred = PctpValidateCredentialHandle(phCredential);
if (!pCred)
{
return( SEC_E_INVALID_HANDLE );
}
if ((!pOutput || !pOutput) ||
(pOutput->cBuffers < 1) ||
(pInput->cBuffers < 1))
{
return( SEC_E_INVALID_TOKEN );
}
fContextReq &= VALID_REQUEST_FLAGS;
pBuffer = &pInput->pBuffers[0]; // BUGBUG: Find DATA
if (!UnpackClientHello( TRUE,
&ErrorDetail,
(PPct_Client_Hello) pBuffer->pvBuffer,
pBuffer->cbBuffer,
&pHello ) )
{
if (ErrorDetail == PCT_ERR_SSL_STYLE_MSG)
return (SEC_E_INVALID_TOKEN);
XmitError.Error = PCT_ERR_ILLEGAL_MESSAGE;
XmitError.ErrInfoLen = 0;
// build a fake context...
pBuffer = pOutput->pBuffers;
if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
{
pBuffer->pvBuffer = NULL;
pBuffer->cbBuffer = 0;
*pfContextAttr = ISC_RET_ALLOCATED_MEMORY;
}
pContext = PctCreateContext();
if (!pContext)
{
return(SEC_E_INSUFFICIENT_MEMORY);
}
pAuth = pContext->pAuthData;
pContext->ContextAttr = fContextReq;
PctReferenceCredential(pCred);
pAuth->pCred = pCred;
pContext->Flags = 0;
phNewContext->dwUpper = (DWORD) pContext;
// now build the error.
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
if (!fPack)
{
return (SEC_E_INVALID_TOKEN);
}
else
{
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_INVALID_TOKEN;
return (SEC_I_CONTINUE_NEEDED);
}
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 (di = 0 ; di < pHello->cCipherSpecs ; di++ )
{
DebugLog((DEB_TRACE, " Cipher[%di] = %06x (%s)\n", di,
pHello->pCipherSpecs[di],
DbgGetNameOfCrypto(pHello->pCipherSpecs[di]) ));
}
#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;
}
pContext = PctCreateContext();
if (!pContext)
{
return(SEC_E_INSUFFICIENT_MEMORY);
}
pAuth = pContext->pAuthData;
pContext->ContextAttr = fContextReq;
PctReferenceCredential(pCred);
pAuth->pCred = pCred;
pContext->Flags = 0;
CopyMemory(pAuth->pRawCert, pCred->pCertificate,
pCred->cbCertificate);
pAuth->CertLen = pCred->cbCertificate;
CopyMemory( pAuth->Challenge,
pHello->Challenge.bChallenge,
PCT_CHALLENGE_SIZE );
pAuth->SessionId = pHello->SessionId;
ZeroMemory( &Reply, sizeof( Reply ) );
// no matter what, we need to make a new connection id
GenerateRandomBits( Reply.Connection.bSessionId,
PCT_SESSION_ID_SIZE );
pAuth->ConnectionId = Reply.Connection;
// check if the client wants a restart.
fRealSessId = FALSE;
for(i=0;i<PCT_SESSION_ID_SIZE;i++)
if (pHello->SessionId.bSessionId[i])
{
fRealSessId = TRUE;
break;
}
if ((fRealSessId) &&
(PctFindSessIdInCache(&(pHello->SessionId), &CachedSession)))
{
Reply.RestartOk = TRUE;
pAuth->CipherType = Reply.SrvCipherSpec = CachedSession.SessCiphSpec;
pAuth->HashType = Reply.SrvHashSpec = CachedSession.SessHashSpec;
pAuth->ExchType = Reply.SrvExchSpec = CachedSession.SessExchSpec;
pAuth->CertLen = CachedSession.SessCertLen;
memcpy(pAuth->pRawCert, CachedSession.CertData, pAuth->CertLen);
// setup the context
memcpy(pAuth->MasterKey, CachedSession.MasterKey,
MASTER_KEY_SIZE);
memcpy(pAuth->Challenge, pHello->Challenge.bChallenge,
PCT_CHALLENGE_SIZE);
// now, we don't need the message anymore
PctExternalFree(pHello);
// BUGBUG - handle exchange selection
pContext->pSystem = NULL;
for(i=0;i<PctNumAvailableCiphers;i++)
if (PctAvailableCiphers[i].Spec == pAuth->CipherType)
pContext->pSystem = PctAvailableCiphers[i].System;
if (pContext->pSystem == NULL)
return (SEC_E_INVALID_TOKEN);
pContext->pCheck = NULL;
for(i=0;i<PctNumAvailableHashes;i++)
if (PctAvailableHashes[i].Spec == pAuth->HashType)
{
pContext->pCheck = PctAvailableHashes[i].System;
pContext->pCheck->Initialize(0, &pContext->InitMACState);
}
if (pContext->pCheck == NULL)
{
return (SEC_E_INVALID_TOKEN);
}
if ((pAuth->CipherType & PCT_CIPHER_STRENGTH) <
PCT_ENC_BITS_128)
{
pExportKey = CachedSession.ClearData;
PctMakeSessionKeys(pContext, pExportKey->ClearKey,
pExportKey->dwClearLen);
}
else
{
PctMakeSessionKeys(pContext, NULL, 0);
}
// calculate the response
CloneHashBuf(SumBuf, pContext->WriteMACState, pContext->pCheck);
InitHashBuf(SubSumBuf, pContext);
pSum = (PCheckSumBuffer)SumBuf;
pSubSum = (PCheckSumBuffer)SubSumBuf;
pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP);
pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE,
pAuth->Challenge);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pAuth->ConnectionId.bSessionId);
pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE,
pAuth->SessionId.bSessionId);
pContext->pCheck->Finalize(pSubSum, Reply.Response);
pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum,
Reply.Response);
pContext->pCheck->Finalize(pSum, Reply.Response);
Reply.ResponseLen = pContext->pCheck->cbCheckSum;
pContext->ReadCounter = 1;
pContext->WriteCounter = 1;
// delete the unneeded authentication data
pContext->pAuthData = NULL;
PctDeleteAuthContext(pAuth);
}
else
{
// no restart case
Reply.RestartOk = FALSE;
Reply.SrvCertSpec = PCT_CERT_X509;
Reply.CertificateLen = pCred->cbCertificate;
Reply.pCertificate = pCred->pCertificate;
fMismatch = 0;
pContext->pSystem = NULL;
for(j=0;j<pHello->cCipherSpecs;j++)
{
for(i=0;i<PctNumAvailableCiphers;i++)
if (pHello->pCipherSpecs[j] == PctAvailableCiphers[i].Spec)
{
pAuth->CipherType = PctAvailableCiphers[i].Spec;
Reply.SrvCipherSpec = PctAvailableCiphers[i].Spec;
pContext->pSystem = PctAvailableCiphers[i].System;
break;
}
if (pContext->pSystem != NULL)
break;
}
if (pContext->pSystem == NULL)
fMismatch |= PCT_IMIS_CIPHER;
pContext->pCheck = NULL;
for(j=0;j<pHello->cHashSpecs;j++)
{
for(i=0;i<PctNumAvailableHashes;i++)
if (pHello->pHashSpecs[j] == PctAvailableHashes[i].Spec)
{
pAuth->HashType = PctAvailableHashes[i].Spec;
Reply.SrvHashSpec = PctAvailableHashes[i].Spec;
pContext->pCheck = PctAvailableHashes[i].System;
pContext->pCheck->Initialize(0, &pContext->InitMACState);
break;
}
if (pContext->pCheck != NULL)
break;
}
if (pContext->pCheck == NULL)
fMismatch |= PCT_IMIS_HASH;
ExchOK = 0;
for(j=0;j<pHello->cExchSpecs;j++)
{
for(i=0;i<PctNumAvailableExch;i++)
if (pHello->pExchSpecs[j] == PctAvailableExch[i])
{
Reply.SrvExchSpec = PctAvailableExch[i];
ExchOK = 1;
break;
}
if (ExchOK)
break;
}
if (ExchOK == 0)
fMismatch |= PCT_IMIS_EXCH;
else
pAuth->ExchType = Reply.SrvExchSpec = pHello->pExchSpecs[0];
Reply.SrvCertSpec = pHello->pCertSpecs[0];
if (fMismatch)
{
#if DBG
DebugLog((DEB_TRACE, "Server sending spec mismatch\n"));
#endif
for(i=0;i<PCT_NUM_MISMATCHES;i++)
{
MisData[i] = (BYTE)(fMismatch & 1);
fMismatch = fMismatch >> 1;
}
XmitError.Error = PCT_ERR_SPECS_MISMATCH;
XmitError.ErrInfoLen = PCT_NUM_MISMATCHES;
XmitError.ErrInfo = MisData;
fPack = PackPctError(&XmitError,
(PPct_Error *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
PctExternalFree(pHello);
pBuffer->BufferType = SECBUFFER_TOKEN;
if (!fPack)
{
return (SEC_E_INVALID_TOKEN);
}
else
{
phNewContext->dwUpper = (DWORD) pContext;
pContext->Flags |= CONTEXT_FLAG_ERRMODE;
pContext->Error = SEC_E_INVALID_TOKEN;
return (SEC_I_CONTINUE_NEEDED);
}
return( SEC_E_INVALID_TOKEN );
}
#if DBG
DebugLog((DEB_TRACE, "Server picks cipher %06x (%s)\n",
Reply.SrvCipherSpec,
DbgGetNameOfCrypto(Reply.SrvCipherSpec) ));
#endif
PctExternalFree(pHello);
Reply.ResponseLen = 0;
}
fPack = PackServerHello(&Reply,
(PPct_Server_Hello *) &pBuffer->pvBuffer,
&pBuffer->cbBuffer);
pBuffer->BufferType = SECBUFFER_TOKEN;
if (fPack)
{
phNewContext->dwUpper = (DWORD) pContext;
pContext->Flags |= CONTEXT_FLAG_HELLO;
if (Reply.ResponseLen == 0)
return(SEC_I_CONTINUE_NEEDED);
else
return(SEC_E_OK);
}
PctDeleteContext( pContext );
return(SEC_E_INSUFFICIENT_MEMORY);
}
SECURITY_STATUS SEC_ENTRY
PctDeleteSecurityContext(
PCtxtHandle phContext // Context to delete
)
{
PPctContext pContext;
pContext = PctpValidateContextHandle(phContext);
if (!pContext)
{
return( SEC_E_INVALID_HANDLE );
}
PctDeleteContext( pContext );
}
SECURITY_STATUS SEC_ENTRY
PctQueryContextAttributesA(
PCtxtHandle phContext, // Context to query
unsigned long ulAttribute, // Attribute to query
void SEC_FAR * pBuffer // Buffer for attributes
)
{
PPctContext pContext;
PSecPkgContext_Sizes pSizes;
PSecPkgContext_StreamSizes pStrSizes;
pContext = PctpValidateContextHandle( 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( Pct_Record_Header_Ex ) :
sizeof( Pct_Record_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;
}
pStrSizes = (PSecPkgContext_StreamSizes)pBuffer;
pStrSizes->cbHeader = 3;
pStrSizes->cbTrailer = pContext->pCheck->cbCheckSum;
pStrSizes->cbMaximumMessage = 32768;
pStrSizes->cBuffers = 4;
return( SEC_E_OK );
case SECPKG_ATTR_NAMES:
case SECPKG_ATTR_LIFESPAN:
case SECPKG_ATTR_DCE_INFO:
return( SEC_E_UNSUPPORTED_FUNCTION );
default:
return( SEC_E_INVALID_TOKEN );
}
return( SEC_E_INVALID_TOKEN );
}
SECURITY_STATUS SEC_ENTRY
PctQueryContextAttributesW(
PCtxtHandle phContext, // Context to query
unsigned long ulAttribute, // Attribute to query
void SEC_FAR * pBuffer // Buffer for attributes
)
{
return( PctQueryContextAttributesA( phContext, ulAttribute, pBuffer ) );
}
SECURITY_STATUS SEC_ENTRY
PctImpersonateSecurityContext(
PCtxtHandle phContext // Context to impersonate
)
{
PPctContext pContext;
pContext = PctpValidateContextHandle( phContext );
if ( pContext )
{
return( SEC_E_NO_IMPERSONATION );
}
return( SEC_E_INVALID_HANDLE );
}
SECURITY_STATUS SEC_ENTRY
PctRevertSecurityContext(
PCtxtHandle phContext // Context from which to re
)
{
PPctContext pContext;
pContext = PctpValidateContextHandle( phContext );
if ( pContext )
{
return( SEC_E_NO_IMPERSONATION );
}
return( SEC_E_INVALID_HANDLE );
}