1321 lines
32 KiB
C
1321 lines
32 KiB
C
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1995.
|
|
//
|
|
// File: context.c
|
|
//
|
|
// Contents: Schannel context management routines.
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 09-23-97 jbanes LSA integration stuff.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <spbase.h>
|
|
#include <certmap.h>
|
|
#include <mapper.h>
|
|
#include <dsysdbg.h>
|
|
|
|
DWORD g_cContext = 0;
|
|
|
|
#if DBG
|
|
void DumpContexts(void);
|
|
static void AddContextToList(PSPContext pContext);
|
|
static void DeleteContextFromList(PSPContext pContext);
|
|
#endif
|
|
|
|
/************************************************************************
|
|
* SPContextCreate
|
|
*
|
|
* Create a new SPContext, and initialize it.
|
|
*
|
|
* Returns - PSPContext pointer to context object.
|
|
*
|
|
\***********************************************************************/
|
|
|
|
PSPContext SPContextCreate(LPWSTR pszTarget)
|
|
{
|
|
|
|
PSPContext pContext;
|
|
|
|
SP_BEGIN("SPContextCreate");
|
|
|
|
pContext = (PSPContext)SPExternalAlloc( sizeof(SPContext));
|
|
if(!pContext)
|
|
{
|
|
SP_RETURN(NULL);
|
|
}
|
|
|
|
DebugLog((DEB_TRACE, "Create context:0x%p\n", pContext));
|
|
|
|
FillMemory(pContext, sizeof(SPContext), 0);
|
|
|
|
pContext->Magic = SP_CONTEXT_MAGIC;
|
|
pContext->Flags = 0;
|
|
|
|
GenerateRandomThumbprint(&pContext->ContextThumbprint);
|
|
|
|
if(pszTarget)
|
|
{
|
|
pContext->pszTarget = SPExternalAlloc((lstrlenW(pszTarget) + 1) * sizeof(WCHAR));
|
|
if(pContext->pszTarget == NULL)
|
|
{
|
|
SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
|
SPExternalFree(pContext);
|
|
SP_RETURN(NULL);
|
|
}
|
|
lstrcpyW(pContext->pszTarget, pszTarget);
|
|
}
|
|
|
|
|
|
pContext->dwRequestedCF = CF_EXPORT;
|
|
|
|
if(SslGlobalStrongEncryptionPermitted)
|
|
{
|
|
pContext->dwRequestedCF |= CF_DOMESTIC;
|
|
}
|
|
|
|
pContext->fCertChainsAllowed = FALSE;
|
|
|
|
|
|
g_cContext++;
|
|
|
|
#if DBG
|
|
//AddContextToList(pContext);
|
|
#endif
|
|
|
|
SP_RETURN(pContext);
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* VOID SPContextClean(PSPContext pContext)
|
|
*
|
|
* Clean out everything used by the handshake (in case we want
|
|
* to do another).
|
|
*
|
|
\***********************************************************************/
|
|
|
|
BOOL
|
|
SPContextClean(PSPContext pContext)
|
|
{
|
|
SP_BEGIN("SPContextClean");
|
|
|
|
if(pContext == NULL || pContext->Magic != SP_CONTEXT_MAGIC) {
|
|
DebugLog((DEB_WARN, "Attempt to delete invalid context\n"));
|
|
SP_RETURN(FALSE);
|
|
}
|
|
|
|
if(pContext->pbEncryptedKey)
|
|
{
|
|
SPExternalFree(pContext->pbEncryptedKey);
|
|
pContext->pbEncryptedKey = NULL;
|
|
}
|
|
|
|
if(pContext->pbServerKeyExchange)
|
|
{
|
|
SPExternalFree(pContext->pbServerKeyExchange);
|
|
pContext->pbServerKeyExchange = NULL;
|
|
}
|
|
|
|
if(pContext->pbIssuerList)
|
|
{
|
|
SPExternalFree(pContext->pbIssuerList);
|
|
pContext->pbIssuerList = NULL;
|
|
}
|
|
|
|
if(pContext->pClientHello)
|
|
{
|
|
SPExternalFree(pContext->pClientHello);
|
|
pContext->pClientHello = NULL;
|
|
}
|
|
|
|
if((pContext->Flags & CONTEXT_FLAG_FULL_HANDSHAKE) &&
|
|
(pContext->RipeZombie != NULL) &&
|
|
(pContext->RipeZombie->pClientCred != NULL))
|
|
{
|
|
// We've just done a client-side full handshake in which a default
|
|
// client certificate was selected. This client credential
|
|
// technically belongs to the cache (so that other contexts can
|
|
// query the certificate etc) but we want to free up the
|
|
// application-process hProv now, while we're in the context
|
|
// of the owning process.
|
|
PSPCredential pClientCred = pContext->RipeZombie->pClientCred;
|
|
|
|
if(pClientCred->hRemoteProv)
|
|
{
|
|
if(!RemoteCryptReleaseContext(
|
|
pClientCred->hRemoteProv,
|
|
0,
|
|
pClientCred->dwCapiFlags))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
}
|
|
pClientCred->hRemoteProv = 0;
|
|
}
|
|
}
|
|
|
|
pContext->fExchKey = FALSE;
|
|
|
|
SP_RETURN(TRUE);
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* VOID SPDeleteContext(PSPContext pContext)
|
|
*
|
|
* Delete an existing context object.
|
|
*
|
|
\***********************************************************************/
|
|
|
|
BOOL
|
|
SPContextDelete(PSPContext pContext)
|
|
{
|
|
SP_BEGIN("SPContextDelete");
|
|
|
|
DebugLog((DEB_TRACE, "Delete context:0x%p\n", pContext));
|
|
|
|
if(pContext == NULL || pContext->Magic != SP_CONTEXT_MAGIC)
|
|
{
|
|
DebugLog((DEB_WARN, "Attempt to delete invalid context\n"));
|
|
SP_RETURN(FALSE);
|
|
}
|
|
|
|
// DsysAssert((pContext->pCredGroup->dwFlags & CRED_FLAG_DELETED) == 0);
|
|
|
|
if(pContext->State != SP_STATE_CONNECTED &&
|
|
pContext->State != SP_STATE_SHUTDOWN)
|
|
{
|
|
DebugLog((DEB_WARN, "Attempting to delete an incompleted context\n"));
|
|
|
|
// The context is being deleted in the middle of a handshake,
|
|
// which is curious. This may be caused by the user aborting
|
|
// an operation, or it may be caused by a reconfiguration of
|
|
// the remote computer that caused the reconnect attempt to
|
|
// fail. If it's the latter cause, then the only way to recover
|
|
// is to request a full handshake next time. We have no way
|
|
// of knowing which it is, so it's probably best that we kill
|
|
// the current cache entry.
|
|
if(pContext->RipeZombie)
|
|
{
|
|
pContext->RipeZombie->ZombieJuju = FALSE;
|
|
}
|
|
}
|
|
|
|
SPContextClean(pContext);
|
|
|
|
if(pContext->pszTarget)
|
|
{
|
|
SPExternalFree(pContext->pszTarget);
|
|
pContext->pszTarget = NULL;
|
|
}
|
|
|
|
if(pContext->pszCredentialName)
|
|
{
|
|
SPExternalFree(pContext->pszCredentialName);
|
|
pContext->pszCredentialName = NULL;
|
|
}
|
|
|
|
//
|
|
// Delete session keys.
|
|
//
|
|
|
|
if(pContext->hReadKey)
|
|
{
|
|
if(pContext->pReadCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hReadKey, 0);
|
|
pContext->hReadKey = 0;
|
|
}
|
|
}
|
|
if(pContext->hPendingReadKey)
|
|
{
|
|
if(pContext->pPendingCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hPendingReadKey, 0);
|
|
pContext->hPendingReadKey = 0;
|
|
}
|
|
}
|
|
if(pContext->hWriteKey)
|
|
{
|
|
if(pContext->pWriteCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hWriteKey, 0);
|
|
pContext->hWriteKey = 0;
|
|
}
|
|
}
|
|
if(pContext->hPendingWriteKey)
|
|
{
|
|
if(pContext->pPendingCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hPendingWriteKey, 0);
|
|
pContext->hPendingWriteKey = 0;
|
|
}
|
|
}
|
|
|
|
if(pContext->hReadMAC)
|
|
{
|
|
if(pContext->pReadCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hReadMAC, 0);
|
|
pContext->hReadMAC = 0;
|
|
}
|
|
}
|
|
if(pContext->hPendingReadMAC)
|
|
{
|
|
if(pContext->pPendingCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hPendingReadMAC, 0);
|
|
pContext->hPendingReadMAC = 0;
|
|
}
|
|
}
|
|
if(pContext->hWriteMAC)
|
|
{
|
|
if(pContext->pWriteCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hWriteMAC, 0);
|
|
pContext->hWriteMAC = 0;
|
|
}
|
|
}
|
|
if(pContext->hPendingWriteMAC)
|
|
{
|
|
if(pContext->pPendingCipherInfo->aiCipher != CALG_SKIPJACK)
|
|
{
|
|
SchCryptDestroyKey(pContext->hPendingWriteMAC, 0);
|
|
pContext->hPendingWriteMAC = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Delete the handshake hashes
|
|
//
|
|
|
|
if(pContext->hMd5Handshake)
|
|
{
|
|
SchCryptDestroyHash(pContext->hMd5Handshake, pContext->RipeZombie->dwCapiFlags);
|
|
pContext->hMd5Handshake = 0;
|
|
}
|
|
if(pContext->hShaHandshake)
|
|
{
|
|
SchCryptDestroyHash(pContext->hShaHandshake, pContext->RipeZombie->dwCapiFlags);
|
|
pContext->hShaHandshake = 0;
|
|
}
|
|
|
|
SPDereferenceCredential(pContext->pCredGroup);
|
|
|
|
SPCacheDereference(pContext->RipeZombie);
|
|
|
|
#if DBG
|
|
//DeleteContextFromList(pContext);
|
|
#endif
|
|
|
|
FillMemory( pContext, sizeof( SPContext ), 0 );
|
|
g_cContext--;
|
|
|
|
SPExternalFree( pContext );
|
|
SP_RETURN(TRUE);
|
|
}
|
|
|
|
/************************************************************************
|
|
* SPContext SPContextSetCredentials
|
|
*
|
|
* Associate a set of credentials with a context.
|
|
*
|
|
* Returns - PSPContext pointer to context object.
|
|
*
|
|
\***********************************************************************/
|
|
SP_STATUS
|
|
SPContextSetCredentials(
|
|
PSPContext pContext,
|
|
PSPCredentialGroup pCred)
|
|
{
|
|
DWORD cbThumbprint;
|
|
BOOL fNewCredentials = FALSE;
|
|
|
|
SP_BEGIN("SPContextSetCredentials");
|
|
|
|
if(pContext->Magic != SP_CONTEXT_MAGIC)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
|
|
}
|
|
|
|
// DsysAssert((pCred->dwFlags & CRED_FLAG_DELETED) == 0);
|
|
|
|
|
|
//
|
|
// Associate the credential group with the context.
|
|
//
|
|
|
|
if(pCred != pContext->pCredGroup)
|
|
{
|
|
if(pContext->pCredGroup)
|
|
{
|
|
SPDereferenceCredential(pContext->pCredGroup);
|
|
}
|
|
|
|
SPReferenceCredential(pCred);
|
|
|
|
pContext->pCredGroup = pCred;
|
|
|
|
fNewCredentials = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Set the protocol.
|
|
//
|
|
|
|
if(pContext->State == SP_STATE_NONE)
|
|
{
|
|
switch(pCred->grbitProtocol)
|
|
{
|
|
case SP_PROT_UNI_CLIENT:
|
|
case SP_PROT_UNI_SERVER:
|
|
case SP_PROT_PCT1_CLIENT:
|
|
case SP_PROT_PCT1_SERVER:
|
|
case SP_PROT_SSL2_CLIENT:
|
|
case SP_PROT_SSL2_SERVER:
|
|
case SP_PROT_SSL3_CLIENT:
|
|
case SP_PROT_SSL3_SERVER:
|
|
case SP_PROT_TLS1_CLIENT:
|
|
case SP_PROT_TLS1_SERVER:
|
|
pContext->ProtocolHandler = ServerProtocolHandler;
|
|
pContext->InitiateHello = GenerateHello;
|
|
break;
|
|
|
|
default:
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If the client application has supplied a new credential, then
|
|
// attempt to choose a suitable client certificate to send to
|
|
// the server.
|
|
//
|
|
|
|
if(fNewCredentials &&
|
|
pContext->State == SSL3_STATE_GEN_SERVER_HELLORESP)
|
|
{
|
|
Ssl3CheckForExistingCred(pContext);
|
|
}
|
|
|
|
|
|
//
|
|
// Allow the "manual cred validation" flag to be set from either
|
|
// AcquireCredentialsHandle or InitializeSecurityContext.
|
|
//
|
|
|
|
if(pCred->dwFlags & CRED_FLAG_MANUAL_CRED_VALIDATION)
|
|
{
|
|
if((pContext->Flags & CONTEXT_FLAG_MUTUAL_AUTH) == 0)
|
|
{
|
|
pContext->Flags |= CONTEXT_FLAG_MANUAL_CRED_VALIDATION;
|
|
}
|
|
}
|
|
|
|
SP_RETURN(PCT_ERR_OK);
|
|
}
|
|
|
|
SP_STATUS
|
|
ContextInitCiphersFromCache(SPContext *pContext)
|
|
{
|
|
PSessCacheItem pZombie;
|
|
SP_STATUS pctRet;
|
|
|
|
pZombie = pContext->RipeZombie;
|
|
|
|
pContext->pPendingCipherInfo = GetCipherInfo(pZombie->aiCipher, pZombie->dwStrength);
|
|
pContext->pPendingHashInfo = GetHashInfo(pZombie->aiHash);
|
|
pContext->pKeyExchInfo = GetKeyExchangeInfo(pZombie->SessExchSpec);
|
|
|
|
pContext->dwPendingCipherSuiteIndex = pZombie->dwCipherSuiteIndex;
|
|
|
|
if(!IsCipherAllowed(pContext,
|
|
pContext->pPendingCipherInfo,
|
|
pZombie->fProtocol,
|
|
pZombie->dwCF))
|
|
{
|
|
pContext->pPendingCipherInfo = NULL;
|
|
return (SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH));
|
|
}
|
|
|
|
// Load the pending hash structure
|
|
pContext->pPendingHashInfo = GetHashInfo(pZombie->aiHash);
|
|
|
|
if(!IsHashAllowed(pContext,
|
|
pContext->pPendingHashInfo,
|
|
pZombie->fProtocol))
|
|
{
|
|
pContext->pPendingHashInfo = NULL;
|
|
return (SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH));
|
|
}
|
|
|
|
// load the exch info structure
|
|
pContext->pKeyExchInfo = GetKeyExchangeInfo(pZombie->SessExchSpec);
|
|
if(!IsExchAllowed(pContext,
|
|
pContext->pKeyExchInfo,
|
|
pZombie->fProtocol))
|
|
{
|
|
pContext->pKeyExchInfo = NULL;
|
|
return (SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH));
|
|
}
|
|
|
|
|
|
// Determine the CSP to use, based on the key exchange algorithm.
|
|
pctRet = DetermineClientCSP(pContext);
|
|
if(pctRet != PCT_ERR_OK)
|
|
{
|
|
return SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
|
|
}
|
|
|
|
#if DBG
|
|
switch(pZombie->fProtocol)
|
|
{
|
|
case SP_PROT_PCT1_CLIENT:
|
|
DebugLog((DEB_TRACE, "Protocol:PCT Client\n"));
|
|
break;
|
|
|
|
case SP_PROT_PCT1_SERVER:
|
|
DebugLog((DEB_TRACE, "Protocol:PCT Server\n"));
|
|
break;
|
|
|
|
case SP_PROT_SSL2_CLIENT:
|
|
DebugLog((DEB_TRACE, "Protocol:SSL2 Client\n"));
|
|
break;
|
|
|
|
case SP_PROT_SSL2_SERVER:
|
|
DebugLog((DEB_TRACE, "Protocol:SSL2 Server\n"));
|
|
break;
|
|
|
|
case SP_PROT_SSL3_CLIENT:
|
|
DebugLog((DEB_TRACE, "Protocol:SSL3 Client\n"));
|
|
break;
|
|
|
|
case SP_PROT_SSL3_SERVER:
|
|
DebugLog((DEB_TRACE, "Protocol:SSL3 Server\n"));
|
|
break;
|
|
|
|
case SP_PROT_TLS1_CLIENT:
|
|
DebugLog((DEB_TRACE, "Protocol:TLS Client\n"));
|
|
break;
|
|
|
|
case SP_PROT_TLS1_SERVER:
|
|
DebugLog((DEB_TRACE, "Protocol:TLS Server\n"));
|
|
break;
|
|
|
|
default:
|
|
DebugLog((DEB_TRACE, "Protocol:0x%x\n", pZombie->fProtocol));
|
|
}
|
|
|
|
DebugLog((DEB_TRACE, "Cipher: %s\n", pContext->pPendingCipherInfo->szName));
|
|
DebugLog((DEB_TRACE, "Strength:%d\n", pContext->pPendingCipherInfo->dwStrength));
|
|
DebugLog((DEB_TRACE, "Hash: %s\n", pContext->pPendingHashInfo->szName));
|
|
DebugLog((DEB_TRACE, "Exchange:%s\n", pContext->pKeyExchInfo->szName));
|
|
#endif
|
|
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
|
|
SP_STATUS
|
|
DetermineClientCSP(PSPContext pContext)
|
|
{
|
|
PSPCredential pCred = NULL;
|
|
|
|
if(!(pContext->RipeZombie->fProtocol & SP_PROT_CLIENTS))
|
|
{
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
if(pContext->RipeZombie->hMasterProv != 0)
|
|
{
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
switch(pContext->pKeyExchInfo->Spec)
|
|
{
|
|
case SP_EXCH_RSA_PKCS1:
|
|
pContext->RipeZombie->hMasterProv = g_hRsaSchannel;
|
|
break;
|
|
|
|
case SP_EXCH_DH_PKCS3:
|
|
pContext->RipeZombie->hMasterProv = g_hDhSchannelProv;
|
|
break;
|
|
|
|
default:
|
|
DebugLog((DEB_ERROR, "Appropriate Schannel CSP not available!\n"));
|
|
pContext->RipeZombie->hMasterProv = 0;
|
|
return SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
|
|
}
|
|
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
|
|
SP_STATUS
|
|
ContextInitCiphers(
|
|
SPContext *pContext,
|
|
BOOL fRead,
|
|
BOOL fWrite)
|
|
{
|
|
SP_BEGIN("ContextInitCiphers");
|
|
|
|
if((pContext == NULL) ||
|
|
(pContext->RipeZombie == NULL))
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
|
|
}
|
|
|
|
|
|
pContext->pCipherInfo = pContext->pPendingCipherInfo;
|
|
if ((NULL == pContext->pCipherInfo) || ((pContext->RipeZombie->fProtocol & pContext->pCipherInfo->fProtocol) == 0))
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH));
|
|
}
|
|
|
|
pContext->pHashInfo = pContext->pPendingHashInfo;
|
|
if ((NULL == pContext->pHashInfo)|| ((pContext->RipeZombie->fProtocol & pContext->pHashInfo->fProtocol) == 0))
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH));
|
|
}
|
|
|
|
if (NULL == pContext->pKeyExchInfo)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH));
|
|
}
|
|
|
|
if(fRead)
|
|
{
|
|
pContext->pReadCipherInfo = pContext->pPendingCipherInfo;
|
|
pContext->pReadHashInfo = pContext->pPendingHashInfo;
|
|
}
|
|
if(fWrite)
|
|
{
|
|
pContext->pWriteCipherInfo = pContext->pPendingCipherInfo;
|
|
pContext->pWriteHashInfo = pContext->pPendingHashInfo;
|
|
}
|
|
|
|
|
|
SP_RETURN(PCT_ERR_OK);
|
|
}
|
|
|
|
|
|
SP_STATUS
|
|
SPContextDoMapping(
|
|
PSPContext pContext)
|
|
{
|
|
PSessCacheItem pZombie;
|
|
PSPCredentialGroup pCred;
|
|
SP_STATUS pctRet;
|
|
LONG iState;
|
|
BOOL fSuccess;
|
|
LONG iMapper;
|
|
|
|
const SCH_MAPPER_DEFAULT_STATE = 0;
|
|
const SCH_MAPPER_NORMAL_STATE = 1;
|
|
|
|
SP_BEGIN("SPContextDoMapping");
|
|
|
|
pZombie = pContext->RipeZombie;
|
|
pCred = pContext->RipeZombie->pServerCred;
|
|
|
|
if(pCred->cMappers)
|
|
{
|
|
// Clear "called" flags.
|
|
for(iMapper = 0; iMapper < pCred->cMappers; iMapper++)
|
|
{
|
|
pCred->pahMappers[iMapper]->m_dwFlags &= ~SCH_FLAG_MAPPER_CALLED;
|
|
}
|
|
pZombie->phMapper = NULL;
|
|
|
|
|
|
for(iState = 0; iState < 2; iState++)
|
|
{
|
|
for(iMapper = 0; iMapper < pCred->cMappers; iMapper++)
|
|
{
|
|
if(pCred->pahMappers[iMapper]->m_dwFlags & SCH_FLAG_MAPPER_CALLED)
|
|
{
|
|
// This mapper has already had its chance.
|
|
continue;
|
|
}
|
|
|
|
if(iState == SCH_MAPPER_DEFAULT_STATE)
|
|
{
|
|
if(!(pCred->pahMappers[iMapper]->m_dwFlags & SCH_FLAG_DEFAULT_MAPPER))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if((pCred->dwFlags & CRED_FLAG_NO_SYSTEM_MAPPER) &&
|
|
(pCred->pahMappers[iMapper]->m_dwFlags & SCH_FLAG_SYSTEM_MAPPER))
|
|
{
|
|
// Skip the system mapper.
|
|
DebugLog((DEB_TRACE, "Skip the system mapper\n"));
|
|
continue;
|
|
}
|
|
|
|
#if DBG
|
|
if(pCred->pahMappers[iMapper]->m_dwFlags & SCH_FLAG_SYSTEM_MAPPER)
|
|
{
|
|
DebugLog((DEB_TRACE, "Invoke the system mapper\n"));
|
|
}
|
|
else
|
|
{
|
|
DebugLog((DEB_TRACE, "Invoke the application mapper\n"));
|
|
}
|
|
#endif
|
|
|
|
// Invoke mapper.
|
|
pctRet = SslMapCredential(
|
|
pCred->pahMappers[iMapper],
|
|
X509_ASN_CHAIN,
|
|
pZombie->pRemoteCert,
|
|
NULL,
|
|
&pZombie->hLocator);
|
|
|
|
pCred->pahMappers[iMapper]->m_dwFlags |= SCH_FLAG_MAPPER_CALLED;
|
|
|
|
if(NT_SUCCESS(pctRet))
|
|
{
|
|
// Mapping was successful.
|
|
DebugLog((DEB_TRACE, "Mapping was successful (0x%p)\n", pZombie->hLocator));
|
|
|
|
SslReferenceMapper(pCred->pahMappers[iMapper]);
|
|
if(pZombie->phMapper)
|
|
{
|
|
SslDereferenceMapper(pZombie->phMapper);
|
|
}
|
|
pZombie->phMapper = pCred->pahMappers[iMapper];
|
|
pZombie->LocatorStatus = SEC_E_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Mapping failed.
|
|
DebugLog((DEB_TRACE, "Mapping failed (0x%x)\n", pctRet));
|
|
|
|
pZombie->LocatorStatus = pctRet;
|
|
}
|
|
}
|
|
|
|
if(pZombie->phMapper)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SP_RETURN(PCT_ERR_OK);
|
|
}
|
|
|
|
SP_STATUS
|
|
RemoveDuplicateIssuers(
|
|
PBYTE pbIssuers,
|
|
PDWORD pcbIssuers)
|
|
{
|
|
DWORD cbIssuers = *pcbIssuers;
|
|
DWORD cBlob;
|
|
PCRYPT_DATA_BLOB rgBlob;
|
|
DWORD cbIssuer;
|
|
PBYTE pbIssuer;
|
|
PBYTE pbSource, pbDest;
|
|
DWORD i, j;
|
|
|
|
|
|
if(pbIssuers == NULL || cbIssuers < 2)
|
|
{
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
// Count number of issuers.
|
|
cBlob = 0;
|
|
pbIssuer = pbIssuers;
|
|
while(pbIssuer + 1 < pbIssuers + cbIssuers)
|
|
{
|
|
cbIssuer = MAKEWORD(pbIssuer[1], pbIssuer[0]);
|
|
|
|
pbIssuer += 2 + cbIssuer;
|
|
cBlob++;
|
|
}
|
|
|
|
// Allocate memory for blob list.
|
|
rgBlob = SPExternalAlloc(cBlob * sizeof(CRYPT_DATA_BLOB));
|
|
if(rgBlob == NULL)
|
|
{
|
|
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
|
}
|
|
|
|
// Build blob list.
|
|
cBlob = 0;
|
|
pbIssuer = pbIssuers;
|
|
while(pbIssuer + 1 < pbIssuers + cbIssuers)
|
|
{
|
|
cbIssuer = MAKEWORD(pbIssuer[1], pbIssuer[0]);
|
|
rgBlob[cBlob].cbData = 2 + cbIssuer;
|
|
rgBlob[cBlob].pbData = pbIssuer;
|
|
|
|
pbIssuer += 2 + cbIssuer;
|
|
cBlob++;
|
|
}
|
|
|
|
// Mark duplicates.
|
|
for(i = 0; i < cBlob; i++)
|
|
{
|
|
if(rgBlob[i].pbData == NULL) continue;
|
|
|
|
for(j = i + 1; j < cBlob; j++)
|
|
{
|
|
if(rgBlob[j].pbData == NULL) continue;
|
|
|
|
if(rgBlob[i].cbData == rgBlob[j].cbData &&
|
|
memcmp(rgBlob[i].pbData, rgBlob[j].pbData, rgBlob[j].cbData) == 0)
|
|
{
|
|
// duplicate found
|
|
rgBlob[j].pbData = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compact list.
|
|
pbSource = pbIssuers;
|
|
pbDest = pbIssuers;
|
|
for(i = 0; i < cBlob; i++)
|
|
{
|
|
if(rgBlob[i].pbData)
|
|
{
|
|
if(pbDest != pbSource)
|
|
{
|
|
MoveMemory(pbDest, pbSource, rgBlob[i].cbData);
|
|
}
|
|
pbDest += rgBlob[i].cbData;
|
|
}
|
|
pbSource += rgBlob[i].cbData;
|
|
}
|
|
*pcbIssuers = (DWORD)(pbDest - pbIssuers);
|
|
|
|
// Free blob list.
|
|
SPExternalFree(rgBlob);
|
|
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
|
|
SP_STATUS
|
|
SPContextGetIssuers(
|
|
PSPCredentialGroup pCredGroup)
|
|
{
|
|
LONG i;
|
|
PBYTE pbIssuerList;
|
|
DWORD cbIssuerList;
|
|
PBYTE pbIssuer;
|
|
DWORD cbIssuer;
|
|
PBYTE pbNew;
|
|
DWORD Status;
|
|
|
|
LockCredential(pCredGroup);
|
|
|
|
if((pCredGroup->pbTrustedIssuers != NULL) &&
|
|
!(pCredGroup->dwFlags & CRED_FLAG_UPDATE_ISSUER_LIST))
|
|
{
|
|
// Issuer list has already been built.
|
|
Status = PCT_ERR_OK;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
// Free existing issuer list.
|
|
if(pCredGroup->pbTrustedIssuers)
|
|
{
|
|
LocalFree(pCredGroup->pbTrustedIssuers);
|
|
pCredGroup->pbTrustedIssuers = NULL;
|
|
pCredGroup->cbTrustedIssuers = 0;
|
|
}
|
|
pCredGroup->dwFlags &= ~CRED_FLAG_UPDATE_ISSUER_LIST;
|
|
|
|
|
|
//
|
|
// Get issuers from application-specified ROOT store.
|
|
//
|
|
|
|
pbIssuerList = NULL;
|
|
cbIssuerList = 0;
|
|
|
|
while(pCredGroup->hApplicationRoots)
|
|
{
|
|
Status = ExtractIssuerNamesFromStore(pCredGroup->hApplicationRoots,
|
|
NULL,
|
|
&cbIssuerList);
|
|
if(Status != PCT_ERR_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pbIssuerList = LocalAlloc(LPTR, cbIssuerList);
|
|
if(pbIssuerList == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = ExtractIssuerNamesFromStore(pCredGroup->hApplicationRoots,
|
|
pbIssuerList,
|
|
&cbIssuerList);
|
|
if(Status != PCT_ERR_OK)
|
|
{
|
|
LocalFree(pbIssuerList);
|
|
pbIssuerList = NULL;
|
|
cbIssuerList = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Call each of the mappers in turn, building a large
|
|
// list of all trusted issuers.
|
|
//
|
|
|
|
for(i = 0; i < pCredGroup->cMappers; i++)
|
|
{
|
|
#if 0
|
|
if((pCredGroup->dwFlags & CRED_FLAG_NO_SYSTEM_MAPPER) &&
|
|
(pCredGroup->pahMappers[i]->m_dwFlags & SCH_FLAG_SYSTEM_MAPPER))
|
|
{
|
|
// Skip the system mapper.
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
Status = SslGetMapperIssuerList(pCredGroup->pahMappers[i],
|
|
&pbIssuer,
|
|
&cbIssuer);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(pbIssuerList == NULL)
|
|
{
|
|
pbIssuerList = LocalAlloc(LPTR, cbIssuer);
|
|
if(pbIssuerList == NULL)
|
|
{
|
|
SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pbNew = LocalReAlloc(pbIssuerList,
|
|
cbIssuerList + cbIssuer,
|
|
LMEM_MOVEABLE);
|
|
if(pbNew == NULL)
|
|
{
|
|
SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
|
break;
|
|
}
|
|
pbIssuerList = pbNew;
|
|
}
|
|
|
|
CopyMemory(pbIssuerList + cbIssuerList,
|
|
pbIssuer,
|
|
cbIssuer);
|
|
|
|
cbIssuerList += cbIssuer;
|
|
|
|
SPExternalFree(pbIssuer);
|
|
}
|
|
|
|
|
|
//
|
|
// Remove duplicates from list.
|
|
//
|
|
|
|
if(pbIssuerList)
|
|
{
|
|
Status = RemoveDuplicateIssuers(pbIssuerList, &cbIssuerList);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
LocalFree(pbIssuerList);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
pCredGroup->cbTrustedIssuers = cbIssuerList; // do not reverse these lines
|
|
pCredGroup->pbTrustedIssuers = pbIssuerList;
|
|
|
|
Status = PCT_ERR_OK;
|
|
|
|
cleanup:
|
|
|
|
UnlockCredential(pCredGroup);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
SP_STATUS
|
|
SPPickClientCertificate(
|
|
PSPContext pContext,
|
|
DWORD dwExchSpec)
|
|
{
|
|
PSPCredentialGroup pCred;
|
|
PSPCredential pCurrentCred;
|
|
SP_STATUS pctRet;
|
|
DWORD i;
|
|
|
|
pCred = pContext->pCredGroup;
|
|
if((pCred == NULL) || (pCred->pCredList == NULL))
|
|
{
|
|
return SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
|
|
}
|
|
|
|
pContext->pActiveClientCred = NULL;
|
|
|
|
pctRet = PCT_ERR_SPECS_MISMATCH;
|
|
|
|
for(i = 0; i < pCred->cCredList; i++)
|
|
{
|
|
pCurrentCred = pCred->pCredList + i;
|
|
|
|
if(pCurrentCred->pCert == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(pCurrentCred->pPublicKey == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Does this cert contain the proper key type.
|
|
if(dwExchSpec != pCurrentCred->dwExchSpec)
|
|
{
|
|
continue; // try the next cert.
|
|
}
|
|
|
|
// Does this cert have the proper encoding type?
|
|
if(pCurrentCred->pCert->dwCertEncodingType != X509_ASN_ENCODING)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// WE FOUND ONE
|
|
pContext->pActiveClientCred = pCurrentCred;
|
|
|
|
pctRet = PCT_ERR_OK;
|
|
break;
|
|
}
|
|
|
|
return pctRet;
|
|
}
|
|
|
|
SP_STATUS
|
|
SPPickServerCertificate(
|
|
PSPContext pContext,
|
|
DWORD dwExchSpec)
|
|
{
|
|
PSPCredentialGroup pCred;
|
|
PSPCredential pCurrentCred;
|
|
SP_STATUS pctRet;
|
|
DWORD i;
|
|
|
|
pCred = pContext->RipeZombie->pServerCred;
|
|
if((pCred == NULL) || (pCred->pCredList == NULL))
|
|
{
|
|
return SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
|
|
}
|
|
|
|
DsysAssert((pContext->RipeZombie->dwFlags & SP_CACHE_FLAG_READONLY) == 0);
|
|
|
|
#if 0
|
|
if(pContext->RipeZombie->dwFlags & SP_CACHE_FLAG_READONLY)
|
|
{
|
|
// What in the world causes this case to occur? Anything?
|
|
|
|
// Don't mess with the current credentials.
|
|
if(pContext->RipeZombie->pActiveServerCred == NULL)
|
|
{
|
|
return SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
|
|
}
|
|
else
|
|
{
|
|
return PCT_ERR_OK;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pContext->RipeZombie->pActiveServerCred = NULL;
|
|
|
|
pctRet = PCT_ERR_SPECS_MISMATCH;
|
|
|
|
for(i = 0; i < pCred->cCredList; i++)
|
|
{
|
|
pCurrentCred = pCred->pCredList + i;
|
|
|
|
if(pCurrentCred->pCert == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(pCurrentCred->pPublicKey == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Does this cert contain the proper key type.
|
|
if(dwExchSpec != pCurrentCred->dwExchSpec)
|
|
{
|
|
continue; // try the next cert.
|
|
}
|
|
|
|
// Does this cert have the proper encoding type?
|
|
if(pCurrentCred->pCert->dwCertEncodingType != X509_ASN_ENCODING)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// WE FOUND ONE
|
|
pContext->RipeZombie->pActiveServerCred = pCurrentCred;
|
|
pContext->RipeZombie->CredThumbprint = pCred->CredThumbprint;
|
|
pContext->RipeZombie->CertThumbprint = pCurrentCred->CertThumbprint;
|
|
|
|
// Set "master" provider handle to current credential's. Note that
|
|
// SSL3 will sometimes overide this selection in favor of its
|
|
// ephemeral key pair.
|
|
pContext->RipeZombie->hMasterProv = pCurrentCred->hProv;
|
|
|
|
pctRet = PCT_ERR_OK;
|
|
break;
|
|
}
|
|
|
|
return pctRet;
|
|
}
|
|
|
|
|
|
// This routine is called by the user process. It frees a context
|
|
// structure that was originally allocated by the LSA process,
|
|
// and passed over via the SPContextDeserialize routine.
|
|
BOOL
|
|
LsaContextDelete(PSPContext pContext)
|
|
{
|
|
if(pContext)
|
|
{
|
|
if(pContext->hReadKey)
|
|
{
|
|
SchCryptDestroyKey(pContext->hReadKey, 0);
|
|
pContext->hReadKey = 0;
|
|
}
|
|
if(pContext->hReadMAC)
|
|
{
|
|
SchCryptDestroyKey(pContext->hReadMAC, 0);
|
|
pContext->hReadMAC = 0;
|
|
}
|
|
if(pContext->hWriteKey)
|
|
{
|
|
SchCryptDestroyKey(pContext->hWriteKey, 0);
|
|
pContext->hWriteKey = 0;
|
|
}
|
|
if(pContext->hWriteMAC)
|
|
{
|
|
SchCryptDestroyKey(pContext->hWriteMAC, 0);
|
|
pContext->hWriteMAC = 0;
|
|
}
|
|
|
|
// Close locator if this handle belongs to the system
|
|
// default certificate mapper.
|
|
if(pContext->RipeZombie)
|
|
{
|
|
if(pContext->RipeZombie->hLocator && pContext->RipeZombie->phMapper)
|
|
{
|
|
if(pContext->RipeZombie->phMapper->m_dwFlags & SCH_FLAG_SYSTEM_MAPPER)
|
|
{
|
|
NtClose((HANDLE)pContext->RipeZombie->hLocator);
|
|
pContext->RipeZombie->hLocator = 0;
|
|
}
|
|
}
|
|
|
|
if(pContext->RipeZombie->pbServerCertificate)
|
|
{
|
|
SPExternalFree(pContext->RipeZombie->pbServerCertificate);
|
|
pContext->RipeZombie->pbServerCertificate = NULL;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Misc Utility functions.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#if DBG
|
|
typedef struct _DbgMapCrypto {
|
|
DWORD C;
|
|
PSTR psz;
|
|
} DbgMapCrypto;
|
|
|
|
DbgMapCrypto DbgCryptoNames[] = { {CALG_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 == DbgCryptoNames[i].C)
|
|
{
|
|
wsprintf(DbgNameSpace, "%s",
|
|
(DbgCryptoNames[i].psz));
|
|
return DbgNameSpace;
|
|
}
|
|
}
|
|
|
|
return("Unknown");
|
|
}
|
|
#endif
|
|
|
|
|
|
#if 0
|
|
PSTR
|
|
CopyString(
|
|
PSTR pszString)
|
|
{
|
|
PSTR pszNewString;
|
|
DWORD cchString;
|
|
|
|
cchString = lstrlen(pszString) + 1;
|
|
|
|
pszNewString = (PSTR)SPExternalAlloc(cchString);
|
|
|
|
if (pszNewString)
|
|
{
|
|
CopyMemory(pszNewString, pszString, cchString);
|
|
}
|
|
|
|
return(pszNewString);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef LTS
|
|
|
|
int __cdecl memcmp (
|
|
const void * buf1,
|
|
const void * buf2,
|
|
size_t count
|
|
)
|
|
{
|
|
if (!count)
|
|
return(0);
|
|
|
|
while ( --count && *(char *)buf1 == *(char *)buf2 ) {
|
|
buf1 = (char *)buf1 + 1;
|
|
buf2 = (char *)buf2 + 1;
|
|
}
|
|
|
|
return( *((unsigned char *)buf1) - *((unsigned char *)buf2) );
|
|
}
|
|
|
|
#endif
|
|
|
|
#if DBG
|
|
typedef struct _CONTEXTLIST {
|
|
struct _CONTEXTLIST *pNext;
|
|
PSPContext pContext;
|
|
} CONTEXTLIST, *PCONTEXTLIST;
|
|
|
|
PCONTEXTLIST g_ContextList = NULL;
|
|
|
|
static void AddContextToList(PSPContext pContext)
|
|
{
|
|
PCONTEXTLIST pNew = LocalAlloc(LMEM_FIXED, sizeof(CONTEXTLIST));
|
|
pNew->pNext = g_ContextList;
|
|
pNew->pContext = pContext;
|
|
g_ContextList = pNew;
|
|
}
|
|
|
|
static void DeleteContextFromList(PSPContext pContext)
|
|
{
|
|
PCONTEXTLIST pItem, pPrevItem;
|
|
BOOL fFound = FALSE;
|
|
|
|
if(g_ContextList == NULL)
|
|
{
|
|
DebugLog((DEB_ERROR, "List is empty!\n"));
|
|
}
|
|
else if(g_ContextList->pContext == pContext)
|
|
{
|
|
// Delete first item in list
|
|
g_ContextList = g_ContextList->pNext;
|
|
fFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pPrevItem = g_ContextList;
|
|
|
|
for(pItem = g_ContextList->pNext; pItem; pItem = pItem->pNext)
|
|
{
|
|
if(pItem->pContext == pContext)
|
|
{
|
|
pPrevItem->pNext = pItem->pNext;
|
|
LocalFree(pItem);
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
pPrevItem = pItem;
|
|
}
|
|
}
|
|
|
|
if(!fFound)
|
|
{
|
|
DebugLog((DEB_ERROR, "Unknown context 0x%p\n", pContext));
|
|
}
|
|
}
|
|
|
|
void DumpContexts(void)
|
|
{
|
|
PCONTEXTLIST pItem;
|
|
DWORD dwCount = 0;
|
|
|
|
SP_BEGIN("DumpContexts");
|
|
|
|
for(pItem = g_ContextList; pItem; pItem = pItem->pNext)
|
|
{
|
|
dwCount++;
|
|
|
|
DebugLog((DEB_TRACE, "Context:%8x, Cache:%8x\n",
|
|
pItem->pContext,
|
|
pItem->pContext->RipeZombie));
|
|
}
|
|
|
|
if(dwCount != g_cContext)
|
|
{
|
|
DebugLog((DEB_ERROR, "Context count discrepancy %d vs. %d\n",
|
|
dwCount,
|
|
g_cContext));
|
|
}
|
|
|
|
SP_END();
|
|
}
|
|
|
|
#endif
|