//+--------------------------------------------------------------------------- // // Microsoft Windows NT Security // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: api.cpp // // Contents: Certificate Chaining Infrastructure // // History: 28-Jan-98 kirtd Created // //---------------------------------------------------------------------------- #include #include // // Globals // HMODULE g_hCryptnet = NULL; CRITICAL_SECTION g_CryptnetLock; CDefaultChainEngineMgr DefaultChainEngineMgr; CRITICAL_SECTION g_RoamingLogoffNotificationLock; BOOL g_fRoamingLogoffNotificationInitialized = FALSE; HMODULE g_hChainInst; VOID WINAPI CreateRoamingLogoffNotificationEvent(); VOID WINAPI InitializeRoamingLogoffNotification(); VOID WINAPI UninitializeRoamingLogoffNotification(); //+--------------------------------------------------------------------------- // // Function: ChainDllMain // // Synopsis: Chaining infrastructure initialization // //---------------------------------------------------------------------------- BOOL WINAPI ChainDllMain ( IN HMODULE hModule, IN ULONG ulReason, IN LPVOID pvReserved ) { BOOL fResult = TRUE; switch ( ulReason ) { case DLL_PROCESS_ATTACH: g_hChainInst = hModule; fResult = Pki_InitializeCriticalSection( &g_CryptnetLock ); if (fResult) { fResult = Pki_InitializeCriticalSection( &g_RoamingLogoffNotificationLock ); if (fResult) { fResult = DefaultChainEngineMgr.Initialize(); if (fResult) { CreateRoamingLogoffNotificationEvent(); } else { DeleteCriticalSection( &g_RoamingLogoffNotificationLock ); } } if (!fResult) { DeleteCriticalSection( &g_CryptnetLock ); } } break; case DLL_PROCESS_DETACH: UninitializeRoamingLogoffNotification(); DefaultChainEngineMgr.Uninitialize(); if ( g_hCryptnet != NULL ) { FreeLibrary( g_hCryptnet ); } DeleteCriticalSection( &g_CryptnetLock ); DeleteCriticalSection( &g_RoamingLogoffNotificationLock ); break; } return( fResult ); } //+--------------------------------------------------------------------------- // // Function: InternalCertCreateCertificateChainEngine // // Synopsis: create a chain engine handle // //---------------------------------------------------------------------------- BOOL WINAPI InternalCertCreateCertificateChainEngine ( IN PCERT_CHAIN_ENGINE_CONFIG pConfig, IN BOOL fDefaultEngine, OUT HCERTCHAINENGINE* phChainEngine ) { BOOL fResult = TRUE; PCCERTCHAINENGINE pChainEngine = NULL; CERT_CHAIN_ENGINE_CONFIG Config; if ( pConfig->cbSize != sizeof( CERT_CHAIN_ENGINE_CONFIG ) ) { SetLastError( (DWORD) E_INVALIDARG ); return( FALSE ); } Config = *pConfig; if ( Config.MaximumCachedCertificates == 0 ) { Config.MaximumCachedCertificates = DEFAULT_MAX_INDEX_ENTRIES; } pChainEngine = new CCertChainEngine( &Config, fDefaultEngine, fResult ); if ( pChainEngine == NULL ) { SetLastError( (DWORD) E_OUTOFMEMORY ); fResult = FALSE; } if ( fResult == TRUE ) { *phChainEngine = (HCERTCHAINENGINE)pChainEngine; } else { delete pChainEngine; } return( fResult ); } //+--------------------------------------------------------------------------- // // Function: CertCreateCertificateChainEngine // // Synopsis: create a certificate chain engine // //---------------------------------------------------------------------------- BOOL WINAPI CertCreateCertificateChainEngine ( IN PCERT_CHAIN_ENGINE_CONFIG pConfig, OUT HCERTCHAINENGINE* phChainEngine ) { return( InternalCertCreateCertificateChainEngine( pConfig, FALSE, phChainEngine ) ); } //+--------------------------------------------------------------------------- // // Function: CertFreeCertificateChainEngine // // Synopsis: free the chain engine handle // //---------------------------------------------------------------------------- VOID WINAPI CertFreeCertificateChainEngine ( IN HCERTCHAINENGINE hChainEngine ) { if ( ( hChainEngine == HCCE_CURRENT_USER ) || ( hChainEngine == HCCE_LOCAL_MACHINE ) ) { DefaultChainEngineMgr.FlushDefaultEngine( hChainEngine ); return; } ( (PCCERTCHAINENGINE)hChainEngine )->Release(); } //+--------------------------------------------------------------------------- // // Function: CertResyncCertificateChainEngine // // Synopsis: resync the chain engine // //---------------------------------------------------------------------------- BOOL WINAPI CertResyncCertificateChainEngine ( IN HCERTCHAINENGINE hChainEngine ) { BOOL fResult; PCCERTCHAINENGINE pChainEngine = (PCCERTCHAINENGINE)hChainEngine; PCCHAINCALLCONTEXT pCallContext = NULL; if ( ( hChainEngine == HCCE_LOCAL_MACHINE ) || ( hChainEngine == HCCE_CURRENT_USER ) ) { if ( DefaultChainEngineMgr.GetDefaultEngine( hChainEngine, (HCERTCHAINENGINE *)&pChainEngine ) == FALSE ) { return( FALSE ); } } else { pChainEngine->AddRef(); } fResult = CallContextCreateCallObject( pChainEngine, NULL, // pRequestedTime NULL, // pChainPara CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL, &pCallContext ); if (fResult) { pChainEngine->LockEngine(); fResult = pChainEngine->Resync( pCallContext, TRUE ); CertPerfIncrementChainRequestedEngineResyncCount(); pChainEngine->UnlockEngine(); CallContextFreeCallObject(pCallContext); } pChainEngine->Release(); return( fResult ); } //+--------------------------------------------------------------------------- // // Function: CertGetCertificateChain // // Synopsis: get the certificate chain for the given end certificate // //---------------------------------------------------------------------------- BOOL WINAPI CertGetCertificateChain ( IN OPTIONAL HCERTCHAINENGINE hChainEngine, IN PCCERT_CONTEXT pCertContext, IN OPTIONAL LPFILETIME pTime, IN OPTIONAL HCERTSTORE hAdditionalStore, IN OPTIONAL PCERT_CHAIN_PARA pChainPara, IN DWORD dwFlags, IN LPVOID pvReserved, OUT PCCERT_CHAIN_CONTEXT* ppChainContext ) { BOOL fResult; PCCERTCHAINENGINE pChainEngine = (PCCERTCHAINENGINE)hChainEngine; InitializeRoamingLogoffNotification(); if ( ( pChainPara == NULL ) || ( pvReserved != NULL ) ) { SetLastError( (DWORD) E_INVALIDARG ); return( FALSE ); } if ( ( hChainEngine == HCCE_LOCAL_MACHINE ) || ( hChainEngine == HCCE_CURRENT_USER ) ) { if ( DefaultChainEngineMgr.GetDefaultEngine( hChainEngine, (HCERTCHAINENGINE *)&pChainEngine ) == FALSE ) { return( FALSE ); } } else { pChainEngine->AddRef(); } fResult = pChainEngine->GetChainContext( pCertContext, pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext ); pChainEngine->Release(); return( fResult ); } //+--------------------------------------------------------------------------- // // Function: CertFreeCertificateChain // // Synopsis: free a certificate chain context // //---------------------------------------------------------------------------- VOID WINAPI CertFreeCertificateChain ( IN PCCERT_CHAIN_CONTEXT pChainContext ) { ChainReleaseInternalChainContext( (PINTERNAL_CERT_CHAIN_CONTEXT)pChainContext ); } //+--------------------------------------------------------------------------- // // Function: CertDuplicateCertificateChain // // Synopsis: duplicate (add a reference to) a certificate chain // //---------------------------------------------------------------------------- PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain ( IN PCCERT_CHAIN_CONTEXT pChainContext ) { ChainAddRefInternalChainContext( (PINTERNAL_CERT_CHAIN_CONTEXT)pChainContext ); return( pChainContext ); } //+--------------------------------------------------------------------------- // // Function: ChainGetCryptnetModule // // Synopsis: get the cryptnet.dll module handle // //---------------------------------------------------------------------------- HMODULE WINAPI ChainGetCryptnetModule () { HMODULE hModule; EnterCriticalSection( &g_CryptnetLock ); if ( g_hCryptnet == NULL ) { g_hCryptnet = LoadLibraryA( "cryptnet.dll" ); } hModule = g_hCryptnet; LeaveCriticalSection( &g_CryptnetLock ); return( hModule ); } //+=========================================================================== // RegisterWaitForSingleObject and UnregisterWaitEx are only supported // in kernel32.dll on NT5. // // Internal functions to do dynamic calls //-=========================================================================== typedef BOOL (WINAPI *PFN_REGISTER_WAIT_FOR_SINGLE_OBJECT)( PHANDLE hNewWaitObject, HANDLE hObject, WAITORTIMERCALLBACK Callback, PVOID Context, ULONG dwMilliseconds, ULONG dwFlags ); typedef BOOL (WINAPI *PFN_UNREGISTER_WAIT_EX)( HANDLE WaitHandle, HANDLE CompletionEvent // INVALID_HANDLE_VALUE => create event // to wait for ); #define sz_KERNEL32_DLL "kernel32.dll" #define sz_RegisterWaitForSingleObject "RegisterWaitForSingleObject" #define sz_UnregisterWaitEx "UnregisterWaitEx" BOOL WINAPI InternalRegisterWaitForSingleObject( PHANDLE hNewWaitObject, HANDLE hObject, WAITORTIMERCALLBACK Callback, PVOID Context, ULONG dwMilliseconds, ULONG dwFlags ) { BOOL fResult; HMODULE hKernel32Dll = NULL; PFN_REGISTER_WAIT_FOR_SINGLE_OBJECT pfnRegisterWaitForSingleObject; if (NULL == (hKernel32Dll = LoadLibraryA(sz_KERNEL32_DLL))) goto LoadKernel32DllError; if (NULL == (pfnRegisterWaitForSingleObject = (PFN_REGISTER_WAIT_FOR_SINGLE_OBJECT) GetProcAddress( hKernel32Dll, sz_RegisterWaitForSingleObject))) goto GetRegisterWaitForSingleObjectProcAddressError; fResult = pfnRegisterWaitForSingleObject( hNewWaitObject, hObject, Callback, Context, dwMilliseconds, dwFlags ); CommonReturn: if (hKernel32Dll) { DWORD dwErr = GetLastError(); FreeLibrary(hKernel32Dll); SetLastError(dwErr); } return fResult; ErrorReturn: *hNewWaitObject = NULL; fResult = FALSE; goto CommonReturn; TRACE_ERROR(LoadKernel32DllError) TRACE_ERROR(GetRegisterWaitForSingleObjectProcAddressError) } BOOL WINAPI InternalUnregisterWaitEx( HANDLE WaitHandle, HANDLE CompletionEvent // INVALID_HANDLE_VALUE => create event // to wait for ) { BOOL fResult; HMODULE hKernel32Dll = NULL; PFN_UNREGISTER_WAIT_EX pfnUnregisterWaitEx; if (NULL == (hKernel32Dll = LoadLibraryA(sz_KERNEL32_DLL))) goto LoadKernel32DllError; if (NULL == (pfnUnregisterWaitEx = (PFN_UNREGISTER_WAIT_EX) GetProcAddress( hKernel32Dll, sz_UnregisterWaitEx))) goto GetUnregisterWaitExProcAddressError; fResult = pfnUnregisterWaitEx( WaitHandle, CompletionEvent ); CommonReturn: if (hKernel32Dll) { DWORD dwErr = GetLastError(); FreeLibrary(hKernel32Dll); SetLastError(dwErr); } return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(LoadKernel32DllError) TRACE_ERROR(GetUnregisterWaitExProcAddressError) } //+=========================================================================== // We only get logoff notification in winlogon.exe. // // The work around is to have the winlogon ChainWlxLogoffEvent pulse a // named event. All processes where crypt32.dll is loaded will be doing // a RegisterWaitForObject for this event. // // Note, there is a very small window where we might not be waiting at the // time the event is pulsed. //-=========================================================================== #define CRYPT32_LOGOFF_EVENT "Global\\crypt32LogoffEvent" HANDLE g_hLogoffEvent; HANDLE g_hLogoffRegWaitFor; typedef BOOL (WINAPI *PFN_WLX_LOGOFF)( PWLX_NOTIFICATION_INFO pNotificationInfo ); VOID NTAPI LogoffWaitForCallback( PVOID Context, BOOLEAN fWaitOrTimedOut // ??? ) { HMODULE hModule; CertFreeCertificateChainEngine( HCCE_CURRENT_USER ); // Only call if cryptnet has been loaded if (NULL != GetModuleHandleA("cryptnet.dll")) { hModule = ChainGetCryptnetModule(); if (hModule) { PFN_WLX_LOGOFF pfn; pfn = (PFN_WLX_LOGOFF) GetProcAddress(hModule, "CryptnetWlxLogoffEvent"); if (pfn) pfn(NULL); } } } // Note, the event must not be created while impersonating. That's why it // is created at ProcessAttach. VOID WINAPI CreateRoamingLogoffNotificationEvent() { SECURITY_ATTRIBUTES sa; SECURITY_DESCRIPTOR sd; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY siaWorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; PSID psidLocalSystem = NULL; PSID psidEveryone = NULL; PACL pDacl = NULL; DWORD dwAclSize; if (!FIsWinNT5()) return; // Allow Everyone to have SYNCHRONIZE access to the logoff event. // Only allow LocalSystem to have ALL access if (!AllocateAndInitializeSid( &siaNtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidLocalSystem )) goto AllocateAndInitializeSidError; if (!AllocateAndInitializeSid( &siaWorldSidAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone )) goto AllocateAndInitializeSidError; // // compute size of ACL // dwAclSize = sizeof(ACL) + 2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) + GetLengthSid(psidLocalSystem) + GetLengthSid(psidEveryone) ; // // allocate storage for Acl // if (NULL == (pDacl = (PACL) PkiNonzeroAlloc(dwAclSize))) goto OutOfMemory; if (!InitializeAcl(pDacl, dwAclSize, ACL_REVISION)) goto InitializeAclError; if (!AddAccessAllowedAce( pDacl, ACL_REVISION, EVENT_ALL_ACCESS, psidLocalSystem )) goto AddAceError; if (!AddAccessAllowedAce( pDacl, ACL_REVISION, SYNCHRONIZE, psidEveryone )) goto AddAceError; if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) goto InitializeSecurityDescriptorError; if (!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) goto SetSecurityDescriptorDaclError; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = &sd; sa.bInheritHandle = FALSE; g_hLogoffEvent = CreateEventA( &sa, TRUE, // fManualReset, must be TRUE to pulse all waitors FALSE, // fInitialState CRYPT32_LOGOFF_EVENT ); if (NULL == g_hLogoffEvent) { // Try to open with only SYNCHRONIZE access g_hLogoffEvent = OpenEventA( SYNCHRONIZE, FALSE, // fInherit CRYPT32_LOGOFF_EVENT ); if (NULL == g_hLogoffEvent) goto CreateEventError; } CommonReturn: if (psidLocalSystem) FreeSid(psidLocalSystem); if (psidEveryone) FreeSid(psidEveryone); PkiFree(pDacl); return; ErrorReturn: goto CommonReturn; TRACE_ERROR(AllocateAndInitializeSidError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(InitializeAclError) TRACE_ERROR(AddAceError) TRACE_ERROR(InitializeSecurityDescriptorError) TRACE_ERROR(SetSecurityDescriptorDaclError) TRACE_ERROR(CreateEventError) } VOID WINAPI InitializeRoamingLogoffNotification() { if (!FIsWinNT5()) return; if (g_fRoamingLogoffNotificationInitialized) return; EnterCriticalSection(&g_RoamingLogoffNotificationLock); if (g_fRoamingLogoffNotificationInitialized) goto CommonReturn; if (NULL == g_hLogoffEvent) goto NoLogoffEvent; // No need to do roaming logoff notification for TS processes if (0 != GetSystemMetrics(SM_REMOTESESSION)) goto CommonReturn; // Note, this can't be called at ProcessAttach if (!InternalRegisterWaitForSingleObject( &g_hLogoffRegWaitFor, g_hLogoffEvent, LogoffWaitForCallback, NULL, // Context INFINITE, // no timeout WT_EXECUTEINWAITTHREAD )) goto RegisterWaitForError; CommonReturn: g_fRoamingLogoffNotificationInitialized = TRUE; LeaveCriticalSection(&g_RoamingLogoffNotificationLock); return; ErrorReturn: goto CommonReturn; SET_ERROR(NoLogoffEvent, E_UNEXPECTED) TRACE_ERROR(RegisterWaitForError) } VOID WINAPI UninitializeRoamingLogoffNotification() { if (g_hLogoffRegWaitFor) { InternalUnregisterWaitEx(g_hLogoffRegWaitFor, INVALID_HANDLE_VALUE); g_hLogoffRegWaitFor = NULL; } if (g_hLogoffEvent) { CloseHandle(g_hLogoffEvent); g_hLogoffEvent = NULL; } } //+--------------------------------------------------------------------------- // // Function: ChainWlxLogoffEvent // // Synopsis: logoff event processing // //---------------------------------------------------------------------------- BOOL WINAPI ChainWlxLogoffEvent (PWLX_NOTIFICATION_INFO pNotificationInfo) { if (g_hLogoffRegWaitFor) { InternalUnregisterWaitEx(g_hLogoffRegWaitFor, INVALID_HANDLE_VALUE); g_hLogoffRegWaitFor = NULL; } CertFreeCertificateChainEngine( HCCE_CURRENT_USER ); if (g_hLogoffEvent) { // Trigger all non-winlogon processes to do logoff processing PulseEvent(g_hLogoffEvent); } return( TRUE ); }