1425 lines
40 KiB
C++
1425 lines
40 KiB
C++
#include "precomp.h"
|
||
DEBUG_FILEZONE(ZONE_T120_MSMCSTCP);
|
||
|
||
// #define FORCE_SSL3_NEGOTIATION
|
||
|
||
#include "tprtsec.h"
|
||
|
||
/* Tprtsec.cpp
|
||
*
|
||
* Copyright (c) 1997 by Microsoft Corporation
|
||
*
|
||
* Abstract:
|
||
* This module maintains security for the TCP transport.
|
||
*
|
||
*/
|
||
|
||
/* External definitions */
|
||
extern HINSTANCE g_hDllInst;
|
||
|
||
/*
|
||
* The following array contains a template for the X.224 data header.
|
||
* The 5 of the 7 bytes that it initializes are actually sent to the
|
||
* wire. Bytes 3 and 4 will be set to contain the size of the PDU.
|
||
* The array is only used when we encode a data PDU.
|
||
*/
|
||
extern UChar g_X224Header[];
|
||
|
||
|
||
#ifdef DEBUG
|
||
//#define TESTHACKS // DANGER! don't turn on in public build!
|
||
//#define DUMP
|
||
//#define DUMPCERTS
|
||
//#undef TRACE_OUT
|
||
//#define TRACE_OUT WARNING_OUT
|
||
#endif //DEBUG
|
||
|
||
#define SZSECPKG UNISP_NAME_A
|
||
|
||
#define ISC_REQ_FLAGS ( ISC_REQ_SEQUENCE_DETECT |\
|
||
ISC_REQ_REPLAY_DETECT |\
|
||
ISC_REQ_CONFIDENTIALITY |\
|
||
ISC_REQ_EXTENDED_ERROR |\
|
||
ISC_REQ_ALLOCATE_MEMORY |\
|
||
ISC_REQ_STREAM)
|
||
|
||
#define ASC_REQ_FLAGS ( ASC_REQ_SEQUENCE_DETECT |\
|
||
ASC_REQ_REPLAY_DETECT |\
|
||
ASC_REQ_CONFIDENTIALITY |\
|
||
ASC_REQ_EXTENDED_ERROR |\
|
||
ASC_REQ_ALLOCATE_MEMORY |\
|
||
ASC_REQ_MUTUAL_AUTH |\
|
||
ASC_REQ_STREAM)
|
||
|
||
|
||
#if defined(DUMP) || defined(DUMPCERTS)
|
||
|
||
#define MAX_DUMP_BYTES 512
|
||
|
||
void dumpbytes(PSTR szComment, PBYTE p, int cb)
|
||
{
|
||
int i,j;
|
||
char buf[80];
|
||
char buf2[80];
|
||
DWORD dwCheckSum = 0;
|
||
int cbShow = min(MAX_DUMP_BYTES,cb);
|
||
|
||
for (i=0; i<cb; i++)
|
||
dwCheckSum += p[i];
|
||
|
||
wsprintf(buf,"%s (%d bytes, checksum %x):",
|
||
szComment? szComment : "unknown", cb, dwCheckSum);
|
||
OutputDebugString(buf);
|
||
WARNING_OUT(("%s",buf));
|
||
OutputDebugString("\n\r");
|
||
|
||
for (i=0; i<cbShow/16; i++)
|
||
{
|
||
wsprintf(buf, "%08x: ", (DWORD) &p[(i*16)] );
|
||
for (j=0; j<16; j++)
|
||
{
|
||
wsprintf(buf2," %02x", (int) (unsigned char) p[(i*16)+j] );
|
||
lstrcat ( buf, buf2 );
|
||
}
|
||
WARNING_OUT(("%s",buf));
|
||
lstrcat ( buf, "\n\r");
|
||
OutputDebugString(buf);
|
||
}
|
||
if ( cbShow%16 )
|
||
{
|
||
wsprintf(buf, "%08x: ", (DWORD) &p[(i*16)] );
|
||
for (j=0; j<cbShow%16; j++)
|
||
{
|
||
wsprintf(buf2," %02x", (int) (unsigned char) p[(i*16)+j] );
|
||
lstrcat ( buf, buf2 );
|
||
}
|
||
WARNING_OUT(("%s",buf));
|
||
lstrcat(buf,"\n\r");
|
||
OutputDebugString(buf);
|
||
}
|
||
if ( cbShow < cb )
|
||
{
|
||
OutputDebugString("...\n\r");
|
||
WARNING_OUT(("..."));
|
||
}
|
||
}
|
||
#endif //DUMP or DUMPCERTS
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////
|
||
// Security Interface
|
||
///////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
|
||
SecurityInterface::SecurityInterface(void) :
|
||
LastError(TPRTSEC_NOERROR),
|
||
bInboundCredentialValid(FALSE),
|
||
bOutboundCredentialValid(FALSE),
|
||
m_pbEncodedCert(NULL),
|
||
m_cbEncodedCert(0),
|
||
hSecurityDll(NULL),
|
||
pfnTable(NULL)
|
||
{
|
||
}
|
||
|
||
SecurityInterface::~SecurityInterface(VOID)
|
||
{
|
||
if ( pfnTable && bInboundCredentialValid )
|
||
{
|
||
pfnTable->FreeCredentialHandle ( &hInboundCredential );
|
||
}
|
||
|
||
if ( pfnTable && bOutboundCredentialValid )
|
||
{
|
||
pfnTable->FreeCredentialHandle ( &hOutboundCredential );
|
||
}
|
||
|
||
if ( NULL != m_pbEncodedCert )
|
||
{
|
||
delete m_pbEncodedCert;
|
||
}
|
||
|
||
if ( NULL != hSecurityDll )
|
||
{
|
||
FreeLibrary( hSecurityDll );
|
||
}
|
||
}
|
||
|
||
#ifdef DUMPCERTS
|
||
VOID DumpCertStore ( SecurityInterface * pSI, char * sz, HCERTSTORE hStore)
|
||
{
|
||
WARNING_OUT(("************ %s *************", sz));
|
||
PCCERT_CONTEXT pC = NULL;
|
||
int i = 0;
|
||
char buf[256];
|
||
|
||
while ( pC = CertEnumCertificatesInStore(
|
||
hStore, (PCERT_CONTEXT)pC ))
|
||
{
|
||
WARNING_OUT(("----------- Entry %d: ----------------", i));
|
||
|
||
// Dump stuff in pC->pCertInfo
|
||
//DWORD dwVersion;
|
||
//CRYPT_INTEGER_BLOB SerialNumber;
|
||
//CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
|
||
//CERT_NAME_BLOB Issuer;
|
||
//FILETIME NotBefore;
|
||
//FILETIME NotAfter;
|
||
//CERT_NAME_BLOB Subject;
|
||
//CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo;
|
||
//CRYPT_BIT_BLOB IssuerUniqueId;
|
||
//CRYPT_BIT_BLOB SubjectUniqueId;
|
||
//DWORD cExtension;
|
||
//PCERT_EXTENSION rgExtension;
|
||
|
||
WARNING_OUT(("dwVersion: %x", pC->pCertInfo->dwVersion));
|
||
|
||
dumpbytes("SerialNumber",
|
||
pC->pCertInfo->SerialNumber.pbData,
|
||
pC->pCertInfo->SerialNumber.cbData );
|
||
|
||
WARNING_OUT(("SignatureAlgorithm (name): %s",
|
||
pC->pCertInfo->SignatureAlgorithm.pszObjId ));
|
||
|
||
CertNameToStr( pC->dwCertEncodingType, &pC->pCertInfo->Issuer,
|
||
CERT_X500_NAME_STR, buf, sizeof(buf) );
|
||
WARNING_OUT(("Issuer: %s", buf ));
|
||
|
||
WARNING_OUT(("NotBefore: %x,%x",
|
||
pC->pCertInfo->NotBefore.dwLowDateTime,
|
||
pC->pCertInfo->NotBefore.dwHighDateTime ));
|
||
WARNING_OUT(("NotAfter: %x,%x",
|
||
pC->pCertInfo->NotAfter.dwLowDateTime,
|
||
pC->pCertInfo->NotAfter.dwHighDateTime ));
|
||
|
||
CertNameToStr( pC->dwCertEncodingType, &pC->pCertInfo->Subject,
|
||
CERT_X500_NAME_STR, buf, sizeof(buf) );
|
||
WARNING_OUT(("Subject: %s", buf ));
|
||
|
||
WARNING_OUT(("<stuff omitted for now>"));
|
||
|
||
dumpbytes("IssuerUniqueId",
|
||
pC->pCertInfo->IssuerUniqueId.pbData,
|
||
pC->pCertInfo->IssuerUniqueId.cbData );
|
||
|
||
dumpbytes("SubjectUniqueId",
|
||
pC->pCertInfo->SubjectUniqueId.pbData,
|
||
pC->pCertInfo->SubjectUniqueId.cbData );
|
||
|
||
WARNING_OUT(("cExtension: %x", pC->pCertInfo->cExtension ));
|
||
WARNING_OUT(("<stuff omitted for now>"));
|
||
|
||
i++;
|
||
}
|
||
}
|
||
#endif // DUMPCERTS
|
||
|
||
TransportSecurityError SecurityInterface::InitializeCreds(
|
||
PCCERT_CONTEXT pCertContext )
|
||
{
|
||
SECURITY_STATUS ss;
|
||
SCHANNEL_CRED CredData;
|
||
|
||
CredHandle hNewInboundCred;
|
||
CredHandle hNewOutboundCred;
|
||
|
||
//
|
||
// Are we going to create new creds or just clean up?
|
||
//
|
||
|
||
if ( NULL != pCertContext )
|
||
{
|
||
ZeroMemory(&CredData, sizeof(CredData));
|
||
CredData.dwVersion = SCHANNEL_CRED_VERSION;
|
||
|
||
#ifdef FORCE_SSL3_NEGOTIATION
|
||
CredData.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT |
|
||
SP_PROT_SSL3_SERVER;
|
||
#endif // FORCE_SSL3_NEGOTIATION
|
||
|
||
CredData.dwFlags = SCH_CRED_NO_SERVERNAME_CHECK |
|
||
SCH_CRED_NO_DEFAULT_CREDS |
|
||
SCH_CRED_MANUAL_CRED_VALIDATION;
|
||
|
||
CredData.cCreds = 1;
|
||
CredData.paCred = &pCertContext;
|
||
|
||
// Acquire client and server credential handles
|
||
|
||
ss = pfnTable->AcquireCredentialsHandle (
|
||
NULL,
|
||
SZSECPKG,
|
||
SECPKG_CRED_INBOUND,
|
||
NULL,
|
||
&CredData,
|
||
NULL,
|
||
NULL,
|
||
&hNewInboundCred,
|
||
&tsExpiry );
|
||
|
||
if ( SEC_E_OK != ss )
|
||
{
|
||
WARNING_OUT(("AcquireCredentialsHandle (inbound) failed %lx", ss));
|
||
LastError = TPRTSEC_SSPIFAIL;
|
||
goto error;
|
||
}
|
||
|
||
ss = pfnTable->AcquireCredentialsHandle (
|
||
NULL,
|
||
SZSECPKG,
|
||
SECPKG_CRED_OUTBOUND,
|
||
NULL,
|
||
&CredData,
|
||
NULL,
|
||
NULL,
|
||
&hNewOutboundCred,
|
||
&tsExpiry );
|
||
|
||
if ( SEC_E_OK != ss )
|
||
{
|
||
WARNING_OUT(("AcquireCredentialsHandle (outbound) failed %lx", ss));
|
||
pfnTable->FreeCredentialHandle( &hNewInboundCred );
|
||
LastError = TPRTSEC_SSPIFAIL;
|
||
goto error;
|
||
}
|
||
|
||
// Empty the SSL cache
|
||
if (pfn_SslEmptyCache)
|
||
{
|
||
pfn_SslEmptyCache();
|
||
}
|
||
|
||
// This member can be called even when we're already initialized, as
|
||
// when the user chooses a different cert and we need to build new
|
||
// credentials based on it. Clear out the old information as necessary:
|
||
|
||
if ( NULL != m_pbEncodedCert )
|
||
{
|
||
delete m_pbEncodedCert;
|
||
m_pbEncodedCert = NULL;
|
||
}
|
||
}
|
||
|
||
if ( bInboundCredentialValid )
|
||
pfnTable->FreeCredentialHandle ( &hInboundCredential );
|
||
|
||
if ( bOutboundCredentialValid )
|
||
pfnTable->FreeCredentialHandle ( &hOutboundCredential );
|
||
|
||
if ( NULL != pCertContext )
|
||
{
|
||
hInboundCredential = hNewInboundCred;
|
||
hOutboundCredential = hNewOutboundCred;
|
||
bInboundCredentialValid = TRUE;
|
||
bOutboundCredentialValid = TRUE;
|
||
|
||
//
|
||
// Save the cert name for later use
|
||
//
|
||
|
||
ASSERT( NULL == m_pbEncodedCert );
|
||
m_pbEncodedCert = new BYTE[pCertContext->cbCertEncoded];
|
||
|
||
if ( NULL == m_pbEncodedCert )
|
||
{
|
||
ERROR_OUT(("Error allocating data for encoded Cert"));
|
||
goto error;
|
||
}
|
||
|
||
memcpy( m_pbEncodedCert, pCertContext->pbCertEncoded,
|
||
pCertContext->cbCertEncoded );
|
||
|
||
ASSERT(pCertContext->cbCertEncoded);
|
||
m_cbEncodedCert = pCertContext->cbCertEncoded;
|
||
}
|
||
else
|
||
{
|
||
bInboundCredentialValid = FALSE;
|
||
bOutboundCredentialValid = FALSE;
|
||
}
|
||
|
||
LastError = TPRTSEC_NOERROR;
|
||
|
||
error:
|
||
|
||
return LastError;
|
||
}
|
||
|
||
TransportSecurityError SecurityInterface::Initialize(VOID)
|
||
{
|
||
TRACE_OUT(("Initializing security interface"));
|
||
|
||
// Load the security provider DLL
|
||
|
||
hSecurityDll = LoadLibrary("SCHANNEL");
|
||
|
||
if ( !hSecurityDll )
|
||
{
|
||
ERROR_OUT(("Loadlib schannel.dll failed"));
|
||
LastError = TPRTSEC_NODLL;
|
||
goto error;
|
||
}
|
||
|
||
// Get the initialization entrypoint
|
||
pfnInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(
|
||
hSecurityDll,
|
||
SECURITY_ENTRYPOINT );
|
||
|
||
if ( NULL == pfnInitSecurityInterface )
|
||
{
|
||
ERROR_OUT(("GetProcAddr %s failed", SECURITY_ENTRYPOINT));
|
||
LastError = TPRTSEC_NOENTRYPT;
|
||
goto error;
|
||
}
|
||
|
||
// Get the SSPI function table
|
||
pfnTable = (*pfnInitSecurityInterface)();
|
||
|
||
if ( NULL == pfnTable )
|
||
{
|
||
ERROR_OUT(("InitializeSecurityProvider failed"));
|
||
LastError = TPRTSEC_SSPIFAIL;
|
||
goto error;
|
||
}
|
||
|
||
pfn_SslEmptyCache = (PFN_SSL_EMPTY_CACHE)GetProcAddress(hSecurityDll, SZ_SSLEMPTYCACHE);
|
||
if ( NULL == pfnInitSecurityInterface )
|
||
{
|
||
ERROR_OUT(("GetProcAddr %s failed", SZ_SSLEMPTYCACHE));
|
||
LastError = TPRTSEC_NOENTRYPT;
|
||
goto error;
|
||
}
|
||
|
||
error:
|
||
|
||
return LastError;
|
||
}
|
||
|
||
BOOL SecurityInterface::GetUserCert(PBYTE pInfo, PDWORD pcbInfo)
|
||
{
|
||
if ( NULL == m_pbEncodedCert)
|
||
{
|
||
WARNING_OUT(("GetUserCert: no encoded certname"));
|
||
return FALSE;
|
||
}
|
||
|
||
ASSERT(m_cbEncodedCert > 0);
|
||
|
||
if ( NULL == pInfo )
|
||
{
|
||
// Caller wants to know how much to allocate
|
||
ASSERT(pcbInfo);
|
||
*pcbInfo = m_cbEncodedCert;
|
||
return TRUE;
|
||
}
|
||
|
||
if ( *pcbInfo < m_cbEncodedCert )
|
||
{
|
||
ERROR_OUT(("GetUserCert: insufficient buffer (%ld) %ld required",
|
||
*pcbInfo, m_cbEncodedCert ));
|
||
return FALSE;
|
||
}
|
||
|
||
memcpy ( (PCHAR)pInfo, m_pbEncodedCert, m_cbEncodedCert );
|
||
*pcbInfo = m_cbEncodedCert;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////
|
||
// Security Context
|
||
///////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
|
||
|
||
SecurityContext::SecurityContext(PSecurityInterface pSI, LPCSTR szHostName) :
|
||
scstate(SECCTX_STATE_NEW),
|
||
fContinueNeeded(FALSE),
|
||
LastError(TPRTSEC_NOERROR),
|
||
bContextHandleValid(FALSE)
|
||
{
|
||
ASSERT(pSI);
|
||
ASSERT(szHostName);
|
||
|
||
pSecurityInterface = pSI;
|
||
|
||
OutBuffers[0].pvBuffer = NULL;
|
||
OutBuffers[0].cbBuffer = 0;
|
||
|
||
if ( NULL != szHostName )
|
||
{
|
||
wsprintf( szTargetName, "%s:%x%x", szHostName,
|
||
pSI->hOutboundCredential.dwUpper,
|
||
pSI->hOutboundCredential.dwLower);
|
||
TRACE_OUT(("SecurityContext::SecurityContext: targ %s",szTargetName));
|
||
}
|
||
ASSERT(pSecurityInterface);
|
||
}
|
||
|
||
SecurityContext::~SecurityContext(VOID)
|
||
{
|
||
ASSERT(pSecurityInterface);
|
||
if ( NULL != OutBuffers[0].pvBuffer )
|
||
{
|
||
ASSERT(pSecurityInterface->pfnTable);
|
||
|
||
pSecurityInterface->pfnTable->FreeContextBuffer(OutBuffers[0].pvBuffer);
|
||
OutBuffers[0].pvBuffer = NULL;
|
||
}
|
||
if ( bContextHandleValid )
|
||
{
|
||
pSecurityInterface->pfnTable->DeleteSecurityContext(&hContext);
|
||
}
|
||
}
|
||
|
||
|
||
TransportSecurityError SecurityContext::InitContextAttributes(VOID)
|
||
{
|
||
SECURITY_STATUS ss;
|
||
|
||
ss = pSecurityInterface->pfnTable->QueryContextAttributes(&hContext,
|
||
SECPKG_ATTR_STREAM_SIZES,
|
||
&Sizes );
|
||
if (ss != ERROR_SUCCESS)
|
||
{
|
||
ERROR_OUT(("QueryContextAttributes returned [%x]", ss));
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
}
|
||
else
|
||
{
|
||
ASSERT (Sizes.cbHeader + Sizes.cbTrailer <= PROTOCOL_OVERHEAD_SECURITY);
|
||
TRACE_OUT(("QueryContextAttributes returned header=%d trailer=%d",
|
||
Sizes.cbHeader, Sizes.cbTrailer));
|
||
}
|
||
|
||
#ifdef DEBUG //////////////////////////////////////////////////////////
|
||
SecPkgContext_KeyInfo KeyInfo;
|
||
|
||
ss = pSecurityInterface->pfnTable->QueryContextAttributes(&hContext,
|
||
SECPKG_ATTR_KEY_INFO,
|
||
&KeyInfo );
|
||
if (ss != ERROR_SUCCESS)
|
||
{
|
||
ERROR_OUT(("QueryContextAttributes (KEY_INFO) failed %x", ss));
|
||
}
|
||
else
|
||
{
|
||
WARNING_OUT(("KEY INFO: Sign:%s Encrypt:%s Keysize:%d",
|
||
KeyInfo.sSignatureAlgorithmName,
|
||
KeyInfo.sEncryptAlgorithmName,
|
||
KeyInfo.KeySize ));
|
||
pSecurityInterface->pfnTable->FreeContextBuffer(
|
||
KeyInfo.sSignatureAlgorithmName );
|
||
pSecurityInterface->pfnTable->FreeContextBuffer(
|
||
KeyInfo.sEncryptAlgorithmName );
|
||
}
|
||
|
||
#endif //DEBUG ///////////////////////////////////////////////////////
|
||
|
||
return TPRTSEC_NOERROR;
|
||
}
|
||
|
||
|
||
TransportSecurityError SecurityContext::Initialize(PBYTE pData, DWORD cbData)
|
||
{
|
||
SECURITY_STATUS ss;
|
||
DWORD dwReqFlags;
|
||
|
||
TRACE_OUT(("SecurityContext Initialize (%x,%d)", pData, cbData));
|
||
|
||
fContinueNeeded = FALSE;
|
||
|
||
ASSERT(pSecurityInterface);
|
||
ASSERT(SECCTX_STATE_INIT == scstate || SECCTX_STATE_NEW == scstate);
|
||
|
||
if ( !pSecurityInterface->bOutboundCredentialValid )
|
||
{
|
||
WARNING_OUT(("SecurityContext::Initialize: no outbound cred"));
|
||
return TPRTSEC_SSPIFAIL;
|
||
}
|
||
|
||
if ( SECCTX_STATE_INIT == scstate)
|
||
{
|
||
ASSERT(NULL != pData);
|
||
ASSERT(0 != cbData);
|
||
|
||
if ( NULL == pData || 0 == cbData )
|
||
{
|
||
ERROR_OUT(("Second initialize call with no data"));
|
||
return LastError = TPRTSEC_INVALID_PARAMETER;
|
||
}
|
||
|
||
// Build the input buffer descriptor
|
||
|
||
InputBufferDescriptor.cBuffers = 2;
|
||
InputBufferDescriptor.pBuffers = InBuffers;
|
||
InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
|
||
|
||
InBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||
InBuffers[0].cbBuffer = cbData;
|
||
InBuffers[0].pvBuffer = pData;
|
||
|
||
InBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||
InBuffers[1].cbBuffer = 0;
|
||
InBuffers[1].pvBuffer = NULL;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(NULL == pData);
|
||
ASSERT(0 == cbData);
|
||
}
|
||
|
||
OutputBufferDescriptor.cBuffers = 1;
|
||
OutputBufferDescriptor.pBuffers = OutBuffers;
|
||
OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
|
||
|
||
// If there's a output buffer from a previous call, free it here
|
||
if ( NULL != OutBuffers[0].pvBuffer )
|
||
{
|
||
pSecurityInterface->pfnTable->FreeContextBuffer(OutBuffers[0].pvBuffer);
|
||
}
|
||
|
||
dwReqFlags = ISC_REQ_FLAGS;
|
||
|
||
while ( 1 )
|
||
{
|
||
OutBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||
OutBuffers[0].cbBuffer = 0;
|
||
OutBuffers[0].pvBuffer = NULL;
|
||
|
||
#ifdef DUMP
|
||
if (SECCTX_STATE_INIT == scstate)
|
||
{
|
||
dumpbytes("input token", (unsigned char *)InBuffers[0].pvBuffer,
|
||
InBuffers[0].cbBuffer);
|
||
}
|
||
#endif //DUMP
|
||
|
||
ss = pSecurityInterface->pfnTable->InitializeSecurityContext(
|
||
&(pSecurityInterface->hOutboundCredential),
|
||
SECCTX_STATE_INIT == scstate ? &hContext : NULL,
|
||
szTargetName, // TargetName
|
||
dwReqFlags,
|
||
0, // Reserved
|
||
SECURITY_NATIVE_DREP,
|
||
SECCTX_STATE_INIT == scstate ? &InputBufferDescriptor : NULL,
|
||
0, // reserved
|
||
&hContext,
|
||
&OutputBufferDescriptor,
|
||
&ContextAttributes,
|
||
&Expiration );
|
||
|
||
// Some security providers don't process all the packet data
|
||
// in one call to SCA - readjust the input buffers with the offset
|
||
// returned in the extra buffer and iterate as necessary
|
||
|
||
if (( SEC_I_CONTINUE_NEEDED == ss
|
||
&& NULL == OutBuffers[0].pvBuffer )
|
||
&& SECBUFFER_EXTRA == InBuffers[1].BufferType
|
||
&& 0 != InBuffers[1].cbBuffer )
|
||
{
|
||
InBuffers[0].pvBuffer = (PBYTE)(InBuffers[0].pvBuffer) +
|
||
( InBuffers[0].cbBuffer - InBuffers[1].cbBuffer );
|
||
InBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||
InBuffers[0].cbBuffer = InBuffers[1].cbBuffer;
|
||
|
||
InBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||
InBuffers[1].cbBuffer = 0;
|
||
InBuffers[1].pvBuffer = NULL;
|
||
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
|
||
|
||
#ifdef DUMP
|
||
if ( SEC_E_OK == ss || SEC_I_CONTINUE_NEEDED == ss )
|
||
{
|
||
dumpbytes("output token",
|
||
(unsigned char *)OutBuffers[0].pvBuffer,
|
||
OutBuffers[0].cbBuffer);
|
||
}
|
||
#endif //DUMP
|
||
|
||
#ifdef ALLOW_NON_AUTHENTICATED_CLIENTS
|
||
if ( SEC_I_INCOMPLETE_CREDENTIALS == ss )
|
||
{
|
||
WARNING_OUT(("InitializeSecurityContext:SEC_I_INCOMPLETE_CREDENTIALS"));
|
||
|
||
dwReqFlags |= ISC_REQ_USE_SUPPLIED_CREDS;
|
||
|
||
ss = pSecurityInterface->pfnTable->InitializeSecurityContext(
|
||
&(pSecurityInterface->hOutboundCredential),
|
||
SECCTX_STATE_INIT == scstate ? &hContext : NULL,
|
||
szTargetName, // TargetName
|
||
dwReqFlags,
|
||
0, // Reserved
|
||
SECURITY_NATIVE_DREP,
|
||
SECCTX_STATE_INIT == scstate ? &InputBufferDescriptor : NULL,
|
||
0, // reserved
|
||
&hContext,
|
||
&OutputBufferDescriptor,
|
||
&ContextAttributes,
|
||
&Expiration );
|
||
}
|
||
#endif // ALLOW_NON_AUTHENTICATED_CLIENTS
|
||
|
||
if ( SEC_E_OK != ss )
|
||
{
|
||
if ( SEC_I_CONTINUE_NEEDED == ss && NULL != OutBuffers[0].pvBuffer )
|
||
{
|
||
ASSERT(SECCTX_STATE_NEW == scstate || SECCTX_STATE_INIT == scstate);
|
||
|
||
TRACE_OUT(("Initialize: SEC_I_CONTINUE_NEEDED"));
|
||
scstate = SECCTX_STATE_INIT;
|
||
}
|
||
else
|
||
{
|
||
ERROR_OUT(("Initialize failed: %x in state %d",(DWORD)ss,scstate));
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// We're almost done,
|
||
// find the header and trailer sizes
|
||
//
|
||
|
||
if ( TPRTSEC_NOERROR != InitContextAttributes() )
|
||
return LastError;
|
||
|
||
if ( !Verify() )
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
|
||
TRACE_OUT(("INITIALIZE OK"));
|
||
|
||
scstate = SECCTX_STATE_INIT_COMPLETE;
|
||
}
|
||
|
||
// If there is an output buffer, set the flag to get it sent accross
|
||
if ( ( SEC_E_OK == ss || SEC_I_CONTINUE_NEEDED == ss ) &&
|
||
NULL != OutBuffers[0].pvBuffer )
|
||
{
|
||
fContinueNeeded = TRUE;
|
||
}
|
||
|
||
bContextHandleValid = TRUE;
|
||
return LastError = TPRTSEC_NOERROR;
|
||
}
|
||
|
||
|
||
TransportSecurityError SecurityContext::Accept(PBYTE pData, DWORD cbData)
|
||
{
|
||
SECURITY_STATUS ss;
|
||
|
||
fContinueNeeded = FALSE;
|
||
|
||
ASSERT(SECCTX_STATE_NEW == scstate || SECCTX_STATE_ACCEPT == scstate);
|
||
|
||
if ( !pSecurityInterface->bInboundCredentialValid )
|
||
{
|
||
WARNING_OUT(("SecurityContext::Initialize: no inbound cred"));
|
||
return TPRTSEC_SSPIFAIL;
|
||
}
|
||
|
||
// Check to see if the required data is present
|
||
if ( NULL == pData || 0 == cbData )
|
||
{
|
||
ERROR_OUT(("Accept: no data"));
|
||
return LastError = TPRTSEC_INVALID_PARAMETER;
|
||
}
|
||
|
||
// Build the input buffer descriptor
|
||
|
||
InputBufferDescriptor.cBuffers = 2;
|
||
InputBufferDescriptor.pBuffers = InBuffers;
|
||
InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
|
||
|
||
InBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||
InBuffers[0].cbBuffer = cbData;
|
||
InBuffers[0].pvBuffer = pData;
|
||
|
||
InBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||
InBuffers[1].cbBuffer = 0;
|
||
InBuffers[1].pvBuffer = NULL;
|
||
|
||
// Build the output buffer descriptor
|
||
|
||
OutputBufferDescriptor.cBuffers = 1;
|
||
OutputBufferDescriptor.pBuffers = OutBuffers;
|
||
OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
|
||
|
||
// If there's a output buffer from a previous call, free it here
|
||
if ( NULL != OutBuffers[0].pvBuffer )
|
||
{
|
||
pSecurityInterface->pfnTable->FreeContextBuffer(OutBuffers[0].pvBuffer);
|
||
}
|
||
|
||
while ( 1 )
|
||
{
|
||
OutBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||
OutBuffers[0].cbBuffer = 0;
|
||
OutBuffers[0].pvBuffer = NULL;
|
||
|
||
#ifdef DUMP
|
||
dumpbytes("input token", (unsigned char *)InBuffers[0].pvBuffer,
|
||
InBuffers[0].cbBuffer);
|
||
#endif //DUMP
|
||
|
||
ss = pSecurityInterface->pfnTable->AcceptSecurityContext(
|
||
&(pSecurityInterface->hInboundCredential),
|
||
SECCTX_STATE_NEW == scstate ? NULL : &hContext,
|
||
&InputBufferDescriptor,
|
||
ASC_REQ_FLAGS,
|
||
SECURITY_NATIVE_DREP,
|
||
&hContext, // receives new context handle
|
||
&OutputBufferDescriptor, // receives output security token
|
||
&ContextAttributes, // receives context attributes
|
||
&Expiration ); // receives expiration time
|
||
|
||
// Some security providers don't process all the packet data
|
||
// in one call to SCA - readjust the input buffers with the offset
|
||
// returned in the extra buffer and iterate as necessary
|
||
|
||
if (( SEC_I_CONTINUE_NEEDED == ss
|
||
&& NULL == OutBuffers[0].pvBuffer )
|
||
&& SECBUFFER_EXTRA == InBuffers[1].BufferType
|
||
&& 0 != InBuffers[1].cbBuffer )
|
||
{
|
||
InBuffers[0].pvBuffer = (PBYTE)(InBuffers[0].pvBuffer) +
|
||
( InBuffers[0].cbBuffer - InBuffers[1].cbBuffer );
|
||
InBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||
InBuffers[0].cbBuffer = InBuffers[1].cbBuffer;
|
||
|
||
InBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||
InBuffers[1].cbBuffer = 0;
|
||
InBuffers[1].pvBuffer = NULL;
|
||
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
|
||
#ifdef DUMP
|
||
if ( SEC_E_OK == ss || SEC_I_CONTINUE_NEEDED == ss )
|
||
{
|
||
dumpbytes("output token",
|
||
(unsigned char *)OutBuffers[0].pvBuffer,
|
||
OutBuffers[0].cbBuffer);
|
||
}
|
||
#endif //DUMP
|
||
|
||
if ( SEC_E_OK != ss )
|
||
{
|
||
if ( SEC_I_CONTINUE_NEEDED == ss )
|
||
{
|
||
TRACE_OUT(("Accept: SEC_I_CONTINUE_NEEDED"));
|
||
|
||
scstate = SECCTX_STATE_ACCEPT;
|
||
}
|
||
else
|
||
{
|
||
ERROR_OUT(("AcceptSecurityContext failed: %x", (DWORD)ss));
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
|
||
// We're almost done,
|
||
// find the header and trailer sizes
|
||
//
|
||
|
||
if ( TPRTSEC_NOERROR != InitContextAttributes() )
|
||
return LastError;
|
||
|
||
if ( !Verify() )
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
|
||
TRACE_OUT(("ACCEPT OK"));
|
||
|
||
scstate = SECCTX_STATE_ACCEPT_COMPLETE;
|
||
}
|
||
|
||
// If there is an output buffer, set the flag to get it sent accross
|
||
if ( ( SEC_E_OK == ss || SEC_I_CONTINUE_NEEDED == ss ) &&
|
||
NULL != OutBuffers[0].pvBuffer )
|
||
{
|
||
fContinueNeeded = TRUE;
|
||
}
|
||
|
||
bContextHandleValid = TRUE;
|
||
return LastError = TPRTSEC_NOERROR;
|
||
}
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Encrypt()
|
||
//
|
||
// Description:
|
||
// Encrypts a packet to be sent using SSL/PCT by calling SealMessage().
|
||
//
|
||
// Parameters:
|
||
// phContext - security context handle returned from InitiateSecConnection
|
||
// pBufIn1, pBufIn2 - buffers to be encrypted
|
||
// cbBufIn1,cbBufIn2 - lengths of buffers to be encrypted
|
||
// ppBufOut - allocated encrypted buffer, to be freed by caller
|
||
// pcbBufOut - length of encrypted buffer
|
||
//
|
||
// Return:
|
||
// TransprotSecurityError
|
||
//
|
||
TransportSecurityError SecurityContext::Encrypt(
|
||
LPBYTE pBufIn1,
|
||
UINT cbBufIn1,
|
||
LPBYTE pBufIn2,
|
||
UINT cbBufIn2,
|
||
LPBYTE *ppBufOut,
|
||
UINT *pcbBufOut)
|
||
{
|
||
SECURITY_STATUS scRet = ERROR_SUCCESS;
|
||
SecBufferDesc Buffer;
|
||
SecBuffer Buffers[4];
|
||
UINT cbBufInTotal;
|
||
LPBYTE pbTemp;
|
||
|
||
// pBufIn2 and cbBufIn2 maybe NULL and 0, respectively.
|
||
ASSERT(pBufIn1);
|
||
ASSERT(cbBufIn1);
|
||
ASSERT(ppBufOut);
|
||
ASSERT(pcbBufOut);
|
||
|
||
ASSERT(SECCTX_STATE_INIT_COMPLETE == scstate ||
|
||
SECCTX_STATE_ACCEPT_COMPLETE == scstate);
|
||
if (SECCTX_STATE_INIT_COMPLETE != scstate &&
|
||
SECCTX_STATE_ACCEPT_COMPLETE != scstate)
|
||
return LastError = TPRTSEC_INCOMPLETE_CONTEXT;
|
||
|
||
*pcbBufOut = 0;
|
||
cbBufInTotal = cbBufIn1 + cbBufIn2;
|
||
|
||
// We allocate a buffer to hold the (larger) encrypted data.
|
||
// This must be freed by the caller!
|
||
// christts: The buffer will now also hold the X.224 header.
|
||
if (NULL == (*ppBufOut = (LPBYTE)LocalAlloc(0, cbBufInTotal
|
||
+ Sizes.cbHeader + Sizes.cbTrailer +
|
||
sizeof(X224_DATA_PACKET))))
|
||
return LastError = TPRTSEC_NOMEM;
|
||
|
||
pbTemp = *ppBufOut + sizeof(X224_DATA_PACKET);
|
||
|
||
//
|
||
// prepare data for SecBuffer
|
||
//
|
||
Buffers[0].pvBuffer = pbTemp;
|
||
Buffers[0].cbBuffer = Sizes.cbHeader;
|
||
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
|
||
|
||
Buffers[1].pvBuffer = pbTemp + Sizes.cbHeader;
|
||
// Copy the user's data
|
||
CopyMemory(Buffers[1].pvBuffer, pBufIn1, cbBufIn1);
|
||
if (NULL != pBufIn2) {
|
||
CopyMemory((PVoid) ((PUChar) (Buffers[1].pvBuffer) + cbBufIn1),
|
||
pBufIn2, cbBufIn2);
|
||
}
|
||
Buffers[1].cbBuffer = cbBufInTotal;
|
||
Buffers[1].BufferType = SECBUFFER_DATA;
|
||
|
||
Buffers[2].pvBuffer = pbTemp + Sizes.cbHeader + cbBufInTotal;
|
||
Buffers[2].cbBuffer = Sizes.cbTrailer;
|
||
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
|
||
|
||
Buffers[3].pvBuffer = NULL;
|
||
Buffers[3].cbBuffer = 0;
|
||
Buffers[3].BufferType = SECBUFFER_EMPTY;
|
||
|
||
Buffer.cBuffers = 4;
|
||
Buffer.pBuffers = Buffers;
|
||
Buffer.ulVersion = SECBUFFER_VERSION;
|
||
|
||
#ifdef DUMP
|
||
dumpbytes("data BEFORE encryption", (PBYTE)Buffers[1].pvBuffer,
|
||
Buffers[1].cbBuffer);
|
||
#endif // DUMP
|
||
|
||
// Call the semi-documented SealMessage function (Reserved3)
|
||
|
||
scRet = ((SEAL_MESSAGE_FN)pSecurityInterface->pfnTable->Reserved3)(
|
||
&hContext, 0, &Buffer, 0);
|
||
|
||
|
||
if (scRet != ERROR_SUCCESS)
|
||
{
|
||
//
|
||
// Map the SSPI error.
|
||
//
|
||
ERROR_OUT(("SealMessage failed: %x", scRet));
|
||
LocalFree(*ppBufOut);
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
}
|
||
|
||
// We also have to add the X.224 header.
|
||
*pcbBufOut = cbBufInTotal + Sizes.cbHeader + Sizes.cbTrailer + sizeof(X224_DATA_PACKET);
|
||
memcpy (*ppBufOut, g_X224Header, sizeof(X224_DATA_PACKET));
|
||
AddRFCSize(*ppBufOut, *pcbBufOut);
|
||
|
||
#ifdef TESTHACKS
|
||
// Inject an error...
|
||
if (GetAsyncKeyState(VK_CONTROL)&0x8000) {
|
||
OutputDebugString("*** INJECTING ERROR IN OUTGOING PACKET ***\n\r");
|
||
pbTemp[(*pcbBufOut - sizeof(X224_DATA_PACKET))/2] ^= 0x55;
|
||
}
|
||
#endif //TESTHACKS
|
||
|
||
#ifdef DUMP
|
||
dumpbytes("data AFTER encryption", pbTemp, *pcbBufOut - sizeof(X224_DATA_PACKET));
|
||
#endif // DUMP
|
||
|
||
TRACE_OUT(("SealMessage returned Buffer = %p, EncryptBytes = %d, UnencryptBytes = %d", pbTemp,
|
||
*pcbBufOut - sizeof(X224_DATA_PACKET), cbBufInTotal));
|
||
|
||
return LastError = TPRTSEC_NOERROR;
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Decrypt
|
||
//
|
||
// Description:
|
||
// Decrypts a buffer received using SCHANNEL by calling UnsealMessage().
|
||
//
|
||
// Parameters:
|
||
// pBuf - buffer to be decrypted
|
||
// cbBufIn - length of buffer to be decrypted
|
||
//
|
||
TransportSecurityError SecurityContext::Decrypt( PBYTE pBuf, DWORD cbBuf)
|
||
{
|
||
SecBufferDesc Buffer;
|
||
SecBuffer Buffers[4];
|
||
DWORD scRet = ERROR_SUCCESS;
|
||
SecBuffer * pDataBuffer;
|
||
int i;
|
||
|
||
LastError = TPRTSEC_SSPIFAIL;
|
||
|
||
ASSERT(SECCTX_STATE_INIT_COMPLETE == scstate ||
|
||
SECCTX_STATE_ACCEPT_COMPLETE == scstate);
|
||
if (SECCTX_STATE_INIT_COMPLETE != scstate &&
|
||
SECCTX_STATE_ACCEPT_COMPLETE != scstate)
|
||
return LastError = TPRTSEC_INCOMPLETE_CONTEXT;
|
||
|
||
ASSERT(!IsBadWritePtr(pBuf,cbBuf));
|
||
|
||
#ifdef TESTHACKS
|
||
// Inject an error...
|
||
if ( GetAsyncKeyState(VK_SHIFT) & 0x8000 ) {
|
||
OutputDebugString("*** INJECTING ERROR IN INCOMING PACKET ***\n\r");
|
||
pBuf[cbBuf/2] ^= 0x55;
|
||
}
|
||
#endif //TESTHACKS
|
||
|
||
//
|
||
// prepare data the SecBuffer for a call to SSL/PCT decryption code.
|
||
//
|
||
Buffers[0].pvBuffer = pBuf;
|
||
Buffers[0].cbBuffer = cbBuf;
|
||
|
||
Buffers[0].BufferType = SECBUFFER_DATA;
|
||
|
||
Buffers[1].pvBuffer = NULL;
|
||
Buffers[1].cbBuffer = 0;
|
||
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
||
Buffers[2].pvBuffer = NULL;
|
||
Buffers[2].cbBuffer = 0;
|
||
Buffers[2].BufferType = SECBUFFER_EMPTY;
|
||
Buffers[3].pvBuffer = NULL;
|
||
Buffers[3].cbBuffer = 0;
|
||
Buffers[3].BufferType = SECBUFFER_EMPTY;
|
||
|
||
Buffer.cBuffers = 4;
|
||
Buffer.pBuffers = Buffers;
|
||
Buffer.ulVersion = SECBUFFER_VERSION;
|
||
|
||
// Call the semi-documented UnsealMessage function (Reserved4)
|
||
|
||
#ifdef DUMP
|
||
dumpbytes("data BEFORE decryption:", (PBYTE)Buffers[0].pvBuffer,
|
||
Buffers[0].cbBuffer);
|
||
#endif // DUMP
|
||
|
||
scRet = ((UNSEAL_MESSAGE_FN)pSecurityInterface->pfnTable->Reserved4)(
|
||
&hContext, &Buffer, 0, NULL);
|
||
|
||
pDataBuffer = NULL;
|
||
|
||
for( i=0; i<4; i++ )
|
||
{
|
||
if ( NULL == pDataBuffer && SECBUFFER_DATA == Buffers[i].BufferType )
|
||
{
|
||
pDataBuffer = &Buffers[i];
|
||
}
|
||
}
|
||
|
||
if ( NULL == pDataBuffer )
|
||
{
|
||
ERROR_OUT(("Unseal: no data buffer found"));
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
}
|
||
|
||
#ifdef DUMP
|
||
dumpbytes("data AFTER decryption:", (PBYTE)pDataBuffer->pvBuffer,
|
||
pDataBuffer->cbBuffer);
|
||
#endif // DUMP
|
||
|
||
if (scRet != ERROR_SUCCESS)
|
||
{
|
||
ERROR_OUT(("UnsealMessage failed with [%x]", scRet));
|
||
return LastError = TPRTSEC_SSPIFAIL;
|
||
}
|
||
return LastError = TPRTSEC_NOERROR;
|
||
}
|
||
|
||
|
||
TransportSecurityError SecurityContext::AdvanceState(PBYTE pIncomingData,
|
||
DWORD cbBuf)
|
||
{
|
||
TRACE_OUT(("AdvanceState: state %d using data %x (%d)",
|
||
scstate, pIncomingData, cbBuf ));
|
||
|
||
switch ( scstate )
|
||
{
|
||
case SECCTX_STATE_INIT:
|
||
if ( TPRTSEC_NOERROR != Initialize( pIncomingData, cbBuf ) )
|
||
{
|
||
WARNING_OUT(("AdvanceState: Initialize failed in INIT"));
|
||
goto error;
|
||
}
|
||
break;
|
||
|
||
case SECCTX_STATE_ACCEPT:
|
||
case SECCTX_STATE_NEW:
|
||
if ( TPRTSEC_NOERROR != Accept( pIncomingData, cbBuf ) )
|
||
{
|
||
WARNING_OUT(("AdvanceState: Accept failed in ACCEPT or NEW"));
|
||
goto error;
|
||
}
|
||
break;
|
||
|
||
case SECCTX_STATE_INIT_COMPLETE:
|
||
case SECCTX_STATE_ACCEPT_COMPLETE:
|
||
case SECCTX_STATE_ERROR:
|
||
default:
|
||
ERROR_OUT(("AdvanceState: called in unexpected state %d"));
|
||
goto error;
|
||
|
||
}
|
||
return LastError = TPRTSEC_NOERROR;
|
||
|
||
error:
|
||
|
||
scstate = SECCTX_STATE_ERROR;
|
||
return LastError;
|
||
}
|
||
|
||
#define CHECKFLAGS (CERT_STORE_REVOCATION_FLAG |\
|
||
CERT_STORE_SIGNATURE_FLAG |\
|
||
CERT_STORE_TIME_VALIDITY_FLAG)
|
||
|
||
BOOL SecurityContext::Verify(VOID)
|
||
{
|
||
BOOL fRet = TRUE;
|
||
DWORD sc;
|
||
PCCERT_CONTEXT pCert = NULL, pIssuerCert = NULL, pCACert = NULL;
|
||
DWORD dwFlags;
|
||
HCERTSTORE hStore = NULL;
|
||
HCERTSTORE hCAStore = NULL;
|
||
CHAR * pIssuer = NULL;
|
||
|
||
ASSERT( NULL != pSecurityInterface );
|
||
|
||
// Get the subject cert context
|
||
sc = pSecurityInterface->pfnTable->QueryContextAttributes(&hContext,
|
||
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
|
||
(PVOID)&pCert );
|
||
|
||
if ( SEC_E_OK != sc )
|
||
{
|
||
ERROR_OUT(("QueryContextAttributes (REMOTE_CERT_CONTEXT) failed"));
|
||
goto error;
|
||
}
|
||
|
||
if ( NULL == pCert )
|
||
{
|
||
// The caller is not authenticated
|
||
WARNING_OUT(("No remote cred data"));
|
||
goto error;
|
||
}
|
||
|
||
// Open the root store for certificate verification
|
||
hStore = CertOpenSystemStore(0, "Root");
|
||
|
||
if( NULL == hStore )
|
||
{
|
||
ERROR_OUT(("Couldn't open root certificate store"));
|
||
goto error;
|
||
}
|
||
|
||
dwFlags = CHECKFLAGS;
|
||
|
||
// Get the issuer of this cert
|
||
|
||
pIssuerCert = CertGetIssuerCertificateFromStore(
|
||
hStore,
|
||
pCert,
|
||
NULL,
|
||
&dwFlags );
|
||
|
||
// If the issuer of the certificate cannot be found in the root store,
|
||
// check the CA store iteratively until we work our way back to a root
|
||
// certificate
|
||
|
||
pCACert = pCert;
|
||
|
||
while ( NULL == pIssuerCert )
|
||
{
|
||
PCCERT_CONTEXT pTmpCert;
|
||
|
||
if ( NULL == hCAStore )
|
||
{
|
||
hCAStore = CertOpenSystemStore(0, "CA");
|
||
|
||
if ( NULL == hCAStore )
|
||
{
|
||
ERROR_OUT(("Couldn't open CA certificate store"));
|
||
goto error;
|
||
}
|
||
}
|
||
|
||
dwFlags = CERT_STORE_REVOCATION_FLAG |
|
||
CERT_STORE_SIGNATURE_FLAG |
|
||
CERT_STORE_TIME_VALIDITY_FLAG;
|
||
|
||
pTmpCert = CertGetIssuerCertificateFromStore(
|
||
hCAStore,
|
||
pCACert,
|
||
NULL,
|
||
&dwFlags );
|
||
|
||
if ( NULL == pTmpCert )
|
||
{
|
||
TRACE_OUT(("Issuer not found in CA store either"));
|
||
break;
|
||
}
|
||
|
||
if ( pCACert != pCert )
|
||
CertFreeCertificateContext(pCACert);
|
||
pCACert = pTmpCert;
|
||
|
||
if ((( CERT_STORE_REVOCATION_FLAG & dwFlags ) &&
|
||
!( CERT_STORE_NO_CRL_FLAG & dwFlags )) ||
|
||
( CERT_STORE_SIGNATURE_FLAG & dwFlags ) ||
|
||
( CERT_STORE_TIME_VALIDITY_FLAG & dwFlags ))
|
||
{
|
||
TRACE_OUT(("Problem with issuer in CA store: %x", dwFlags));
|
||
break;
|
||
}
|
||
|
||
dwFlags = CERT_STORE_REVOCATION_FLAG |
|
||
CERT_STORE_SIGNATURE_FLAG |
|
||
CERT_STORE_TIME_VALIDITY_FLAG;
|
||
|
||
pIssuerCert = CertGetIssuerCertificateFromStore(
|
||
hStore,
|
||
pCACert,
|
||
NULL,
|
||
&dwFlags );
|
||
|
||
}
|
||
|
||
if ( pCACert != pCert )
|
||
CertFreeCertificateContext ( pCACert );
|
||
|
||
if ( NULL == pIssuerCert )
|
||
{
|
||
WARNING_OUT(("Verify: Can't find issuer in store"));
|
||
}
|
||
|
||
// Check certificate
|
||
|
||
if ( NULL != pIssuerCert && 0 != dwFlags )
|
||
{
|
||
if ( dwFlags & CERT_STORE_SIGNATURE_FLAG )
|
||
{
|
||
WARNING_OUT(("Verify: Signature invalid"));
|
||
}
|
||
if ( dwFlags & CERT_STORE_TIME_VALIDITY_FLAG )
|
||
{
|
||
WARNING_OUT(("Verify: Cert expired"));
|
||
}
|
||
if ( dwFlags & CERT_STORE_REVOCATION_FLAG )
|
||
{
|
||
if (!(dwFlags & CERT_STORE_NO_CRL_FLAG))
|
||
{
|
||
WARNING_OUT(("Verify: Cert revoked"));
|
||
}
|
||
else
|
||
{
|
||
// We have no CRL for this issuer, do not
|
||
// treat as revoked by default:
|
||
dwFlags &= ~CERT_STORE_REVOCATION_FLAG;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check for no-incomplete-certs policy
|
||
//
|
||
|
||
#if 0
|
||
if (( NULL == pIssuerCert || ( 0 != ( CHECKFLAGS & dwFlags )))
|
||
{
|
||
WARNING_OUT(("Verify: policy prevents cert use"));
|
||
fRet = FALSE;
|
||
goto error;
|
||
}
|
||
|
||
//
|
||
// Is there a mandatory issuer?
|
||
//
|
||
|
||
if ( lstrlen(rePol.GetString( REGVAL_POL_ISSUER )))
|
||
{
|
||
DWORD cbIssuer;
|
||
|
||
//
|
||
// Get the issuer information
|
||
//
|
||
|
||
cbIssuer = CertNameToStr (
|
||
pCert->dwCertEncodingType,
|
||
&pCert->pCertInfo->Issuer,
|
||
CERT_FORMAT_FLAGS,
|
||
NULL, 0);
|
||
|
||
if ( 0 == cbIssuer )
|
||
{
|
||
ERROR_OUT(("GetUserInfo: no issuer string"));
|
||
fRet = FALSE;
|
||
goto error;
|
||
}
|
||
|
||
pIssuer = new CHAR[cbIssuer + 1];
|
||
|
||
if ( NULL == pIssuer )
|
||
{
|
||
ERROR_OUT(("GetUserInfo: error allocating issuer name"));
|
||
}
|
||
|
||
if ( 0 >= CertNameToStr (
|
||
pCert->dwCertEncodingType,
|
||
&pCert->pCertInfo->Issuer,
|
||
CERT_FORMAT_FLAGS,
|
||
pIssuer, cbIssuer+1))
|
||
{
|
||
ERROR_OUT(("GetUserInfo: error getting issuer string"));
|
||
fRet = FALSE;
|
||
goto error;
|
||
}
|
||
|
||
if ( lstrcmp ( rePol.GetString(REGVAL_POL_ISSUER),
|
||
pIssuer ))
|
||
{
|
||
WARNING_OUT(("Issuer (%s) didn't match policy (%s)",
|
||
pIssuer, rePol.GetString(REGVAL_POL_ISSUER)));
|
||
fRet = FALSE;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
error:
|
||
|
||
if ( NULL != hStore )
|
||
{
|
||
CertCloseStore(hStore, 0);
|
||
}
|
||
|
||
if ( NULL != hCAStore )
|
||
{
|
||
CertCloseStore(hCAStore, 0);
|
||
}
|
||
|
||
if ( NULL != pCert )
|
||
{
|
||
CertFreeCertificateContext ( pCert );
|
||
}
|
||
|
||
if ( NULL != pIssuerCert )
|
||
{
|
||
CertFreeCertificateContext ( pIssuerCert );
|
||
}
|
||
|
||
if ( NULL != pIssuer )
|
||
{
|
||
delete pIssuer;
|
||
}
|
||
|
||
return fRet;
|
||
}
|
||
|
||
|
||
BOOL SecurityContext::GetUserCert(PBYTE pInfo, PDWORD pcbInfo)
|
||
{
|
||
BOOL fRet = FALSE;
|
||
DWORD sc;
|
||
PCCERT_CONTEXT pCert = NULL;
|
||
|
||
ASSERT( NULL != pSecurityInterface );
|
||
|
||
//
|
||
// Get the certificate from the context
|
||
//
|
||
|
||
sc = pSecurityInterface->pfnTable->QueryContextAttributes(&hContext,
|
||
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
|
||
(PVOID)&pCert );
|
||
|
||
if ( SEC_E_OK != sc )
|
||
{
|
||
ERROR_OUT(("QueryContextAttributes failed"));
|
||
goto cleanup;
|
||
}
|
||
|
||
if ( NULL == pCert )
|
||
{
|
||
// The caller is not authenticated
|
||
WARNING_OUT(("No remote cred data"));
|
||
goto cleanup;
|
||
}
|
||
|
||
|
||
if ( NULL != pInfo && *pcbInfo >= pCert->cbCertEncoded )
|
||
{
|
||
memcpy ( pInfo, pCert->pbCertEncoded, pCert->cbCertEncoded );
|
||
}
|
||
*pcbInfo = pCert->cbCertEncoded;
|
||
|
||
fRet = TRUE;
|
||
|
||
cleanup:
|
||
|
||
if ( NULL != pCert )
|
||
{
|
||
CertFreeCertificateContext ( pCert );
|
||
}
|
||
|
||
return fRet;
|
||
}
|
||
|
||
|