windows-nt/Source/XPSP1/NT/inetsrv/iis/iisrearc/iisplus/ulw3/iiscertmap.cxx

740 lines
21 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}