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.
 
 
 
 
 
 

4950 lines
116 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
iismap.cxx
Abstract:
Classes implementing IIS 1to1 Client Certificate Mapping
Author:
Philippe Choquier (phillich) 17-may-1996
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>
#include <iis64.h>
#include <buffer.hxx>
#define DLL_IMPLEMENTATION
#include <iismap.hxx>
#include "iismaprc.h"
#include "mapmsg.h"
#include "iiscmr.hxx"
#include <icrypt.hxx>
#include "dbgutil.h"
#define INITGUID
#include <ole2.h>
#include <iadmw.h>
#include <iiscnfg.h>
#include <iiscnfgp.h>
#include "simplemb.hxx"
#include <stringau.hxx>
//
// static vars
//
//
// Guid that is part of the *.MP file name
// MP file stores 1to1 mappings information
//
DWORD s_dwFileGuid = 0;
//
// Install path of IIS -> *.MP file is placed in the same directory
// as IIS files
//
LPSTR s_pszIisInstallPath = NULL;
DECLARE_DEBUG_PRINTS_OBJECT();
//
// global functions
//
HRESULT
GetSecurityDescriptorForMetabaseExtensionFile(
PSECURITY_DESCRIPTOR * ppsdStorage
)
/*++
Routine Description:
Build security descriptor that will be set for the extension file
*.mp (includes Administrators and System )
Arguments:
ppsdStorage - Security Descriptor to be set for extension file
Returns:
HRESULT
--*/
{
HRESULT hr = ERROR_SUCCESS;
BOOL fRet = FALSE;
DWORD dwDaclSize = 0;
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
PSID psidSystem = NULL;
PSID psidAdmin = NULL;
PACL paclDiscretionary = NULL;
DBG_ASSERT( ppsdStorage != NULL );
*ppsdStorage = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if ( *ppsdStorage == NULL )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
//
// Initialize the security descriptor.
//
fRet = InitializeSecurityDescriptor(
*ppsdStorage,
SECURITY_DESCRIPTOR_REVISION
);
if( !fRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
//
// Create the SIDs for the local system and admin group.
//
fRet = AllocateAndInitializeSid(
&ntAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,
0,
0,
0,
0,
0,
0,
&psidSystem
);
if( !fRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
fRet= AllocateAndInitializeSid(
&ntAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&psidAdmin
);
if( !fRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
//
// Create the DACL containing an access-allowed ACE
// for the local system and admin SIDs.
//
dwDaclSize = sizeof(ACL)
+ sizeof(ACCESS_ALLOWED_ACE)
+ GetLengthSid(psidAdmin)
+ sizeof(ACCESS_ALLOWED_ACE)
+ GetLengthSid(psidSystem)
- sizeof(DWORD);
paclDiscretionary = (PACL)LocalAlloc(LPTR, dwDaclSize );
if ( paclDiscretionary == NULL )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
fRet = InitializeAcl(
paclDiscretionary,
dwDaclSize,
ACL_REVISION
);
if( !fRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
fRet = AddAccessAllowedAce(
paclDiscretionary,
ACL_REVISION,
FILE_ALL_ACCESS,
psidSystem
);
if ( !fRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
fRet = AddAccessAllowedAce(
paclDiscretionary,
ACL_REVISION,
FILE_ALL_ACCESS,
psidAdmin
);
if ( !fRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
//
// Set the DACL into the security descriptor.
//
fRet = SetSecurityDescriptorDacl(
*ppsdStorage,
TRUE,
paclDiscretionary,
FALSE
);
if ( !fRet ) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
hr = S_OK;
exit:
if ( psidAdmin != NULL )
{
FreeSid( psidAdmin );
psidAdmin = NULL;
}
if ( psidSystem != NULL )
{
FreeSid( psidSystem );
psidSystem = NULL;
}
if ( FAILED( hr ) ) {
if ( paclDiscretionary != NULL )
{
LocalFree( paclDiscretionary );
paclDiscretionary = NULL;
}
if ( *ppsdStorage != NULL )
{
LocalFree( *ppsdStorage );
*ppsdStorage = NULL;
}
}
return hr;
}
HRESULT
FreeSecurityDescriptorForMetabaseExtensionFile(
PSECURITY_DESCRIPTOR psdStorage
)
/*++
Routine Description:
Free security descriptor generated by
GetSecurityDescriptorForMetabaseExtentionFile()
Arguments:
psdStorage - Security Descriptor to be freed
Returns:
HRESULT
--*/
{
HRESULT hr = ERROR_SUCCESS;
BOOL fRet = FALSE;
BOOL fDaclPresent;
PACL paclDiscretionary = NULL;
BOOL fDaclDefaulted;
//
// Get the DACL from the security descriptor.
//
if ( psdStorage == NULL )
{
hr = S_OK;
goto exit;
}
fRet = GetSecurityDescriptorDacl(
psdStorage,
&fDaclPresent,
&paclDiscretionary,
&fDaclDefaulted
);
if ( !fRet )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto exit;
}
if ( fDaclPresent && paclDiscretionary != NULL )
{
LocalFree( paclDiscretionary );
paclDiscretionary = NULL;
}
LocalFree( psdStorage );
hr = S_OK;
exit:
return hr;
}
//
// Fields for the Certificate 1:1 mapper
//
IISMDB_Fields IisCert11MappingFields[] = {
// certificate blob
{IISMDB_INDEX_CERT11_CERT, NULL, IDS_IISMAP11_FLDC, 40 },
// NT account name
{IISMDB_INDEX_CERT11_NT_ACCT, NULL, IDS_IISMAP11_FLDA, 40 },
// mapping name
{IISMDB_INDEX_CERT11_NAME, NULL, IDS_IISMAP11_FLDN, 40 },
// mapping enabled
{IISMDB_INDEX_CERT11_ENABLED, NULL, IDS_IISMAP11_FLDE, 40 },
// NT account password
{IISMDB_INDEX_CERT11_NT_PWD, NULL, IDS_IISMAP11_FLDP, 40 },
} ;
//
// default hierarchy for the Certificate 1:1 mapper
// This is basically list of fields that are used for
// mapping comparison
// 1 to 1 mapper uses only CERTIFICATE BLOB
//
IISMDB_HEntry IisCert11MappingHierarchy[] = {
{IISMDB_INDEX_CERT11_CERT, TRUE},
} ;
HINSTANCE s_hIISMapDll = (HINSTANCE)INVALID_HANDLE_VALUE;
// CIisAcctMapper
CIisAcctMapper::CIisAcctMapper(
)
/*++
Routine Description:
Constructor for CIisAcctMapper
Arguments:
None
Returns:
Nothing
--*/
{
m_cInit = -1;
m_pMapping = NULL;
m_cMapping = 0;
m_pAltMapping = NULL;
m_cAltMapping = 0;
m_pHierarchy = NULL;
m_cHierarchy = 0;
m_achFileName[0] = '\0';
m_pClasses = NULL;
m_pSesKey = NULL;
m_dwSesKey = 0;
m_hNotifyEvent = NULL;
m_fRequestTerminate = FALSE;
INITIALIZE_CRITICAL_SECTION( &csLock );
}
CIisAcctMapper::~CIisAcctMapper(
)
/*++
Routine Description:
Destructor for CIisAcctMapper
Arguments:
None
Returns:
Nothing
--*/
{
Reset();
if ( m_pHierarchy != NULL )
{
LocalFree( m_pHierarchy );
}
if ( m_pSesKey != NULL )
{
LocalFree( m_pSesKey );
}
DeleteCriticalSection( &csLock );
}
BOOL
CIisAcctMapper::Create(
VOID
)
/*++
Routine Description:
Create file for CIisAcctMapper with proper SD
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
// create file name, store in m_achFileName
// from s_dwFileGuid
// and s_pszIisInstallPath
UINT cLen;
HANDLE hF;
HRESULT hr = E_FAIL;
BOOL fRet = FALSE;
PSECURITY_DESCRIPTOR psdForMetabaseExtensionFile = NULL;
SECURITY_ATTRIBUTES saStorage;
// calculate the max # of characters that m_achFileName can take
DWORD cchMaxFileName = sizeof ( m_achFileName ) / sizeof (CHAR) - 1;
Reset();
if ( s_pszIisInstallPath )
{
cLen = (DWORD) strlen(s_pszIisInstallPath);
if ( cchMaxFileName < cLen )
{
return FALSE;
}
memcpy( m_achFileName, s_pszIisInstallPath, cLen );
// if path is not terminated by "\" then add one
if ( cLen && m_achFileName[cLen-1] != '\\' )
{
if ( cchMaxFileName < cLen + 1 )
{
return FALSE;
}
m_achFileName[cLen++] = '\\';
}
}
else
{
cLen = 0;
}
if( sizeof(m_achFileName) - cLen > 8 + 3 /*%08x.mp*/ )
{
wsprintf( m_achFileName+cLen,
"%08x.mp",
s_dwFileGuid );
}
else
{
//
// buffer m_achFileName is not sufficiently big
//
return FALSE;
}
//
// build security descriptor (Administrators and SYSTEM)
// to be set on metabase extension file
//
hr = GetSecurityDescriptorForMetabaseExtensionFile(
&psdForMetabaseExtensionFile );
if ( SUCCEEDED(hr) && psdForMetabaseExtensionFile != NULL )
{
saStorage.nLength = sizeof(SECURITY_ATTRIBUTES);
saStorage.lpSecurityDescriptor = psdForMetabaseExtensionFile;
saStorage.bInheritHandle = FALSE;
}
else
{
return FALSE;
}
//
// Open file and close it right away
// If file didn't exist, then empty file with good SD (Security Descriptor)
// will be created. That will later be opened using C runtime (fopen)
// in Save() method and good SD will persist.
// Ideally Save() should be using Win32 CreateFile()
// instead fopen and it could set SD itself. But since this is legacy
// source file and rather unsafe for making too many changes, pragmatic
// approach was chosen to set SD here in Create() method
//
if ( (hF = CreateFile( m_achFileName,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&saStorage,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL ) ) != INVALID_HANDLE_VALUE )
{
CloseHandle( hF );
fRet = TRUE;
}
else
{
fRet = FALSE;
}
if ( psdForMetabaseExtensionFile != NULL )
{
FreeSecurityDescriptorForMetabaseExtensionFile(
psdForMetabaseExtensionFile );
psdForMetabaseExtensionFile = NULL;
}
return fRet;
}
BOOL
CIisAcctMapper::Delete(
VOID
)
/*++
Routine Description:
Delete external storage used by this mapper ( i.e. file )
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
BOOL fSt = TRUE;
Lock();
if ( m_achFileName[0] )
{
fSt = DeleteFile( m_achFileName );
m_achFileName[0] = '\0';
}
Unlock();
return fSt;
}
BOOL
CIisAcctMapper::Serialize(
CStoreXBF* pXbf
)
/*++
Routine Description:
Serialize mapper reference ( NOT mapping info ) to buffer
Save() must be called to save mapping info before calling Serialize()
Arguments:
pXbf - ptr to extensible buffer to serialize to
Returns:
TRUE if success, FALSE if error
--*/
{
return pXbf->Append( (DWORD)strlen(m_achFileName) ) &&
pXbf->Append( (LPBYTE)m_achFileName, (DWORD)strlen(m_achFileName) ) &&
pXbf->Append( (LPBYTE)m_md5.digest, (DWORD)sizeof(m_md5.digest) ) &&
pXbf->Append( (DWORD)m_dwSesKey ) &&
pXbf->Append( (LPBYTE)m_pSesKey, (DWORD)m_dwSesKey ) ;
}
BOOL
CIisAcctMapper::Unserialize(
CStoreXBF* pXBF
)
/*++
Routine Description:
Unserialize mapper reference ( NOT mapping info ) from extensible buffer
Load() must be called to load mapping info
Arguments:
pXBF - ptr to extensible buffer
Returns:
TRUE if success, FALSE if error
--*/
{
LPBYTE pb = pXBF->GetBuff();
DWORD c = pXBF->GetUsed();
return Unserialize( &pb, &c );
}
BOOL
CIisAcctMapper::Unserialize(
LPBYTE* ppb,
LPDWORD pc
)
/*++
Routine Description:
Unserialize mapper reference ( NOT mapping info ) from buffer
Load() must be called to load mapping info
Arguments:
ppb - ptr to buffer
pc - ptr to buffer length
Returns:
TRUE if success, FALSE if error
--*/
{
DWORD cName;
if ( ::Unserialize( ppb, pc, &cName ) &&
cName <= *pc )
{
// calculate the max # of characters that m_achFileName can take
DWORD cchMaxFileName = sizeof ( m_achFileName ) / sizeof (CHAR) - 1;
if ( cchMaxFileName < cName )
{
return FALSE;
}
memcpy( m_achFileName, *ppb, cName );
m_achFileName[ cName ] = '\0';
*ppb += cName;
*pc -= cName;
if ( sizeof(m_md5.digest) <= *pc )
{
memcpy( m_md5.digest, *ppb, sizeof(m_md5.digest) );
*ppb += sizeof(m_md5.digest);
*pc -= sizeof(m_md5.digest);
if ( ::Unserialize( ppb, pc, &m_dwSesKey ) &&
cName <= *pc )
{
if ( m_pSesKey != NULL )
{
LocalFree( m_pSesKey );
}
if ( (m_pSesKey = (LPBYTE)LocalAlloc( LMEM_FIXED, m_dwSesKey )) == NULL )
{
m_dwSesKey = 0;
return FALSE;
}
memcpy( m_pSesKey, *ppb, m_dwSesKey );
*ppb += m_dwSesKey;
*pc -= m_dwSesKey;
return TRUE;
}
}
}
return FALSE;
}
BOOL
CIisAcctMapper::Serialize(
VOID
)
/*++
Routine Description:
Serialize mapper reference ( NOT mapping info ) to registry
Save() must be called to save mapping info before calling Serialize()
Warning : this allow only 1 instance
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
BOOL fSt = TRUE;
HKEY hKey;
LONG st;
if ( (st = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
GetRegKeyName(),
0,
KEY_WRITE,
&hKey )) != ERROR_SUCCESS )
{
SetLastError( st );
return FALSE;
}
if ( RegSetValueEx( hKey,
FILE_VALIDATOR,
NULL,
REG_BINARY,
(LPBYTE)m_md5.digest,
sizeof(m_md5.digest) ) != ERROR_SUCCESS ||
RegSetValueEx( hKey,
FILE_LOCATION,
NULL,
REG_SZ,
(LPBYTE)m_achFileName,
(DWORD) strlen(m_achFileName) ) != ERROR_SUCCESS )
{
fSt = FALSE;
}
RegCloseKey( hKey );
return fSt;
}
BOOL
CIisAcctMapper::Unserialize(
VOID
)
/*++
Routine Description:
Unserialize mapper reference ( NOT mapping info ) From registry
Load() must be called to load mapping info
Warning : this allow only 1 instance
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
BOOL fSt = FALSE;
HKEY hKey;
DWORD dwLen;
DWORD dwType;
LONG st;
// Check registry
if ( (st = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
GetRegKeyName(),
0,
KEY_READ,
&hKey )) != ERROR_SUCCESS )
{
SetLastError( st );
return FALSE;
}
dwLen = sizeof(m_md5.digest);
if ( RegQueryValueEx( hKey,
FILE_VALIDATOR,
NULL,
&dwType,
(LPBYTE)m_md5.digest,
&dwLen ) == ERROR_SUCCESS &&
dwType == REG_BINARY &&
(( dwLen = sizeof(m_achFileName) ), TRUE ) &&
RegQueryValueEx( hKey,
FILE_LOCATION,
NULL,
&dwType,
(LPBYTE)m_achFileName,
&dwLen ) == ERROR_SUCCESS &&
dwType == REG_SZ )
{
fSt = TRUE;
}
RegCloseKey( hKey );
return fSt;
}
BOOL
CIisAcctMapper::UpdateClasses(
BOOL fComputeMask
)
/*++
Routine Description:
Constructor for CIisAcctMapper
Arguments:
fComputeMask -- TRUE if mask to be recomputed
Returns:
TRUE if success, FALSE if error
--*/
{
UINT x;
UINT mx = 1 << m_cHierarchy;
if ( fComputeMask )
{
for ( x = 0 ; x < m_cMapping ; ++x )
{
m_pMapping[x]->UpdateMask( m_pHierarchy, m_cHierarchy );
}
}
SortMappings();
if ( m_pClasses == NULL )
{
m_pClasses = (MappingClass*)LocalAlloc(
LMEM_FIXED,
sizeof(MappingClass)*(mx+1) );
if ( m_pClasses == NULL )
{
return FALSE;
}
}
DWORD dwN = 0; // current class index in m_pClasses
DWORD dwLastClass = 0;
DWORD dwFirst = 0; // first entry for current dwLastClass
for ( x = 0 ; x <= m_cMapping ; ++x )
{
DWORD dwCur;
dwCur = (x==m_cMapping) ? dwLastClass+1: m_pMapping[x]->GetMask();
if ( dwCur > dwLastClass )
{
if ( x > dwFirst )
{
m_pClasses[dwN].dwClass = dwLastClass;
m_pClasses[dwN].dwFirst = dwFirst;
m_pClasses[dwN].dwLast = x - 1;
++dwN;
}
dwLastClass = dwCur;
dwFirst = x;
}
}
m_pClasses[dwN].dwClass = 0xffffffff;
return TRUE;
}
//static
int __cdecl
CIisAcctMapper::QsortIisMappingCmp(
const void *pA,
const void *pB )
/*++
Routine Description:
Compare function for 2 CIisMapping entries
compare using mask, then all fields as defined in
the linked CIisAcctMapper hierarchy
Arguments:
pA -- ptr to 1st element
pB -- ptr tp 2nd elemet
Returns:
-1 if *pA < *pB, 0 if *pA == *pB, 1 if *pA > *pB
--*/
{
return (*(CIisMapping**)pA)->Cmp( *(CIisMapping**)pB, FALSE );
}
BOOL
CIisAcctMapper::SortMappings(
)
/*++
Routine Description:
Sort the mappings. Masks for mapping objects are assumed
to be already computed.
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
qsort( (LPVOID)m_pMapping,
m_cMapping,
sizeof(CIisMapping*),
QsortIisMappingCmp
);
return TRUE;
}
//static
int __cdecl
CIisAcctMapper::MatchIisMappingCmp(
const void *pA,
const void *pB
)
/*++
Routine Description:
Compare function for 2 CIisMapping entries
do not uses mask, uses all fields as defined in
the linked CIisAcctMapper hierarchy
(used by CIisAcctMapper::FindMatch() )
Arguments:
pA -- ptr to 1st element
pB -- ptr tp 2nd elemet
Returns:
-1 if *pA < *pB, 0 if *pA == *pB, 1 if *pA > *pB
--*/
{
return ( *(CIisMapping**)pA)->Cmp( *(CIisMapping**)pB, TRUE );
}
BOOL
CIisAcctMapper::FindMatch(
CIisMapping* pQuery,
CIisMapping** pResult,
LPDWORD piResult
)
/*++
Routine Description:
Find a match base on field contents in CIisMapping
Arguments:
pQuery -- describe fields to consider for mapping
pResult -- updated with result if found mapping
Returns:
TRUE if mapping found, else FALSE
Lock:
mapper must be locked for ptr to remain valid
--*/
{
// iterate through classes, do bsearch on each
MappingClass *pH = m_pClasses;
if ( pH == NULL )
{
return FALSE;
}
while ( pH->dwClass != 0xffffffff )
{
CIisMapping **pRes = (CIisMapping **)bsearch( (LPVOID)&pQuery,
(LPVOID)(m_pMapping+pH->dwFirst),
pH->dwLast - pH->dwFirst + 1,
sizeof(CIisMapping*),
MatchIisMappingCmp );
if ( piResult != NULL )
{
*piResult = (DWORD) DIFF(pRes - m_pMapping);
}
if ( pRes != NULL )
{
*pResult = *pRes;
return TRUE;
}
++pH;
}
return FALSE;
}
void
CIisAcctMapper::Lock(
)
/*++
Routine Description:
Prevent access to mapper from other threads
Arguments:
None
Returns:
Nothing
--*/
{
EnterCriticalSection( &csLock );
}
void
CIisAcctMapper::Unlock(
)
/*++
Routine Description:
Re-enabled access to mapper from other threads
Arguments:
None
Returns:
Nothing
--*/
{
LeaveCriticalSection( &csLock );
}
BOOL
CIisAcctMapper::FlushAlternate(
BOOL fApply
)
/*++
Routine Description:
Flush alternate list, optionaly commiting it to the main list
Arguments:
fApply -- TRUE to commit changes made in alternate list
Returns:
TRUE if success, otherwise FALSE
--*/
{
UINT i;
UINT iM;
BOOL fSt = TRUE;
if ( m_pAltMapping )
{
if ( fApply )
{
//
// Transfer non existing objects from regular to alternate list
//
iM = min( m_cMapping, m_cAltMapping );
for ( i = 0 ; i < iM ; ++ i )
{
if ( m_pAltMapping[i] == NULL )
{
m_pAltMapping[i] = m_pMapping[i];
}
else
{
delete m_pMapping[i];
}
}
//
// delete extra objects
//
if ( m_cMapping > m_cAltMapping )
{
for ( i = m_cAltMapping ; i < m_cMapping ; ++i )
{
delete m_pMapping[i];
}
}
if ( m_pMapping )
{
LocalFree( m_pMapping );
}
m_pMapping = m_pAltMapping;
m_cMapping = m_cAltMapping;
fSt = UpdateClasses( TRUE );
}
else
{
for ( i = 0 ; i < m_cAltMapping ; ++i )
{
if ( m_pAltMapping[i] )
{
delete m_pAltMapping[i];
}
}
LocalFree( m_pAltMapping );
}
}
m_pAltMapping = NULL;
m_cAltMapping = 0;
return fSt;
}
BOOL
CIisAcctMapper::GetMapping(
DWORD iIndex,
CIisMapping** pM,
BOOL fGetFromAlternate,
BOOL fPutOnAlternate
)
/*++
Routine Description:
Get mapping entry based on index
Arguments:
iIndex -- index in mapping array
pM -- updated with pointer to mapping. mapping object
still owned by the mapper object.
fGetFromAlternate -- TRUE if retrieve from alternate list
fPutOnAlternate -- TRUE if put returned mapping on alternate list
Returns:
TRUE if success, FALSE if error
--*/
{
if ( fPutOnAlternate )
{
// create alternate list if not exist
if ( !m_pAltMapping && m_cMapping )
{
m_pAltMapping = (CIisMapping**)LocalAlloc( LMEM_FIXED, sizeof(CIisMapping*)*(m_cMapping) );
if ( m_pAltMapping == NULL )
{
return FALSE;
}
ZeroMemory( m_pAltMapping, sizeof(CIisMapping*) * m_cMapping );
m_cAltMapping = m_cMapping;
}
if ( iIndex < m_cAltMapping )
{
if ( m_pAltMapping[iIndex] == NULL &&
m_pMapping != NULL ) // work-around for compiler bug
{
// duplicate mapping to alternate list if not exist
if ( m_pMapping[iIndex]->Clone( pM ) )
{
m_pAltMapping[iIndex] = *pM;
}
else
{
return FALSE;
}
}
else
{
*pM = m_pAltMapping[iIndex];
}
return TRUE;
}
return FALSE;
}
if ( fGetFromAlternate &&
m_pAltMapping &&
iIndex < m_cAltMapping )
{
if ( m_pAltMapping[iIndex] )
{
*pM = m_pAltMapping[iIndex];
}
else
{
*pM = m_pMapping[iIndex];
}
return TRUE;
}
if ( iIndex < m_cMapping )
{
*pM = m_pMapping[iIndex];
return TRUE;
}
return FALSE;
}
BOOL
CIisAcctMapper::Update(
DWORD iIndex,
CIisMapping* pM
)
/*++
Routine Description:
Update a mapping
Arguments:
iIndex -- index in mapping array
pM -- pointer to mapping.
Returns:
TRUE if success, FALSE if error
--*/
{
if ( iIndex < m_cMapping )
{
pM->UpdateMask( m_pHierarchy, m_cHierarchy);
return m_pMapping[iIndex]->Copy( pM ) && UpdateClasses( FALSE );
}
return FALSE;
}
BOOL
CIisAcctMapper::Update(
DWORD iIndex
)
/*++
Routine Description:
Update a mapping
Arguments:
iIndex -- index in mapping array
Returns:
TRUE if success, FALSE if error
--*/
{
if ( iIndex < m_cMapping )
{
return m_pMapping[iIndex]->UpdateMask( m_pHierarchy, m_cHierarchy);
}
return FALSE;
}
BOOL
CIisAcctMapper::Add(
CIisMapping* pM,
BOOL fAlternate
)
/*++
Routine Description:
Add a mapping entry to mapping array
Transfer ownership of mapping object to mapper
Arguments:
pM -- pointer to mapping to be added to mapper
fAlternate - TRUE if add to alternate list
Returns:
TRUE if success, FALSE if error
--*/
{
CIisMapping **pMapping;
if ( fAlternate )
{
DWORD dwC = m_pAltMapping ? m_cAltMapping : m_cMapping;
CIisMapping** pMap = m_pAltMapping ? m_pAltMapping : m_pMapping;
pMapping = (CIisMapping**)LocalAlloc( LMEM_FIXED, sizeof(CIisMapping*)*(dwC+1) );
if ( pMapping == NULL )
{
return FALSE;
}
if ( m_pAltMapping )
{
memcpy( pMapping, pMap, sizeof(CIisMapping*) * dwC );
LocalFree( m_pAltMapping );
}
else
{
ZeroMemory( pMapping, sizeof(CIisMapping*)*(dwC+1) );
}
m_pAltMapping = pMapping;
m_pAltMapping[dwC] = pM;
m_cAltMapping = dwC + 1;
return TRUE;
}
else
{
pMapping = (CIisMapping**)LocalAlloc( LMEM_FIXED, sizeof(CIisMapping*)*(m_cMapping+1) );
if ( pMapping == NULL )
{
return FALSE;
}
if ( m_pMapping )
{
memcpy( pMapping, m_pMapping, sizeof(CIisMapping*) * m_cMapping );
LocalFree( m_pMapping );
}
m_pMapping = pMapping;
pM->UpdateMask( m_pHierarchy, m_cHierarchy );
m_pMapping[m_cMapping] = pM;
++m_cMapping;
SortMappings();
return UpdateClasses( FALSE );
}
}
DWORD
CIisAcctMapper::AddEx(
CIisMapping* pM
)
/*++
Routine Description:
Add a mapping entry to mapping array
Transfer ownership of mapping object to mapper
Arguments:
pM -- pointer to mapping to be added to mapper
Returns:
Index of entry if success, otherwise 0xffffffff
--*/
{
CIisMapping **pMapping = (CIisMapping**)LocalAlloc( LMEM_FIXED, sizeof(CIisMapping*)*(m_cMapping+1) );
if ( pMapping == NULL )
{
return 0xffffffff;
}
if ( m_pMapping )
{
memcpy( pMapping, m_pMapping, sizeof(CIisMapping*) * m_cMapping );
LocalFree( m_pMapping );
}
m_pMapping = pMapping;
pM->UpdateMask( m_pHierarchy, m_cHierarchy );
m_pMapping[m_cMapping] = pM;
++m_cMapping;
SortMappings();
if ( UpdateClasses( FALSE ) )
{
return m_cMapping-1;
}
return 0xffffffff;
}
BOOL
CIisAcctMapper::Delete(
DWORD dwIndex,
BOOL fUseAlternate
)
/*++
Routine Description:
Delete a mapping entry based on index
Arguments:
iIndex -- index in mapping array
fUseAlternate -- TRUE if update alternate list
Returns:
TRUE if success, FALSE if error
--*/
{
UINT i;
UINT iM;
if ( fUseAlternate )
{
//
// clone all entries from main to alternate list
//
if ( !m_pAltMapping )
{
m_pAltMapping = (CIisMapping**)LocalAlloc( LMEM_FIXED, sizeof(CIisMapping*)*(m_cMapping) );
if ( m_pAltMapping == NULL )
{
return FALSE;
}
ZeroMemory( m_pAltMapping, sizeof(CIisMapping*) * m_cMapping );
m_cAltMapping = m_cMapping;
}
iM = min( m_cMapping, m_cAltMapping );
for ( i = 0 ; i < iM ; ++i )
{
if ( m_pAltMapping[i] == NULL )
{
if ( !m_pMapping[i]->Clone( &m_pAltMapping[i] ) )
{
return FALSE;
}
}
}
if ( dwIndex < m_cAltMapping )
{
delete m_pAltMapping[dwIndex];
memmove( m_pAltMapping+dwIndex,
m_pAltMapping+dwIndex+1,
(m_cAltMapping - dwIndex - 1) * sizeof(CIisMapping*) );
--m_cAltMapping;
return TRUE;
}
return FALSE;
}
if ( dwIndex < m_cMapping )
{
delete m_pMapping[dwIndex];
memmove( m_pMapping+dwIndex,
m_pMapping+dwIndex+1,
(m_cMapping - dwIndex - 1) * sizeof(CIisMapping*) );
--m_cMapping;
return UpdateClasses( FALSE );
}
return FALSE;
}
BOOL
CIisAcctMapper::Save(
)
/*++
Routine Description:
Save mapper ( mappings, hierarchy, derived class private data )
to a file, updating registry entry with MD5 signature
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
UINT x;
FILE * fOut = NULL;
BOOL fSt = TRUE;
DWORD dwVal;
IIS_CRYPTO_STORAGE storage;
PIIS_CRYPTO_BLOB blob;
Lock();
MD5Init( &m_md5 );
if ( FAILED(storage.Initialize()) )
{
fSt = FALSE;
goto cleanup;
}
if ( m_pSesKey != NULL )
{
LocalFree( m_pSesKey );
m_pSesKey = NULL;
m_dwSesKey = 0;
}
if ( FAILED( storage.GetSessionKeyBlob( &blob ) ) ||
blob == NULL )
{
fSt = FALSE;
goto cleanup;
}
m_dwSesKey = IISCryptoGetBlobLength( blob );
if ( (m_pSesKey = (LPBYTE)LocalAlloc( LMEM_FIXED, m_dwSesKey)) == NULL )
{
m_dwSesKey = 0;
fSt = FALSE;
goto cleanup;
}
memcpy( m_pSesKey, (LPBYTE)blob, m_dwSesKey );
if ( (fOut = fopen( m_achFileName, "wb" )) == NULL )
{
fSt = FALSE;
goto cleanup;
}
// magic value & version
dwVal = IISMDB_FILE_MAGIC_VALUE;
if( fwrite( (LPVOID)&dwVal, sizeof(dwVal), 1, fOut ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &m_md5, (LPBYTE)&dwVal, sizeof(dwVal) );
dwVal = IISMDB_CURRENT_VERSION;
if( fwrite( (LPVOID)&dwVal, sizeof(dwVal), 1, fOut ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &m_md5, (LPBYTE)&dwVal, sizeof(dwVal) );
// mappings
if( fwrite( (LPVOID)&m_cMapping, sizeof(m_cMapping), 1, fOut ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &m_md5, (LPBYTE)&m_cMapping, sizeof(m_cMapping) );
for ( x = 0 ; x < m_cMapping ; ++x )
{
if ( !m_pMapping[x]->Serialize( fOut ,(VALID_CTX)&m_md5, (LPVOID)&storage) )
{
fSt = FALSE;
goto cleanup;
}
}
// save hierarchy
if( fwrite( (LPVOID)&m_cHierarchy, sizeof(m_cHierarchy), 1, fOut ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &m_md5, (LPBYTE)&m_cHierarchy, sizeof(m_cHierarchy) );
if( fwrite( (LPVOID)m_pHierarchy, sizeof(IISMDB_HEntry), m_cHierarchy, fOut ) != m_cHierarchy )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &m_md5, (LPBYTE)m_pHierarchy, sizeof(IISMDB_HEntry)*m_cHierarchy );
// save private data
fSt = SavePrivate( fOut, (VALID_CTX)&m_md5 );
MD5Final( &m_md5 );
cleanup:
if ( fOut != NULL )
{
fclose( fOut );
}
// update registry
if ( !fSt )
{
ZeroMemory( m_md5.digest, sizeof(m_md5.digest) );
}
Unlock();
return fSt;
}
BOOL
CIisAcctMapper::Reset(
)
/*++
Routine Description:
Reset mapper to empty state
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
UINT x;
// free all mapping
if ( m_pMapping != NULL )
{
for ( x = 0 ; x < m_cMapping ; ++x )
{
delete m_pMapping[x];
}
LocalFree( m_pMapping );
m_pMapping = NULL;
}
m_cMapping = 0;
if ( m_pClasses != NULL )
{
LocalFree( m_pClasses );
m_pClasses = NULL;
}
// default hierarchy
if ( m_pHierarchy == NULL )
{
IISMDB_HEntry *pH = GetDefaultHierarchy( &m_cHierarchy );
m_pHierarchy = (IISMDB_HEntry*)LocalAlloc( LMEM_FIXED, sizeof(IISMDB_HEntry)*m_cHierarchy );
if ( m_pHierarchy == NULL )
{
return FALSE;
}
memcpy( m_pHierarchy, pH, m_cHierarchy * sizeof(IISMDB_HEntry) );
}
return ResetPrivate();
}
BOOL
CIisAcctMapper::Load(
)
/*++
Routine Description:
Load mapper ( mappings, hierarchy, derived class private data )
from a file, checking registry entry for MD5 signature
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
UINT x;
MD5_CTX md5Check;
FILE * fIn;
BOOL fSt = TRUE;
DWORD dwVal;
IIS_CRYPTO_STORAGE storage;
Reset();
MD5Init( &md5Check );
if ( FAILED( storage.Initialize( (PIIS_CRYPTO_BLOB)m_pSesKey ) ) )
{
return FALSE;
}
if ( (fIn = fopen( m_achFileName, "rb" )) == NULL )
{
return FALSE;
}
// magic value & version
if( fread( (LPVOID)&dwVal, sizeof(dwVal), 1, fIn ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
if ( dwVal != IISMDB_FILE_MAGIC_VALUE )
{
SetLastError( ERROR_BAD_FORMAT );
fSt = FALSE;
goto cleanup;
}
MD5Update( &md5Check, (LPBYTE)&dwVal, sizeof(dwVal) );
if( fread( (LPVOID)&dwVal, sizeof(dwVal), 1, fIn ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &md5Check, (LPBYTE)&dwVal, sizeof(dwVal) );
// mappings
if( fread( (LPVOID)&m_cMapping, sizeof(m_cMapping), 1, fIn ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &md5Check, (LPBYTE)&m_cMapping, sizeof(m_cMapping) );
m_pMapping = (CIisMapping**)LocalAlloc( LMEM_FIXED, sizeof(CIisMapping*)*m_cMapping );
if ( m_pMapping == NULL )
{
fSt = FALSE;
goto cleanup;
}
for ( x = 0 ; x < m_cMapping ; ++x )
{
if ( (m_pMapping[x] = CreateNewMapping()) == NULL )
{
m_cMapping = x;
fSt = FALSE;
goto cleanup;
}
if ( !m_pMapping[x]->Deserialize( fIn ,(VALID_CTX)&md5Check, (LPVOID)&storage ) )
{
m_cMapping = x;
fSt = FALSE;
goto cleanup;
}
}
// load hierarchy
if( fread( (LPVOID)&m_cHierarchy, sizeof(m_cHierarchy), 1, fIn ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( &md5Check, (LPBYTE)&m_cHierarchy, sizeof(m_cHierarchy) );
m_pHierarchy = (IISMDB_HEntry*)LocalAlloc( LMEM_FIXED, sizeof(IISMDB_HEntry)*m_cHierarchy );
if ( m_pHierarchy == NULL )
{
fSt = FALSE;
goto cleanup;
}
if( fread( (LPVOID)m_pHierarchy, sizeof(IISMDB_HEntry), m_cHierarchy, fIn ) != m_cHierarchy )
{
fSt = FALSE;
goto cleanup;
}
//
// insure hierarchy correct
//
for ( x = 0 ; x < m_cHierarchy; ++x )
{
if ( m_pHierarchy[x].m_dwIndex >= m_cFields )
{
fSt = FALSE;
goto cleanup;
}
}
MD5Update( &md5Check, (LPBYTE)m_pHierarchy, sizeof(IISMDB_HEntry)*m_cHierarchy );
// load private data
fSt = LoadPrivate( fIn, (VALID_CTX)&md5Check );
MD5Final( &md5Check );
#if 0
//
// Don't use signature for now - a metabase Restore operation
// may have restored another signature, so metabase and
// file won't match
//
if ( !(fSt = !memcmp( m_md5.digest,
md5Check.digest,
sizeof(md5Check.digest) )) )
{
SetLastError( ERROR_INVALID_ACCESS );
}
#endif
cleanup:
fclose( fIn );
if ( !fSt && GetLastError() != ERROR_INVALID_ACCESS )
{
Reset();
}
else
{
UpdateClasses();
}
if ( !fSt )
{
char achErr[32];
LPCTSTR pA[2];
pA[0] = m_achFileName;
pA[1] = achErr;
_itoa( GetLastError(), achErr, 10 );
ReportIisMapEvent( EVENTLOG_ERROR_TYPE,
IISMAP_EVENT_LOAD_ERROR,
2,
pA );
}
return fSt;
}
// CIisCert11Mapper
CIisCert11Mapper::CIisCert11Mapper(
)
/*++
Routine Description:
Constructor for CIisCert11Mapper
Arguments:
None
Returns:
Nothing
--*/
{
m_pIssuers = NULL;
m_cIssuers = 0;
m_pFields = IisCert11MappingFields;
m_cFields = sizeof(IisCert11MappingFields)/sizeof(IISMDB_Fields);
m_dwOptions = IISMDB_CERT11_OPTIONS;
}
CIisCert11Mapper::~CIisCert11Mapper(
)
/*++
Routine Description:
Destructor for CIisCert11Mapper
Arguments:
None
Returns:
Nothing
--*/
{
}
BOOL
CIisCert11Mapper::Add(
CIisMapping* pM
)
/*++
Routine Description:
Add a mapping entry to mapping array
Transfer ownership of mapping object to mapper
Check is mapping to same NT account does not already exist.
Arguments:
pM -- pointer to mapping to be added to mapper
Returns:
TRUE if success, FALSE if error
--*/
{
UINT x;
// check if NT acct not already present.
// if so, return FALSE, SetLastError( ERROR_INVALID_PARAMETER );
if ( pM == NULL )
{
return FALSE;
}
LPSTR pCe;
DWORD dwCe;
LPSTR pCeIter;
DWORD dwCeIter;
if ( !pM->MappingGetField( IISMDB_INDEX_CERT11_CERT, (PBYTE *) &pCe, &dwCe, FALSE )
|| pCe == NULL )
{
dwCe = 0;
}
for ( x = 0 ; x < m_cMapping ; ++x )
{
if ( !m_pMapping[x]->MappingGetField( IISMDB_INDEX_CERT11_CERT, (PBYTE *) &pCeIter, &dwCeIter, FALSE )
|| pCeIter == NULL )
{
dwCeIter = 0;
}
if ( dwCe == dwCeIter && !memcmp( pCe, pCeIter, dwCe ) )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
}
return CIisAcctMapper::Add( pM );
}
IISMDB_HEntry*
CIisCert11Mapper::GetDefaultHierarchy(
LPDWORD pdwN
)
/*++
Routine Description:
return ptr to default hierarchy for certificates mapping
Arguments:
pdwN -- updated with hierarchy entries count
Returns:
ptr to hierarchy entries or NULL if error
--*/
{
*pdwN = sizeof(IisCert11MappingHierarchy) / sizeof(IISMDB_HEntry);
return IisCert11MappingHierarchy;
}
CIisMapping*
CIisCert11Mapper::CreateNewMapping(
LPBYTE pC,
DWORD dwC
)
/*++
Routine Description:
Create a new mapping from a certificate
Arguments:
pC -- cert ( ASN.1 format )
dwC -- length of cert
Returns:
ptr to mapping. ownership of this object is transfered to caller.
NULL if error
--*/
{
CCert11Mapping *pCM = new CCert11Mapping( this );
if ( pCM == NULL )
{
return NULL;
}
if ( pCM->Init( pC, dwC, m_pHierarchy, m_cHierarchy ) )
{
return (CIisMapping*)pCM;
}
delete pCM;
return NULL;
}
BOOL
CIisCert11Mapper::ResetPrivate(
)
/*++
Routine Description:
Reset CIisCert11Mapper issuer list
Arguments:
None
Returns:
TRUE if success, FALSE if error
--*/
{
// free issuer list
if ( m_pIssuers != NULL )
{
for ( UINT x = 0 ; x < m_cIssuers ; ++x )
{
LocalFree( m_pIssuers[x].pbIssuer );
}
LocalFree( m_pIssuers );
m_pIssuers = NULL;
}
m_cIssuers = 0;
return TRUE;
}
BOOL
CIisCert11Mapper::LoadPrivate(
FILE* fIn,
VALID_CTX pMD5
)
/*++
Routine Description:
Load issuer list
Arguments:
fIn -- file to read from
pMD5 -- MD5 to update with signature from input byte stream
Returns:
TRUE if success, FALSE if error
--*/
{
BOOL fSt = TRUE;
UINT x;
UINT cLen;
CHAR achBuf[64];
if( fread( (LPVOID)&m_cIssuers, sizeof(m_cIssuers), 1, fIn ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)&m_cIssuers, sizeof(m_cIssuers) );
m_pIssuers = (IssuerAccepted*)LocalAlloc( LMEM_FIXED, sizeof(IssuerAccepted)*m_cIssuers );
if ( m_pIssuers == NULL )
{
fSt = FALSE;
goto cleanup;
}
for ( x = 0 ; x < m_cIssuers ; ++x )
{
if ( fread( (LPVOID)&m_pIssuers[x].cbIssuerLen, sizeof(m_pIssuers[x].cbIssuerLen), 1, fIn ) != 1 )
{
m_cIssuers = x;
fSt = FALSE;
goto cleanup;
}
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)&m_pIssuers[x].cbIssuerLen, sizeof(m_pIssuers[x].cbIssuerLen) );
if ( (m_pIssuers[x].pbIssuer = (LPBYTE)LocalAlloc( LMEM_FIXED, m_pIssuers[x].cbIssuerLen )) == NULL )
{
m_cIssuers = x;
fSt = FALSE;
goto cleanup;
}
if ( fread( m_pIssuers[x].pbIssuer, m_pIssuers[x].cbIssuerLen, 1, fIn ) != 1 )
{
m_cIssuers = x;
fSt = FALSE;
goto cleanup;
}
MD5Update( (MD5_CTX*)pMD5, m_pIssuers[x].pbIssuer, m_pIssuers[x].cbIssuerLen );
}
//
// Read subject source (read to comply with legacy format)
//
if( Iisfgets( achBuf, sizeof(achBuf), fIn ) == NULL )
{
fSt = FALSE;
goto cleanup;
}
cLen = (DWORD) strlen(achBuf);
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)achBuf, cLen );
//
// We don't save the "subject source"
// It is legacy value not used anywhere
//
//
// Read default domain (read to comply with legacy format)
//
if( Iisfgets( achBuf, sizeof(achBuf), fIn ) == NULL )
{
fSt = FALSE;
goto cleanup;
}
cLen = (DWORD) strlen(achBuf);
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)achBuf, cLen );
//
// We don't save the "default domain"
// It is legacy value not used anywhere
//
cleanup:
return fSt;
}
BOOL
CIisCert11Mapper::SavePrivate(
FILE* fOut,
VALID_CTX pMD5
)
/*++
Routine Description:
Save issuer list
Arguments:
fOut -- file to write to
pMD5 -- MD5 to update with signature of output byte stream
Returns:
TRUE if success, FALSE if error
--*/
{
BOOL fSt = TRUE;
UINT x;
if( fwrite( (LPVOID)&m_cIssuers, sizeof(m_cIssuers), 1, fOut ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)&m_cIssuers, sizeof(m_cIssuers) );
for ( x = 0 ; x < m_cIssuers ; ++x )
{
if ( fwrite( (LPVOID)&m_pIssuers[x].cbIssuerLen, sizeof(m_pIssuers[x].cbIssuerLen), 1, fOut ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)&m_pIssuers[x].cbIssuerLen, sizeof(m_pIssuers[x].cbIssuerLen) );
if ( fwrite( m_pIssuers[x].pbIssuer, m_pIssuers[x].cbIssuerLen, 1, fOut ) != 1 )
{
fSt = FALSE;
goto cleanup;
}
MD5Update( (MD5_CTX*)pMD5, m_pIssuers[x].pbIssuer, m_pIssuers[x].cbIssuerLen );
}
//
// Write subject source
// legacy value, we don't write it out any more
// write empty string to avoid potential compatibility issues
//
Iisfputs( "", fOut );
//
// Write default domain
// legacy value, we don't write it out any more
// write empty string to avoid potential compatibility issues
//
Iisfputs( "", fOut );
cleanup:
return fSt;
}
//static
INT
CIisCert11Mapper::Iisfputs(
const char* pBuf,
FILE* fOut
)
/*++
Routine Description:
write string to the output file
string will be terminated by new line
Arguments:
Returns:
TRUE if success, FALSE if error
--*/
{
return (fputs( pBuf, fOut ) == EOF || fputc( '\n', fOut ) == EOF)
? EOF
: 0;
}
//static
LPSTR
CIisCert11Mapper::Iisfgets(
LPSTR pBuf,
INT cMax,
FILE* fIn
)
/*++
Routine Description:
Read string from the output file
string is terminated by new line
Arguments:
Returns:
TRUE if success, FALSE if error
--*/
{
LPSTR pszWas = pBuf;
INT ch = 0;
while ( cMax > 1 && (ch=fgetc(fIn))!= EOF && ch != '\n' )
{
*pBuf++ = (CHAR)ch;
--cMax;
}
if ( ch != EOF )
{
*pBuf = '\0';
return pszWas;
}
return NULL;
}
// CCert11Mapping
CCert11Mapping::CCert11Mapping(
CIisAcctMapper* pMap
)
/*++
Routine Description:
Constructor for CCert11Mapping
Arguments:
pMap -- ptr to mapper object linked to this mapping
Returns:
Nothing
--*/
{
m_pMapper = (CIisAcctMapper*)pMap;
for ( int x = 0 ; x < sizeof(m_pFields)/sizeof(LPSTR) ; ++x )
{
m_pFields[x] = NULL;
}
for ( x = 0 ; x < sizeof(m_pFields)/sizeof(LPSTR) ; ++x )
{
m_cFields[x] = 0;
}
}
CCert11Mapping::~CCert11Mapping(
)
/*++
Routine Description:
Destructor for CCert11Mapping
Arguments:
None
Returns:
Nothing
--*/
{
}
BOOL
CCert11Mapping::Init(
LPBYTE pC,
DWORD dwC,
IISMDB_HEntry *pH,
DWORD dwH
)
/*++
Routine Description:
Constructor for CCert11Mapping
Arguments:
pC -- cert ( ASN.1 format )
dwC -- length of cert
pH -- ptr to hierarchy info
dwH -- number of hierarchy entries
Returns:
TRUE if success, FALSE if error
--*/
{
StoreFieldRef( IISMDB_INDEX_CERT11_CERT, (LPSTR)pC, dwC );
UpdateMask( pH, dwH );
return TRUE;
}
BOOL
CCert11Mapping::MappingSetField(
DWORD dwIndex,
LPSTR pszNew
)
/*++
Routine Description:
Set field in mapping entry to specified content
data pointed by pszNew is copied inside mapping entry
Arguments:
dwIndex -- index of field
pszNew -- data to copy inside field
Returns:
TRUE if success, FALSE if error
Lock:
mapper must be locked for ptr to remain valid
--*/
{
LPSTR *pFields;
LPDWORD pcFields;
DWORD iMax = GetNbField( &pFields, &pcFields );
if ( dwIndex >= iMax )
{
return FALSE;
}
return StoreField( pFields, pcFields, dwIndex, iMax, pszNew, (DWORD) strlen(pszNew)+1, FALSE );
}
// CIisMapping
CIisMapping::CIisMapping(
)
/*++
Routine Description:
Constructor for CIisMapping
Arguments:
None
Returns:
Nothing
--*/
{
m_pBuff = NULL;
m_cUsedBuff = m_cAllocBuff = 0;
m_dwMask = 0;
}
BOOL
CIisMapping::CloneEx(
CIisMapping** ppM,
LPSTR* ppTargetS,
LPSTR* ppS,
LPDWORD pTargetC,
LPDWORD pC,
UINT cF
)
/*++
Routine Description:
Clone a mapping entry
Arguments:
Returns:
TRUE if success, otherwise FALSE
--*/
{
CIisMapping* pM = *ppM;
UINT i;
if ( ppTargetS && ppS )
{
//
// ppTargetS is already allocated by caller and has cF entries
//
memcpy( ppTargetS, ppS, sizeof(LPSTR*) * cF );
}
if ( pTargetC && pC )
{
//
// pTargetC is already allocated by caller and has cF entries
//
memcpy( pTargetC, pC, sizeof(DWORD) * cF );
}
if ( ( pM->m_pBuff = (LPBYTE)LocalAlloc( LMEM_FIXED, m_cAllocBuff ) ) == NULL )
{
delete pM;
*ppM = NULL;
return FALSE;
}
DBG_ASSERT( m_cAllocBuff >= m_cUsedBuff );
memcpy( pM->m_pBuff, m_pBuff, m_cUsedBuff );
pM->m_cUsedBuff = m_cUsedBuff;
pM->m_cAllocBuff = m_cAllocBuff;
pM->m_pMapper = m_pMapper;
pM->m_dwMask = m_dwMask;
//
// Adjust ptr to point to new buffer
//
for ( i = 0 ; i < cF ; ++i )
{
if ( ppTargetS[i] )
{
ppTargetS[i] += pM->m_pBuff - m_pBuff;
}
}
return TRUE;
}
BOOL
CIisMapping::UpdateMask(
IISMDB_HEntry* pH,
DWORD dwI
)
/*++
Routine Description:
Update mask of significant fields for a mapping object
Field is significant if not containing "*"
mask if bitmask of n bits where n is # of hierarchy entries
bit of rank m == 0 means field pointed by hierarchy entry n - 1 - m
is significant. ( i.e. MSB is hierarchy entry 0, the most significant )
Arguments:
pH -- ptr to hierarchy info
dwI -- number of hierarchy entries
Returns:
TRUE if success, FALSE if error
--*/
{
LPSTR *pFields;
LPDWORD pcFields;
LPSTR pF;
DWORD dwC;
int iMax;
m_dwMask = (1u << dwI)-1;
iMax = GetNbField( &pFields, &pcFields );
if ( pcFields )
{
for ( UINT x = 0 ; x < dwI ; ++x )
{
MappingGetField( pH[x].m_dwIndex, (PBYTE *) &pF, &dwC, FALSE );
if ( !pF || dwC != 1 || *pF != '*' )
{
m_dwMask &= ~(1u << (dwI - 1 - x) );
}
}
}
else
{
for ( UINT x = 0 ; x < dwI ; ++x )
{
MappingGetField( pH[x].m_dwIndex, &pF );
if ( !pF || strcmp( pF, "*" ) )
{
m_dwMask &= ~(1u << (dwI - 1 - x) );
}
}
}
return TRUE;
}
BOOL
CIisMapping::Copy(
CIisMapping* pM
)
/*++
Routine Description:
Copy the specified mapping in this
Arguments:
pM - ptr to mapping to duplicate
Returns:
TRUE if success, FALSE if error
--*/
{
LPSTR *pFields;
LPSTR pF;
UINT iMax = GetNbField( &pFields );
for ( UINT x = 0 ; x < iMax ; ++x )
{
if ( pM->MappingGetField( x, &pF ) && *pF )
{
if ( !MappingSetField( x, pF ) )
{
return FALSE;
}
}
}
return TRUE;
}
int
CIisMapping::Cmp(
CIisMapping* pM,
BOOL fCmpForMatch
)
/*++
Routine Description:
Compare 2 mappings, return -1, 0 or 1 as suitable for qsort or bsearch
Can compare either for full sort order ( using mask & significant fields )
or for a match ( not using mask )
Arguments:
pM -- ptr to mapping to compare to. This is to be used as the 2nd
entry for purpose of lexicographical order.
fCmpForMatch -- TRUE if comparing for a match inside a given mask class
Returns:
-1 if *this < *pM, 0 if *this == *pM, 1 if *this > *pM
--*/
{
DWORD dwCmpMask = 0xffffffff;
// if not compare for match, consider mask
if ( !fCmpForMatch )
{
if ( m_dwMask < pM->GetMask() )
{
return -1;
}
else if ( m_dwMask > pM->GetMask() )
{
return 1;
}
// mask are identical, have to consider fields
}
// compute common significant fields : bit is 1 if significant
dwCmpMask = (~m_dwMask) & (~pM->GetMask());
DWORD dwH;
IISMDB_HEntry* pH = m_pMapper->GetHierarchy( &dwH );
UINT x;
LPSTR *pFL;
LPDWORD pcFL;
GetNbField( &pFL, &pcFL );
for ( x = 0 ; x < dwH ; ++x )
{
if( ! (dwCmpMask & (1u << (dwH - 1 - x) )) )
{
continue;
}
LPSTR pA;
LPSTR pB;
DWORD dwA;
DWORD dwB;
int fC;
if ( pcFL ) // check if length available
{
MappingGetField( pH[x].m_dwIndex, (PBYTE *) &pA, &dwA, FALSE );
pM->MappingGetField( pH[x].m_dwIndex, (PBYTE *) &pB, &dwB, FALSE );
if ( pA == NULL )
{
dwA = 0;
}
if ( pB == NULL )
{
dwB = 0;
}
if ( dwA != dwB )
{
return dwA < dwB ? -1 : 1;
}
fC = memcmp( pA, pB, dwA );
}
else
{
MappingGetField( pH[x].m_dwIndex, &pA );
pM->MappingGetField( pH[x].m_dwIndex, &pB );
if ( pA == NULL )
{
pA = "";
}
if ( pB == NULL )
{
pB = "";
}
fC = strcmp( pA, pB );
}
if ( fC )
{
return fC;
}
}
return 0;
}
BOOL
CIisMapping::MappingGetField(
DWORD dwIndex,
LPSTR *pF
)
/*++
Routine Description:
Get ptr to field in mapping entry
ownership of field remains with mapping entry
Arguments:
dwIndex -- index of field
pF -- updated with ptr to field entry. can be NULL if
field empty.
Returns:
TRUE if success, FALSE if error
Lock:
mapper must be locked for ptr to remain valid
--*/
{
LPSTR *pFields;
DWORD iMax = GetNbField( &pFields );
if ( dwIndex >= iMax )
{
return FALSE;
}
*pF = pFields[dwIndex];
return TRUE;
}
BOOL
CIisMapping::MappingGetField(
DWORD dwIndex,
PBYTE *ppbF,
LPDWORD pcbF,
BOOL fUuEncode
)
/*++
Routine Description:
Get ptr to field in mapping entry
ownership of field remains with mapping entry
Arguments:
dwIndex -- index of field
ppbF -- updated with ptr to field entry. can be NULL if
field empty.
pcbF -- updated with length of fields, 0 if empty
fUuEncode -- TRUE if result is to be uuencoded.
if TRUE, caller must LocalFree( *pF )
Returns:
TRUE if success, FALSE if error
Lock:
mapper must be locked for ptr to remain valid
--*/
{
LPSTR *pFields;
LPDWORD pcFields;
DWORD iMax = GetNbField( &pFields, &pcFields );
if ( dwIndex >= iMax )
{
return FALSE;
}
if ( fUuEncode )
{
LPSTR pU = (LPSTR)LocalAlloc( LMEM_FIXED, ((pcFields[dwIndex]+3)*4)/3+1 );
if ( pU == NULL )
{
return FALSE;
}
IISuuencode( (LPBYTE)pFields[dwIndex], pcFields[dwIndex], (LPBYTE)pU, FALSE );
*ppbF = (PBYTE) pU;
*pcbF = (DWORD) strlen(pU);
}
else
{
*ppbF = (PBYTE) pFields[dwIndex];
*pcbF = pcFields[dwIndex];
}
return TRUE;
}
BOOL
CIisMapping::MappingSetField(
DWORD dwIndex,
LPSTR pszNew
)
/*++
Routine Description:
Set field in mapping entry to specified content
data pointed by pszNew is copied inside mapping entry
Arguments:
dwIndex -- index of field
pszNew -- data to copy inside field
Returns:
TRUE if success, FALSE if error
Lock:
mapper must be locked for ptr to remain valid
--*/
{
LPSTR *pFields;
DWORD iMax = GetNbField( &pFields );
if ( dwIndex >= iMax )
{
return FALSE;
}
return StoreField( pFields, dwIndex, iMax, pszNew );
}
BOOL
CIisMapping::MappingSetField(
DWORD dwIndex,
PBYTE pbNew,
DWORD cbNew,
BOOL fIsUuEncoded
)
/*++
Routine Description:
Set field in mapping entry to specified content
data pointed by pszNew is copied inside mapping entry
Arguments:
dwIndex -- index of field
pbNew -- data to copy inside field
cbNew -- length of data
fIsUuEncoded -- TRUE if pszNew is UUEncoded
Returns:
TRUE if success, FALSE if error
Lock:
mapper must be locked for ptr to remain valid
--*/
{
LPSTR *pFields;
LPDWORD pcFields;
DWORD iMax = GetNbField( &pFields, &pcFields );
if ( dwIndex >= iMax )
{
return FALSE;
}
return StoreField( pFields, pcFields, dwIndex, iMax, (LPSTR) pbNew, cbNew, fIsUuEncoded );
}
BOOL
CIisMapping::StoreField(
LPSTR* ppszFields,
DWORD dwIndex,
DWORD dwNbIndex,
LPSTR pszNew
)
/*++
Routine Description:
Update field array in mapping entry with new field
data pointed by pszNew is copied inside mapping entry
Arguments:
ppszFields -- array of field pointers to be updated
dwIndex -- index of field
dwNbIndex -- number of fields in array
pszNew -- data to copy inside field
Returns:
TRUE if success, FALSE if error
--*/
{
UINT x;
// pszOld is assumed to point inside m_pBuff if non NULL
// is has to be removed
LPSTR pszOld = ppszFields[dwIndex];
if ( pszOld && m_pBuff && (LPBYTE)pszOld > m_pBuff && (LPBYTE)pszOld < m_pBuff+m_cUsedBuff )
{
DWORD lO = (DWORD) strlen( pszOld ) + 1;
DWORD lM = (DWORD) DIFF((m_pBuff + m_cUsedBuff) - (LPBYTE)pszOld) - lO;
if ( lM )
{
// remove the old field from the middle of the m_pBuff
// new value will be then appended to the end to the m_pBuff
memmove( pszOld, pszOld + lO, lM );
for ( x = 0 ; x < dwNbIndex ; ++x )
{
if ( x != dwIndex && ppszFields[x] > pszOld )
{
ppszFields[x] -= lO;
}
}
}
ppszFields[ dwIndex ] = NULL;
m_cUsedBuff -= lO;
}
// pszNew is to appended to m_pBuff
DWORD lN = (DWORD) strlen( pszNew ) + 1;
if ( m_cUsedBuff + lN > m_cAllocBuff )
{
UINT cNewBuff = (( m_cUsedBuff + lN + IIS_MAP_BUFF_GRAN ) / IIS_MAP_BUFF_GRAN) * IIS_MAP_BUFF_GRAN;
LPSTR pNewBuff = (LPSTR)LocalAlloc( LMEM_FIXED, cNewBuff );
if ( pNewBuff == NULL )
{
return FALSE;
}
if ( m_pBuff )
{
DBG_ASSERT( m_cUsedBuff <= cNewBuff );
memcpy( pNewBuff, m_pBuff, m_cUsedBuff );
LocalFree( m_pBuff );
}
m_cAllocBuff = cNewBuff;
// adjust pointers
for ( x = 0 ; x < dwNbIndex ; ++x )
{
if ( x != dwIndex )
{
if ( ppszFields[x] != NULL )
{
ppszFields[x] += ((LPBYTE)pNewBuff - m_pBuff);
}
}
}
m_pBuff = (LPBYTE)pNewBuff;
}
DBG_ASSERT( m_cAllocBuff - m_cUsedBuff >= lN );
memcpy( m_pBuff + m_cUsedBuff, pszNew, lN );
ppszFields[dwIndex] = (LPSTR)(m_pBuff + m_cUsedBuff);
m_cUsedBuff += lN;
return TRUE;
}
BOOL
CIisMapping::StoreField(
LPSTR* ppszFields,
LPDWORD ppdwFields,
DWORD dwIndex,
DWORD dwNbIndex,
LPSTR pbNew,
DWORD cNew,
BOOL fIsUuEncoded
)
/*++
Routine Description:
Update field array in mapping entry with new field
data pointed by pszNew is copied inside mapping entry
Arguments:
ppszFields -- array of field pointers to be updated
ppdwFields -- array of field length to be updated
dwIndex -- index of field
dwNbIndex -- number of fields in array
pbNew -- data to copy inside field
cNew -- length of data
fIsUuEncoded -- TRUE if pbNew is UUEncoded
Returns:
TRUE if success, FALSE if error
--*/
{
UINT x;
// pszOld is assumed to point inside m_pBuff if non NULL
// it has to be removed
LPSTR pszOld = ppszFields[dwIndex];
if ( pszOld && m_pBuff && (LPBYTE)pszOld > m_pBuff && (LPBYTE)pszOld < m_pBuff+m_cUsedBuff )
{
DWORD lO = ppdwFields[dwIndex];
DWORD lM = (DWORD) DIFF((m_pBuff + m_cUsedBuff) - (LPBYTE)pszOld) - lO;
if ( lM )
{
// remove the old field from the middle of the m_pBuff
// new value will be then appended to the end of m_pBuff
//
memmove( pszOld, pszOld + lO, lM );
for ( x = 0 ; x < dwNbIndex ; ++x )
{
if ( x != dwIndex && ppszFields[x] > pszOld )
{
ppszFields[x] -= lO;
}
}
}
ppszFields[ dwIndex ] = NULL;
m_cUsedBuff -= lO;
}
// pszNew is to appended to m_pBuff
UINT lN = cNew;
if ( fIsUuEncoded )
{
LPSTR pU = (LPSTR)LocalAlloc( LMEM_FIXED, lN + 3);
if ( pU == NULL )
{
return FALSE;
}
DWORD cO;
IISuudecode( pbNew, (LPBYTE)pU, &cO, FALSE );
pbNew = pU;
cNew = lN = cO;
}
if ( m_cUsedBuff + lN > m_cAllocBuff )
{
UINT cNewBuff = (( m_cUsedBuff + lN + IIS_MAP_BUFF_GRAN ) / IIS_MAP_BUFF_GRAN) * IIS_MAP_BUFF_GRAN;
LPSTR pNewBuff = (LPSTR)LocalAlloc( LMEM_FIXED, cNewBuff );
if ( pNewBuff == NULL )
{
if ( fIsUuEncoded )
{
LocalFree( pbNew );
}
return FALSE;
}
if ( m_pBuff )
{
DBG_ASSERT( cNewBuff >= m_cUsedBuff );
memcpy( pNewBuff, m_pBuff, m_cUsedBuff );
LocalFree( m_pBuff );
}
m_cAllocBuff = cNewBuff;
// adjust pointers
for ( x = 0 ; x < dwNbIndex ; ++x )
{
if ( x != dwIndex )
{
if ( ppszFields[x] != NULL )
{
ppszFields[x] += ((LPBYTE)pNewBuff - m_pBuff);
}
}
}
m_pBuff = (LPBYTE)pNewBuff;
}
DBG_ASSERT( m_cAllocBuff - m_cUsedBuff >= lN );
memcpy( m_pBuff + m_cUsedBuff, pbNew, lN );
ppszFields[dwIndex] = (LPSTR)(m_pBuff + m_cUsedBuff);
if ( ppdwFields )
{
ppdwFields[dwIndex] = cNew;
}
m_cUsedBuff += lN;
if ( fIsUuEncoded )
{
LocalFree( pbNew );
}
return TRUE;
}
BOOL
CIisMapping::Serialize(
FILE* pFile,
VALID_CTX pMD5,
LPVOID pStorage
)
/*++
Routine Description:
Serialize a mapping entry
Arguments:
pFile -- file to write to
pMD5 -- MD5 to update with signature of written bytes
Returns:
TRUE if success, FALSE if error
Lock:
mapper must be locked while serializing
--*/
{
LPSTR *pFields;
LPDWORD pcFields;
LPSTR pO = NULL;
DWORD dwO = 0;
UINT iMax = GetNbField( &pFields, &pcFields );
UINT x;
LPBYTE pB = NULL;
BOOL fMustFree = FALSE;
for ( x = 0 ; x < iMax ; ++x )
{
LPSTR pF;
DWORD dwF;
fMustFree = FALSE;
if ( pcFields )
{
MappingGetField( x, (PBYTE *) &pF, &dwF, FALSE );
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)pF, dwF );
store_as_binary:
if ( IsCrypt( x ) && dwF )
{
if ( FAILED(((IIS_CRYPTO_STORAGE*)pStorage)->EncryptData(
(PIIS_CRYPTO_BLOB*)&pB,
pF,
dwF,
REG_BINARY )) )
{
return FALSE;
}
pF = (LPSTR)pB;
dwF = IISCryptoGetBlobLength( (PIIS_CRYPTO_BLOB)pB );
fMustFree = TRUE;
}
if ( dwF )
{
DWORD dwNeed = ((dwF+2)*4)/3 + 1;
if ( dwNeed > dwO )
{
if ( pO != NULL )
{
LocalFree( pO );
}
dwNeed += 100; // alloc more than needed
// to minimize # of allocation
if ( (pO = (LPSTR)LocalAlloc( LMEM_FIXED, dwNeed )) == NULL )
{
return FALSE;
}
dwO = dwNeed;
}
/* INTRINSA suppress = null */
IISuuencode( (LPBYTE)pF, dwF, (LPBYTE)pO, FALSE );
fputs( pO, pFile );
}
if ( fMustFree )
{
IISCryptoFreeBlob( (PIIS_CRYPTO_BLOB)pB );
}
}
else
{
MappingGetField( x, &pF );
if ( pF != NULL )
{
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)pF, (DWORD)strlen(pF) );
if ( IsCrypt( x ) )
{
dwF = (DWORD)strlen( pF ) + 1;
goto store_as_binary;
}
fputs( pF, pFile );
}
}
fputs( "|", pFile );
}
fputs( "\r\n", pFile );
if ( pO != NULL )
{
LocalFree( pO );
}
return TRUE;
}
BOOL
CIisMapping::Deserialize(
FILE* pFile,
VALID_CTX pMD5,
LPVOID pStorage
)
/*++
Routine Description:
Deserialize a mapping entry
Arguments:
pFile -- file to read from
pMD5 -- MD5 to update with signature of read bytes
Returns:
TRUE if success, FALSE if error
Lock:
mapper must be locked while serializing
--*/
{
LPSTR *pFields;
LPDWORD pcFields;
UINT iMax;
UINT x;
int c;
CHAR achBuf[4096]; // seems to be hardcoded limit on mapping saved
DWORD dwType;
LPBYTE pB;
iMax = GetNbField( &pFields, &pcFields );
for ( x = 0 ; x < iMax ; ++x )
{
StoreFieldRef( x, NULL );
}
for ( x = 0 ; x < sizeof(achBuf) && (c=fgetc(pFile))!= EOF ; )
{
achBuf[x++] = (CHAR)c;
if ( c == '\n' )
{
break;
}
}
if ( x == sizeof(achBuf ) )
{
return FALSE;
}
if ( x > 1 )
{
achBuf[x-2] = '\0';
m_cUsedBuff = m_cAllocBuff = x - 1;
if ( (m_pBuff = (LPBYTE)LocalAlloc( LMEM_FIXED, m_cAllocBuff )) == NULL )
{
m_cAllocBuff = m_cUsedBuff = 0;
return FALSE;
}
DBG_ASSERT( m_cAllocBuff >= m_cUsedBuff );
memcpy( m_pBuff, achBuf, m_cUsedBuff );
LPSTR pCur = (LPSTR)m_pBuff;
LPSTR pNext;
LPSTR pStore = (LPSTR)m_pBuff;
DWORD dwDec;
if ( pcFields )
{
for ( x = 0 ; x < iMax ; ++x )
{
pNext = strchr( pCur, '|' );
if ( pNext != NULL )
{
*pNext = '\0';
++pNext;
}
else
{
pNext = NULL;
}
IISuudecode( pCur, (PBYTE)pStore, &dwDec, FALSE );
if ( IsCrypt( x ) && dwDec )
{
if ( FAILED(((IIS_CRYPTO_STORAGE*)pStorage)->DecryptData(
(PVOID*)&pB,
&dwDec,
&dwType,
(PIIS_CRYPTO_BLOB)pStore )) )
{
return FALSE;
}
DBG_ASSERT( m_cAllocBuff >= dwDec );
memmove( pStore, pB, dwDec );
}
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)pStore, dwDec );
StoreFieldRef( x, pStore, dwDec );
pCur = pNext;
pStore += dwDec;
}
}
else
{
for ( x = 0 ; x < iMax ; ++x )
{
pNext = strchr( pCur, '|' );
if ( pNext != NULL )
{
*pNext = '\0';
++pNext;
}
if ( *pCur && IsCrypt( x ) )
{
IISuudecode( pCur, (PBYTE)pCur, &dwDec, FALSE );
if ( FAILED(((IIS_CRYPTO_STORAGE*)pStorage)->DecryptData(
(PVOID*)&pB,
&dwDec,
&dwType,
(PIIS_CRYPTO_BLOB)pCur )) )
{
return FALSE;
}
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)pB, dwDec );
StoreFieldRef( x, (LPSTR)pB );
pCur = pNext;
}
else
{
MD5Update( (MD5_CTX*)pMD5, (LPBYTE)pCur, (DWORD) strlen(pCur) );
StoreFieldRef( x, pCur );
pCur = pNext;
}
}
}
return TRUE;
}
return FALSE;
}
//
extern "C" BOOL WINAPI
DllMain(
HANDLE hModule,
DWORD dwReason,
LPVOID /*pV*/
)
/*++
Routine Description:
DLL init/terminate notification function
Arguments:
hModule - DLL handle
dwReason - notification type
LPVOID - not used
Returns:
TRUE if success, FALSE if failure
--*/
{
switch ( dwReason )
{
case DLL_PROCESS_ATTACH:
#ifdef _NO_TRACING_
CREATE_DEBUG_PRINT_OBJECT( "IISMAP" );
#endif
// record the module handle to access module info later
s_hIISMapDll = (HINSTANCE)hModule;
InitializeWildcardMapping( hModule );
InitializeMapping( hModule );
if ( IISCryptoInitialize() != NO_ERROR )
{
return FALSE;
}
return TRUE;
case DLL_PROCESS_DETACH:
IISCryptoTerminate();
TerminateWildcardMapping();
TerminateMapping();
#ifdef _NO_TRACING_
DELETE_DEBUG_PRINT_OBJECT( );
#endif
break;
}
return TRUE;
}
static
BOOL
LoadFieldNames(
IISMDB_Fields* pFields,
UINT cFields
)
/*++
Routine Description:
Load fields names from resource
- helper function for InitializeMapping()
Arguments:
pFields - ptr to array where to store reference to names
cFields - count of element in array
Return Value:
TRUE if success, otherwise FALSE
--*/
{
UINT x;
BOOL fSt = TRUE;
for ( x = 0 ;
x < cFields ;
++x )
{
char achTmp[128];
if ( LoadString( s_hIISMapDll,
pFields[x].m_dwResID,
achTmp,
sizeof( achTmp ) ) != 0 )
{
DWORD lN = (DWORD) strlen( achTmp ) + sizeof(CHAR);
if ( (pFields[x].m_pszDisplayName = (LPSTR)LocalAlloc( LMEM_FIXED, lN )) == NULL )
{
fSt = FALSE;
break;
}
memcpy( pFields[x].m_pszDisplayName, achTmp, lN );
}
else
{
fSt = FALSE;
break;
}
}
return fSt;
}
static
VOID
FreeFieldNames(
IISMDB_Fields* pFields,
UINT cFields
)
/*++
Routine Description:
Free fields names loaded from resource
- helper function for TerminateMapping()
Arguments:
pFields - ptr to array where reference to names are stored
cFields - count of element in array
Return Value:
Nothing
--*/
{
UINT x;
for ( x = 0 ;
x < cFields ;
++x )
{
if ( pFields[x].m_pszDisplayName )
{
LocalFree( pFields[x].m_pszDisplayName );
}
}
}
BOOL
InitializeMapping(
HANDLE /*hModule*/
)
/*++
Routine Description:
Initialize mapping
Arguments:
hModule - module handle of this DLL (not used)
Return Value:
Nothing
--*/
{
HKEY hKey = NULL;
// get install path
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
W3_PARAMS,
0,
KEY_READ|KEY_SET_VALUE,
&hKey ) == ERROR_SUCCESS )
{
DWORD dwLen = 0;
DWORD dwType;
if ( RegQueryValueEx( hKey,
INSTALL_PATH,
NULL,
&dwType,
NULL,
&dwLen ) != ERROR_SUCCESS ||
dwType != REG_SZ )
{
//
// Read next value
//
goto ReadNext;
}
//
// allocate space for extra terminator
//
if ( ( s_pszIisInstallPath = (LPSTR)LocalAlloc(
LMEM_FIXED,
dwLen +
sizeof( s_pszIisInstallPath[0] ) ) )
== NULL )
{
//
// Read next value
//
goto ReadNext;
}
if ( RegQueryValueEx( hKey,
INSTALL_PATH,
NULL,
&dwType,
(LPBYTE)s_pszIisInstallPath,
&dwLen ) != ERROR_SUCCESS )
{
if ( s_pszIisInstallPath )
{
LocalFree( s_pszIisInstallPath );
s_pszIisInstallPath = NULL;
}
//
// Read next value
//
goto ReadNext;
}
//
// RegQueryValueEx may not return NULL terminated string
// To be sure let's add termination (extra space was allocated already)
//
s_pszIisInstallPath[ dwLen ] = '\0';
ReadNext:
dwLen = sizeof( s_dwFileGuid );
if ( RegQueryValueEx( hKey,
MAPPER_GUID,
NULL,
&dwType,
(LPBYTE)&s_dwFileGuid,
&dwLen ) != ERROR_SUCCESS ||
dwType != REG_DWORD )
{
s_dwFileGuid = 0;
}
RegCloseKey( hKey );
return LoadFieldNames( IisCert11MappingFields,
sizeof(IisCert11MappingFields)/sizeof(IISMDB_Fields) );
}
else
{
hKey = NULL;
}
return FALSE;
}
VOID
TerminateMapping(
)
/*++
Routine Description:
Terminate mapping
Arguments:
None
Return Value:
Nothing
--*/
{
if ( s_pszIisInstallPath )
{
LocalFree( s_pszIisInstallPath );
}
FreeFieldNames( IisCert11MappingFields,
sizeof(IisCert11MappingFields)/sizeof(IISMDB_Fields) );
}
//
dllexp
BOOL
ReportIisMapEvent(
WORD wType,
DWORD dwEventId,
WORD cNbStr,
LPCTSTR* pStr
)
/*++
Routine Description:
Log an event based on type, ID and insertion strings
Arguments:
wType -- event type ( error, warning, information )
dwEventId -- event ID ( as defined by the .mc file )
cNbStr -- nbr of LPSTR in the pStr array
pStr -- insertion strings
Returns:
TRUE if success, FALSE if failure
--*/
{
BOOL fSt = TRUE;
HANDLE hEventLog = NULL;
hEventLog = RegisterEventSource(NULL,"IISMAP");
if ( hEventLog != NULL )
{
if (!ReportEvent(hEventLog, // event log handle
wType, // event type
0, // category zero
(DWORD) dwEventId, // event identifier
NULL, // no user security identifier
cNbStr, // count of substitution strings (may be no strings)
// less ProgName (argv[0]) and Event ID (argv[1])
0, // no data
(LPCTSTR *) pStr, // address of string array
NULL)) // address of data
{
fSt = FALSE;
}
DeregisterEventSource( hEventLog );
}
else
{
fSt = FALSE;
}
return fSt;
}
dllexp
BOOL
ReportIisMapEventW(
WORD wType,
DWORD dwEventId,
WORD cNbStr,
LPCWSTR* pStr
)
/*++
Routine Description:
Log an event based on type, ID and insertion strings
Arguments:
wType -- event type ( error, warning, information )
dwEventId -- event ID ( as defined by the .mc file )
cNbStr -- nbr of LPSTR in the pStr array
pStr -- insertion strings
Returns:
TRUE if success, FALSE if failure
--*/
{
BOOL fSt = TRUE;
HANDLE hEventLog = NULL;
hEventLog = RegisterEventSource(NULL,"IISMAP");
if ( hEventLog != NULL )
{
if (!ReportEventW(hEventLog, // event log handle
wType, // event type
0, // category zero
(DWORD) dwEventId, // event identifier
NULL, // no user security identifier
cNbStr, // count of substitution strings (may be no strings)
// less ProgName (argv[0]) and Event ID (argv[1])
0, // no data
(LPCWSTR *) pStr, // address of string array
NULL)) // address of data
{
fSt = FALSE;
}
DeregisterEventSource( hEventLog );
}
else
{
fSt = FALSE;
}
return fSt;
}
/////////////////////////////////////////
///////////////////////
//
// Taken from NCSA HTTP and wwwlib.
//
// NOTE: These conform to RFC1113, which is slightly different then the Unix
// uuencode and uudecode!
//
const int _pr2six[256]={
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64
};
char _six2pr[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9','+','/'
};
const int _pr2six64[256]={
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,
40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64
};
char _six2pr64[64] = {
'`','!','"','#','$','%','&','\'','(',')','*','+',',',
'-','.','/','0','1','2','3','4','5','6','7','8','9',
':',';','<','=','>','?','@','A','B','C','D','E','F',
'G','H','I','J','K','L','M','N','O','P','Q','R','S',
'T','U','V','W','X','Y','Z','[','\\',']','^','_'
};
BOOL IISuudecode(char * bufcoded,
BYTE * bufout,
DWORD * pcbDecoded,
BOOL fBase64
)
{
int nbytesdecoded;
char *bufin = bufcoded;
int nprbytes;
int *pr2six = (int*)(fBase64 ? _pr2six64 : _pr2six);
int chL = 0;
/* Strip leading whitespace. */
while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
/* Figure out how many characters are in the input buffer.
* If this would decode into more bytes than would fit into
* the output buffer, adjust the number of input bytes downwards.
*/
bufin = bufcoded;
while(pr2six[*(bufin++)] <= 63);
nprbytes = (DWORD) DIFF(bufin - bufcoded) - 1;
nbytesdecoded = ((nprbytes+3)/4) * 3;
bufin = bufcoded;
while (nprbytes > 0) {
chL = bufin[2];
*(bufout++) =
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
*(bufout++) =
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
*(bufout++) =
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
bufin += 4;
nprbytes -= 4;
}
if(nprbytes & 03) {
if(pr2six[chL] > 63)
nbytesdecoded -= 2;
else
nbytesdecoded -= 1;
}
if ( pcbDecoded )
*pcbDecoded = nbytesdecoded;
return TRUE;
}
//
// NOTE NOTE NOTE
// If the buffer length isn't a multiple of 3, we encode one extra byte beyond the
// end of the buffer. This garbage byte is stripped off by the uudecode code, but
// -IT HAS TO BE THERE- for uudecode to work. This applies not only our uudecode, but
// to every uudecode() function that is based on the lib-www distribution [probably
// a fairly large percentage of the code that's floating around out there].
//
BOOL IISuuencode( BYTE * bufin,
DWORD nbytes,
BYTE * outptr,
BOOL fBase64 )
{
unsigned int i;
unsigned int iRemainder = 0;
unsigned int iClosestMultOfThree = 0;
char *six2pr = fBase64 ? _six2pr64 : _six2pr;
BOOL fOneByteDiff = FALSE;
BOOL fTwoByteDiff = FALSE;
iRemainder = nbytes % 3; //also works for nbytes == 1, 2
fOneByteDiff = (iRemainder == 1 ? TRUE : FALSE);
fTwoByteDiff = (iRemainder == 2 ? TRUE : FALSE);
iClosestMultOfThree = ((nbytes - iRemainder)/3) * 3 ;
//
// Encode bytes in buffer up to multiple of 3 that is closest to nbytes.
//
for (i=0; i< iClosestMultOfThree ; i += 3) {
*(outptr++) = six2pr[*bufin >> 2]; /* c1 */
*(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
*(outptr++) = six2pr[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
*(outptr++) = six2pr[bufin[2] & 077]; /* c4 */
bufin += 3;
}
//
// We deal with trailing bytes by pretending that the input buffer has been padded with
// zeros. Expressions are thus the same as above, but the second half drops off b'cos
// ( a | ( b & 0) ) = ( a | 0 ) = a
//
if (fOneByteDiff)
{
*(outptr++) = six2pr[*bufin >> 2]; /* c1 */
*(outptr++) = six2pr[((*bufin << 4) & 060)]; /* c2 */
//pad with '='
*(outptr++) = '='; /* c3 */
*(outptr++) = '='; /* c4 */
}
else if (fTwoByteDiff)
{
*(outptr++) = six2pr[*bufin >> 2]; /* c1 */
*(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
*(outptr++) = six2pr[((bufin[1] << 2) & 074)];/*c3*/
//pad with '='
*(outptr++) = '='; /* c4 */
}
//encoded buffer must be zero-terminated
*outptr = '\0';
return TRUE;
}
const int SHA1_HASH_SIZE = 20;
HRESULT
GetCertificateHashString(
PBYTE pbCert,
DWORD cbCert,
WCHAR *pwszCertHash,
DWORD cchCertHashBuffer)
/*++
Routine Description:
verifies validity of cert blob by creating cert context
and retrieves SHA1 hash and converts it to WCHAR *
Arguments:
pbCert - X.509 certificate blob
cbCert - size of the cert blob in bytes
pwszCertHash - buffer must be big enough to fit SHA1 hash in hex string form
(40 WCHAR + terminating 0 )
cchCertHashBuffer - size of the CertHash buffer in WCHARS (including terminating 0)
Returns:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
BYTE rgbHash[ SHA1_HASH_SIZE ];
DWORD cbSize = SHA1_HASH_SIZE;
PCCERT_CONTEXT pCertContext = NULL;
#ifndef HEX_DIGIT
#define HEX_DIGIT( nDigit ) \
(WCHAR)((nDigit) > 9 ? \
(nDigit) - 10 + L'a' \
: (nDigit) + L'0')
#endif
pCertContext = CertCreateCertificateContext(
X509_ASN_ENCODING,
(const BYTE *)pbCert,
cbCert );
if ( pCertContext == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
//
// get hash of the certificate to be verified
//
if ( !CertGetCertificateContextProperty( pCertContext,
CERT_SHA1_HASH_PROP_ID,
rgbHash,
&cbSize ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
CertFreeCertificateContext( pCertContext );
pCertContext = NULL;
if ( cchCertHashBuffer < SHA1_HASH_SIZE * 2 + 1 )
{
// we don't have big enough buffer to store
// hex string of the SHA1 hash each byte takes 2 chars + terminating 0
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
DBG_ASSERT( cchCertHashBuffer < SHA1_HASH_SIZE * 2 + 1 );
goto ExitPoint;
}
//
// convert to text
//
for (int i = 0; i < sizeof(rgbHash); i ++ )
{
*(pwszCertHash++) = HEX_DIGIT( ( rgbHash[ i ] >> 4 ) );
*(pwszCertHash++) = HEX_DIGIT( ( rgbHash[ i ] & 0x0F ) );
}
*(pwszCertHash) = L'\0';
#undef HEX_DIGIT
ExitPoint:
if ( pCertContext != NULL )
{
CertFreeCertificateContext( pCertContext );
pCertContext = NULL;
}
return S_OK;
}
VOID
ReportImportProblem(
HRESULT hr
)
/*++
Routine Description:
Utility function for ImportIISCertMappingsToIIS6()
that reports first problem occured with import to eventlog
Arguments:
hr - hresult to report
Returns:
VOID
--*/
{
static BOOL fWasReported = FALSE;
if ( fWasReported == FALSE )
{
fWasReported = TRUE;
char achErr[ 40 ];
LPCTSTR pA[ 1 ];
pA[ 0 ] = achErr;
_ultoa( hr, achErr, 16 );
ReportIisMapEvent( EVENTLOG_ERROR_TYPE,
IISMAP_EVENT_ERROR_IMPORT,
1,
pA );
}
}
dllexp
VOID
ImportIISCertMappingsToIIS6(
VOID
)
/*++
Routine Description:
IIS6 stores 1to1 certificate in the metabase
IIS5 used to store MD_SERIAL_CERT11 reference info in the metabase
but actual mapping details were stored in metabase extension file
*.mp
This routine will load old (IIS5) style mapping for each site
into memory, then save it in the metabase and delete old reference
info from metabase
This function should be called only once
Arguments:
None
Returns:
HRESULT
--*/
{
IMSAdminBase * pMetabase = NULL;
HRESULT hr = E_FAIL;
HKEY hKey = NULL;
DWORD dwLen = 0;
DWORD dwType;
DWORD dwError;
BOOL fCoInitialized = FALSE;
BOOL fMBSaved = FALSE;
if ( ( dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
W3_PARAMS,
0,
KEY_READ|KEY_SET_VALUE,
&hKey ) ) != ERROR_SUCCESS )
{
if ( dwError != ERROR_FILE_NOT_FOUND &&
dwError != ERROR_PATH_NOT_FOUND )
{
hr = HRESULT_FROM_WIN32( dwError );
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Error in Opening registry(). hr = %x\n",
hr ));
}
goto Finished;
}
dwLen = sizeof( s_dwFileGuid );
if ( RegQueryValueEx( hKey,
MAPPER_GUID,
NULL,
&dwType,
(LPBYTE)&s_dwFileGuid,
&dwLen ) != ERROR_SUCCESS ||
dwType != REG_DWORD )
{
//
// If MapperGuid Value doesn't exist
// then it means that IIS Cert Mappings
// in old format are not present
//
goto Finished;
}
if ( s_dwFileGuid == 0xffffffff )
{
// This flags that import has been already attempted
// It is valid to run Import only once
// If Import needs to be rerun then
// MAPPER_GUID should be reset to value different from MAX_DWORD
goto Finished;
}
//
// Write to registry that import of 1to1 certmapping info
// was invoked (we will do it only once)
//
s_dwFileGuid = 0xffffffff;
if( ( dwError = RegSetValueEx( hKey,
MAPPER_GUID,
NULL,
REG_DWORD,
(LPBYTE)&s_dwFileGuid,
sizeof(s_dwFileGuid) ) ) != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( dwError );
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Error in Opening registry(). hr = %x\n",
hr ));
goto Finished;
}
RegCloseKey( hKey );
hKey = NULL;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if ( hr == RPC_E_CHANGED_MODE )
{
// If you get RPC_E_MODE_CHANGE the thread is coinited
// but don't couninit
}
else if ( FAILED(hr) )
{
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Error in CoInitializeEx(). hr = %x\n",
hr ));
goto Finished;
}
else
{
fCoInitialized = TRUE;
// we will have to couninit
}
//
// Initialize the metabase access (ABO)
//
hr = CoCreateInstance( CLSID_MSAdminBase,
NULL,
CLSCTX_SERVER,
IID_IMSAdminBase,
(LPVOID *) &pMetabase
);
if( FAILED(hr) )
{
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Error creating ABO object. hr = %x\n",
hr ));
goto Finished;
}
//
// Instances of 2 services could be using 1to1 certificate mappings
// one is W3SVC and another one is NNTP
for ( DWORD dwServiceType = 0; dwServiceType < 2; dwServiceType ++ )
{
SimpleMB mb( pMetabase );
CIisCert11Mapper AcctMapper;
DWORD dwIndex = 0;
WCHAR * pszServicePath = NULL;
WCHAR achServiceInstance[ ADMINDATA_MAX_NAME_LEN + 1 ];
STRAU strObjectPath;
BUFFER buffSerializedCert11Info;
DWORD cbSerializedCert11Info;
switch( dwServiceType)
{
case 0:
pszServicePath = L"/LM/W3SVC";
break;
case 1:
pszServicePath = L"/LM/NNTPSVC";
break;
}
// We need access to ServicePath in the metabase
if ( ! mb.Open( pszServicePath,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
if ( hr != HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) )
{
//
// Report error if other than ERROR_PATH_NOT_FOUND
// ERROR_PATH_NOT_FOUND indicates that service (such as w3svc or nntp)
// is not installed
//
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Failed to open metabase path %s (import of 1to1 cert mappings will fail). hr = %x\n",
pszServicePath, hr ));
// still try another service if there is any left
}
continue;
}
//
// Enumerate service instances (such as w3svc/1, w3svc/2 etc)
// and upgrade 1to1 mapping info to new format for all of them
//
for ( dwIndex = 0; ; dwIndex++ )
{
if ( !mb.EnumObjects( L"",
achServiceInstance,
dwIndex ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
if ( hr != HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) &&
hr != HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) )
{
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Failed to enum metabase nodes. hr = %x\n",
hr ));
}
else
{
// done enumerating all instances of the service
hr = S_OK;
}
// Done with enumeration
// Exit the for loop
break;
}
//
// Read 1to1 certificate mapping serialized reference info
// from metabase
//
if ( !mb.GetBuffer( achServiceInstance,
MD_SERIAL_CERT11,
IIS_MD_UT_SERVER,
&buffSerializedCert11Info,
&cbSerializedCert11Info
) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
if ( hr != MD_ERROR_DATA_NOT_FOUND )
{
// this service instance may have
// and 1to1 certificate mappings configured
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Failed to read MD_SERIAL_CERT11 for %s/%s. hr = %x\n",
pszServicePath, achServiceInstance, hr ));
}
// don't take error as fatal move on to another service
// instance
goto EnumNext;
}
//
// Unserialize 1to1 Certificate mapping info
//
PBYTE pbSerializedCert11Info = (PBYTE) buffSerializedCert11Info.QueryPtr();
if ( !AcctMapper.Unserialize( &pbSerializedCert11Info,
&cbSerializedCert11Info ) ||
!AcctMapper.Load() )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Failed to unserialize mapper for %s/%s. hr = %x\n",
pszServicePath, achServiceInstance, hr ));
goto EnumNext;
}
// walk all the mappings and save them to metabase
// in the new format
//
for ( DWORD dwMappingIndex = 0;
dwMappingIndex < AcctMapper.GetMappingCount();
dwMappingIndex ++ )
{
PBYTE pbCert = NULL;
DWORD cbCert = 0;
CHAR * pszPassword;
DWORD cbPassword;
STRAU strPassword;
CHAR * pszAcctName;
DWORD cbAcctName;
STRAU strAcctName;
CHAR * pszMappingName;
DWORD cbMappingName;
STRAU strMappingName;
DWORD * pdwEnabled;
DWORD cbEnabled;
STRAU strObjectPath;
WCHAR achCertHash[ 2*SHA1_HASH_SIZE + 1];
CIisMapping* pCurrentMapping;
if ( !AcctMapper.GetMapping(
dwMappingIndex,
&pCurrentMapping ) )
{
hr = E_FAIL;
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Failed to read mapping for %s/%s/%d. 0x%x\n",
pszServicePath, achServiceInstance,
dwMappingIndex, hr));
// There must be some inconsistency in data
// or low memory condition
// No hope to process mappings for this Service Site.
// Move on to next Service site
break;
}
if ( !pCurrentMapping->MappingGetField(
IISMDB_INDEX_CERT11_CERT,
(PBYTE *)&pbCert,
&cbCert,
FALSE ) )
{
hr = E_FAIL;
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Failed to read mapping for %s/%s/%d. 0x%x\n",
pszServicePath, achServiceInstance,
dwMappingIndex, hr));
// Move on to next Service site
break;
}
if ( !pCurrentMapping->MappingGetField(
IISMDB_INDEX_CERT11_NT_ACCT,
(PBYTE *)&pszAcctName,
&cbAcctName,
FALSE ) )
{
hr = E_FAIL;
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Failed to read mapping for %s/%s/%d. 0x%x\n",
pszServicePath, achServiceInstance,
dwMappingIndex, hr));
// Move on to next Service site
break;
}
if ( !pCurrentMapping->MappingGetField(
IISMDB_INDEX_CERT11_NT_PWD,
(PBYTE *)&pszPassword,
&cbPassword,
FALSE ) )
{
hr = E_FAIL;
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Failed to read mapping for %s/%s/%d. 0x%x\n",
pszServicePath, achServiceInstance,
dwMappingIndex, hr));
// Move on to next Service site
break;
}
if ( !pCurrentMapping->MappingGetField(
IISMDB_INDEX_CERT11_NAME,
(PBYTE *)&pszMappingName,
&cbMappingName,
FALSE ) )
{
hr = E_FAIL;
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Failed to read mapping for %s/%s/%d. 0x%x\n",
pszServicePath, achServiceInstance,
dwMappingIndex, hr));
// Move on to next Service site
break;
}
if ( !pCurrentMapping->MappingGetField(
IISMDB_INDEX_CERT11_ENABLED,
(PBYTE *)&pdwEnabled,
&cbEnabled,
FALSE ) ||
cbEnabled != sizeof( DWORD ) )
{
hr = E_FAIL;
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Failed to read mapping for %s/%s/%d. 0x%x\n",
pszServicePath, achServiceInstance,
dwMappingIndex, hr));
// Move on to next Service site
break;
}
//
// Finished reading current mapping details
// Now convert Mapping Name, AcctName and Password
// from ANSI to Unicode (the actual conversion may happen later
// - STRAU class will take care of it)
if ( !strMappingName.Copy( pszMappingName,
cbMappingName - sizeof(CHAR) ) ||
!strPassword.Copy( pszPassword,
cbPassword - sizeof(CHAR) ) ||
!strAcctName.Copy( pszAcctName,
cbAcctName - sizeof(CHAR) ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Failed to read mapping for %s/%s/%d. 0x%x\n",
pszServicePath, achServiceInstance,
dwMappingIndex, hr));
break;
}
// Now calculate hex string with current cert's hash
// it will be used as a unique name for the mapping
// object in the metabase
if ( FAILED( hr = GetCertificateHashString(
pbCert,
cbCert,
achCertHash,
sizeof( achCertHash )/sizeof(WCHAR) ) ) )
{
ReportImportProblem( hr );
DBGPRINTF((DBG_CONTEXT,
"Invalid cert passed to CreateMapping() 0x%x\n", hr));
// goto next mapping
goto NextMapping;
}
if ( ! strObjectPath.Copy( achServiceInstance ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
DBGPRINTF((DBG_CONTEXT,
"Failed to import mapping. 0x%x\n", hr));
goto NextMapping;
}
if ( ! strObjectPath.Append( "/Cert11/Mappings/" ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
DBGPRINTF((DBG_CONTEXT,
"Failed to import mapping. 0x%x\n", hr));
goto NextMapping;
}
if ( ! strObjectPath.Append( achCertHash ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
DBGPRINTF((DBG_CONTEXT,
"Failed to import mapping. 0x%x\n", hr));
goto NextMapping;
}
//
// Add new node for the mapping
//
if ( !mb.AddObject( strObjectPath.QueryStrW() ) )
{
// Don't report problem here
// Just fall through
// Next SetData will fail and report problem
// if there is one.
}
//
// Save 1to1 Certificate mapping info
// directly to metabase in the new format
//
// save the certificate
if ( !mb.SetData( strObjectPath.QueryStrW(), MD_MAPCERT,
IIS_MD_UT_SERVER, BINARY_METADATA,
pbCert, cbCert, 0 ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to write MD_MAPCERT. hr = %x\n",
hr ));
// Don't consider fatal. Move on
}
// save the NTAccount
if ( !mb.SetString( strObjectPath.QueryStrW(),
MD_MAPNTACCT,
IIS_MD_UT_SERVER,
strAcctName.QueryStrW(),
0 ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to write MD_MAPNTACCT. hr = %x\n",
hr ));
// Don't consider fatal. Move on }
}
// save the password - secure
if ( !mb.SetString( strObjectPath.QueryStrW(),
MD_MAPNTPWD,
IIS_MD_UT_SERVER,
strPassword.QueryStrW(),
METADATA_SECURE ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to write MD_MAPNTPWD. hr = %x\n",
hr ));
// Don't consider fatal. Move on }
}
// save the map's name
if ( !mb.SetString( strObjectPath.QueryStrW(),
MD_MAPNAME,
IIS_MD_UT_SERVER,
strMappingName.QueryStrW(),
0 ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to write MD_MAPNAME. hr = %x\n",
hr ));
// Don't consider fatal. Move on }
}
// save the Enabled flag
// server reads the flag as the value of the dword
if ( !mb.SetDword( strObjectPath.QueryStrW(),
MD_MAPENABLED,
IIS_MD_UT_SERVER,
*pdwEnabled,
0 ) )
{
ReportImportProblem( hr );
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to write MD_MAPENABLED. hr = %x\n",
hr ));
// Don't consider fatal. Move on }
}
NextMapping:
;
}
//
// Flush metabase changes after each instance
// we have to close our metabase handles to make Save possible
mb.Close();
if ( !mb.Save() )
{
// we failed to save metabase
// there is no guarantee that data we converted so far
// will be persisted safely
// In this case we rather leave legacy structure around
hr = HRESULT_FROM_WIN32( GetLastError() );
ReportImportProblem( hr );
fMBSaved = FALSE;
}
else
{
fMBSaved = TRUE;
}
// Reopen metabase
// we had to close it to be able to perform Save
// Note: This function assumes that nobody is messing with the metabase
// at this time (should be called only during upgrade) on iisadmin startup
// If new service instance nodes are added or removed then
// our site instance enumeration index could be off
if ( !mb.Open( pszServicePath,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Failed to open metabase path %s (import of 1to1 cert mappings will fail). hr = %x\n",
pszServicePath, hr ));
// still try another service if there is any left
continue;
}
if ( fMBSaved )
{
//
// delete external storage - the *.mp file
// for this mapper
//
if ( !AcctMapper.Delete() )
{
//
// Unfortunately there will be some legacy data left behind
//
hr = HRESULT_FROM_WIN32( GetLastError() );
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Failed to delete mp file for %s/%s. hr = %x\n",
pszServicePath, achServiceInstance, hr ));
// don't take error as fatal and move on
}
if ( !mb.DeleteData( achServiceInstance, MD_SERIAL_CERT11,
IIS_MD_UT_SERVER, BINARY_METADATA ) )
{
//
// Unfortunately there will be some legacy data left behind
//
hr = HRESULT_FROM_WIN32( GetLastError() );
ReportImportProblem( hr );
DBGPRINTF(( DBG_CONTEXT,
"Failed to delete MD_SERIAL_CERT11 for %s/%s. hr = %x\n",
pszServicePath, achServiceInstance, hr ));
// don't take error as fatal and move on
}
}
EnumNext:
;
}
mb.Close();
}
Finished:
if ( hKey != NULL )
{
RegCloseKey( hKey );
hKey = NULL;
}
if ( fCoInitialized )
{
CoUninitialize();
}
return;
}