|
|
/*++
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
Return Value:
HRESULT
--*/ { MB mb( g_pW3Server->QueryMDObject() ); WCHAR achMBPath[ 256 ]; HRESULT hr = NO_ERROR; // besides terminating '\0' one extra char is needed for leading slash
WCHAR achMappingName[ ADMINDATA_MAX_NAME_LEN + 1 + 1 ]; BOOL fRet; DWORD dwIndex; STACK_BUFFER( buff, 1024 ); DWORD cbRequired; STACK_STRU( strTemp, 64 ); STACK_STRU( strUserName, 64 ); STACK_STRU( strPassword, 64 ); DWORD dwEnabled; CIisMapping * pCertMapping; //
// Setup the metabase path to get at 1-1 mappings
//
_snwprintf( achMBPath, sizeof( achMBPath ) / sizeof( WCHAR ) - 1, L"/LM/W3SVC/%d/Cert11/Mappings", dwSiteId ); achMBPath[ sizeof( achMBPath ) / sizeof( WCHAR ) - 1 ] = '\0'; //
// Open the metabase and read 1-1 mapping properties
//
fRet = mb.Open( achMBPath, METADATA_PERMISSION_READ ); if ( fRet ) { dwIndex = 0; achMappingName[ 0 ] = L'/'; for ( ; ; ) // loop un
{ dwEnabled = FALSE; //
// 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() ); if ( hr == MD_ERROR_DATA_NOT_FOUND ) { // if cert blob is not present we skip this entry
goto NextEntry; } else { break; } } } else { hr = HRESULT_FROM_WIN32( GetLastError() ); if ( hr == MD_ERROR_DATA_NOT_FOUND ) { // if cert blob is not present we skip this entry
goto NextEntry; } else { break; } } }
//
// Get NT account name
//
if ( !mb.GetStr( achMappingName, MD_MAPNTACCT, IIS_MD_UT_SERVER, &strUserName ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); if ( hr == MD_ERROR_DATA_NOT_FOUND ) { // if account name is not present we skip this entry
goto NextEntry; } else { break; }
break; } //
// Get NT password
//
if ( !mb.GetStr( achMappingName, MD_MAPNTPWD, IIS_MD_UT_SERVER, &strPassword ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); if ( hr == MD_ERROR_DATA_NOT_FOUND ) { // we assume default password - empty;
strPassword.Reset(); } else { break; } }
//
// Is this mapping enabled?
//
if ( !mb.GetDword( achMappingName, MD_MAPENABLED, IIS_MD_UT_SERVER, &dwEnabled ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); if ( hr == MD_ERROR_DATA_NOT_FOUND ) { // we assume default 0 (FALSE);
dwEnabled = 0; } else { 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, (PBYTE) buff.QueryPtr(), cbRequired, FALSE ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); break; } if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_ACCT, (PBYTE) strUserName.QueryStr(), strUserName.QueryCB() + sizeof (WCHAR), FALSE ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); delete pCertMapping; pCertMapping = NULL; break; } if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_PWD, (PBYTE) strPassword.QueryStr(), strPassword.QueryCB() + sizeof (WCHAR), FALSE ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); delete pCertMapping; pCertMapping = NULL; break; }
if ( !((CIisAcctMapper*)_pCert11Mapper)->Add( pCertMapping, FALSE ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); delete pCertMapping; pCertMapping = NULL; break; } } NextEntry: 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 pSerializedMapping; //
// Setup the metabase path to get at wildcard mappings
//
_snwprintf( achMBPath, sizeof( achMBPath ) / sizeof( WCHAR ) - 1, L"/LM/W3SVC/%d/", dwSiteId ); achMBPath[ sizeof( achMBPath ) / sizeof( WCHAR ) - 1 ] = '\0';
//
// 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() ); } } else { 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() ); }
if( !_pCertWildcardMapper->IsValid() ) { //
// creation of _pCertWildcardMapper failed
// assume out of memory
//
return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); }
pSerializedMapping = (PUCHAR) buff.QueryPtr(); // Unserialize will change the value of pSerializedMapping
// It will point to the end of the unserialized data
// We don't need that modified pointer. But remember not to use
// pSerializedMapping any more after Unserialize call
//
fRet = _pCertWildcardMapper->Unserialize( &pSerializedMapping, &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 ) ) { DBGPRINTF(( DBG_CONTEXT, "Error reading 1to1 Certificate Mappings. hr = %x\n", hr ));
goto Finished; } hr = NO_ERROR;
//
// Read wildcards
//
hr = pCertMapping->ReadWildcardMappings( dwSiteId ); if ( FAILED( hr ) && hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) { DBGPRINTF(( DBG_CONTEXT, "Error reading Wildcard Certificate Mappings. hr = %x\n", hr ));
goto Finished; } hr = NO_ERROR; Finished: if ( FAILED( hr ) ) { if ( pCertMapping != NULL ) { delete pCertMapping; pCertMapping = NULL; } } else { DBG_ASSERT( pCertMapping != NULL ); *ppCertificateMapping = pCertMapping; } return hr; }
HRESULT IIS_CERTIFICATE_MAPPING::DoMapCredential( W3_MAIN_CONTEXT * pMainContext, PBYTE pClientCertBlob, DWORD cbClientCertBlob, TOKEN_CACHE_ENTRY ** ppCachedToken, BOOL * pfClientCertDeniedByMapper ) { CIisMapping * pQuery; CIisMapping * pResult; WCHAR wszUserName[ UNLEN + IIS_DNLEN + 1 + 1 ]; LPWSTR pwszUserName; DWORD cbUserName; WCHAR wszPassword[ PWLEN + 1 ]; LPWSTR pwszPassword; DWORD cbPassword; CHAR * pszDomain; BOOL fMatch = FALSE; DWORD dwLogonError = NO_ERROR; HRESULT hr = S_OK;
BOOL fPossibleUPNLogon = FALSE; //
// add 1 to strUserDomainW for separator "\"
//
STACK_STRU( strUserDomainW, UNLEN + IIS_DNLEN + 1 + 1 ); STACK_STRU( strUserNameW, UNLEN + IIS_DNLEN + 1 + 1 ); STACK_STRU( strDomainNameW, IIS_DNLEN + 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; }
//
// no need to lock cert mapper because this is read only copy
// used for mapping execution in worker process
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, (PBYTE *)&pwszUserName, &cbUserName, FALSE ) && pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_PWD, (PBYTE *)&pwszPassword, &cbPassword, FALSE ) ) { //
// No need to check for Enabled (IISMDB_INDEX_CERT11_ENABLED)
// since Read11Mappings() will build mapping table consisting only
// of enabled mappings
//
//
// Make copy of user and password
//
hr = strUserDomainW.Copy( pwszUserName ); if ( FAILED( hr ) ) { return hr; } hr = strPasswordW.Copy( pwszPassword ); if ( FAILED( hr ) ) { return hr; } fMatch = TRUE; } } 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 ); } // Wildcard mapper assumes that
// achUserName buffer is greater than UNLEN+IIS_DNLEN+1 and
// achPassword buffer is greater then PWLEN
//
else if ( !_pCertWildcardMapper->Match( (PCERT_CONTEXT) pClientCert, NULL, // legacy value
wszUserName, wszPassword ) ) { //
// 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; //
// Copy user and password (user name may be fully qualified with domain name in it)
//
hr = strUserDomainW.Copy( wszUserName ); if ( FAILED( hr ) ) { return hr; } hr = strPasswordW.Copy( wszPassword ); if ( FAILED( hr ) ) { return hr; }
} 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
// Note: DefaultLogonDomain is not used for cert mapping at all
// This is to keep behaviour equivalent with former versions of IIS
//
hr = W3_STATE_AUTHENTICATION::SplitUserDomain( strUserDomainW, &strUserNameW, &strDomainNameW, NULL, // no default domain
&fPossibleUPNLogon ); 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(), strDomainNameW.QueryStr(), strPasswordW.QueryStr(), LOGON32_LOGON_NETWORK_CLEARTEXT, FALSE, //don't use subauth
fPossibleUPNLogon, pMainContext->QueryRequest()-> QueryRemoteSockAddress(), 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; }
|