/*++ Copyright (c) 1992 Microsoft Corporation Module Name: provider.c Abstract: Implements support routines for trust providers. Author: Robert Reichel (Robertre) April 9, 1996 Revision History: --*/ #ifdef _DEBUG #include #include #include #endif #include #include #include "provider.h" #include "trust.h" /////////////////////////////////////////////////////////////////////////////// // / // Routines to maintain list of loaded trust providers for the client side / // of WinVerifyTrust. / // / /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// // / // Private data structures / // / ///////////////////////////////// // // Location of trust provider information in the registry. // #define REGISTRY_TRUSTPROVIDERS TEXT("System\\CurrentControlSet\\Services\\WinTrust\\TrustProviders") #define REGISTRY_ROOT HKEY_LOCAL_MACHINE #define ACTION_IDS TEXT("$ActionIDs") #define DLL_NAME TEXT("$DLL") #define IsEqualActionID( id1, id2) (!memcmp(id1, id2, sizeof(GUID))) // // List of loaded providers. Each loaded provider is represented // by a LOADED_PROVIDER structure on this list. // PLOADED_PROVIDER ProviderList = NULL; // // Local Prototypes // PLOADED_PROVIDER ProviderTestProviderForAction( IN HKEY hKey, IN LPTSTR KeyName, IN GUID * ActionID ); PLOADED_PROVIDER ProviderLoadProvider( IN HKEY hKey, IN LPTSTR KeyName ); PLOADED_PROVIDER ProviderIsProviderLoaded( IN LPTSTR KeyName ); PLOADED_PROVIDER ProviderCheckLoadedProviders( IN GUID * ActionID ); PLOADED_PROVIDER ProviderScanKnownProviders( IN GUID * ActionID ); PLOADED_PROVIDER ProviderSearchAllProviders( IN GUID * ActionID ); /////////////////////////////////////////////////////////////////////////////// // / // Routines exported from this module / // / /////////////////////////////////////////////////////////////////////////////// BOOL WinSubmitCertificate( LPWIN_CERTIFICATE lpCertificate ) /*++ Routine Description: This routine will call into the set of loaded trust providers giving each the opportunity the add the passed certificate to their certificate cache. Arguments: lpCertificate - Supplies the Certificate to be cached. Return Value: Returns TRUE for success, FALSE for failure. Extended error status is available using GetLastError. --*/ { PLOADED_PROVIDER Provider; AcquireReadLock(); Provider = ProviderList; while (Provider != NULL && // // We only want to call fully initialized providers here, // Provider->ProviderInitialized == PROVIDER_INITIALIZATION_SUCCESS ) { (*Provider->ClientInfo->lpServices->SubmitCertificate)( lpCertificate ); Provider = Provider->Next; } ReleaseReadLock(); return( TRUE ); } PLOADED_PROVIDER WinTrustFindActionID( IN GUID * dwActionID ) /*++ Routine Description: This routine will perform the following actions in sequence: 1) All loaded providers will be searched for the desired ActionID. The first match that implements the requested ActionID will be returned. 2) If no loaded provider is suitable, then the list of providers in the registry will be searched for hint information. Every potential provider will be loaded and queried to see if it exports the desired ActionID. Also, hint information will be updated on every loaded provider. 3) If no hint information leads us to a suitable provider, an exhaustive search of every provider mentioned in the registry will take place. If none are found, failure is returned. Processing will stop as soon as a suitable provider is discovered. Arguments: dwActionID - The ActionID to be found. Return Value: On success, returns a pointer to a LOADED_PROVIDER structure representing the provider. On failure, returns NULL. --*/ { PLOADED_PROVIDER Provider = NULL; Provider = ProviderCheckLoadedProviders( dwActionID ); if (NULL == Provider) { Provider = ProviderScanKnownProviders( dwActionID ); } if (NULL == Provider) { Provider = ProviderSearchAllProviders( dwActionID ); } #ifdef _DEBUG if (NULL == Provider) { DbgPrint("Provider not found for ActionId "); DbgPrint("%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", dwActionID->Data1, dwActionID->Data2, dwActionID->Data3, dwActionID->Data4[0], dwActionID->Data4[1], dwActionID->Data4[2], dwActionID->Data4[3], dwActionID->Data4[4], dwActionID->Data4[5], dwActionID->Data4[6], dwActionID->Data4[7]); DbgPrint("\n"); } #endif return( Provider ); } BOOL WinLoadTrustProvider( GUID * ActionID ) /*++ Routine Description: Determines if a trust provider exporting the passed Action ID exists. Arguments: ActionID - provides the desired Action ID. Return Value: TRUE - A trust provider with the desired action has been found. FALSE - No trust provider with the desired action could be found. --*/ { if (WinTrustFindActionID( ActionID ) != NULL) { return( TRUE ); } else { return( FALSE ); } } /////////////////////////////////////////////////////////////////////////////// // / // Routines local to this module / // / /////////////////////////////////////////////////////////////////////////////// PLOADED_PROVIDER ProviderCheckLoadedProviders( IN GUID * ActionID ) /*++ Routine Description: Walks the list of loaded providers, attempting to find one that implements the specified action ID. Arguments: ActionID - Specifies the desired ActionID. Return Value: On success, returns a pointer to a LOADED_PROVIDER structure. On failure, returns NULL. --*/ { PLOADED_PROVIDER Provider; ULONG i; AcquireReadLock(); Provider = ProviderList; while (Provider != NULL && // // We only want to look at fully initialized providers here, // we'll pick up any that are partially initialized in a later // pass. // Provider->ProviderInitialized == PROVIDER_INITIALIZATION_SUCCESS ) { for (i=0; i < Provider->ClientInfo->dwActionIdCount; i++) { if (IsEqualActionID(&Provider->ClientInfo->lpActionIdArray[i], ActionID )) { ReleaseReadLock(); return( Provider ); } } Provider = Provider->Next; } ReleaseReadLock(); return( NULL ); } PLOADED_PROVIDER ProviderScanKnownProviders( IN GUID * ActionID ) /*++ Routine Description: This routine will examine the contents of the registry to determine if we have ever loaded a provider with the specified ActionID. If so, we will re-load the provider and verify that it still exports the desired ActionID. Arguments: ActionID - Supplies the desired ActionID. Return Value: On success, returns a pointer to a LOADED_PROVIDER block. Returns NULL on failure. --*/ { HKEY hKey; // Handle to the base of the provider information. HKEY hSubKey; // Handle to the provider currently being examined. LONG Result; // Returned by registry API. DWORD cSubKeys; // Number of providers under the root key. DWORD cbMaxSubKeyLen; // Maximum provider name length. ULONG i,j; // Indicies for iterating through providers and action IDs. LPTSTR SubKeyName; // Points to the name of the current provider. GUID Buffer[10]; // Assume no more than 10 action ids in a provider LPBYTE Data; DWORD cbData; GUID * ActionIds; PLOADED_PROVIDER FoundProvider = NULL; // // Open the registry and get a list of installed trust providers // Result = RegOpenKeyEx( REGISTRY_ROOT, REGISTRY_TRUSTPROVIDERS, 0L, GENERIC_READ, &hKey ); if (Result != ERROR_SUCCESS) { return( NULL ); } // // Find out how many subkeys there are. // Result = RegQueryInfoKey ( hKey, // handle of key to query NULL, // address of buffer for class string NULL, // address of size of class string buffer NULL, // reserved &cSubKeys, // address of buffer for number of subkeys &cbMaxSubKeyLen, // address of buffer for longest subkey name length NULL, // address of buffer for longest class string length NULL, // address of buffer for number of value entries NULL, // address of buffer for longest value name length NULL, // address of buffer for longest value data length NULL, // address of buffer for security descriptor length NULL // address of buffer for last write time ); if (ERROR_SUCCESS != Result) { RegCloseKey( hKey ); return( NULL ); } // // Iterate through the subkeys, looking for ones with hint information. // cbMaxSubKeyLen += sizeof( WCHAR ); SubKeyName = LocalAlloc( 0, cbMaxSubKeyLen ); if (NULL == SubKeyName) { RegCloseKey( hKey ); return(NULL); } for (i=0; iClientInfo; ActionIds = ClientInfo->lpActionIdArray; for (i=0; idwActionIdCount; i++) { if (IsEqualActionID(ActionID, &ActionIds[i])) { return( Provider ); } } return(NULL); } PLOADED_PROVIDER ProviderIsProviderLoaded( IN LPTSTR KeyName ) /*++ Routine Description: This routine will examine the list of loaded providers and determine (by examining the key name information) whether or not the passed provider is already on the list. Note: this routine does not acquire or release any locks. It is up to the caller to decide what kind of lock is appropriate and make the necessary calls. Arguments: KeyName - The key name of this provider. Return Value: If the passed provider is found, returns a pointer to the loaded provider. If not found, it returns NULL. --*/ { PLOADED_PROVIDER Provider; Provider = ProviderList; while (Provider != NULL ) { if (lstrcmp( KeyName, Provider->SubKeyName) == 0) { return(Provider); } Provider = Provider->Next; } return(NULL); } PLOADED_PROVIDER ProviderLoadProvider( IN HKEY hKey, IN LPTSTR KeyName ) /*++ Routine Description: This routine will load the provider described in the passed registry key and add it to the loaded providers list. It will also update any hint information that is maintained under the registry key. Following is an example of a system configured to load "Software Publisher" and "Windows Compatible" Trust Providers: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\WinTrust\TrustProviders \Software Publisher \$DLL [REG_EXPAND_SZ]: SoftPub.dll \State [REG_DWORD]: 0x0006 \Windows Compatible \$DLL [REG_EXPAND_SZ]: WinComp.dll \EXCEPTIONS \Terror Scanner \GaudAwful Video In this example, hKey refers to either the "Software Publisher" or "Windows Compatible" keys. Arguments: hKey - Supplies a handle to the registry key describing the current provider. KeyName - Supplies the name of the key passed in the first parameter. Return Value: return-value - Description of conditions needed to return value. - or - None. --*/ { LPTSTR ModuleName = NULL; HINSTANCE LibraryHandle = NULL; LPWINTRUST_PROVIDER_CLIENT_INFO ClientInfo = NULL; PLOADED_PROVIDER Provider = NULL; PLOADED_PROVIDER FoundProvider = NULL; LPWSTR ProviderName = NULL; LPTSTR SubKeyName = NULL; DWORD Type; DWORD cbData = 0; LONG Result; LPWINTRUST_PROVIDER_CLIENT_INITIALIZE ProcAddr; BOOL Referenced = FALSE; DWORD size; BOOL Inited; // // Take a write lock on the provider list so we can see if this // module is already on the list or not. // AcquireWriteLock(); if (NULL != (FoundProvider = ProviderIsProviderLoaded( KeyName ))) { // // We've found a provider with the same name on the list. // It is in one of three states: // // 1) Fully initialized: in this case, we have no more work to do, // simply return a pointer to the provider we found. // // 2) In the process of being initialized: in this case, we need // to increment the reference count on this provider and wait // on the list event for a wakeup telling us that init has // completed. // // 3) Initialization was attempted, and failed. In this case, // one of the waiters will clean up. Just leave and return // NULL. // while (TRUE) { switch (FoundProvider->ProviderInitialized) { case PROVIDER_INITIALIZATION_SUCCESS: { // // BUGBUG: should the refcount be zero'd out here? // We have a writelock, we can if we need to. // ReleaseWriteLock(); return( FoundProvider ); } case PROVIDER_INITIALIZATION_IN_PROGRESS: { FoundProvider->RefCount++; // protected by write lock, no need for interlocked. Referenced = TRUE; // // Loop forever until something happens. // do { // // Event <- not signaled, forcing wait // ResetListEvent(); ReleaseWriteLock(); // // Note that someone may set the event here. In // this case, we'll wake up immediately, but that's // ok. // WaitForListEvent(); AcquireWriteLock(); if (PROVIDER_INITIALIZATION_IN_PROGRESS == FoundProvider->ProviderInitialized) { // // Spurious wakeup, reset the list event and wait again. // } else { // // We've changed state, break out of this loop and go back to the // top of the switch and see what happened. Note that we have a // write lock at this point, which is expected at the top of the // switch. // break; } } while ( TRUE ); // // Go back to top of outer loop. // break; } case PROVIDER_INITIALIZATION_FAILED: { // // If we referenced this provider to wait on it, // we may need to be the one to clean him up. // // If we didn't reference it (it was dead when we // got here), then just leave and let someone // who did reference it clean it up. // #ifdef _DEBUG DbgPrint("Top of ProviderLoadProvider, init failed, refcount = %d\n",FoundProvider->RefCount); #endif if (Referenced) { if (--FoundProvider->RefCount == 0) { #ifdef _DEBUG DbgPrint("Top of ProviderLoadProvider, thread %x removing provider at %X from list\n",GetCurrentThreadId(),FoundProvider); #endif // // Remove this module from doubly linked list. // if (FoundProvider->Prev != NULL) { FoundProvider->Prev->Next = FoundProvider->Next; } else { // // We're at the head of the list // ProviderList = FoundProvider->Next; } if (FoundProvider->Next != NULL) { FoundProvider->Next->Prev = FoundProvider->Prev; } LocalFree( FoundProvider->SubKeyName ); LocalFree( FoundProvider ); } } ReleaseWriteLock(); return( NULL ); break; } } } } // // Extract the dll name from the $DLL value // Result = RegQueryValueEx( hKey, // handle of key to query TEXT("$DLL"), // address of name of value to query NULL, // reserved &Type, // address of buffer for value type NULL, // address of data buffer &cbData // address of data buffer size ); // if (ERROR_MORE_DATA != Result) { // goto error_cleanup; // } if (ERROR_SUCCESS != Result) { goto error_cleanup; } cbData += sizeof( TCHAR ); ModuleName = LocalAlloc( 0, cbData ); if (NULL == ModuleName) { goto error_cleanup; } ModuleName[cbData - 1] = TEXT('\0'); Result = RegQueryValueEx( hKey, // handle of key to query TEXT("$DLL"), // address of name of value to query NULL, // reserved &Type, // address of buffer for value type (LPBYTE)ModuleName, // address of data buffer &cbData // address of data buffer size ); if (ERROR_SUCCESS != Result) { goto error_cleanup; } // // Expand environment strings if necessary // if (Type == REG_EXPAND_SZ) { DWORD ExpandedLength = 0; LPTSTR ExpandedModuleName = NULL; ExpandedLength = ExpandEnvironmentStrings( ModuleName, NULL, 0 ); if (0 == ExpandedLength) { goto error_cleanup; } ExpandedModuleName = LocalAlloc( 0, ExpandedLength ); if (NULL == ExpandedModuleName) { goto error_cleanup; } ExpandedLength = ExpandEnvironmentStrings( ModuleName, ExpandedModuleName, ExpandedLength ); if (0 == ExpandedLength) { LocalFree( ExpandedModuleName ); goto error_cleanup; } // // Free the old module name, use the new one // LocalFree( ModuleName ); ModuleName = ExpandedModuleName; } size = (lstrlen( KeyName ) + 1) * sizeof( WCHAR ); ProviderName = LocalAlloc( 0, size ); if (NULL == ProviderName) { goto error_cleanup; } #ifdef UNICODE // // If we've been compiled as unicode, the KeyName we got from // the registry consists of WCHARs, so we can just copy it into // the Name buffer. // lstrcpy( ProviderName, KeyName ); #else // // If we've been compiled as ANSI, then KeyName is an ANSI string, // and we need to convert it to WCHARs. // MultiByteToWideChar ( CP_ACP, 0, KeyName, -1, ProviderName, size ); #endif // !UNICODE // // ModuleName now contains the module name, attempt to load it // and ask it to initialize itself. // LibraryHandle = LoadLibrary( (LPTSTR)ModuleName ); if (NULL == LibraryHandle) { DWORD Error; Error = GetLastError(); goto error_cleanup; } ProcAddr = (LPWINTRUST_PROVIDER_CLIENT_INITIALIZE) GetProcAddress( LibraryHandle, (LPCSTR)"WinTrustProviderClientInitialize"); if (NULL == ProcAddr) { goto error_cleanup; } SubKeyName = LocalAlloc( 0, (lstrlen( KeyName ) + 1) * sizeof( TCHAR )); if (NULL == SubKeyName) { goto error_cleanup; } lstrcpy( SubKeyName, KeyName ); Provider = LocalAlloc( 0, sizeof( LOADED_PROVIDER )); if (NULL == Provider) { LocalFree( SubKeyName ); goto error_cleanup; } // // Ready to call init routine. // Provider->RefCount = 1; Provider->ProviderInitialized = PROVIDER_INITIALIZATION_IN_PROGRESS; // // Set the subkey name so anyone else looking for this provider will // find this one and wait. // // Note that we don't want to use the ProviderName as will be passed into // the init routine here, because we've forced that to WCHARs regardless // of whether we're ANSI or Unicode, and we want this string to reflect // the base system for efficiency. // Provider->SubKeyName = SubKeyName; Provider->Next = ProviderList; Provider->Prev = NULL; if (Provider->Next != NULL) { Provider->Next->Prev = Provider; } ProviderList = Provider; ReleaseWriteLock(); // // bugbug try-except probably appropriate here... // #ifdef _DEBUG DbgPrint("Calling WinTrustProviderClientInitialize in %s\n",ModuleName); #endif Inited = (*ProcAddr)( WIN_TRUST_REVISION_1_0, &WinTrustClientTPInfo, ProviderName, &ClientInfo ); AcquireWriteLock(); if (TRUE != Inited) { #ifdef _DEBUG DbgPrint("WinTrustProviderClientInitialize in %s failed\n",ModuleName); #endif Provider->ProviderInitialized = PROVIDER_INITIALIZATION_FAILED; // // Signal event, waking up waiters. // SetListEvent(); if (--Provider->RefCount == 0) { // // Remove this module from doubly linked list. // #ifdef _DEBUG DbgPrint("Provider init failed, thread %x removing provider at %X from list\n",GetCurrentThreadId(),Provider); #endif if (Provider->Prev != NULL) { Provider->Prev->Next = Provider->Next; } else { // // We're at the head of the list // ProviderList = Provider->Next; } if (Provider->Next != NULL) { Provider->Next->Prev = Provider->Prev; } LocalFree( Provider->SubKeyName ); LocalFree( Provider ); } // // We could release the lock now, because we're either going to // do nothing to this provider, or we've removed it from // the list and no one else can get to it. // goto error_cleanup; } // // Since we have a write lock, it doesn't matter what order we // do this in, since there are no readers. Just be sure to signal // the event under the write lock. // Provider->ProviderInitialized = PROVIDER_INITIALIZATION_SUCCESS; Provider->ModuleHandle = LibraryHandle; Provider->ModuleName = ModuleName; Provider->ClientInfo = ClientInfo; // // Init is done and successful. Wake anyone who might be waiting. // SetListEvent(); ReleaseWriteLock(); // // Attempt to update (or create) hint information about // the provider in the key. // // Failure here is non-critical. // ( VOID ) RegSetValueEx( hKey, // handle of key to set value for ACTION_IDS, // address of value to set 0, // reserved REG_BINARY, // flag for value type (CONST BYTE *)ClientInfo->lpActionIdArray, // address of value data ClientInfo->dwActionIdCount * sizeof( GUID ) // size of value data ); return( Provider ); error_cleanup: ReleaseWriteLock(); if (NULL != LibraryHandle) { FreeLibrary( LibraryHandle ); } if (NULL != ModuleName) { LocalFree( ModuleName ); } if (NULL != ProviderName) { LocalFree( ProviderName ); } return( NULL ); }