/*++ opyright (c) 1996 Microsoft Corporation Module Name: iiscrmap.cxx Abstract: Certificate to NT account mapper Author: Philippe Choquier (phillich) 17-may-1996 Alex Mallet (amallet) 13-Feb-1998 --*/ #ifdef __cplusplus extern "C" { #endif # include # include # include # include #include #if 1 // DBCS #include #endif #include #include #include #include #include #include #ifdef __cplusplus }; #endif #include #include extern "C" { #include #include } // extern "C" #include #include #include #include "mapmsg.h" #include #include #include #include #include #include #include #include #include #include #include #define CALLC WINAPI CRITICAL_SECTION g_csInitCritSec; DWORD g_dwNumLocators; extern MAPPER_VTABLE g_MapperVtable; HANDLE g_hModule; // // // DECLARE_DEBUG_PRINTS_OBJECT(); #include DEFINE_GUID(IisCrMapGuid, 0x784d8913, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e); extern "C" BOOL WINAPI DLLEntry( HINSTANCE hDll, DWORD dwReason, LPVOID lpvReserved ) { switch ( dwReason ) { case DLL_PROCESS_ATTACH: #ifdef _NO_TRACING_ CREATE_DEBUG_PRINT_OBJECT( "IISCRMAP" ); #else CREATE_DEBUG_PRINT_OBJECT( "IISCRMAP" , IisCrMapGuid); #endif INITIALIZE_CRITICAL_SECTION( &g_csInitCritSec ); g_dwNumLocators = 0; g_hModule = hDll; break; case DLL_PROCESS_DETACH: if ( g_dwNumLocators ) { DBGPRINTF((DBG_CONTEXT, "Still have %d locators left !\n", g_dwNumLocators)); } DELETE_DEBUG_PRINT_OBJECT(); DeleteCriticalSection( &g_csInitCritSec ); break; default: break; } return TRUE; } extern "C" DWORD CALLC CreateInstance( OUT HMAPPER** pHMapper ) /*++ Routine Description: Called to initialize the mapper Arguments: None Returns: Ptr to mapper vtable if success, otherwise NULL --*/ { IisMapper *pMap; if ( !(pMap = (IisMapper*)LocalAlloc( LMEM_FIXED, sizeof(IisMapper) )) ) { return SEC_E_INSUFFICIENT_MEMORY; } memcpy ( &pMap->mvtEntryPoints, &g_MapperVtable, sizeof(MAPPER_VTABLE) ); pMap->hMapper.m_vtable = &pMap->mvtEntryPoints; pMap->hMapper.m_dwMapperVersion = MAPPER_INTERFACE_VER; pMap->hMapper.m_Reserved1 = NULL; pMap->lRefCount = 1; pMap->fIsIisCompliant = TRUE; pMap->hInst = (HINSTANCE)g_hModule; pMap->pCert11Mapper = NULL; pMap->pCertWMapper = NULL; pMap->pvInfo = NULL; pMap->dwSignature = IIS_MAPPER_SIGNATURE; *pHMapper = (HMAPPER*)pMap; return SEC_E_OK; } extern "C" LONG CALLC IisReferenceMapper( OUT HMAPPER* pMap ) /*++ Routine Description: Increment reference count to mapper Arguments: pMap - ptr to mapper struct Returns: Ref count --*/ { LONG l; EnterCriticalSection( &g_csInitCritSec ); l = ++((IisMapper*)pMap)->lRefCount; LeaveCriticalSection( &g_csInitCritSec ); return l; } extern "C" LONG CALLC IisDeReferenceMapper( OUT HMAPPER* pMap ) /*++ Routine Description: Decrement reference count to mapper Arguments: pMap - ptr to mapper struct Returns: Ref count --*/ { LONG l; EnterCriticalSection( &g_csInitCritSec ); if ( !(l = --((IisMapper*)pMap)->lRefCount) ) { LocalFree( pMap ); } LeaveCriticalSection( &g_csInitCritSec ); return l; } extern "C" DWORD CALLC IisGetChallenge( HMAPPER*, PBYTE, DWORD, PBYTE, LPDWORD ) /*++ Routine Description: Get challenge for auth sequence Arguments: Not used Returns: FALSE ( not supported ) --*/ { return SEC_E_UNSUPPORTED_FUNCTION; } extern "C" DWORD CALLC IisGetIssuerList( IN HMAPPER* phMapper, IN LPVOID pvReserved, OUT LPBYTE pIssuer, IN OUT DWORD * pdwIssuer ) /*++ Routine Description: Called to retrieve the list of preferred cert issuers Arguments: phMapper - pointer to mapper object pvReserved - nothing useful, right now pIssuer -- updated with ptr buffer of issuers. If NULL, caller wants to get size of buffer required for issuer list pdwIssuer -- updated with issuers buffer size Returns: SEC_E_* error code --*/ { IIS_SSL_INFO *pSslInfo = (IIS_SSL_INFO *) ((IisMapper*) phMapper)->pvInfo; BOOL fSuccess = FALSE; // // Can't get at any instance-specific information, so we'll just pretend // that nothing happened // if ( !pSslInfo ) { *pdwIssuer = 0; return SEC_E_OK; } // // Pull out all the trusted CAs // PCCERT_CONTEXT *apContexts = NULL; PCCERT_CONTEXT pCert = NULL; DWORD dwCertsFound = 0; DWORD dwCertsInCTL = 0; DWORD dwTotalSize = 0; DWORD dwPosition = 0; if ( !pSslInfo->GetTrustedIssuerCerts( &apContexts, &dwCertsFound ) ) { return SEC_E_UNTRUSTED_ROOT; } // // Figure out total size of chain and whether the buffer passed in is big // enough. Each cert is to be encoded as [MSB of length] [LSB of length] [cert], // so two extra bytes need to be added to the length for each cert // for ( DWORD dwIndex = 0; dwIndex < dwCertsFound; dwIndex++ ) { dwTotalSize += apContexts[dwIndex]->cbCertEncoded; } dwTotalSize += 2*dwCertsFound; // // Caller is only interested in size or buffer is too small // if ( !pIssuer || ( pIssuer && (*pdwIssuer < dwTotalSize) ) ) { *pdwIssuer = dwTotalSize; goto cleanup; } // // Fill in the cert info : [MSB of length] [LSB of length] [Actual cert] // for ( dwIndex = 0 ; dwIndex < dwCertsFound; dwIndex++ ) { pIssuer[dwPosition++] = (BYTE) ((apContexts[dwIndex]->cbCertEncoded) & 0xFF00) >> 8; pIssuer[dwPosition++] = (BYTE) (apContexts[dwIndex]->cbCertEncoded) & 0xFF; memcpy( pIssuer + dwPosition, apContexts[dwIndex]->pbCertEncoded, apContexts[dwIndex]->cbCertEncoded ); dwPosition += apContexts[dwIndex]->cbCertEncoded; } DBG_ASSERT( dwPosition == dwTotalSize ); *pdwIssuer = dwTotalSize; fSuccess = TRUE; cleanup: if ( apContexts ) { for ( dwIndex = 0; dwIndex < dwCertsFound; dwIndex++ ) { CertFreeCertificateContext( apContexts[dwIndex] ); } delete [] apContexts; } return fSuccess ? SEC_E_OK : SEC_E_UNTRUSTED_ROOT; #if 0 return ((CIisCert11Mapper*)(((IisMapper*)phMapper)->pCert11Mapper))->GetIssuerBuffer( (LPBYTE)pIssuer, pdwIssuer ); #endif } extern "C" __declspec( dllexport ) DWORD CALLC MapperFree( LPVOID pvBuff ) /*++ Routine Description: Called to delete list of issuers returned by GetIssuerList Arguments: pvBuff -- ptr to buffer alloced by the mapper DLL Returns: SEC_E_* error code --*/ { // // Dead code, I believe // DBG_ASSERT( TRUE ); LocalFree( pvBuff ); return SEC_E_OK; } extern "C" BOOL CALLC TerminateMapper( VOID ) /*++ Routine Description: Called to terminate the mapper Arguments: None Returns: --*/ { return TRUE; } extern "C" DWORD CALLC IisMapCredential( IN HMAPPER * phMapper, IN DWORD dwCredentialType, IN PVOID pCredential, IN PVOID pAuthority, OUT HLOCATOR * phToken ) /*++ Routine Description: Called to map a certificate to a NT account Arguments: phMapper - ptr to hmapper struct dwCredentialType -- type of credential pCredential - ptr to client cert pAuthority - ptr to Certifying Authority cert phToken -- updated with impersonation access token Returns: SEC_E_* error code --*/ { CIisMapping * pQuery = NULL; CIisMapping * pResult = NULL; BOOL fSt; LPSTR pAcct; LPSTR pPwd; LPSTR pA; DWORD dwA; DWORD dwD; int x; BOOL fAllocedAcct = FALSE; CHAR achDomain[256]; CHAR achUser[64]; CHAR achCookie[64]; CIisCert11Mapper* pCertMapper; CIisRuleMapper* pCertWildcard; LPSTR pEnabled; DWORD dwE; DWORD dwP; CHAR achPwd[PWLEN + 1]; PCERT_CONTEXT pClientCert = (PCERT_CONTEXT) pCredential; DBG_ASSERT( pClientCert ); // // Reject request if we do not understand the format // if ( dwCredentialType != X509_ASN_CHAIN ) { SetLastError( ERROR_INVALID_PARAMETER ); return SEC_E_NOT_SUPPORTED; } // // IIS 4 used the pAuthority parameter; IIS 5 doesn't, and Schannel always passes in // NULL, so remove the error check for it // #if 0 // // Reject request if CA not recognized // // // Removing IsCeritificateVerified() since we don't have what // we need to do the appropriate queries // if ( pAuthority == NULL ) { SetLastError( ERROR_INVALID_PARAMETER ); return SEC_E_INTERNAL_ERROR; } #endif pCertMapper = (CIisCert11Mapper*)((IisMapper*)phMapper)->pCert11Mapper; pCertWildcard = (CIisRuleMapper*)((IisMapper*)phMapper)->pCertWMapper; if ( !pCertMapper ) { goto wildcard_mapper; } #if DBG CHAR szSubjectName[1024]; CHAR szIssuerName[1024]; if ( CertGetNameString( pClientCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, szSubjectName, 1024 ) && CertGetNameString( pClientCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, szIssuerName, 1024 ) ) { DBGPRINTF((DBG_CONTEXT, "[IisMapCredential] Trying to map client cert for subject %s, issued by %s \n", szSubjectName, szIssuerName)); } else { DBGPRINTF((DBG_CONTEXT, "[IisMapCredential] Couldn't get subject or issuer name for client cert : 0x%x\n", GetLastError())); } #endif // // Create a mapping request object // if ( !(pQuery = pCertMapper->CreateNewMapping( pClientCert->pbCertEncoded, pClientCert->cbCertEncoded ) ) ) { DBGPRINTF((DBG_CONTEXT, "[IisMapCredential] Failed to create cert mapping query !\n")); return SEC_E_INTERNAL_ERROR; } // // Look for a match. If not found, log event // pCertMapper->Lock(); if ( pCertMapper->FindMatch( pQuery, &pResult ) ) { if ( !pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_ACCT, &pAcct, &dwA, FALSE ) || !pResult->MappingGetField( IISMDB_INDEX_CERT11_ENABLED, &pEnabled, &dwE, FALSE ) || !pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_PWD, &pPwd, &dwP, FALSE ) ) { pCertMapper->Unlock(); delete pQuery; return SEC_E_INTERNAL_ERROR; } strncpy( achPwd, pPwd, sizeof( achPwd ) - 1 ); delete pQuery; // // if mapping not enabled, ignore it // if ( !dwE || ( dwE == sizeof(DWORD) && *(UNALIGNED64 DWORD *)pEnabled == 0 ) || ( dwE && *pEnabled == '0' ) ) { pCertMapper->Unlock(); pCertMapper = NULL; goto wildcard_mapper; } } else { #if 0 // // log event // LPCTSTR pA[CERT_MAP_NB_FIELDS]; for ( UINT x = 0 ; x < CERT_MAP_NB_FIELDS ; ++x ) { if ( !pQuery->MappingGetField( x, (LPSTR*)(pA+x) ) || pA[x] == NULL ) { pA[x] = ""; } } ReportIisMapEvent( EVENTLOG_INFORMATION_TYPE, IISMAP_EVENT_NO_MAPPING, CERT_MAP_NB_FIELDS, pA ); #endif pCertMapper->Unlock(); pCertMapper = NULL; delete pQuery; // // Try to find a match using wildcard mapper // wildcard_mapper: if ( pCertWildcard ) { if ( !pCertWildcard->Match( pClientCert, (PCERT_CONTEXT)pAuthority, achCookie, achPwd ) ) { // // Set token to special value '1' for mappings that deny access // if ( GetLastError() == ERROR_ACCESS_DENIED ) { *phToken = (HLOCATOR)1; return SEC_E_OK; } else { pAcct = NULL; } } else { pAcct = achCookie; dwA = strlen( pAcct ); } } else { pAcct = NULL; } } if ( pAcct == NULL ) { if ( pCertMapper ) { pCertMapper->Unlock(); } return SEC_E_UNKNOWN_CREDENTIALS; } // break in domain & user name // copy to local storage so we can unlock mapper object LPSTR pSep; LPSTR pUser; #if 1 // DBCS enabling for user name // pAcct is always zero terminated if ( (pSep = (LPSTR)_mbschr( (PUCHAR)pAcct, '\\' )) ) #else if ( (pSep = (LPSTR)memchr( pAcct, '\\', dwA )) ) #endif { if ( (pSep - pAcct) < sizeof(achDomain) ) { memcpy( achDomain, pAcct, DIFF(pSep - pAcct) ); achDomain[pSep - pAcct] = '\0'; } else { SetLastError( ERROR_INVALID_PARAMETER ); pCertMapper->Unlock(); return SEC_E_UNKNOWN_CREDENTIALS; } pUser = pSep + 1; dwA -= DIFF(pSep - pAcct) + 1; } else { achDomain[0] = '\0'; pUser = pAcct; } if ( dwA >= sizeof(achUser) || dwA <= 0 ) { SetLastError( ERROR_INVALID_PARAMETER ); pCertMapper->Unlock(); return SEC_E_UNKNOWN_CREDENTIALS; } memcpy( achUser, pUser, dwA ); achUser[dwA] = '\0'; if ( pCertMapper ) { pCertMapper->Unlock(); } if ( fAllocedAcct ) { LocalFree( pAcct ); } DBGPRINTF((DBG_CONTEXT, "Found a mapping, %s\\%s\n", (achDomain[0] == '\0' ? "" : achDomain), achUser)); fSt = LogonUserA( achUser, achDomain, achPwd, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, (HANDLE*)phToken ); if ( !fSt ) { LPCTSTR pA[2]; CHAR achAcct[128]; DBGPRINTF((DBG_CONTEXT, "Logon of %s\\%s failed, error 0x%x\n", (achDomain[0] == '\0' ? "" : achDomain), achUser, GetLastError())); wsprintf( achAcct, "%s\\%s", achDomain, achUser ); if ( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&pA[1], 0, NULL ) ) { pA[0] = achAcct; ReportIisMapEvent( EVENTLOG_ERROR_TYPE, IISMAP_EVENT_ERROR_LOGON, sizeof(pA)/sizeof(LPCTSTR), pA ); LocalFree( (LPVOID)pA[1] ); } } else { DBGPRINTF((DBG_CONTEXT, "[IisMapCredential] Successful logon, token 0x%p\n", *phToken)); g_dwNumLocators++; } return fSt ? SEC_E_OK : SEC_E_UNKNOWN_CREDENTIALS; } extern "C" DWORD CALLC IisQueryMappedCredentialAttributes( IN HMAPPER *phMapper, IN HLOCATOR hLocator, IN ULONG ulAttribute, OUT PVOID pBuffer, IN OUT DWORD *pcbBuffer ) { if ( !pBuffer || ( *pcbBuffer < sizeof( HLOCATOR ) ) ) { *pcbBuffer = sizeof( HLOCATOR ); } else { *((HLOCATOR*)pBuffer) = hLocator; } return SEC_E_OK; } extern "C" DWORD CALLC IisGetAccessToken( IN HMAPPER* phMapper, IN HLOCATOR tokenhandle, OUT HANDLE *phToken ) /*++ Routine Description: Called to retrieve an access token from a mapping Arguments: phMapper - pointer to mapper to use tokenhandle -- HLOCATOR returned by MapCredential phToken -- updated with potentially new token Returns: SEC_E_* error code --*/ { #if 1 *phToken = (HANDLE) tokenhandle; return SEC_E_OK; #else // // Special value '1' is used to denote mappings that -deny- access // if ( tokenhandle == 1 ) { *phToken = (HANDLE)tokenhandle; } else { if ( !DuplicateTokenEx( (HANDLE)tokenhandle, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, phToken )) { return SEC_E_INVALID_TOKEN; } } return SEC_E_OK; #endif } extern "C" DWORD CALLC IisCloseLocator( IN HMAPPER* phMapper, IN HLOCATOR tokenhandle ) /*++ Routine Description: Called to close a HLOCATOR returned by MapCredential Arguments: tokenhandle -- HLOCATOR Returns: SEC_E_* error code --*/ { DBG_ASSERT( g_dwNumLocators > 0 ); if ( tokenhandle != 1 && tokenhandle != NULL ) { CloseHandle( (HANDLE)tokenhandle ); g_dwNumLocators--; } return SEC_E_OK; } MAPPER_VTABLE g_MapperVtable={ (REF_MAPPER_FN)IisReferenceMapper, (DEREF_MAPPER_FN)IisDeReferenceMapper, (GET_ISSUER_LIST_FN)IisGetIssuerList, (GET_CHALLENGE_FN)IisGetChallenge, (MAP_CREDENTIAL_FN)IisMapCredential, (GET_ACCESS_TOKEN_FN)IisGetAccessToken, (CLOSE_LOCATOR_FN)IisCloseLocator, (QUERY_MAPPED_CREDENTIAL_ATTRIBUTES_FN)IisQueryMappedCredentialAttributes } ;