/*++ Copyright (c) 1992 Microsoft Corporation Module Name: sip.c Abstract: Implements support routines for subject interface pacakges. Author: Robert Reichel (Robertre) April 9, 1996 Revision History: --*/ #ifdef _DEBUG #include #include #include #endif #include #include #include "sip.h" #include "trust.h" /////////////////////////////////////////////////////////////////////////////// // / // Routines to maintain list of loaded subject interface packages. / // / /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// // / // Private data structures / // / ///////////////////////////////// // // Location of trust provider information in the registry. // #define REGISTRY_SIPS TEXT("System\\CurrentControlSet\\SERVICES\\WinTrust\\SubjectPackages") #define REGISTRY_ROOT HKEY_LOCAL_MACHINE #define SUBJECT_FORMS TEXT("$SubjectForms") #define DLL_NAME TEXT("$DLL") #define IsEqualSubject( id1, id2) (!memcmp(id1, id2, sizeof(GUID))) // // List of loaded sips. Each loaded sip is represented // by a LOADED_SIP structure on this list. // PLOADED_SIP SIPList = NULL; // // Local Prototypes // PLOADED_SIP SipTestSipForSubject( IN HKEY hKey, IN LPTSTR KeyName, IN GUID * Subject ); PLOADED_SIP SipLoadSip( IN HKEY hKey, IN LPTSTR KeyName ); PLOADED_SIP SipIsSipLoaded( IN LPTSTR KeyName ); PLOADED_SIP SipCheckLoadedSips( IN GUID * Subject ); PLOADED_SIP SipScanKnownSips( IN GUID * Subject ); PLOADED_SIP SipSearchAllSips( IN GUID * Subject ); /////////////////////////////////////////////////////////////////////////////// // / // Routines exported from this module / // / /////////////////////////////////////////////////////////////////////////////// PLOADED_SIP WinTrustFindSubjectForm( IN GUID * SubjectForm ) /*++ Routine Description: This routine will perform the following actions in sequence: 1) All loaded SIPs will be searched for the desired SubjectForm. The first match that supports the requested subject will be returned. 2) If no loaded SIP is suitable, then the list of SIPs in the registry will be searched for hint information. Every potential SIP will be loaded and queried to see if it exports the desired Subject. Also, hint information will be updated on every loaded SIP. 3) If no hint information leads us to a suitable SIP, an exhaustive search of every SIP mentioned in the registry will take place. If none are found, failure is returned. Processing will stop as soon as a suitable SIP is discovered. Arguments: SubjectForm - The SubjectForm to be found. Return Value: On success, returns a pointer to a LOADED_SIP structure representing a loaded SIP. On failure, returns NULL. --*/ { PLOADED_SIP Sip = NULL; Sip = SipCheckLoadedSips( SubjectForm ); if (NULL == Sip) { Sip = SipScanKnownSips( SubjectForm ); } if (NULL == Sip) { Sip = SipSearchAllSips( SubjectForm ); } #ifdef _DEBUG if (NULL == Sip) { DbgPrint("Sip not found for SubjectForm "); DbgPrint("%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", SubjectForm->Data1, SubjectForm->Data2, SubjectForm->Data3, SubjectForm->Data4[0], SubjectForm->Data4[1], SubjectForm->Data4[2], SubjectForm->Data4[3], SubjectForm->Data4[4], SubjectForm->Data4[5], SubjectForm->Data4[6], SubjectForm->Data4[7]); DbgPrint("\n"); } #endif return( Sip ); } /////////////////////////////////////////////////////////////////////////////// // / // Routines local to this module / // / /////////////////////////////////////////////////////////////////////////////// PLOADED_SIP SipCheckLoadedSips( IN GUID * SubjectForm ) /*++ Routine Description: Walks the list of loaded Sips, attempting to find one that implements the specified subject form. Arguments: SubjectForm - Specifies the desired SubjectForm. Return Value: On success, returns a pointer to a LOADED_SIP structure. On failure, returns NULL. --*/ { PLOADED_SIP Sip; ULONG i; AcquireReadLock(); Sip = SIPList; while (Sip != NULL && // // We only want to look at fully initialized providers here, // we'll pick up any that are partially initialized in a later // pass. // Sip->SipInitialized == SIP_INITIALIZATION_SUCCESS ) { for (i=0; iSipInfo->dwSubjectTypeCount; i++) { if (IsEqualSubject(&Sip->SipInfo->lpSubjectTypeArray[i], SubjectForm )) { ReleaseReadLock(); return( Sip ); } } Sip = Sip->Next; } ReleaseReadLock(); return( NULL ); } PLOADED_SIP SipScanKnownSips( IN GUID * SubjectForm ) /*++ Routine Description: This routine will examine the contents of the registry to determine if we have ever loaded a Sip with the specified SubjectForm. If so, we will re-load the Sip and verify that it still exports the desired SubjectForm. Arguments: SubjectForm - Supplies the desired SubjectForm. Return Value: On success, returns a pointer to a LOADED_SIP block. Returns NULL on failure. --*/ { HKEY hKey; // Handle to the base of the Sip information. HKEY hSubKey; // Handle to the Sip currently being examined. LONG Result; // Returned by registry API. DWORD cSubKeys; // Number of Sips under the root key. DWORD cbMaxSubKeyLen; // Maximum Sip name length. ULONG i,j; // Indicies for iterating through Sips and subject forms. LPTSTR SubKeyName; // Points to the name of the current Sip. GUID Buffer[10]; // Assume no more than 10 subject forms in a Sip LPBYTE Data; DWORD cbData; GUID * SubjectForms; PLOADED_SIP FoundSip = NULL; // // Open the registry and get a list of installed trust Sips // Result = RegOpenKeyEx( REGISTRY_ROOT, REGISTRY_SIPS, 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; iSipInfo; SubjectForms = SipInfo->lpSubjectTypeArray; for (i=0; idwSubjectTypeCount; i++) { if (IsEqualSubject(SubjectForm, &SubjectForms[i])) { return( Sip ); } } return(NULL); } PLOADED_SIP SipIsSipLoaded( IN LPTSTR KeyName ) /*++ Routine Description: This routine will examine the list of loaded Sips and determine (by examining the key name information) whether or not the passed Sip 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 Sip. Return Value: Returns TRUE if the Sip is already on the loaded Sips list, otherwise FALSE. --*/ { PLOADED_SIP Sip; Sip = SIPList; while (Sip != NULL) { if (lstrcmp( KeyName, Sip->SubKeyName) == 0) { return(Sip); } Sip = Sip->Next; } return(NULL); } PLOADED_SIP SipLoadSip( IN HKEY hKey, IN LPTSTR KeyName ) /*++ Routine Description: This routine will load the Sip described in the passed registry key and add it to the loaded Sips 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 "PE Image" and "Java" Sips: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\WinTrust\SubjectPackages \PE Image \$DLL [REG_EXPAND_SZ]: PESip.dll \State [REG_DWORD]: 0x0006 \Jave \$DLL [REG_EXPAND_SZ]: JavaSip.dll \EXCEPTIONS \Terror Scanner \GaudAwful Video In this example, hKey refers to either the "PE Image" or "Java" keys. Arguments: hKey - Supplies a handle to the registry key describing the current sip. KeyName - Supplies the name of the key passed in the first parameter. Return Value: Returns a pointer to a LOADED_SIP structure if a suitable SIP is found, else NULL. --*/ { LPTSTR ModuleName = NULL; HINSTANCE LibraryHandle = NULL; LPWINTRUST_SIP_INFO SipInfo = NULL; PLOADED_SIP Sip = NULL; PLOADED_SIP FoundSip = NULL; LPTSTR SubKeyName = NULL; DWORD Type; DWORD cbData = 0; LONG Result; LPWINTRUST_SUBJECT_PACKAGE_INITIALIZE ProcAddr; BOOL Referenced = FALSE; BOOL bool; // // Take a write lock on the sip list so we can see if this // module is already on the list or not. // AcquireWriteLock(); if (NULL != (FoundSip = SipIsSipLoaded( KeyName ))) { // // We've found a sip 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 sip we found. // // 2) In the process of being initialized: in this case, we need // to increment the reference count on this sip 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 (FoundSip->SipInitialized) { case SIP_INITIALIZATION_SUCCESS: { // // BUGBUG: should the refcount be zero'd out here? // We have a writelock, we can if we need to. // ReleaseWriteLock(); return( FoundSip ); } case SIP_INITIALIZATION_IN_PROGRESS: { FoundSip->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 (SIP_INITIALIZATION_IN_PROGRESS == FoundSip->SipInitialized) { // // 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 SIP_INITIALIZATION_FAILED: { // // If we referenced this sip 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. // if (Referenced) { if (--FoundSip->RefCount == 0) { // // Remove this module from doubly linked list. // if (FoundSip->Prev != NULL) { FoundSip->Prev->Next = FoundSip->Next; } else { // // We're at the head of the list // SIPList = FoundSip->Next; } if (FoundSip->Next != NULL) { FoundSip->Next->Prev = FoundSip->Prev; } LocalFree( FoundSip->SubKeyName ); LocalFree( FoundSip ); } } 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; } // // ModuleName now contains the module name, attempt to load it // and ask it to initialize itself. // LibraryHandle = LoadLibrary( (LPTSTR)ModuleName ); if (NULL == LibraryHandle) { goto error_cleanup; } ProcAddr = (LPWINTRUST_SUBJECT_PACKAGE_INITIALIZE) GetProcAddress( LibraryHandle, (LPCSTR)"WinTrustSipInitialize"); if (NULL == ProcAddr) { goto error_cleanup; } SubKeyName = LocalAlloc( 0, (lstrlen( KeyName ) + 1) * sizeof( TCHAR )); if (NULL == SubKeyName) { goto error_cleanup; } lstrcpy( SubKeyName, KeyName ); Sip = LocalAlloc( 0, sizeof( LOADED_SIP )); if (NULL == Sip) { LocalFree( SubKeyName ); goto error_cleanup; } // // Ready to call init routine. // Sip->RefCount = 1; Sip->SipInitialized = SIP_INITIALIZATION_IN_PROGRESS; // // Set the subkey name so anyone else looking for this provider will // find this one and wait. // Sip->SubKeyName = SubKeyName; Sip->Next = SIPList; Sip->Prev = NULL; if (Sip->Next != NULL) { Sip->Next->Prev = Sip; } SIPList = Sip; ReleaseWriteLock(); // // bugbug try-except probably appropriate here... // bool = (*ProcAddr)( WIN_TRUST_REVISION_1_0, &SipInfo ); AcquireWriteLock(); if (TRUE != bool) { Sip->SipInitialized = SIP_INITIALIZATION_FAILED; // // Signal event, waking up waiters. // SetListEvent(); if (--Sip->RefCount == 0) { // // Remove this module from doubly linked list. // if (Sip->Prev != NULL) { Sip->Prev->Next = Sip->Next; } else { // // We're at the head of the list // SIPList = Sip->Next; } if (Sip->Next != NULL) { Sip->Next->Prev = Sip->Prev; } LocalFree( Sip->SubKeyName ); LocalFree( Sip ); } // // We could release the lock now, because we're either going to // do nothing to this sip, or we've removed it from // the list and no one else can get to it. // goto error_cleanup; } Sip->SipInitialized = SIP_INITIALIZATION_SUCCESS; Sip->ModuleHandle = LibraryHandle; Sip->ModuleName = ModuleName; Sip->SipInfo = SipInfo; // // Init is done and successful. Wake anyone who might be waiting. // SetListEvent(); ReleaseWriteLock(); // // Attempt to update (or create) hint information about // the Sip in the key. // // Failure here is non-critical. // ( VOID ) RegSetValueEx( hKey, // handle of key to set value for SUBJECT_FORMS, // address of value to set 0, // reserved REG_BINARY, // flag for value type (CONST BYTE *)SipInfo->lpSubjectTypeArray, // address of value data SipInfo->dwSubjectTypeCount * sizeof( GUID ) // size of value data ); return( Sip ); error_cleanup: ReleaseWriteLock(); if (NULL != LibraryHandle) { FreeLibrary( LibraryHandle ); } if (NULL != ModuleName) { LocalFree( ModuleName ); } return( NULL ); }