|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name : iiscertmap.cxx
Abstract: IIS Certificate mapping
Author: Bilal Alam (BAlam) 19-Apr-2000
Environment: Win32 - User Mode
Project: Stream Filter Worker Process --*/
#include "precomp.hxx"
#include <mbstring.h>
IIS_CERTIFICATE_MAPPING::IIS_CERTIFICATE_MAPPING( VOID ) : _pCert11Mapper( NULL ), _pCertWildcardMapper( NULL ), _cRefs( 1 ) { }
IIS_CERTIFICATE_MAPPING::~IIS_CERTIFICATE_MAPPING( VOID ) { if ( _pCert11Mapper != NULL ) { delete _pCert11Mapper; _pCert11Mapper = NULL; } if ( _pCertWildcardMapper != NULL ) { delete _pCertWildcardMapper; _pCertWildcardMapper = NULL; } }
HRESULT IIS_CERTIFICATE_MAPPING::Read11Mappings( DWORD dwSiteId ) /*++
Routine Description:
Read 1-1 mappings from metabase
Arguments:
dwSiteId - Site ID (duh)
Return Value:
HRESULT
--*/ { MB mb( g_pW3Server->QueryMDObject() ); WCHAR achMBPath[ 256 ]; HRESULT hr = NO_ERROR; WCHAR achMappingName[ MAX_PATH + 1 ]; BOOL fRet; DWORD dwIndex; BYTE abBuffer[ 1024 ]; BUFFER buff( abBuffer, sizeof( abBuffer ) ); DWORD cbRequired; STACK_STRU( strTemp, 64 ); STACK_STRU( strUserName, 64 ); STACK_STRU( strPassword, 64 ); DWORD dwEnabled; CIisMapping * pCertMapping; //
// Setup the NSEPM path to get at 1-1 mappings
//
_snwprintf( achMBPath, sizeof( achMBPath ) / sizeof( WCHAR ) - 1, L"/LM/W3SVC/%d/<nsepm>/Cert11/Mappings", dwSiteId ); //
// Open the metabase and read 1-1 mapping properties
//
fRet = mb.Open( achMBPath, METADATA_PERMISSION_READ ); if ( fRet ) { dwIndex = 0; achMappingName[ 0 ] = L'/'; for ( ; ; ) { //
// We will need to prepend the object name with '/'. Hence
// goofyness of sending an offseted pointed to name
//
fRet = mb.EnumObjects( L"", achMappingName + 1, dwIndex ); if ( !fRet ) { hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); break; } //
// Get certificate blob
//
cbRequired = buff.QuerySize(); fRet = mb.GetData( achMappingName, MD_MAPCERT, IIS_MD_UT_SERVER, BINARY_METADATA, buff.QueryPtr(), &cbRequired, METADATA_NO_ATTRIBUTES ); if ( !fRet ) { if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { DBG_ASSERT( cbRequired > buff.QuerySize() ); if ( !buff.Resize( cbRequired ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } fRet = mb.GetData( achMappingName, MD_MAPCERT, IIS_MD_UT_SERVER, BINARY_METADATA, buff.QueryPtr(), &cbRequired, METADATA_NO_ATTRIBUTES ); if ( !fRet ) { DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER ); hr = HRESULT_FROM_WIN32( GetLastError() ); break; } } else { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } } //
// Get NT account name
//
if ( !mb.GetStr( achMappingName, MD_MAPNTACCT, IIS_MD_UT_SERVER, &strUserName ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } //
// Get NT password
//
if ( !mb.GetStr( achMappingName, MD_MAPNTPWD, IIS_MD_UT_SERVER, &strPassword ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } //
// Is this mapping enabled?
//
if ( !mb.GetDword( achMappingName, MD_MAPENABLED, IIS_MD_UT_SERVER, &dwEnabled ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } //
// If this mapping is enabled, add it to 1-1 mapper
//
if ( dwEnabled ) { if ( _pCert11Mapper == NULL ) { _pCert11Mapper = new CIisCert11Mapper(); if ( _pCert11Mapper == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; }
//
// Reset() will configure default hierarchies
// If hierarchies are not configured then comparison (CIisMapping::Cmp() function
// implemented in iismap.cxx) will fail
// (and mapper will always incorrectly map to first available 1to1 mapping)
//
if(!_pCert11Mapper->Reset()) { delete _pCert11Mapper; _pCert11Mapper = NULL; hr = HRESULT_FROM_WIN32( GetLastError() ); break; } } DBG_ASSERT( _pCert11Mapper != NULL ); pCertMapping = _pCert11Mapper->CreateNewMapping(); if ( pCertMapping == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_CERT, (CHAR*)buff.QueryPtr(), cbRequired, FALSE ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } //
// Encrypted metabase stuff returned as ANSI (LAME!!!!!)
//
if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_ACCT, (CHAR*) strUserName.QueryStr() ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_PWD, (CHAR*) strPassword.QueryStr() ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; }
if ( !((CIisAcctMapper*)_pCert11Mapper)->Add( pCertMapping, FALSE ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } } dwIndex++; } } else { hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); } return hr; }
HRESULT IIS_CERTIFICATE_MAPPING::ReadWildcardMappings( DWORD dwSiteId ) /*++
Routine Description:
Read wildcard mappings from metabase
Arguments:
dwSiteId - Site ID (duh)
Return Value:
HRESULT
--*/ { MB mb( g_pW3Server->QueryMDObject() ); WCHAR achMBPath[ 256 ]; BOOL fRet; BYTE abBuffer[ 1024 ]; BUFFER buff( abBuffer, sizeof( abBuffer ) ); DWORD cbRequired; PUCHAR pLamePointer; //
// Setup the NSEPM path to get at wildcard mappings
//
_snwprintf( achMBPath, sizeof( achMBPath ) / sizeof( WCHAR ) - 1, L"/LM/W3SVC/%d/<nsepm>/CertW", dwSiteId );
//
// Open the metabase and read wildcard mappings
//
fRet = mb.Open( achMBPath, METADATA_PERMISSION_READ ); if ( fRet ) { cbRequired = buff.QuerySize(); fRet = mb.GetData( L"", MD_SERIAL_CERTW, IIS_MD_UT_SERVER, BINARY_METADATA, buff.QueryPtr(), &cbRequired, METADATA_NO_ATTRIBUTES ); if ( !fRet ) { if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { DBG_ASSERT( cbRequired > buff.QuerySize() ); if ( !buff.Resize( cbRequired ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } fRet = mb.GetData( L"", MD_SERIAL_CERTW, IIS_MD_UT_SERVER, BINARY_METADATA, buff.QueryPtr(), &cbRequired, METADATA_NO_ATTRIBUTES ); if ( !fRet ) { DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER ); return HRESULT_FROM_WIN32( GetLastError() ); } } return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); } //
// Thanx to the man, I can just unserialize. XBF rocks ;-)
//
DBG_ASSERT( _pCertWildcardMapper == NULL ); _pCertWildcardMapper = new CIisRuleMapper(); if ( _pCertWildcardMapper == NULL ) { return HRESULT_FROM_WIN32( GetLastError() ); }
pLamePointer = (PUCHAR) buff.QueryPtr(); fRet = _pCertWildcardMapper->Unserialize( &pLamePointer, &cbRequired ); if ( !fRet ) { return HRESULT_FROM_WIN32( GetLastError() ); } } else { return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); } return NO_ERROR; }
//static
HRESULT IIS_CERTIFICATE_MAPPING::GetCertificateMapping( DWORD dwSiteId, IIS_CERTIFICATE_MAPPING ** ppCertificateMapping ) /*++
Routine Description:
Read appropriate metabase configuration to get configured IIS certificate mapping
Arguments:
dwSiteId - Site ID (duh) ppCertificateMapping - Filled with certificate mapping descriptor on success
Return Value:
HRESULT
--*/ { IIS_CERTIFICATE_MAPPING * pCertMapping = NULL; HRESULT hr = NO_ERROR; if ( ppCertificateMapping == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppCertificateMapping = NULL; //
// Create a certificate mapping descriptor
//
pCertMapping = new IIS_CERTIFICATE_MAPPING(); if ( pCertMapping == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Finished; } //
// Read 1-1 mappings
//
hr = pCertMapping->Read11Mappings( dwSiteId ); if ( FAILED( hr ) && hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) { goto Finished; } hr = NO_ERROR;
//
// Read wildcards
//
hr = pCertMapping->ReadWildcardMappings( dwSiteId ); if ( FAILED( hr ) && hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) { goto Finished; } hr = NO_ERROR; Finished: if ( FAILED( hr ) ) { if ( pCertMapping != NULL ) { delete pCertMapping; } } else { DBG_ASSERT( pCertMapping != NULL ); *ppCertificateMapping = pCertMapping; } return hr; }
HRESULT IIS_CERTIFICATE_MAPPING::DoMapCredential( PBYTE pClientCertBlob, DWORD cbClientCertBlob, TOKEN_CACHE_ENTRY ** ppCachedToken, BOOL * pfClientCertDeniedByMapper ) { CIisMapping * pQuery; CIisMapping * pResult; CHAR achUserName[ UNLEN + 1 ]; LPSTR pszUserName; DWORD cbUserName; CHAR achPassword[ PWLEN + 1 ]; LPSTR pszPassword; DWORD cbPassword; CHAR * pszDomain; DWORD dwEnabled = 0; DWORD cbEnabled; BOOL fMatch = FALSE; DWORD dwLogonError = NO_ERROR; HRESULT hr = S_OK; //
// add 1 to strUserDomainW for separator "\"
//
STACK_STRU( strUserDomainW, UNLEN + IIS_DNLEN + 1 + 1 ); STACK_STRU( strUserNameW, UNLEN + 1 ); STACK_STRU( strPasswordW, PWLEN + 1 );
DBG_ASSERT( pClientCertBlob != NULL ); DBG_ASSERT( cbClientCertBlob != 0 ); DBG_ASSERT( ppCachedToken != NULL ); DBG_ASSERT( pfClientCertDeniedByMapper != NULL );
//
// First try the 1-1 mapper
//
if ( _pCert11Mapper != NULL ) { //
// Build a query mapping to check
//
pQuery = _pCert11Mapper->CreateNewMapping( pClientCertBlob, cbClientCertBlob ); if ( pQuery == NULL ) { return SEC_E_INTERNAL_ERROR; }
_pCert11Mapper->Lock(); if ( _pCert11Mapper->FindMatch( pQuery, &pResult ) ) { //
// Awesome. We found a match. Do the deed if the rule is
// enabled
//
if ( pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_ACCT, &pszUserName, &cbUserName, FALSE ) && pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_PWD, &pszPassword, &cbPassword, FALSE ) ) { //
// No need to check for Enabled (IISMDB_INDEX_CERT11_ENABLED)
// since Read11Mappings() will build mapping table consisting only
// of enabled mappings
//
strncpy( achUserName, pszUserName, UNLEN ); achUserName[ UNLEN ] = '\0'; strncpy( achPassword, pszPassword, PWLEN ); achPassword[ PWLEN ] = '\0'; fMatch = TRUE; } } _pCert11Mapper->Unlock(); delete pQuery; pQuery = NULL; } //
// Try the wildcard mapper if we still haven't found a match
//
if ( !fMatch && _pCertWildcardMapper != NULL ) {
PCCERT_CONTEXT pClientCert = CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pClientCertBlob, cbClientCertBlob ); if ( pClientCert == NULL ) { DBG_ASSERT( pClientCert != NULL ); } else if ( !_pCertWildcardMapper->Match( (PCERT_CONTEXT) (pClientCert), NULL, // legacy value
achUserName, achPassword ) ) { //
// If the mapping rule is denied then return
// a NULL pointer through ppCachedToken with SEC_E_OK.
// That indicated to caller that mapping was denied
//
if ( GetLastError() == ERROR_ACCESS_DENIED ) { *ppCachedToken = NULL; *pfClientCertDeniedByMapper = TRUE;
if ( pClientCert != NULL ) { CertFreeCertificateContext( pClientCert ); pClientCert = NULL; }
return SEC_E_OK; } } else { fMatch = TRUE; } if ( pClientCert != NULL ) { CertFreeCertificateContext( pClientCert ); pClientCert = NULL; } } //
// If we still haven't found a match, then return error
//
if ( fMatch ) { //
// Split up the user name into domain/user if needed
//
pszUserName = (LPSTR) _mbspbrk( (UCHAR*) achUserName, (UCHAR*) "/\\" ); if ( pszUserName == NULL ) { //
// No domain in the user name. First try the metabase domain
// name
//
//
// BUGBUG: DefaultLogonDomain is not used for cert mapping at all
//
pszDomain = ""; pszUserName = achUserName; } else { *pszUserName = '\0'; pszDomain = achUserName; pszUserName = pszUserName + 1; }
//
// Convert domain, user and password to unicode
//
hr = strUserNameW.CopyA( pszUserName ); if ( FAILED( hr ) ) { return hr; }
hr = strUserDomainW.CopyA( pszDomain ); if ( FAILED( hr ) ) { return hr; }
hr = strPasswordW.CopyA( achPassword ); if ( FAILED( hr ) ) { return hr; } //
// We should have valid credentials to call LogonUser()
//
DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL ); hr = g_pW3Server->QueryTokenCache()->GetCachedToken( strUserNameW.QueryStr(), strUserDomainW.QueryStr(), strPasswordW.QueryStr(), LOGON32_LOGON_INTERACTIVE, FALSE, // BUGBUG not UPN logon
ppCachedToken, &dwLogonError ); if ( FAILED( hr ) ) { return SEC_E_UNKNOWN_CREDENTIALS; } //
// If *ppCachedToken is NULL, then logon failed
//
if ( *ppCachedToken == NULL ) { //
// Note: With IIS5 we used to log logon failure to event log
// however it doesn't seem to be necessary because if logon/logoff auditing is enabled
// then security log would have relevant information about the logon failure
//
DBG_ASSERT( dwLogonError != ERROR_SUCCESS ); return SEC_E_UNKNOWN_CREDENTIALS; }
DBG_ASSERT( (*ppCachedToken)->CheckSignature() ); *pfClientCertDeniedByMapper = FALSE; return SEC_E_OK; } return SEC_E_UNKNOWN_CREDENTIALS; }
|