Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1942 lines
52 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
credcach.cxx
Abstract:
This module contains the code to associate and cache SSPI credential
handles with local server addresses
Author:
Comments :
This is a two-level cache : the first level is at the instance level [INSTANCE_CACHE_ITEM]
and just contains a pointer to the actual item holding the credential handles. The second level
[CRED_CACHE_ITEM] holds the actual credential handles, as well as pointers to the mappers.
An INSTANCE_CACHE_ITEM points to a CRED_CACHE_ITEM, with a single CRED_CACHE_ITEM potentially
being referenced by several INSTANCE_CACHE_ITEMS, if several instances share the relevant data.
Each instance has 3 SSL-related components : the server certificate for the instance, the
certificate mappers for the instance and the trusted issuers for the instance [a combination of
the certificates in the ROOT store and the Certificate Trust List associated with the server]. Two
instances can share a CRED_CACHE_ITEM under the following circumstances :
1. The same server certificate
2. No/Same CTL
3. No mappers
The INSTANCE_CACHE_ITEM entries are keyed off "<instance ptr>"; the CRED_CACHE_ITEM
entries are keyed off an "SSL info" blob which can be used to uniquely distinguish between
credential sets that don't fulfill the criteria listed above. The SSL info blob is obtained by
querying the instance and consists of "<SHA1 hash of server cert>:<SHA1 hash of CTL>",
which has the advantage of being fixed length [20 bytes for cert hash, 20 bytes for CTL hash,
1 byte for ':']. If there is no CTL, the length is still 41, but everything after the CERT has is
zeroed out
Revision History:
Nimish Khanolkar FEB'98 Changes related to having one cert per instance
--*/
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <wincrypt.h>
#include <stdlib.h>
}
#include <dbgutil.h>
#include <buffer.hxx>
#include <ole2.h>
#include <imd.h>
#include <iadm.h>
#include <mb.hxx>
#include <iiscnfg.h>
//#include <iiscnfgp.h>
#include <refb.hxx>
#include <cmnull.hxx>
#include <iiscrmap.hxx>
#include "iistypes.hxx"
extern "C" {
#define SECURITY_WIN32
#include <sspi.h>
#include <ntsecapi.h>
#include <spseal.h>
#include <schnlsp.h>
#include ".\credcach.hxx"
#include <w3svc.h>
}
#include "iiscert.hxx"
#include "iisctl.hxx"
#include "capiutil.hxx"
#if DBG
#define PRINTF( x ) { char buff[256]; wsprintf x; OutputDebugString( buff ); }
#else
#define PRINTF( x )
#endif
#ifdef UNIT_TEST
DEBUG_PRINTS * g_pDebug = 0;
#endif
#define SSPIFILT_CERT_MAP_LIST "CertMapList"
#define SSPIFILT_INIT_MAP "CreateInstance"
#define SSPIFILT_TERM_MAP "TerminateMapper"
//
// Globals
//
LIST_ENTRY CredCacheList;
LIST_ENTRY InstanceCacheList;
CRED_CACHE_ITEM* g_pcciClient = NULL;
IMDCOM* pMDObject;
IMSAdminBaseW* pAdminObject;
//
// Prototypes
//
BOOL
LoadKeys(
IN IIS_SSL_INFO *pSSLInfo,
IN PVOID pvInstance,
IN HMAPPER** ppMappers,
IN DWORD cNbMappers,
OUT CredHandle * phCreds,
OUT DWORD * pcCred,
OUT CredHandle * phCredsMap,
OUT DWORD * pcCredMap,
IN LPSTR pszMdPath
);
BOOL
AddFullyQualifiedItem(
IN WCHAR * pwszServerPrefix,
CHAR * pszId,
IN UINT cId,
CHAR * pszAddress,
IN CHAR * pszPort,
IN LPVOID pvInstanceId,
IN PVOID pvsmc,
IN DWORD dwInstanceId
);
BOOL
AddItem(
CHAR * pszAddress,
LPVOID pvInstanceId,
RefBlob* pCert11,
RefBlob* pCertW,
IIS_SSL_INFO *pSSLInfo,
CRED_CACHE_ITEM** ppcci,
DWORD dwInstanceId,
LPSTR pszMdPath
);
BOOL
LookupCredential(
IN CHAR * pszAddress,
IN LPVOID pvInstanceId,
OUT CRED_CACHE_ITEM * * ppCCI
);
BOOL
InitializeCertMapping(
LPVOID pCert11,
LPVOID pCertW,
LPVOID pSslInfo,
HMAPPER*** pppMappers,
LPDWORD pdwMappers,
DWORD dwInstanceId
);
VOID WINAPI NotifySslChanges(
DWORD dwNotifyType,
LPVOID pInstance
);
BOOL GenerateSSLIdBlob( IIS_SSL_INFO *pSSLInfoObj,
PBYTE pbBlob );
VOID
InitCredCache(
VOID
)
/*++
Routine Description:
Initializes the credential cache
--*/
{
InitializeListHead( &CredCacheList );
InitializeListHead( &InstanceCacheList );
}
VOID
FreeCredCache(
VOID
)
/*++
Routine Description:
Releases all of the memory associated with the credential cache
--*/
{
//LIST_ENTRY * pEntry;
CRED_CACHE_ITEM * pcci;
INSTANCE_CACHE_ITEM * pici;
while ( !IsListEmpty( &InstanceCacheList ))
{
pici = CONTAINING_RECORD( InstanceCacheList.Flink,
INSTANCE_CACHE_ITEM,
m_ListEntry );
RemoveEntryList( &pici->m_ListEntry );
delete pici;
}
while ( !IsListEmpty( &CredCacheList ))
{
pcci = CONTAINING_RECORD( CredCacheList.Flink,
CRED_CACHE_ITEM,
m_ListEntry );
RemoveEntryList( &pcci->m_ListEntry );
pcci->Release();
}
if ( g_pcciClient != NULL )
{
g_pcciClient->Release();
g_pcciClient = NULL;
}
}
BOOL
LookupFullyQualifiedCredential(
IN WCHAR * pwszServerPrefix,
IN CHAR * pszIpAddress,
IN DWORD cbAddress,
IN CHAR * pszPort,
IN DWORD cbPort,
IN LPVOID pvInstanceId,
OUT CRED_CACHE_ITEM * * ppCCI,
IN PVOID pvsmc,
IN DWORD dwInstanceId
)
/*++
Routine Description:
Finds an entry in the credential cache or creates one if it's not found
Arguments:
pszIpAddress - Address name for this credential
cbAddress - Number of bytes (including '\0') of pszIpAddress
pszPort - Port ID for this credential
cbPort - Number of bytes ( including '\0') of pszPort
pvInstanceId - ptr to be used as w3 instance ID for this credential
ppCCI - Receives pointer to a Credential Cache Item
pvsmc - ptr to map context using the instance specified in pvInstanceId
dwInstanceId - w3 instance Id
Returns:
TRUE on success, FALSE on failure. If this item's key couldn't be found,
then ERROR_INVALID_NAME is returned.
--*/
{
INSTANCE_CACHE_ITEM * pcci;
LIST_ENTRY * pEntry;
CHAR achId[MAX_ADDRESS_LEN];
// LPSTR p = achId;
UINT cId = 0;
/*NimishK : not needed
//
// build ID of this credential request : IP address + port + instance
//
memcpy( p, pszIpAddress, cbAddress );
p += cbAddress;
*p++ = ':';
memcpy( p, pszPort, cbPort );
p += cbPort;
*p++ = ':';
// An INSTANCE_CACHE_ITEM is keyed of Instance ptr
// *(LPVOID*)p = pvInstanceId;
// cId = sizeof(LPVOID);
*/
RescanList:
for ( pEntry = InstanceCacheList.Flink;
pEntry != &InstanceCacheList;
pEntry = pEntry->Flink )
{
pcci = CONTAINING_RECORD( pEntry, INSTANCE_CACHE_ITEM, m_ListEntry );
// An INSTANCE_CACHE_ITEM is keyed of Instance ptr. Compare it.
if ( pcci->m_pvInstanceId == pvInstanceId )
{
//
// If this is an item we failed to find previously, then return
// an error
//
if ( pcci->m_fValid )
{
*ppCCI = pcci->m_pcci;
pcci->m_pcci->AddRef();
return TRUE;
}
SetLastError( ERROR_INVALID_NAME );
*ppCCI = NULL;
return FALSE;
}
}
//
// This address isn't in the list, try getting it credential cache then
// rescan the list for the new item. Note we leave the list locked
// while we try and get the item. This prevents multiple threads from
// trying to create the same item
//
if ( !AddFullyQualifiedItem( pwszServerPrefix, achId, cId, pszIpAddress, pszPort, pvInstanceId, pvsmc, dwInstanceId ))
{
return FALSE;
}
goto RescanList;
}
VOID
ReleaseCredential(
CRED_CACHE_ITEM * pcci
)
/*++
Routine Description:
Release a credential acquired via LookupFullyQualifiedCredential()
Arguments:
pCCI - pointer to a Credential Cache Item
Returns:
Nothing
--*/
{
if ( pcci )
{
pcci->Release();
}
}
//
// Secret value names. Each value exist in 4 variants,
// used to access the Lsa secret, the 1st one using IP + port
// the 2nd one IP only, the 3rd one port only
// The 4th entry specify designate the default value ( common to all address and ports )
//
LPSTR SecretTableA[4] =
{
"%s:%s", "%s", "%0.0s%s", "default",
};
LPWSTR SecretTableW[4] =
{
L"%S:%S", L"%S", L"%0.0S%S", L"default",
};
BOOL
GetMDSecret(
MB* pMB,
LPSTR pszObj,
DWORD dwId,
UNICODE_STRING**ppusOut
)
{
DWORD dwL = 0;
PUNICODE_STRING pusOut = NULL;
if ( pMB->GetData( pszObj,
dwId,
IIS_MD_UT_SERVER,
BINARY_METADATA,
NULL,
&dwL,
METADATA_SECURE ) || GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
if ( (pusOut = (PUNICODE_STRING)LocalAlloc( LMEM_FIXED, sizeof(UNICODE_STRING) )) &&
(pusOut->Buffer = (WORD*)LocalAlloc( LMEM_FIXED, dwL )) )
{
if ( pMB->GetData( pszObj,
dwId,
IIS_MD_UT_SERVER,
BINARY_METADATA,
pusOut->Buffer,
&dwL,
METADATA_SECURE ) )
{
pusOut->Length = pusOut->MaximumLength = (WORD)dwL;
*ppusOut = pusOut;
return TRUE;
}
}
if (pusOut) {
if (pusOut->Buffer) {
LocalFree( pusOut->Buffer );
}
LocalFree( pusOut );
}
}
return FALSE;
}
BOOL
GetAdminSecret(
WCHAR * pwszServerPrefix,
IMSAdminBaseW* pAdminObj,
LPWSTR pszObj,
DWORD dwId,
UNICODE_STRING**ppusOut
)
{
DWORD dwL = 0;
DWORD dwErr = 0;
PUNICODE_STRING pusOut;
HRESULT hRes = S_OK;
METADATA_HANDLE RootHandle;
METADATA_RECORD mdRecord;
WCHAR wszMetabaseRoot[256] ;
wsprintfW( wszMetabaseRoot, SSL_SERVICE_KEYS_MD_PATH_W, pwszServerPrefix ) ;
hRes = pAdminObject->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
wszMetabaseRoot,
METADATA_PERMISSION_READ,
100,
&RootHandle
);
if (FAILED(hRes)) {
return FALSE ;
}
mdRecord.dwMDIdentifier = dwId;
mdRecord.dwMDAttributes = METADATA_SECURE;
mdRecord.dwMDUserType = IIS_MD_UT_SERVER;
mdRecord.dwMDDataType = BINARY_METADATA;
mdRecord.dwMDDataLen = dwL;
mdRecord.pbMDData = NULL;
hRes = pAdminObj->GetData( RootHandle,
pszObj,
&mdRecord,
&dwL ) ;
if( FAILED(hRes) && (HRESULTTOWIN32( hRes ) == ERROR_INSUFFICIENT_BUFFER) )
{
if ( (pusOut = (PUNICODE_STRING)LocalAlloc( LMEM_FIXED, sizeof(UNICODE_STRING) )) &&
(pusOut->Buffer = (WORD*)LocalAlloc( LMEM_FIXED, dwL )) )
{
mdRecord.dwMDIdentifier = dwId;
mdRecord.dwMDAttributes = METADATA_SECURE;
mdRecord.dwMDUserType = IIS_MD_UT_SERVER;
mdRecord.dwMDDataType = BINARY_METADATA;
mdRecord.dwMDDataLen = dwL;
mdRecord.pbMDData = (PBYTE)pusOut->Buffer;
hRes = pAdminObj->GetData( RootHandle,
pszObj,
&mdRecord,
&dwL );
if( SUCCEEDED( hRes ) )
{
pusOut->Length = pusOut->MaximumLength = (WORD)mdRecord.dwMDDataLen;
*ppusOut = pusOut;
pAdminObject->CloseKey(RootHandle);
return TRUE;
}
dwErr = GetLastError();
}
if (pusOut) {
if (pusOut->Buffer) {
LocalFree( pusOut->Buffer );
}
LocalFree( pusOut );
}
}
pAdminObject->CloseKey(RootHandle);
return FALSE;
}
BOOL
AddFullyQualifiedItem(
IN WCHAR * pwszServerPrefix,
IN CHAR * pszId,
IN UINT cId,
IN CHAR * pszAddress,
IN CHAR * pszPort,
IN LPVOID pvInstanceId,
IN PVOID pvsmc,
IN DWORD dwInstanceId
)
/*++
Routine Description:
Creates a new item in the credential cache and adds it to the list
pszAddress must be a simple string that has no odd unicode mappings
This routine must be single threaded
Arguments:
pszId - ID of cache entry to add
pszAddress - Address name for this credential
pszPort - port for this credential
pvInstanceId - ptr to be used as service instance ID for this credential
pvsmc - ptr to map context using the instance specified in pvInstanceId
dwInstanceId - w3 instance Id
Returns:
TRUE on success, FALSE on failure
--*/
{
CHAR achSecretNameA[MAX_SECRET_NAME+1];
/* // NimishK : I don't think these are used anymore
//WCHAR achSecretName[MAX_SECRET_NAME+1];
//UNICODE_STRING * SecretValue[3];
//DWORD i;
//DWORD j;
*/
BOOL fRet = TRUE;
INSTANCE_CACHE_ITEM * pcci;
RefBlob* pBlob11 = NULL;
RefBlob* pBlobW = NULL;
CRED_CACHE_ITEM * pci;
//Added for CAPI stuff
IIS_SSL_INFO *pSSLInfoObj = NULL;
PBYTE pbSSLBlob = NULL;
PSERVICE_MAPPING_CONTEXT psmc = (PSERVICE_MAPPING_CONTEXT)pvsmc;
//Need to clean this up with pvInstanceId->QueryMDPath()
CHAR szMDPath[256] ;
wsprintf( szMDPath, "/LM/%S/%d", pwszServerPrefix,dwInstanceId ) ;
/*
// Nimishk : I dont think this is needed
MB mb( pMDObject );
CHAR szMetabaseRoot[256] ;
wsprintf( szMetabaseRoot, SSL_SERVICE_KEYS_MD_PATH, pwszServerPrefix ) ;
if ( !mb.Open( szMetabaseRoot ))
{
return FALSE;
}
*/
/* NimishK cId is no longer used
if ( cId > MAX_ADDRESS_LEN )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
*/
//
// Create and initialize the context item
//
pcci = new INSTANCE_CACHE_ITEM;
if ( !pcci )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
// memcpy( pcci->m_achId, pszId, cId );
// pcci->m_cbId = cId;
pcci->m_pvInstanceId = pvInstanceId;
pcci->m_fValid = FALSE;
pcci->m_pcci = NULL;
InsertTailList( &InstanceCacheList, &pcci->m_ListEntry );
/*Nimishk : Dont need this anymore
//
// Retrieve the secret from the registry
//
fRet = FALSE;
memset( SecretValue, 0, sizeof( SecretValue ));
*/
//
// Get the "SSL id" blob used to locate the CRED_CACHE_ITEM for this instance
//
if ( pvInstanceId )
{
IIS_SERVER_INSTANCE *pIISInstance = (IIS_SERVER_INSTANCE *) pvInstanceId;
if ( ( pSSLInfoObj = pIISInstance->QueryAndReferenceSSLInfoObj()) &&
GenerateSSLIdBlob( pSSLInfoObj,
(PBYTE) achSecretNameA ) )
{
fRet = TRUE ;
}
}
if ( fRet )
{
if( psmc )
{
//
// Retrieve blobs pointing to cert 1:1 mapper & cert wildcard mapper
//
if ( !psmc->ServerSupportFunction( pvInstanceId,
(LPVOID)&pBlob11,
SIMSSL_PROPERTY_MTCERT11 ) )
{
pBlob11 = NULL;
}
if ( !psmc->ServerSupportFunction( pvInstanceId,
(LPVOID)&pBlobW,
SIMSSL_PROPERTY_MTCERTW ) )
{
pBlobW = NULL;
}
}
//
// All instances w/o mappers maps to NULL, so that they share a CRED_CACHE_ENTRY
//
if ( pBlob11 == NULL && pBlobW == NULL )
{
pvInstanceId = NULL;
}
//
// try fo find it in cache
// returned cache entry's refcount is incremented by LookupCredential or AddItem
// if successfull
//
if ( !LookupCredential( (LPSTR)achSecretNameA, pvInstanceId, &pci ) )
{
if ( GetLastError() == ERROR_NO_MORE_ITEMS )
{
//
// This address isn't in the list, try getting it from the lsa then
// rescan the list for the new item. Note we leave the list locked
// while we try and get the item. This prevents multiple threads from
// trying to create the same item
//
if ( AddItem( (LPSTR)achSecretNameA,
pvInstanceId,
pBlob11,
pBlobW,
pSSLInfoObj,
&pci,
dwInstanceId,
(LPSTR)szMDPath ) )
{
pcci->m_pcci = pci;
pcci->m_fValid = TRUE;
}
}
}
else
{
pcci->m_pcci = pci;
pcci->m_fValid = TRUE;
}
}
//
// Return TRUE to indicate we added the item to the list. If the item
// wasn't found, then it's a place holder for that particular address
//
/* Not needed anymore
for ( i = 0; i < 3; i++ )
{
if( SecretValue[i] != NULL )
{
LocalFree( SecretValue[i]->Buffer );
LocalFree( SecretValue[i] );
}
}
*/
//
// Release blob now. CRED_CACHE_ITEM added a reference to them
// if entry created
//
if ( pBlob11 != NULL )
{
pBlob11->Release();
}
if ( pBlobW != NULL )
{
pBlobW->Release();
}
//
// Release IIS_SSL_INFO object, since we're done with it
//
if ( pSSLInfoObj )
{
IIS_SSL_INFO::Release( pSSLInfoObj );
}
return TRUE;
}
BOOL
LookupCredential(
IN CHAR * pszAddress,
IN LPVOID pvInstanceId,
OUT CRED_CACHE_ITEM * * ppCCI
)
/*++
Routine Description:
Finds an entry in the credential cache or creates one if it's not found
Arguments:
pszAddress - Address name for this credential
pvInstanceId - ptr to be used as w3 instance ID for this credential
ppCCI - Receives pointer to a Credential Cache Item
Returns:
TRUE on success, FALSE on failure. If this item's key couldn't be found,
then ERROR_NO_MORE_ITEMS is returned.
If key exist but entry invalid then ERROR_INVALID_NAME is returned.
--*/
{
CRED_CACHE_ITEM * pcci;
LIST_ENTRY * pEntry;
for ( pEntry = CredCacheList.Flink;
pEntry != &CredCacheList;
pEntry = pEntry->Flink )
{
pcci = CONTAINING_RECORD( pEntry, CRED_CACHE_ITEM, m_ListEntry );
if ( !memcmp( pcci->m_achSSLIdBlob, pszAddress, MAX_SSL_ID_LEN ) &&
pcci->m_pvInstanceId == pvInstanceId )
{
//
// If this is an item we failed to find previously, then return
// an error
//
if ( pcci->m_fValid )
{
*ppCCI = pcci;
pcci->AddRef();
return TRUE;
}
SetLastError( ERROR_INVALID_NAME );
return FALSE;
}
}
SetLastError( ERROR_NO_MORE_ITEMS );
return FALSE;
}
BOOL
AddItem(
CHAR * pszAddress,
LPVOID pvInstanceId,
RefBlob* pCert11,
RefBlob* pCertW,
IIS_SSL_INFO *pSSLInfoObj,
CRED_CACHE_ITEM** ppcci,
DWORD dwInstanceId,
LPSTR pszMdPath
)
/*++
Routine Description:
Creates a new item in the credential cache and adds it to the list
pszAddress must be a simple string that has no odd unicode mappings
This routine must be single threaded
Arguments:
pszAddress - Address name for this credential
pvInstanceId - ptr to be used as service instance ID for this credential
pCert11 - ptr to blob storing cert 1:1 mapper or NULL if no mapper
pCertW - ptr to blob storing cert wildcard mapper or NULL if no mapper
pSSLInfObj - pointer to SSL info to be used for this item
ppCCI - Receives pointer to a Credential Cache Item
dwInstanceId - w3 instance ID
Returns:
TRUE on success, FALSE on failure
--*/
{
BOOL fRet = TRUE;
BOOL fRetM;
BOOL fRetL;
CRED_CACHE_ITEM * pcci;
//
// Create and initialize the context item
//
pcci = new CRED_CACHE_ITEM;
if ( !pcci )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
memcpy( pcci->m_achSSLIdBlob, pszAddress, MAX_SSL_ID_LEN );
pcci->m_pvInstanceId = pvInstanceId;
memset( pcci->m_ahCred, 0, sizeof( pcci->m_ahCred ));
memset( pcci->m_ahCredMap, 0, sizeof( pcci->m_ahCredMap ));
memset( pcci->m_acbTrailer, 0, sizeof( pcci->m_acbTrailer ));
memset( pcci->m_acbHeader, 0, sizeof( pcci->m_acbHeader ));
InsertTailList( &CredCacheList, &pcci->m_ListEntry );
//
// build cert mapper DLLs array
//
if ( fRetM = InitializeCertMapping( pCert11 ? pCert11->QueryPtr() : NULL,
pCertW ? pCertW->QueryPtr() : NULL,
pSSLInfoObj ? (LPVOID) pSSLInfoObj : NULL,
&pcci->m_ppMappers,
&pcci->m_cNbMappers,
dwInstanceId ) )
{
//
// LoadKeys will zero out these values on success or failure. Note
// the password is stored as an ansi string because the SSL
// security structure is expecting an ANSI string
//
fRetL = LoadKeys( pSSLInfoObj,
pvInstanceId,
pcci->m_ppMappers,
pcci->m_cNbMappers,
pcci->m_ahCred,
&pcci->m_cCred,
pcci->m_ahCredMap,
&pcci->m_cCredMap,
pszMdPath );
}
else
{
fRetL = FALSE;
}
//
// Indicate the credential handle is valid on this address if we
// succeeded
//
if ( fRetL && fRetM )
{
pcci->m_fValid = TRUE;
}
else
{
pCert11 = NULL;
pCertW = NULL;
if ( fRetM && !fRetL )
{
TerminateCertMapping( pcci->m_ppMappers, pcci->m_cNbMappers );
pcci->m_ppMappers = NULL;
pcci->m_cNbMappers = 0;
}
}
//
// Store reference to mappers
//
if ( pcci->m_pBlob11 = pCert11 )
{
pCert11->AddRef();
}
if ( pcci->m_pBlobW = pCertW )
{
pCertW->AddRef();
}
//
// Store reference to SSL info
//
if ( pcci->m_pSslInfo = pSSLInfoObj )
{
pSSLInfoObj->Reference();
}
//
// Add ref, as will be referenced by *ppcci
//
pcci->AddRef();
*ppcci = pcci;
//
// Return TRUE to indicate we added the item to the list. If the item
// wasn't found, then it's a place holder for that particular address
//
return TRUE;
}
BOOL
LoadKeys(
IN IIS_SSL_INFO *pSSLInfoObj,
IN PVOID pvInstance,
IN HMAPPER** ppMappers,
IN DWORD cNbMappers,
OUT CredHandle * phCreds,
OUT DWORD * pcCred,
OUT CredHandle * phCredsMap,
OUT DWORD * pcCredMap,
IN LPSTR pszMdPath
)
/*++
Routine Description:
Finds an entry in the credential cache or creates one if it's not found
Arguments:
pSSLInfoObj - object containing SSL info to be used for this credential set
ppMappers - ptr to array of mapper DLLs
cNbMappers - number of entry in ppMappers
phCreds - ptr to array where to store credential handles w/o cert mapping
pcCred - ptr to counter where to store number of entry in phCreds
phCredsMap - ptr to array where to store credential handles w/ cert mapping
pcCredMap - ptr to counter where to store number of entry in phCredsMap
pszMdPath - path to metabase properties
Returns:
TRUE on success, FALSE on failure.
--*/
{
ASSERT( RtlValidateHeap( RtlProcessHeap(), 0, NULL ) );
//SCH_CRED creds;
// CAPI stuff
SCHANNEL_CRED xcreds;
HCERTSTORE hRootStore;
/*NimishK
// SCH_CRED_SECRET_CAPI scsp;
// SCH_CRED_PUBLIC_CERTCHAIN scpc;
*/
SECURITY_STATUS scRet;
SECURITY_STATUS scRetM;
TimeStamp tsExpiry;
DWORD i;
/*NimishK
// LPVOID ascsp[1];
// LPVOID ascpc[1];
// DWORD dwV;
// MB mb( pMDObject );
// BUFFER buAlg;
*/
// Added for CAPI stuff
PCCERT_CONTEXT pcCert = NULL;
LPVOID pcreds;
*pcCred = 0;
*pcCredMap = 0;
memset(&xcreds, 0, sizeof(xcreds));
xcreds.dwVersion = SCHANNEL_CRED_VERSION;
if ( pSSLInfoObj->GetCertificate() &&
pSSLInfoObj->GetCertificate()->IsValid() )
{
xcreds.cCreds = 1;
xcreds.paCred = pSSLInfoObj->GetCertificate()->QueryCertContextAddr();
}
else
{
return TRUE;
}
xcreds.cMappers = cNbMappers ;
xcreds.aphMappers = ppMappers;
if (pSSLInfoObj->GetTrustedIssuerStore( &hRootStore ))
{
xcreds.hRootStore = hRootStore;
}
else
{
xcreds.hRootStore = NULL;
}
pcreds = (LPVOID) &xcreds;
for ( i = 0; pEncProviders[i].pszName && i < MAX_PROVIDERS; i++ )
{
if ( !pEncProviders[i].fEnabled )
{
continue;
}
//creds.cMappers = 0;
//CAPI
// Credentials with no client cert mapping at all
//
((SCHANNEL_CRED*)pcreds)->cMappers = 0;
((SCHANNEL_CRED*)pcreds)->dwFlags = SCH_CRED_NO_SYSTEM_MAPPER;
ASSERT( RtlValidateHeap( RtlProcessHeap(), 0, NULL ) );
scRet = g_AcquireCredentialsHandle( NULL, // My name (ignored)
pEncProviders[i].pszName, // Package
SECPKG_CRED_INBOUND,// Use
NULL, // Logon Id (ign.)
pcreds, // auth data
NULL, // dce-stuff
NULL, // dce-stuff
&phCreds[*pcCred], // Handle
&tsExpiry );
PRINTF((buff, "Cred %08x:%08x : mapper %08x\n", phCreds[*pcCred], NULL ));
//
// DS mapper only - no mappers passed to AcquireCredentialsHandle(), clear the flag
// telling Schannel not to use the DS mapper
//
if ( pSSLInfoObj->UseDSMapper() )
{
((SCHANNEL_CRED*)pcreds)->cMappers = 0;
((SCHANNEL_CRED*)pcreds)->dwFlags = 0;
// DBGPRINTF((DBG_CONTEXT,
// "[SSPIFILT] Using DS mapper \n"));
}
//
// IIS mappers only - pass mappers to AcquireCredentialsHandle(), set flag in each one
// indicating that only IIS mappers are to be called, keep flag telling Schannel
// not to use DS mapper [set to SCH_CRED_NO_SYSTEM_MAPPER above]
//
else
{
((SCHANNEL_CRED*)pcreds)->cMappers = cNbMappers;
for ( DWORD dwI = 0; dwI < cNbMappers; dwI++ )
{
((SCHANNEL_CRED*)pcreds)->aphMappers[dwI]->m_dwFlags = SCH_FLAG_DEFAULT_MAPPER;
}
// DebugTrace(NULL,
// "[SSPIFILT] Using IIS mappers \n"));
}
//creds.cMappers = cNbMappers;
ASSERT( RtlValidateHeap( RtlProcessHeap(), 0, NULL ) );
scRetM = g_AcquireCredentialsHandle( NULL, // My name (ignored)
pEncProviders[i].pszName, // Package
SECPKG_CRED_INBOUND,// Use
NULL, // Logon Id (ign.)
pcreds, // auth data
NULL, // dce-stuff
NULL, // dce-stuff
&phCredsMap[*pcCredMap], // Handle
&tsExpiry );
//PRINTF((buff, "Cred %08x:%08x : mapper %08x\n", phCredsMap[*pcCredMap], creds.aphMappers ));
// Null out creds, it doesn't seem to be used
PRINTF( (buff, "Cred %08x:%08x ", phCredsMap[*pcCredMap] ));
ASSERT( RtlValidateHeap( RtlProcessHeap(), 0, NULL ) );
if ( !FAILED( scRetM ) && !FAILED( scRet ) )
{
*pcCred += 1;
*pcCredMap += 1;
}
}
if ( xcreds.hRootStore )
{
CertCloseStore( xcreds.hRootStore,
0 );
}
//
// Tell the caller about it.
//
if ( !*pcCred && FAILED( scRet ))
{
SetLastError( scRet );
return FALSE;
}
return TRUE;
}
BOOL
GetSecretW(
WCHAR * pszSecretName,
UNICODE_STRING * * ppSecretValue
)
/*++
Description:
Retrieves the specified unicode secret
Arguments:
pszSecretName - LSA Secret to retrieve
ppSecretValue - Receives pointer to secret value. Memory should be
freed by calling LsaFreeMemory
Returns:
TRUE on success and FALSE if any failure.
--*/
{
BOOL fResult;
NTSTATUS ntStatus;
LSA_UNICODE_STRING unicodeSecret;
LSA_HANDLE hPolicy;
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
//
// Open a policy to the remote LSA
//
InitializeObjectAttributes( &ObjectAttributes,
NULL,
0L,
NULL,
NULL );
ntStatus = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_ALL_ACCESS,
&hPolicy );
if ( !NT_SUCCESS( ntStatus ) )
{
SetLastError( LsaNtStatusToWinError( ntStatus ) );
return FALSE;
}
unicodeSecret.Buffer = pszSecretName;
unicodeSecret.Length = wcslen( pszSecretName ) * sizeof(WCHAR);
unicodeSecret.MaximumLength = unicodeSecret.Length + sizeof(WCHAR);
//
// Query the secret value.
//
ntStatus = LsaRetrievePrivateData( hPolicy,
&unicodeSecret,
(PLSA_UNICODE_STRING *) ppSecretValue );
fResult = NT_SUCCESS(ntStatus);
//
// Cleanup & exit.
//
LsaClose( hPolicy );
if ( !fResult )
SetLastError( LsaNtStatusToWinError( ntStatus ));
return fResult;
} // GetSecretW
BOOL
InitializeCertMapping(
LPVOID pCert11,
LPVOID pCertW,
LPVOID pSslInfo,
HMAPPER*** pppMappers,
LPDWORD pdwMappers,
DWORD dwInstanceId
)
/*++
Description:
Initialize the cert mapping DLL list
Arguments:
None
Returns:
TRUE on success and FALSE if any failure.
--*/
{
HKEY hKey;
DWORD dwType;
DWORD cbData;
BOOL fSt = TRUE;
LPSTR pszMapList = NULL;
LPSTR p;
LPSTR pDll;
UINT cMaxMap;
PFN_INIT_CERT_MAP pfnInit;
HINSTANCE hInst;
PMAPPER_VTABLE pVT;
DWORD cNbMappers = 0;
HMAPPER** ppMappers = NULL;
IisMapper* pIM;
if ( pCert11 == NULL && pCertW == NULL )
{
*pppMappers = NULL;
*pdwMappers = 0;
return TRUE;
}
//
// Open reg, count # mappers, allocate array, populate array
//
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
W3_PARAMETERS_KEY,
0,
KEY_ALL_ACCESS,
&hKey ) == NO_ERROR )
{
if ( RegQueryValueEx( hKey,
SSPIFILT_CERT_MAP_LIST,
NULL,
&dwType,
NULL,
&cbData ) == ERROR_SUCCESS &&
dwType == REG_SZ &&
(pszMapList = (LPSTR)LocalAlloc( LMEM_FIXED, cbData )) &&
RegQueryValueEx( hKey,
SSPIFILT_CERT_MAP_LIST,
NULL,
&dwType,
(LPBYTE)pszMapList,
&cbData ) == ERROR_SUCCESS )
{
//
// Count mappers, allocate structures
//
for ( cMaxMap = 1, p = pszMapList ;
p = strchr( p, ',' ) ;
++p, ++cMaxMap )
{
}
if ( !(ppMappers = (HMAPPER**)LocalAlloc( LMEM_FIXED,
sizeof(HMAPPER*) * cMaxMap )) )
{
fSt = FALSE;
goto cleanup;
}
//
// Load libraries, call init entry point
//
for ( pDll = pszMapList, cNbMappers = 0 ;
*pDll ;
)
{
p = strchr( pDll, ',' );
if ( p )
{
*p = '\0';
}
if ( (hInst = LoadLibrary( pDll )) )
{
//
// Use CreateInstance() entry point if present
//
if ( (pfnInit = (PFN_INIT_CERT_MAP)GetProcAddress(
hInst,
SSPIFILT_INIT_MAP )) )
{
if ( SEC_E_OK != (pfnInit)( (HMAPPER**)&pIM ) )
{
FreeLibrary( hInst );
goto next;
}
//
// Mapper handle its own HMAPPER allocation,
// will be freed when refcount drops to zero.
// Initial refcount is 1
//
ppMappers[cNbMappers] = (HMAPPER*)pIM;
pIM->pCert11Mapper = pCert11;
pIM->pCertWMapper = pCertW;
pIM->pvInfo = pSslInfo;
//Nimishk : Not sure if this is needed
pIM->dwInstanceId = dwInstanceId;
}
else
{
pIM = (IisMapper*)LocalAlloc( LMEM_FIXED, sizeof(IisMapper) );
ppMappers[cNbMappers] = (HMAPPER*)pIM;
pIM->hMapper.m_vtable = pVT = &pIM->mvtEntryPoints;
pIM->hMapper.m_dwMapperVersion = MAPPER_INTERFACE_VER;
pIM->hMapper.m_Reserved1 = NULL;
pIM->hInst = hInst;
pIM->lRefCount = 0;
pIM->fIsIisCompliant = FALSE;
pIM->pCert11Mapper = pCert11;
pIM->pCertWMapper = pCertW;
pIM->pvInfo = pSslInfo;
pIM->dwInstanceId = dwInstanceId;
if ( !(pVT->ReferenceMapper
= (REF_MAPPER_FN)GetProcAddress(
hInst,
"ReferenceMapper" )) ||
!(pVT->DeReferenceMapper
= (DEREF_MAPPER_FN)GetProcAddress(
hInst,
"DeReferenceMapper" )) ||
!(pVT->GetIssuerList
= (GET_ISSUER_LIST_FN)GetProcAddress(
hInst,
"GetIssuerList" )) ||
!(pVT->GetChallenge
= (GET_CHALLENGE_FN)GetProcAddress(
hInst,
"GetChallenge" )) ||
!(pVT->MapCredential
= (MAP_CREDENTIAL_FN)GetProcAddress(
hInst,
"MapCredential" )) ||
!(pVT->GetAccessToken
= (GET_ACCESS_TOKEN_FN)GetProcAddress(
hInst,
"GetAccessToken" )) ||
!(pVT->CloseLocator
= (CLOSE_LOCATOR_FN)GetProcAddress(
hInst,
"CloseLocator" ))
)
{
LocalFree( pIM );
FreeLibrary( hInst );
goto next;
}
//
// optional functions
//
if ( !(pVT->QueryMappedCredentialAttributes
= (QUERY_MAPPED_CREDENTIAL_ATTRIBUTES_FN)GetProcAddress(
hInst,
"QueryMappedCredentialAttributes" )) )
{
pVT->QueryMappedCredentialAttributes = NullQueryMappedCredentialAttributes;
}
}
//
// Valid mapper. Store reference
//
++cNbMappers;
}
next:
if ( p )
{
pDll = p + 1;
}
else
{
break;
}
}
}
else
{
fSt = FALSE;
}
RegCloseKey( hKey );
}
cleanup:
if ( fSt == FALSE )
{
if ( ppMappers != NULL )
{
LocalFree( ppMappers );
}
}
else
{
*pppMappers = ppMappers;
*pdwMappers = cNbMappers;
}
if ( pszMapList != NULL )
{
LocalFree( pszMapList );
}
return fSt;
}
VOID
SetMapperToEmpty(
UINT cMappers,
HMAPPER** pMappers
)
/*++
Description:
Set ptr to Null mapper ( fail Mapping requests )
Arguments:
cMappers - mapper count in pCertMapDlls, pMappers
pMappers - ptr to array of mappers
Returns:
Nothing
--*/
{
PMAPPER_VTABLE pTbl;
while ( cMappers-- )
{
if ( (*(IisMapper**)pMappers)->fIsIisCompliant )
{
pTbl = (*pMappers)->m_vtable;
//
// switch to infocomm-embedded mapper, so we can FreeLibrary the
// mapper DLL. refcount is decremented, because we now longer
// have a reference to the HMAPPER struct.
//
pTbl->ReferenceMapper = NullReferenceMapper;
pTbl->DeReferenceMapper = NullDeReferenceMapper;
pTbl->GetIssuerList = NullGetIssuerList;
pTbl->GetChallenge = NullGetChallenge;
pTbl->MapCredential = NullMapCredential;
pTbl->GetAccessToken = NullGetAccessToken;
pTbl->CloseLocator = NullCloseLocator;
pTbl->QueryMappedCredentialAttributes = NullQueryMappedCredentialAttributes;
if ( (*(IisMapper**)pMappers)->hInst != NULL )
{
FreeLibrary( (*(IisMapper**)pMappers)->hInst );
}
(pTbl->DeReferenceMapper)( *pMappers );
}
++pMappers;
}
}
BOOL
TerminateCertMapping(
HMAPPER** ppMappers,
DWORD cNbMappers
)
/*++
Description:
Terminate access to cert mapping DLL list
Arguments:
None
Returns:
TRUE on success and FALSE if any failure.
--*/
{
//
// call terminate mapper for all DLLs, FreeLibrary
//
if ( ppMappers != NULL )
{
SetMapperToEmpty( cNbMappers, ppMappers );
LocalFree( ppMappers );
}
return TRUE;
}
CRED_CACHE_ITEM::~CRED_CACHE_ITEM(
)
{
if ( m_fValid )
{
DWORD i;
for ( i = 0; i < m_cCred; i++ )
{
g_FreeCredentialsHandle( &m_ahCred[i] );
}
for ( i = 0; i < m_cCredMap; i++ )
{
g_FreeCredentialsHandle( &m_ahCredMap[i] );
}
TerminateCertMapping( m_ppMappers, m_cNbMappers );
if ( m_pBlob11 )
{
m_pBlob11->Release();
}
if ( m_pBlobW )
{
m_pBlobW->Release();
}
if ( m_pSslInfo )
{
IIS_SSL_INFO::Release( m_pSslInfo );
}
}
}
BOOL
LookupClientCredential(
IN WCHAR* pwszServerPrefix,
IN BOOL fUseCertificate,
OUT CRED_CACHE_ITEM** ppCCI
)
/*++
Routine Description:
Finds an entry in the credential cache or creates one if it's not found
Arguments:
fUseCertificate - if TRUE, binds client certificate to cred handle
ppCCI - Receives pointer to a Credential Cache Item
Returns:
TRUE on success, FALSE on failure. If this item's key couldn't be found,
then ERROR_INVALID_NAME is returned.
--*/
{
TimeStamp tsExpiry;
DWORD i, j;
CredHandle* phCreds;
DWORD* pcCred;
BOOL fCertSet = FALSE;
WCHAR achSecretName[MAX_SECRET_NAME+1];
//CHAR achSecretNameA[MAX_SECRET_NAME+1];
UNICODE_STRING* SecretValue[3];
PVOID pvPublicKey;
DWORD cbPublicKey;
PVOID pvPrivateKey;
DWORD cbPrivateKey;
CHAR * pszPassword;
SCH_CRED creds;
SCH_CRED_SECRET_PRIVKEY scsp;
SCH_CRED_PUBLIC_CERTCHAIN scpc;
SECURITY_STATUS scRet;
//SECURITY_STATUS scRetM;
LPVOID ascsp[1];
LPVOID ascpc[1];
//TraceFunctEnter( "LookupClientCredential" );
*ppCCI = NULL;
//
// first time thru allocate the single cache entry
//
if ( g_pcciClient == NULL )
{
g_pcciClient = new CRED_CACHE_ITEM;
if ( g_pcciClient == NULL )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
//pcciClient->m_cbAddr = 0;
g_pcciClient->m_cCred = 0;
g_pcciClient->m_cCredMap = 0;
g_pcciClient->m_fValid = FALSE;
g_pcciClient->m_cNbMappers = 0;
g_pcciClient->m_ppMappers = NULL;
g_pcciClient->m_pBlob11 = NULL;
g_pcciClient->m_pBlobW = NULL;
memset( g_pcciClient->m_ahCred, 0, sizeof( g_pcciClient->m_ahCred ));
memset( g_pcciClient->m_ahCredMap, 0, sizeof( g_pcciClient->m_ahCredMap ));
memset( g_pcciClient->m_acbTrailer, 0, sizeof( g_pcciClient->m_acbTrailer ));
memset( g_pcciClient->m_acbHeader, 0, sizeof( g_pcciClient->m_acbHeader ));
memset( g_pcciClient->m_acbBlockSize, 0, sizeof( g_pcciClient->m_acbBlockSize ));
//
// only the provider name is required for OUTBOUND connections
//
phCreds = g_pcciClient->m_ahCred;
pcCred = &g_pcciClient->m_cCred;
memset( SecretValue, 0, sizeof( SecretValue ));
if( fUseCertificate )
{
//
// get certificate from metabase and fill in SCH_CRED struct
//
//
// Try the 4 possible secret names
//
for ( j = 0 ; j < 4 ; ++j )
{
wsprintfW( achSecretName,
SecretTableW[j],
L"127.0.0.1", L"563" );
if ( GetAdminSecret( pwszServerPrefix,
pAdminObject,
achSecretName,
MD_SSL_PUBLIC_KEY,
&SecretValue[0] ) &&
GetAdminSecret( pwszServerPrefix,
pAdminObject,
achSecretName,
MD_SSL_PRIVATE_KEY,
&SecretValue[1] ) &&
GetAdminSecret( pwszServerPrefix,
pAdminObject,
achSecretName,
MD_SSL_KEY_PASSWORD,
&SecretValue[2] ) )
{
fCertSet = TRUE;
pvPublicKey = SecretValue[0]->Buffer;
cbPublicKey = SecretValue[0]->Length;
pvPrivateKey = SecretValue[1]->Buffer;
cbPrivateKey = SecretValue[1]->Length;
pszPassword = (char*) SecretValue[2]->Buffer;
break;
}
}
if( fCertSet )
{
//*pcCred = 0;
scsp.dwType = SCHANNEL_SECRET_PRIVKEY;
scsp.pPrivateKey = ((PBYTE)pvPrivateKey);
scsp.cbPrivateKey = cbPrivateKey;
scsp.pszPassword = pszPassword;
scpc.dwType = SCH_CRED_X509_CERTCHAIN;
scpc.cbCertChain = cbPublicKey - CERT_DER_PREFIX;
scpc.pCertChain = ((PBYTE) pvPublicKey) + CERT_DER_PREFIX;
creds.dwVersion = SCH_CRED_VERSION;
ascsp[0] = (LPVOID)&scsp;
ascpc[0] = (LPVOID)&scpc;
creds.paSecret = (LPVOID*)&ascsp;
creds.paPublic = (LPVOID*)&ascpc;
creds.cCreds = 1;
creds.cMappers = 0;
}
}
for ( i = 0; pEncProviders[i].pszName && i < MAX_PROVIDERS; i++ )
{
if ( !pEncProviders[i].fEnabled )
{
//DebugTrace( 0, "%s disabled", EncProviders[i].pszName );
continue;
}
scRet = g_AcquireCredentialsHandle( NULL, // My name (ignored)
pEncProviders[i].pszName, // Package
SECPKG_CRED_OUTBOUND, // Use
NULL, // Logon Id (ign.)
fCertSet ? &creds : NULL, // auth data
NULL, // dce-stuff
NULL, // dce-stuff
&phCreds[*pcCred], // Handle
&tsExpiry );
if ( !FAILED( scRet ))
{
//DebugTrace( 0, "%s credential: 0x%08X",
// EncProviders[i].pszName,
// phCreds[*pcCred] );
*pcCred += 1;
}
}
if( fCertSet )
{
//
// Zero out and free the key data memory, on success or fail
//
ZeroMemory( scsp.pPrivateKey, scsp.cbPrivateKey );
ZeroMemory( scpc.pCertChain, scpc.cbCertChain );
ZeroMemory( pszPassword, strlen( pszPassword ));
}
for ( i = 0; i < 3; i++ )
{
if( SecretValue[i] != NULL )
{
LocalFree( SecretValue[i]->Buffer );
LocalFree( SecretValue[i] );
}
}
//
// Tell the caller about it.
//
if ( !*pcCred && FAILED( scRet ))
{
SetLastError( scRet );
return FALSE;
}
else
{
g_pcciClient->m_fValid = TRUE;
g_pcciClient->AddRef();
*ppCCI = g_pcciClient;
return TRUE;
}
}
else if ( g_pcciClient->m_fValid == FALSE )
{
//
// cache was allocated but the initialization failed
//
SetLastError( ERROR_INVALID_NAME );
return FALSE;
}
else
{
//
// cache was successfully allocated and initialized on a previous call
//
g_pcciClient->AddRef();
*ppCCI = g_pcciClient;
return TRUE;
}
}
BOOL GenerateSSLIdBlob( IIS_SSL_INFO *pSSLInfoObj,
PBYTE pbBlob )
/*++
Description
Function called to get blob of data that uniquely identifies this set of SSL info
Arguments:
pSSLInfoObj - object containing SSL info to be used to generate the blob
pbBlob - buffer that gets updated with blob
Returns:
True on success, FALSE on failure
--*/
{
//
// If we haven't loaded the info yet, do so now
//
IIS_SERVER_CERT *pCert = pSSLInfoObj->GetCertificate();
IIS_CTL *pCTL = pSSLInfoObj->GetCTL();
//
// Definitely need a certificate
//
if ( !pCert || !pCert->IsValid())
{
return FALSE;
}
DWORD dwSize = MAX_SSL_ID_LEN;
PBYTE pbCurrent = pbBlob;
//
// Clear out old crud
//
memset( pbBlob, 0, MAX_SSL_ID_LEN );
//
//Try to get the cert hash
//
if ( !CertGetCertificateContextProperty( pCert->QueryCertContext(),
CERT_SHA1_HASH_PROP_ID,
(PVOID) pbCurrent,
&dwSize ) )
{
return FALSE;
}
ASSERT( dwSize == SHA1_HASH_LEN );
pbCurrent += dwSize;
dwSize = MAX_SSL_ID_LEN - dwSize - 1;
//
// Get and append the CTL hash, if there is one
//
if ( pCTL && pCTL->IsValid() )
{
if ( !CertGetCTLContextProperty( pCTL->QueryCTLContext(),
CERT_SHA1_HASH_PROP_ID,
(PVOID) pbCurrent,
&dwSize ) )
{
return FALSE;
}
ASSERT( dwSize == SHA1_HASH_LEN );
pbBlob[SHA1_HASH_LEN] = ':';
}
return TRUE;
}