/*++ Copyright (c) 1994 Microsoft Corporation Module Name: registry.c Abstract: Registry reading routines for License Server. Can Scan the registry for all License Service entries, or for a specific service. Author: Arthur Hanson (arth) 07-Dec-1994 Revision History: Jeff Parham (jeffparh) 05-Dec-1995 o Removed unnecessary RegConnect() to local server. o Added secure service list. This list tracks the products that require "secure" license certificates for all licenses; i.e., the products that do not accept the 3.51 Honesty method of "enter the number of license you purchased." o Added routine to update the concurrent limit value in the registry to accurately reflect the connection limit of secure products. --*/ #include #include #include #include #include #include #include #include #include "llsapi.h" #include "debug.h" #include "llssrv.h" #include "registry.h" #include "ntlsapi.h" #include "mapping.h" #include "msvctbl.h" #include "svctbl.h" #include "purchase.h" #include "perseat.h" #include "server.h" #include "llsutil.h" #include //include last // #define API_TRACE 1 #define NUM_MAPPING_ENTRIES 2 const LPTSTR NameMappingTable2[] = { TEXT("Microsoft SQL Server"), TEXT("Microsoft SNA Server") }; // NameMappingTable2 ULONG NumFilePrintEntries = 0; LPTSTR *FilePrintTable = NULL; #define KEY_NAME_SIZE 512 HANDLE LLSRegistryEvent; ULONG LocalServiceListSize = 0; PLOCAL_SERVICE_RECORD *LocalServiceList = NULL; RTL_RESOURCE LocalServiceListLock; static ULONG SecureServiceListSize = 0; static LPTSTR * SecureServiceList = NULL; static ULONG SecureServiceBufferSize = 0; // in bytes! static TCHAR * SecureServiceBuffer = NULL; ///////////////////////////////////////////////////////////////////////// VOID ConfigInfoRegistryInit( DWORD * pReplicationType, DWORD * pReplicationTime, DWORD * pLogLevel, BOOL * pPerServerCapacityWarning ) { HKEY hKey2 = NULL; DWORD dwType, dwSize; static BOOL ReportedError = FALSE; static const TCHAR RegKeyText[] = TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"); LONG Status; DWORD ReplicationType, ReplicationTime; DWORD LogLevel; DWORD DisableCapacityWarning; ReplicationType = ReplicationTime = LogLevel = 0; // // Create registry key-name we are looking for // if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { dwSize = sizeof(ReplicationType); Status = RegQueryValueEx(hKey2, TEXT("ReplicationType"), NULL, &dwType, (LPBYTE) &ReplicationType, &dwSize); if (Status == ERROR_SUCCESS) { dwSize = sizeof(ReplicationTime); Status = RegQueryValueEx(hKey2, TEXT("ReplicationTime"), NULL, &dwType, (LPBYTE) &ReplicationTime, &dwSize); if (Status == ERROR_SUCCESS) { ASSERT(NULL != pReplicationType); ASSERT(NULL != pReplicationTime); *pReplicationType = ReplicationType; *pReplicationTime = ReplicationTime; } else { if (!ReportedError) { ReportedError = TRUE; #ifdef DEBUG dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationTime\n")); #endif } } } else { if (!ReportedError) { ReportedError = TRUE; #ifdef DEBUG dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationType\n")); #endif } } // LogLevel (REG_DWORD): determines how much info is dumped to the EventLog. // Higher values imply more logging. Default: 0. dwSize = sizeof( LogLevel ); Status = RegQueryValueEx( hKey2, TEXT("LogLevel"), NULL, &dwType, (LPBYTE) &LogLevel, &dwSize); ASSERT(NULL != pLogLevel); if ( ERROR_SUCCESS == Status ) *pLogLevel = LogLevel; else *pLogLevel = 0; // // Read the per server capacity warning value. A warning when the per // server license usage nears 90-95% of the total number of licenses. // A non-zero registry value disables the per server capacity warning // mechanism. // // It is not likely this value wll be present. Default to warn. // dwSize = sizeof( DisableCapacityWarning ); Status = RegQueryValueEx( hKey2, TEXT("DisableCapacityWarning"), NULL, &dwType, (LPBYTE)&DisableCapacityWarning, &dwSize); if ( ERROR_SUCCESS == Status && DisableCapacityWarning ) { ASSERT(NULL != pPerServerCapacityWarning); *pPerServerCapacityWarning = FALSE; } else { *pPerServerCapacityWarning = TRUE; } // ProductData (REG_BINARY): an encrypted buffer of concatenated service names // that determine which services need to have secure certificates // for license entry Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, NULL, &dwSize ); if ( ERROR_SUCCESS == Status ) { TCHAR * NewSecureServiceBuffer = NULL; LPTSTR * NewSecureServiceList = NULL; ULONG NewSecureServiceListSize = 0; ULONG NewSecureServiceBufferSize; NewSecureServiceBufferSize = dwSize; NewSecureServiceBuffer = LocalAlloc( LMEM_FIXED, NewSecureServiceBufferSize ); if ( NULL != NewSecureServiceBuffer ) { Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, (LPBYTE) NewSecureServiceBuffer, &dwSize); if ( ERROR_SUCCESS == Status ) { Status = DeBlock( NewSecureServiceBuffer, dwSize ); if ( ( STATUS_SUCCESS == Status ) && ( ( NULL == SecureServiceBuffer ) || ( memcmp( NewSecureServiceBuffer, SecureServiceBuffer, dwSize ) ) ) ) { // process changes in secure product list DWORD i; DWORD ProductNdx; NewSecureServiceListSize = 0; // count number of product names contained in the buffer for ( i=0; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ ) { // skip to beginning of next product name for ( ; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ ); i++; if ( i * sizeof( TCHAR) < dwSize ) { // properly null-terminated product name NewSecureServiceListSize++; } } if ( 0 != NewSecureServiceListSize ) { NewSecureServiceList = LocalAlloc( LMEM_FIXED, sizeof( LPTSTR ) * NewSecureServiceListSize ); if ( NULL != NewSecureServiceList ) { for ( i = ProductNdx = 0; ProductNdx < NewSecureServiceListSize; ProductNdx++ ) { NewSecureServiceList[ ProductNdx ] = &NewSecureServiceBuffer[i]; // skip to beginning of next product name for ( ; NewSecureServiceBuffer[i] != TEXT( '\0' ); i++ ); i++; } // new secure product list read successfully; use it if ( NULL != SecureServiceBuffer ) { LocalFree( SecureServiceBuffer ); } if ( NULL != SecureServiceList ) { LocalFree( SecureServiceList ); } SecureServiceBuffer = NewSecureServiceBuffer; SecureServiceList = NewSecureServiceList; SecureServiceListSize = NewSecureServiceListSize; SecureServiceBufferSize = NewSecureServiceBufferSize; } } } } } // free buffers if we aren't using them anymore if ( ( NULL != NewSecureServiceList ) && ( SecureServiceList != NewSecureServiceList ) ) { LocalFree( NewSecureServiceList ); } if ( ( NULL != NewSecureServiceBuffer ) && ( SecureServiceBuffer != NewSecureServiceBuffer ) ) { LocalFree( NewSecureServiceBuffer ); } } RegCloseKey(hKey2); } } // ConfigInfoRegistryInit ///////////////////////////////////////////////////////////////////////// NTSTATUS FilePrintTableInit( ) /*++ Routine Description: Builds up the FilePrint mapping table by enumerating the keys in the registry init'd by the various install programs. Arguments: Return Value: None. --*/ { HKEY hKey2; static const TCHAR RegKeyText[] = TEXT("System\\CurrentControlSet\\Services\\LicenseService\\FilePrint"); static TCHAR KeyText[KEY_NAME_SIZE], ClassText[KEY_NAME_SIZE]; NTSTATUS Status; DWORD index = 0; DWORD KeySize, ClassSize, NumKeys, NumValue, MaxKey, MaxClass, MaxValue, MaxValueData, MaxSD; FILETIME LastWrite; LPTSTR *pFilePrintTableTmp; HRESULT hr; size_t cch; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: FilePrintTableInit\n")); #endif // // Create registry key-name we are looking for // if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { // // Find out how many sub-keys there are to intialize our table size. // The table can still grow dynamically, this just makes having to // realloc it a rare occurance. // ClassSize = KEY_NAME_SIZE; Status = RegQueryInfoKey(hKey2, ClassText, &ClassSize, NULL, &NumKeys, &MaxKey, &MaxClass, &NumValue, &MaxValue, &MaxValueData, &MaxSD, &LastWrite); if (Status == ERROR_SUCCESS) { FilePrintTable = (LPTSTR *) LocalAlloc(LPTR, sizeof(LPTSTR) * NumKeys); while ((Status == ERROR_SUCCESS) && (FilePrintTable != NULL)) { // // Double check in-case we need to expand the table. // if (index > NumKeys) { pFilePrintTableTmp = (LPTSTR *) LocalReAlloc(FilePrintTable, sizeof(LPTSTR) * (NumKeys+1), LHND); if (pFilePrintTableTmp == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } else { NumKeys++; FilePrintTable = pFilePrintTableTmp; } } // // Now read in the key name and add it to the table // KeySize = KEY_NAME_SIZE; Status = RegEnumKeyEx(hKey2, index, KeyText, &KeySize, NULL, NULL, NULL, &LastWrite); if (Status == ERROR_SUCCESS) { // // Allocate space in our table and copy the key // cch = KeySize + 1; FilePrintTable[index] = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (FilePrintTable[index] != NULL) { hr = StringCchCopy(FilePrintTable[index], cch, KeyText); ASSERT(SUCCEEDED(hr)); index++; } else Status = ERROR_NOT_ENOUGH_MEMORY; } } } #ifdef DEBUG else { dprintf(TEXT("LLS FilePrintTable Error: 0x%lx\n"), Status); } #endif RegCloseKey( hKey2 ); } if (FilePrintTable != NULL) NumFilePrintEntries = index; else NumFilePrintEntries = 0; return Status; } // FilePrintTableInit ///////////////////////////////////////////////////////////////////////// NTSTATUS RegistryMonitor ( IN PVOID ThreadParameter ) /*++ Routine Description: Watches for any changes in the Licensing Keys, and if any updates our internal information. Arguments: ThreadParameter - Indicates how many active threads there currently are. Return Value: None. --*/ { LONG Status = 0; HKEY hKey1 = NULL; HKEY hKey2 = NULL; NTSTATUS NtStatus = STATUS_SUCCESS; static const TCHAR RegKeyText1[] = TEXT("System\\CurrentControlSet\\Services\\LicenseService"); static const TCHAR RegKeyText2[] = TEXT("System\\CurrentControlSet\\Services\\LicenseInfo"); HANDLE Events[2]; DWORD dwWhichEvent = 0; // Keeps track of which event was last triggered UNREFERENCED_PARAMETER(ThreadParameter); // // Open registry key-name we are looking for // if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText1, 0, KEY_NOTIFY, &hKey1)) != ERROR_SUCCESS) { #if DBG dprintf(TEXT("LLS RegistryMonitor - RegOpenKeyEx failed: 0x%lX\n"), Status); #endif return (NTSTATUS) Status; } if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText2, 0, KEY_NOTIFY, &hKey2)) != ERROR_SUCCESS) { #if DBG dprintf(TEXT("LLS RegistryMonitor - RegOpenKeyEx 2 failed: 0x%lX\n"), Status); #endif RegCloseKey(hKey1); return (NTSTATUS) Status; } if ((Status = NtCreateEvent(Events,EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,NULL,SynchronizationEvent,FALSE)) != ERROR_SUCCESS) { #if DBG dprintf(TEXT("LLS RegistryMonitor - RegOpenKeyEx 2 failed: 0x%lX\n"), Status); #endif RegCloseKey(hKey1); RegCloseKey(hKey2); return (NTSTATUS) Status; } Events[1] = LLSRegistryEvent; // // Loop forever // for ( ; ; ) { if ((dwWhichEvent == 0) || (dwWhichEvent == 2)) { Status = RegNotifyChangeKeyValue(hKey1, TRUE, REG_NOTIFY_CHANGE_LAST_SET, LLSRegistryEvent, TRUE); if (Status != ERROR_SUCCESS) { #if DBG dprintf(TEXT("LLS RegNotifyChangeKeyValue Failed: %lu\n"), Status); #endif } } if ((dwWhichEvent == 0) || (dwWhichEvent == 1)) { Status = RegNotifyChangeKeyValue(hKey2, TRUE, REG_NOTIFY_CHANGE_LAST_SET, Events[0], TRUE); if (Status != ERROR_SUCCESS) { #if DBG dprintf(TEXT("LLS RegNotifyChangeKeyValue 2 Failed: %lu\n"), Status); #endif } } NtStatus = NtWaitForMultipleObjects( 2, Events, WaitAny, TRUE, NULL ); switch (NtStatus) { case 0: dwWhichEvent = 1; break; case 1: dwWhichEvent = 2; break; default: dwWhichEvent = 0; break; } #if DELAY_INITIALIZATION EnsureInitialized(); #endif // // Re-synch the lists // LocalServiceListUpdate(); LocalServerServiceListUpdate(); ServiceListResynch(); ConfigInfoRegistryUpdate(); LocalServiceListConcurrentLimitSet(); if (dwWhichEvent == 0) { #if DBG dprintf(TEXT("LLS Registry Event Notification Failed: %lu\n"), NtStatus); #endif // // If we failed - sleep for 2 minutes before looping // Sleep(120000L); } } //return NtStatus; //unreachable line } // RegistryMonitor ///////////////////////////////////////////////////////////////////////// VOID RegistryInit( ) /*++ Routine Description: Looks in registry for given service and sets values accordingly. Arguments: Return Value: None. --*/ { NTSTATUS Status; DWORD Mode, ConcurrentLimit; Mode = 0; ConcurrentLimit = 0; // // Create a key to tell us about any changes in the registry // Status = NtCreateEvent( &LLSRegistryEvent, EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE, NULL, SynchronizationEvent, FALSE ); ASSERT(NT_SUCCESS(Status)); } // RegistryInit ///////////////////////////////////////////////////////////////////////// VOID RegistryStartMonitor( ) /*++ Routine Description: Looks in registry for given service and sets values accordingly. Arguments: Return Value: None. --*/ { HANDLE Thread; DWORD Ignore; // // Now dispatch a thread to watch for any registry changes // Thread = CreateThread( NULL, 0L, (LPTHREAD_START_ROUTINE) RegistryMonitor, 0L, 0L, &Ignore ); if (Thread != NULL) CloseHandle(Thread); } // RegistryStartMonitor ///////////////////////////////////////////////////////////////////////// VOID RegistryInitValues( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit ) /*++ Routine Description: Looks in registry for given service and sets values accordingly. Arguments: Service Name - PerSeatLicensing - SessionLimit - Return Value: None. --*/ { static TCHAR RegKeyText[512]; #ifndef SPECIAL_USER_LIMIT LONG Status; DWORD Mode, ConcurrentLimit; DWORD dwType, dwSize; HKEY hKey2 = NULL; HRESULT hr; size_t cb; #endif #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: RegistryInitValues\n")); #endif #ifdef SPECIAL_USER_LIMIT ASSERT(NULL != PerSeatLicensing && NULL != SessionLimit); *PerSeatLicensing = FALSE; *SessionLimit = SPECIAL_USER_LIMIT; UNREFERENCED_PARAMETER(ServiceName); #else // #ifdef SPECIAL_USER_LIMIT Mode = 0; ConcurrentLimit = 0; // // Create registry key-name we are looking for // cb = sizeof(RegKeyText); hr = StringCbCopy(RegKeyText, cb, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); ASSERT(SUCCEEDED(hr)); hr = StringCbCat(RegKeyText, cb, ServiceName); ASSERT(SUCCEEDED(hr)); if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { // // First Get Mode // dwSize = sizeof(Mode); Status = RegQueryValueEx(hKey2, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize); #if DBG if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) dprintf(TEXT("Found Reg-Key for [%s] Mode: %ld\n"), ServiceName, Mode); #endif // // Now Concurrent Limit // dwSize = sizeof(ConcurrentLimit); Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize); #if DBG if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) dprintf(TEXT("Found Reg-Key for [%s] ConcurrentLimit: %ld\n"), ServiceName, ConcurrentLimit); #endif RegCloseKey(hKey2); } if (Mode == 0) { *PerSeatLicensing = TRUE; *SessionLimit = 0; } else { *PerSeatLicensing = FALSE; *SessionLimit = ConcurrentLimit; } #endif // #else // #ifdef SPECIAL_USER_LIMIT } // RegistryInitValues ///////////////////////////////////////////////////////////////////////// VOID RegistryDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName ) /*++ Routine Description: Arguments: Service Name - Return Value: None. --*/ { HKEY hKey2 = NULL; DWORD dwType, dwSize; static TCHAR RegKeyText[512]; static TCHAR DisplayName[512]; LONG Status; HRESULT hr; size_t cb, cch; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: RegistryDisplayNameGet\n")); #endif hr = StringCbCopy(DisplayName, sizeof(DisplayName), DefaultName); ASSERT(SUCCEEDED(hr)); // // Create registry key-name we are looking for // cb = sizeof(RegKeyText); hr = StringCbCopy(RegKeyText, cb, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); ASSERT(SUCCEEDED(hr)); hr = StringCbCat(RegKeyText, cb, ServiceName); ASSERT(SUCCEEDED(hr)); if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { dwSize = sizeof(DisplayName); Status = RegQueryValueEx(hKey2, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize); # if DBG if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) dprintf(TEXT("Found Reg-Key for [%s] DisplayName: %s\n"), ServiceName, DisplayName); # endif RegCloseKey(hKey2); } ASSERT(NULL != pDisplayName); cch = lstrlen(DisplayName) + 1; *pDisplayName = LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (*pDisplayName != NULL) { hr = StringCchCopy(*pDisplayName, cch, DisplayName); ASSERT(SUCCEEDED(hr)); } } // RegistryDisplayNameGet ///////////////////////////////////////////////////////////////////////// VOID RegistryFamilyDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName ) /*++ Routine Description: Arguments: Service Name - Return Value: None. --*/ { HKEY hKey2 = NULL; DWORD dwType, dwSize; static TCHAR RegKeyText[512]; static TCHAR DisplayName[MAX_PATH + 1]; LONG Status; HRESULT hr; size_t cb, cch; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: RegistryFamilyDisplayNameGet\n")); #endif hr = StringCbCopy(DisplayName, sizeof(DisplayName), DefaultName); ASSERT(SUCCEEDED(hr)); // // Create registry key-name we are looking for // cb = sizeof(RegKeyText); hr = StringCbCopy(RegKeyText, cb, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); ASSERT(SUCCEEDED(hr)); hr = StringCbCat(RegKeyText, cb, ServiceName); ASSERT(SUCCEEDED(hr)); if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { dwSize = sizeof(DisplayName); Status = RegQueryValueEx(hKey2, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize); # if DBG if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) dprintf(TEXT("Found Reg-Key for [%s] FamilyDisplayName: %s\n"), ServiceName, DisplayName); # endif RegCloseKey(hKey2); } ASSERT(NULL != pDisplayName); cch = lstrlen(DisplayName) + 1; *pDisplayName = LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (*pDisplayName != NULL) { hr = StringCchCopy(*pDisplayName, cch, DisplayName); ASSERT(SUCCEEDED(hr)); } } // RegistryFamilyDisplayNameGet ///////////////////////////////////////////////////////////////////////// LPTSTR ServiceFindInTable( LPTSTR ServiceName, const LPTSTR Table[], ULONG TableSize, ULONG *TableIndex ) /*++ Routine Description: Does search of table to find matching service name. Arguments: Service Name - Table - TableSize - TableIndex - Return Value: Pointer to found service or NULL if not found. --*/ { ULONG i = 0; BOOL Found = FALSE; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: ServiceFindInTable\n")); #endif while ((i < TableSize) && (!Found)) { Found = !lstrcmpi(ServiceName, Table[i]); i++; } if (Found) { i--; *TableIndex = i; return Table[i]; } else return NULL; } // ServiceFindInTable ///////////////////////////////////////////////////////////////////////// VOID RegistryInitService( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit ) /*++ Routine Description: Gets init values for a given service from the registry. If not found then just returns default values. Arguments: ServiceName - PerSeatLicensing - SessionLimit - Return Value: --*/ { // // These are the default values // ULONG TableEntry; LPTSTR SvcName = NULL; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: RegistryInitService\n")); #endif ASSERT(NULL != PerSeatLicensing && NULL != SessionLimit); *PerSeatLicensing = FALSE; *SessionLimit = 0; // // Check if it is a file/print service - if so don't worry about rest // of registry entries. // if (ServiceFindInTable(ServiceName, FilePrintTable, NumFilePrintEntries, &TableEntry)) { return; } // // Not FilePrint - see if we need to map the name. // SvcName = ServiceFindInTable(ServiceName, NameMappingTable2, NUM_MAPPING_ENTRIES, &TableEntry); // if it wasn't found, use original ServiceName if (SvcName == NULL) SvcName = ServiceName; RegistryInitValues(SvcName, PerSeatLicensing, SessionLimit); #if DBG if (TraceFlags & TRACE_REGISTRY) if (*PerSeatLicensing) dprintf(TEXT("LLS - Registry Init: PerSeat: Y Svc: %s\n"), SvcName); else dprintf(TEXT("LLS - Registry Init: PerSeat: N Svc: %s\n"), SvcName); #endif } // RegistryInitService ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// NTSTATUS LocalServiceListInit() /*++ Routine Description: Arguments: None. Return Value: None. --*/ { NTSTATUS status = STATUS_SUCCESS; try { RtlInitializeResource(&LocalServiceListLock); } except(EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode(); } if (!NT_SUCCESS(status)) return status; // // Now scan the registry and add all the services // LocalServiceListUpdate(); return STATUS_SUCCESS; } // LocalServiceListInit ///////////////////////////////////////////////////////////////////////// int __cdecl LocalServiceListCompare(const void *arg1, const void *arg2) { PLOCAL_SERVICE_RECORD Svc1, Svc2; Svc1 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg1); Svc2 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg2); return lstrcmpi( Svc1->Name, Svc2->Name ); } // LocalServiceListCompare ///////////////////////////////////////////////////////////////////////// PLOCAL_SERVICE_RECORD LocalServiceListFind( LPTSTR Name ) /*++ Routine Description: Internal routine to actually do binary search on LocalServiceList, this does not do any locking as we expect the wrapper routine to do this. The search is a simple binary search. Arguments: ServiceName - Return Value: Pointer to found server table entry or NULL if not found. --*/ { LONG begin = 0; LONG end = (LONG) LocalServiceListSize - 1; LONG cur; int match; PLOCAL_SERVICE_RECORD Service; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: LocalServiceListFind\n")); #endif if ((LocalServiceListSize == 0) || (Name == NULL)) return NULL; while (end >= begin) { // go halfway in-between cur = (begin + end) / 2; Service = LocalServiceList[cur]; // compare the two result into match match = lstrcmpi(Name, Service->Name); if (match < 0) // move new begin end = cur - 1; else begin = cur + 1; if (match == 0) return Service; } return NULL; } // LocalServiceListFind ///////////////////////////////////////////////////////////////////////// PLOCAL_SERVICE_RECORD LocalServiceListAdd( LPTSTR Name, LPTSTR DisplayName, LPTSTR FamilyDisplayName, DWORD ConcurrentLimit, DWORD FlipAllow, DWORD Mode, DWORD HighMark ) /*++ Routine Description: Arguments: ServiceName - Return Value: Pointer to added service table entry, or NULL if failed. --*/ { LPTSTR NewName; PLOCAL_SERVICE_RECORD Service; PLOCAL_SERVICE_RECORD *pLocalServiceListTmp; HRESULT hr; size_t cch; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: LocalServiceListAdd\n")); #endif if ((Name == NULL) || (*Name == TEXT('\0'))) { #if DBG dprintf(TEXT("Error LLS: LocalServiceListAdd Bad Parms\n")); #endif ASSERT(FALSE); return NULL; } // // Try to find the name // Service = LocalServiceListFind(Name); if (Service != NULL) { Service->ConcurrentLimit = ConcurrentLimit; Service->FlipAllow = FlipAllow; Service->Mode = Mode; return Service; } // // No record - so create a new one // if (LocalServiceList == NULL) { pLocalServiceListTmp = (PLOCAL_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLOCAL_SERVICE_RECORD)); } else { pLocalServiceListTmp = (PLOCAL_SERVICE_RECORD *) LocalReAlloc(LocalServiceList, sizeof(PLOCAL_SERVICE_RECORD) * (LocalServiceListSize + 1), LHND); } // // Make sure we could allocate server table // if (pLocalServiceListTmp == NULL) { return NULL; } else { LocalServiceList = pLocalServiceListTmp; } // // Allocate space for Record. // Service = (PLOCAL_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LOCAL_SERVICE_RECORD)); if (Service == NULL) { ASSERT(FALSE); return NULL; } LocalServiceList[LocalServiceListSize] = Service; // // Name // cch = lstrlen(Name) + 1; NewName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (NewName == NULL) { ASSERT(FALSE); LocalFree(Service); return NULL; } // now copy it over... Service->Name = NewName; hr = StringCchCopy(NewName, cch, Name); ASSERT(SUCCEEDED(hr)); // // DisplayName // cch = lstrlen(DisplayName) + 1; NewName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (NewName == NULL) { ASSERT(FALSE); LocalFree(Service->Name); LocalFree(Service); return NULL; } // now copy it over... Service->DisplayName = NewName; hr = StringCchCopy(NewName, cch, DisplayName); ASSERT(SUCCEEDED(hr)); // // FamilyDisplayName // cch = lstrlen(FamilyDisplayName) + 1; NewName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (NewName == NULL) { ASSERT(FALSE); LocalFree(Service->Name); LocalFree(Service->DisplayName); LocalFree(Service); return NULL; } // now copy it over... Service->FamilyDisplayName = NewName; hr = StringCchCopy(NewName, cch, FamilyDisplayName); ASSERT(SUCCEEDED(hr)); // // Initialize other stuff // Service->ConcurrentLimit = ConcurrentLimit; Service->FlipAllow = FlipAllow; Service->Mode = Mode; Service->HighMark = HighMark; LocalServiceListSize++; // Have added the entry - now need to sort it in order of the service names qsort((void *) LocalServiceList, (size_t) LocalServiceListSize, sizeof(PLOCAL_SERVICE_RECORD), LocalServiceListCompare); return Service; } // LocalServiceListAdd ///////////////////////////////////////////////////////////////////////// VOID LocalServiceListUpdate( ) /*++ Routine Description: Looks in registry for given service and sets values accordingly. Arguments: Return Value: None. --*/ { HKEY hKey2 = NULL; HKEY hKey3 = NULL; static TCHAR KeyName[MAX_PATH + 1]; static TCHAR DisplayName[MAX_PATH + 1]; static TCHAR FamilyDisplayName[MAX_PATH + 1]; LONG EnumStatus; NTSTATUS Status; DWORD iSubKey = 0; DWORD dwType, dwSize; DWORD FlipAllow = 0; DWORD Mode = 0; DWORD ConcurrentLimit = 0; DWORD HighMark = 0; HRESULT hr; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: LocalServiceListUpdate\n")); #endif RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE); EnumStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo"), 0, KEY_READ, &hKey2); while (EnumStatus == ERROR_SUCCESS) { EnumStatus = RegEnumKey(hKey2, iSubKey, KeyName, MAX_PATH + 1); iSubKey++; if (EnumStatus == ERROR_SUCCESS) { if ((Status = RegOpenKeyEx(hKey2, KeyName, 0, KEY_READ, &hKey3)) == ERROR_SUCCESS) { dwSize = sizeof(DisplayName); Status = RegQueryValueEx(hKey3, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize); dwSize = sizeof(FamilyDisplayName); if (Status == ERROR_SUCCESS) { Status = RegQueryValueEx(hKey3, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) FamilyDisplayName, &dwSize); if (Status != ERROR_SUCCESS) { hr = StringCbCopy(FamilyDisplayName, sizeof(FamilyDisplayName), DisplayName); ASSERT(SUCCEEDED(hr)); Status = ERROR_SUCCESS; } } dwSize = sizeof(Mode); if (Status == ERROR_SUCCESS) Status = RegQueryValueEx(hKey3, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize); dwSize = sizeof(FlipAllow); if (Status == ERROR_SUCCESS) Status = RegQueryValueEx(hKey3, TEXT("FlipAllow"), NULL, &dwType, (LPBYTE) &FlipAllow, &dwSize); dwSize = sizeof(ConcurrentLimit); if (Status == ERROR_SUCCESS) if (Mode == 0) ConcurrentLimit = 0; else Status = RegQueryValueEx(hKey3, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize); dwSize = sizeof(HighMark); if (Status == ERROR_SUCCESS) { Status = RegQueryValueEx(hKey3, TEXT("LocalKey"), NULL, &dwType, (LPBYTE) &HighMark, &dwSize); if (Status != ERROR_SUCCESS) { Status = ERROR_SUCCESS; HighMark = 0; } } // // If we read in everything then add to our table // if (Status == ERROR_SUCCESS) LocalServiceListAdd(KeyName, DisplayName, FamilyDisplayName, ConcurrentLimit, FlipAllow, Mode, HighMark); RegCloseKey(hKey3); } } } RegCloseKey(hKey2); RtlReleaseResource(&LocalServiceListLock); } // LocalServiceListUpdate ///////////////////////////////////////////////////////////////////////// VOID LocalServiceListHighMarkSet( ) /*++ Routine Description: Arguments: Return Value: None. --*/ { HKEY hKey2 = NULL; static TCHAR RegKeyText[512]; LONG Status; ULONG i, j; PSERVICE_RECORD Service; HRESULT hr; size_t cb; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: LocalServiceListHighMarkSet\n")); #endif RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE); for (i = 0; i < LocalServiceListSize; i++) { RtlAcquireResourceShared(&ServiceListLock, TRUE); j = 0; Service = NULL; while ( (j < ServiceListSize) && (Service == NULL) ) { if (!lstrcmpi(LocalServiceList[i]->DisplayName, ServiceList[j]->DisplayName) ) Service = ServiceList[j]; j++; } RtlReleaseResource(&ServiceListLock); if (Service != NULL) { // // Create registry key-name we are looking for // cb = sizeof(RegKeyText); hr = StringCbCopy(RegKeyText, cb, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); ASSERT(SUCCEEDED(hr)); hr = StringCbCat(RegKeyText, cb, LocalServiceList[i]->Name); ASSERT(SUCCEEDED(hr)); Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2); if (Status == ERROR_SUCCESS) { Status = RegSetValueEx(hKey2, TEXT("LocalKey"), 0, REG_DWORD, (LPBYTE) &Service->HighMark, sizeof(Service->HighMark)); RegCloseKey( hKey2 ); } } } RtlReleaseResource(&LocalServiceListLock); } // LocalServiceListHighMarkSet /////////////////////////////////////////////////////////////////////////////// VOID LocalServiceListConcurrentLimitSet( ) /*++ Routine Description: Write concurrent limit to the registry for all secure services. Modified from LocalServiceListHighMarkSet() implementation. Arguments: None. Return Value: None. --*/ { HKEY hKey2 = NULL; TCHAR RegKeyText[512]; LONG Status; ULONG i; HRESULT hr; size_t cb; #if DBG if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: LocalServiceListConcurrentLimitSet\n")); #endif RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE); for (i = 0; i < LocalServiceListSize; i++) { // // Create registry key-name we are looking for // cb = sizeof(RegKeyText); hr = StringCbCopy(RegKeyText, cb, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); ASSERT(SUCCEEDED(hr)); hr = StringCbCat(RegKeyText, cb, LocalServiceList[i]->Name); ASSERT(SUCCEEDED(hr)); Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2); if (Status == ERROR_SUCCESS) { DWORD dwConcurrentLimit; DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit ); DWORD dwType; // don't write unless we have to (to avoid triggering the registry monitor thread) Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit ); if ( ServiceIsSecure( LocalServiceList[i]->DisplayName ) ) { LocalServiceList[i]->ConcurrentLimit = LocalServiceList[i]->Mode ? ProductLicensesGet( LocalServiceList[i]->DisplayName, TRUE ) : 0; // secure product if ( ( ERROR_SUCCESS != Status ) || ( REG_DWORD != dwType ) || ( dwConcurrentLimit != LocalServiceList[i]->ConcurrentLimit ) ) { RegCloseKey( hKey2 ); Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2); ASSERT( ERROR_SUCCESS == Status ); if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx(hKey2, TEXT("ConcurrentLimit"), 0, REG_DWORD, (LPBYTE) &LocalServiceList[i]->ConcurrentLimit, sizeof( LocalServiceList[i]->ConcurrentLimit ) ); } } } RegCloseKey( hKey2 ); } } RtlReleaseResource(&LocalServiceListLock); } // LocalServiceListConcurrentLimitSet /////////////////////////////////////////////////////////////////////////////// BOOL ServiceIsSecure( LPTSTR ServiceName ) /*++ Routine Description: Determine whether a given service disallows 3.51 Honesty-style license purchases. Arguments: ServiceName (LPTSTR) Service to check. Return Value: TRUE if service requires secure certificate, FALSE if it accepts 3.51 Honesty-style license purchases. --*/ { BOOL IsSecure = FALSE; if ( NULL != SecureServiceList ) { DWORD i; RtlEnterCriticalSection( &ConfigInfoLock ); for ( i=0; i < SecureServiceListSize; i++ ) { if ( !lstrcmpi( SecureServiceList[i], ServiceName ) ) { IsSecure = TRUE; break; } } RtlLeaveCriticalSection( &ConfigInfoLock ); } return IsSecure; } /////////////////////////////////////////////////////////////////////////////// NTSTATUS ServiceSecuritySet( LPTSTR ServiceName ) /*++ Routine Description: Add a given service to the secure service list. Arguments: ServiceName (LPTSTR) Service to add. Return Value: STATUS_SUCCESS or Win error or NTSTATUS error code. --*/ { NTSTATUS nt; DWORD i; BOOL bChangedValue = FALSE; RtlEnterCriticalSection( &ConfigInfoLock ); for ( i=0; i < SecureServiceListSize; i++ ) { if ( !lstrcmpi( SecureServiceList[i], ServiceName ) ) { // product already registered as secure break; } } if ( i < SecureServiceListSize ) { // product already registered as secure nt = STATUS_SUCCESS; } else { TCHAR * NewSecureServiceBuffer; ULONG NewSecureServiceBufferSize; NewSecureServiceBufferSize = ( SecureServiceBufferSize ? SecureServiceBufferSize : sizeof( TCHAR ) ) + sizeof( TCHAR ) * ( 1 + lstrlen( ServiceName ) ); NewSecureServiceBuffer = LocalAlloc( LPTR, NewSecureServiceBufferSize ); if ( NULL == NewSecureServiceBuffer ) { nt = STATUS_NO_MEMORY; ASSERT( FALSE ); } else { if ( NULL != SecureServiceBuffer ) { // copy over current secure service strings memcpy( NewSecureServiceBuffer, SecureServiceBuffer, SecureServiceBufferSize - sizeof( TCHAR ) ); // add new secure service (don't forget last string is followed by 2 nulls) memcpy( (LPBYTE) NewSecureServiceBuffer + SecureServiceBufferSize - sizeof( TCHAR ), ServiceName, NewSecureServiceBufferSize - SecureServiceBufferSize - sizeof( TCHAR ) ); } else { // add new secure service (don't forget last string is followed by 2 nulls) memcpy( NewSecureServiceBuffer, ServiceName, NewSecureServiceBufferSize - sizeof( TCHAR ) ); } ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - 2 * sizeof( TCHAR ) ) ); ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - sizeof( TCHAR ) ) ); // encrypt buffer nt = EBlock( NewSecureServiceBuffer, NewSecureServiceBufferSize ); ASSERT( STATUS_SUCCESS == nt ); if ( STATUS_SUCCESS == nt ) { HKEY hKeyParameters; // save new list to registry nt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"), 0, KEY_WRITE, &hKeyParameters ); ASSERT( STATUS_SUCCESS == nt ); if ( STATUS_SUCCESS == nt ) { nt = RegSetValueEx( hKeyParameters, TEXT( "ProductData" ), 0, REG_BINARY, (LPBYTE) NewSecureServiceBuffer, NewSecureServiceBufferSize ); ASSERT( STATUS_SUCCESS == nt ); if ( STATUS_SUCCESS == nt ) { bChangedValue = TRUE; } } RegCloseKey( hKeyParameters ); } LocalFree( NewSecureServiceBuffer ); } } RtlLeaveCriticalSection( &ConfigInfoLock ); if ( ( STATUS_SUCCESS == nt ) && bChangedValue ) { // key updated, now update internal copy ConfigInfoRegistryUpdate(); } return nt; } /////////////////////////////////////////////////////////////////////////////// NTSTATUS ProductSecurityPack( LPDWORD pcchProductSecurityStrings, WCHAR ** ppchProductSecurityStrings ) /*++ Routine Description: Pack the secure service list into a contiguous buffer for transmission. NOTE: If the routine succeeds, the caller must later MIDL_user_free() the buffer at *ppchProductSecurityStrings. Arguments: pcchProductSecurityStrings (LPDWORD) On return, holds the size (in characters) of the buffer pointed to by *ppchProductSecurityStrings. ppchProductSecurityStrings (WCHAR **) On return, holds the address of the buffer allocated to hold the names of the secure products. Return Value: STATUS_SUCCESS or STATUS_NO_MEMORY. --*/ { NTSTATUS nt; RtlEnterCriticalSection( &ConfigInfoLock ); ASSERT(NULL != ppchProductSecurityStrings); *ppchProductSecurityStrings = MIDL_user_allocate( SecureServiceBufferSize ); if ( NULL == *ppchProductSecurityStrings ) { nt = STATUS_NO_MEMORY; ASSERT( FALSE ); } else { memcpy( *ppchProductSecurityStrings, SecureServiceBuffer, SecureServiceBufferSize ); ASSERT(NULL != pcchProductSecurityStrings); *pcchProductSecurityStrings = SecureServiceBufferSize / sizeof( TCHAR ); nt = STATUS_SUCCESS; } RtlLeaveCriticalSection( &ConfigInfoLock ); return nt; } /////////////////////////////////////////////////////////////////////////////// NTSTATUS ProductSecurityUnpack( DWORD cchProductSecurityStrings, WCHAR * pchProductSecurityStrings ) /*++ Routine Description: Unpack a secure service list packed by ProductSecurityPack(). The products contained in the pack are added to the current secure product list. Arguments: cchProductSecurityStrings (DWORD) The size (in characters) of the buffer pointed to by pchProductSecurityStrings. pchProductSecurityStrings (WCHAR *) The address of the buffer allocated to hold the names of the secure products. Return Value: STATUS_SUCCESS. --*/ { DWORD i; for ( i=0; ( i < cchProductSecurityStrings ) && ( TEXT('\0') != pchProductSecurityStrings[i] ); i += 1 + lstrlen( &pchProductSecurityStrings[i] ) ) { ServiceSecuritySet( &pchProductSecurityStrings[i] ); } return STATUS_SUCCESS; } #if DBG /////////////////////////////////////////////////////////////////////////////// void ProductSecurityListDebugDump() /*++ Routine Description: Dump contents of product security list to debug console. Arguments: None. Return Value: None. --*/ { if ( NULL == SecureServiceList ) { dprintf( TEXT( "No secure products.\n" ) ); } else { DWORD i; RtlEnterCriticalSection( &ConfigInfoLock ); for ( i=0; i < SecureServiceListSize; i++ ) { dprintf( TEXT( "(%3ld) %s\n" ), (long)i, SecureServiceList[i] ); } RtlLeaveCriticalSection( &ConfigInfoLock ); } } #endif //DBG