/* * Concurrent.cpp * * Author: RobLeit * * The Concurrent (renamed to Per Session) licensing policy. */ /* * Includes */ #include "precomp.h" #include "lscore.h" #include "session.h" #include "concurrent.h" #include "util.h" #include "lctrace.h" #include #define STRSAFE_NO_DEPRECATE #include "strsafe.h" /* * Typedefs */ #define CONCURRENTLICENSEINFO_TYPE_V1 (1) typedef struct { DWORD dwStructVer; DWORD dwLicenseVer; LONG lLicenseCount; HWID hwid; } CONCURRENTLICENSEINFO_V1, *PCONCURRENTLICENSEINFO_V1; typedef struct { ULARGE_INTEGER ulSerialNumber; FILETIME ftNotAfter; DWORD cchServerName; WCHAR szServerName[MAX_COMPUTERNAME_LENGTH + 2]; } LSERVERINFO, *PLSERVERINFO; /* * Function declarations */ NTSTATUS ReturnLicenseToLS( LONG nNum ); LICENSE_STATUS GetLicenseFromStore( PLONG LicenseCount, PHWID phwid, DWORD dwLicenseVer ); LICENSE_STATUS SetLicenseInStore( LONG LicenseCount, HWID hwid, DWORD dwLicenseVer ); /* * extern globals */ extern "C" extern HANDLE hModuleWin; /* * globals */ FILETIME g_ftNotAfter = {0,0}; HANDLE g_hOkayToAdd = NULL; DWORD g_dwWaitTimeAdd; DWORD g_dwWaitTimeRemove; DWORD g_dwIncrement; HANDLE g_rgWaitEvents[4] = {NULL,NULL,NULL,NULL}; CRITICAL_SECTION g_csAddLicenses; RTL_RESOURCE g_rwLockLicense; BOOL g_fLockLicenseInitialized = FALSE; LONG g_lSessionCount = 0; LONG g_lSessionMax; /* * Constants */ // // Dynamic licensing parameters // #define LC_POLICY_CONCURRENT_LICENSE_COUNT_INCREMENT 1 #define LC_POLICY_CONCURRENT_WAIT_TIME_ADD (60) #define LC_POLICY_CONCURRENT_WAIT_TIME_REMOVE (60*30) // // The LSA secret store for the Concurrent licenses // // L$ means only readable from the local machine #define CONCURRENT_LICENSE_STORE_5_1 L"L$CONCURRENT_LICENSE_STORE_AFF8D0DE-BF56-49e2-89F8-1F188C0ACEDD" #define CONCURRENT_LICENSE_STORE_LATEST_VERSION CONCURRENT_LICENSE_STORE_5_1 // // The LSA secret store for the license server info // #define CONCURRENT_LSERVER_STORE L"L$CONCURRENT_LSERVER_STORE_AFF8D0DE-BF56-49e2-89F8-1F188C0ACEDD" // // Registry keys // #define LCREG_CONCURRENTKEY L"System\\CurrentControlSet\\Control\\Terminal Server\\Licensing Core\\Policies\\Concurrent" #define LCREG_INCREMENT L"Increment" #define LCREG_WAIT_TIME_ADD L"WaitTimeAdd" #define LCREG_WAIT_TIME_REMOVE L"WaitTimeRemove" // // Events used to trigger license returns // #define RETURN_LICENSE_START_WAITING 0 #define RETURN_LICENSE_IMMEDIATELY 1 #define RETURN_LICENSE_EXIT 2 #define RETURN_LICENSE_WAITING_DONE 3 /* * Class Implementation */ /* * Creation Functions */ CConcurrentPolicy::CConcurrentPolicy( ) : CPolicy() { } CConcurrentPolicy::~CConcurrentPolicy( ) { } /* * Administrative Functions */ ULONG CConcurrentPolicy::GetFlags( ) { return(LC_FLAG_INTERNAL_POLICY | LC_FLAG_REQUIRE_APP_COMPAT); } ULONG CConcurrentPolicy::GetId( ) { return(5); } NTSTATUS CConcurrentPolicy::GetInformation( LPLCPOLICYINFOGENERIC lpPolicyInfo ) { NTSTATUS Status; ASSERT(lpPolicyInfo != NULL); if (lpPolicyInfo->ulVersion == LCPOLICYINFOTYPE_V1) { int retVal; LPLCPOLICYINFO_V1 lpPolicyInfoV1 = (LPLCPOLICYINFO_V1)lpPolicyInfo; LPWSTR pName; LPWSTR pDescription; ASSERT(lpPolicyInfoV1->lpPolicyName == NULL); ASSERT(lpPolicyInfoV1->lpPolicyDescription == NULL); // // The strings loaded in this fashion are READ-ONLY. They are also // NOT NULL terminated. Allocate and zero out a buffer, then copy the // string over. // retVal = LoadString( (HINSTANCE)hModuleWin, IDS_LSCORE_CONCURRENT_NAME, (LPWSTR)(&pName), 0 ); if (retVal != 0) { lpPolicyInfoV1->lpPolicyName = (LPWSTR)LocalAlloc(LPTR, (retVal + 1) * sizeof(WCHAR)); if (lpPolicyInfoV1->lpPolicyName != NULL) { StringCbCopyN(lpPolicyInfoV1->lpPolicyName, (retVal+1) * sizeof(WCHAR), pName, (retVal+1) * sizeof(WCHAR)); } else { Status = STATUS_NO_MEMORY; goto V1error; } } else { Status = STATUS_INTERNAL_ERROR; goto V1error; } retVal = LoadString( (HINSTANCE)hModuleWin, IDS_LSCORE_CONCURRENT_DESC, (LPWSTR)(&pDescription), 0 ); if (retVal != 0) { lpPolicyInfoV1->lpPolicyDescription = (LPWSTR)LocalAlloc(LPTR, (retVal + 1) * sizeof(WCHAR)); if (lpPolicyInfoV1->lpPolicyDescription != NULL) { StringCbCopyN(lpPolicyInfoV1->lpPolicyDescription, (retVal+1) * sizeof(WCHAR), pDescription, (retVal+1) * sizeof(WCHAR)); } else { Status = STATUS_NO_MEMORY; goto V1error; } } else { Status = STATUS_INTERNAL_ERROR; goto V1error; } Status = STATUS_SUCCESS; goto exit; V1error: // // An error occurred loading/copying the strings. // if (lpPolicyInfoV1->lpPolicyName != NULL) { LocalFree(lpPolicyInfoV1->lpPolicyName); lpPolicyInfoV1->lpPolicyName = NULL; } if (lpPolicyInfoV1->lpPolicyDescription != NULL) { LocalFree(lpPolicyInfoV1->lpPolicyDescription); lpPolicyInfoV1->lpPolicyDescription = NULL; } } else { Status = STATUS_REVISION_MISMATCH; } exit: return(Status); } DWORD WINAPI ReturnLicenseWorker( LPVOID lpParameter ) { DWORD dwWait; HANDLE * rgWaitEvents = (HANDLE *) lpParameter; LONG lLicensesToReturn, lLastBlock; for (;;) { // // wait for events signalling when to return licenses // or start waiting to return licenses // dwWait = WaitForMultipleObjects(4, // nCount rgWaitEvents, FALSE, // fWaitAll INFINITE ); switch (dwWait) { case WAIT_OBJECT_0+RETURN_LICENSE_START_WAITING: LARGE_INTEGER liWait; // relative wait, in 100 nanosecond intervals liWait.QuadPart = (__int64) g_dwWaitTimeRemove * (-10 * 1000 * 1000); SetWaitableTimer(rgWaitEvents[RETURN_LICENSE_WAITING_DONE], &liWait, 0, // lPeriod NULL, // pfnCompletionRoutine NULL, // lpArgToCompletionRoutine FALSE // fResume (from suspended) ); break; case WAIT_OBJECT_0+RETURN_LICENSE_WAITING_DONE: RtlAcquireResourceShared(&g_rwLockLicense,TRUE); lLastBlock = g_lSessionMax - ((g_lSessionMax / g_dwIncrement) * g_dwIncrement); if (lLastBlock == 0) lLastBlock = g_dwIncrement; if (g_lSessionCount + lLastBlock <= g_lSessionMax ) { lLicensesToReturn = lLastBlock + (((g_lSessionMax - g_lSessionCount - lLastBlock) / g_dwIncrement) * g_dwIncrement); (VOID)ReturnLicenseToLS(lLicensesToReturn); } RtlReleaseResource(&g_rwLockLicense); break; case WAIT_OBJECT_0+RETURN_LICENSE_IMMEDIATELY: RtlAcquireResourceShared(&g_rwLockLicense,TRUE); lLastBlock = g_lSessionMax - ((g_lSessionMax / g_dwIncrement) * g_dwIncrement); if (lLastBlock == 0) lLastBlock = g_dwIncrement; if (g_lSessionCount + lLastBlock + g_dwIncrement <= (DWORD)g_lSessionMax ) { lLicensesToReturn = ((g_lSessionMax - g_lSessionCount - lLastBlock) / g_dwIncrement) * g_dwIncrement; (VOID)ReturnLicenseToLS(lLicensesToReturn); } RtlReleaseResource(&g_rwLockLicense); break; case WAIT_OBJECT_0+RETURN_LICENSE_EXIT: if (NULL != rgWaitEvents[RETURN_LICENSE_START_WAITING]) { CloseHandle(rgWaitEvents[RETURN_LICENSE_START_WAITING]); rgWaitEvents[RETURN_LICENSE_START_WAITING] = NULL; } if (NULL != rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]) { CloseHandle(rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]); rgWaitEvents[RETURN_LICENSE_IMMEDIATELY] = NULL; } if (NULL != rgWaitEvents[RETURN_LICENSE_EXIT]) { CloseHandle(rgWaitEvents[RETURN_LICENSE_EXIT]); rgWaitEvents[RETURN_LICENSE_EXIT] = NULL; } if (NULL != rgWaitEvents[RETURN_LICENSE_WAITING_DONE]) { CloseHandle(rgWaitEvents[RETURN_LICENSE_WAITING_DONE]); rgWaitEvents[RETURN_LICENSE_WAITING_DONE] = NULL; } if (g_fLockLicenseInitialized) { // make sure no one else is using it RtlAcquireResourceExclusive(&g_rwLockLicense,TRUE); RtlDeleteResource(&g_rwLockLicense); g_fLockLicenseInitialized = FALSE; } return STATUS_SUCCESS; break; default: { DWORD dwRet = 0; DWORD dwErr = GetLastError(); LPTSTR lpszError; BOOL fFree = TRUE; dwRet=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dwErr, LANG_NEUTRAL, (LPTSTR)&lpszError, 0, NULL); if (dwRet == 0) { lpszError = (LPTSTR) LocalAlloc(LPTR,12 * sizeof(WCHAR)); if (NULL != lpszError) { wsprintf(lpszError,L"%#lX",dwErr); } else { lpszError = L""; fFree = FALSE; } } LicenseLogEvent(EVENTLOG_ERROR_TYPE, EVENT_LICENSING_CONCURRENT_NOT_DYNAMIC, 1, &lpszError ); if (fFree) { LocalFree(lpszError); } return dwErr; break; } } } } /* * Loading and Activation Functions */ NTSTATUS CConcurrentPolicy::Load( ) { NTSTATUS Status; g_rgWaitEvents[RETURN_LICENSE_START_WAITING] = NULL; g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY] = NULL; g_rgWaitEvents[RETURN_LICENSE_EXIT] = NULL; g_rgWaitEvents[RETURN_LICENSE_WAITING_DONE] = NULL; Status = RtlInitializeCriticalSection(&g_csAddLicenses); if (STATUS_SUCCESS != Status) { return Status; } __try { RtlInitializeResource(&g_rwLockLicense); g_fLockLicenseInitialized = TRUE; Status = STATUS_SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } if (STATUS_SUCCESS != Status) { RtlDeleteCriticalSection(&g_csAddLicenses); return Status; } g_hOkayToAdd = CreateWaitableTimer(NULL, // SecurityAttributes, TRUE, // bManualReset NULL // lpName ); if (NULL == g_hOkayToAdd) { RtlDeleteCriticalSection(&g_csAddLicenses); g_fLockLicenseInitialized = FALSE; RtlDeleteResource(&g_rwLockLicense); return GetLastError(); } return(STATUS_SUCCESS); } NTSTATUS CConcurrentPolicy::Unload( ) { // signal worker thread to cleanup and exit if (NULL != g_rgWaitEvents[RETURN_LICENSE_EXIT]) { SetEvent(g_rgWaitEvents[RETURN_LICENSE_EXIT]); } return(STATUS_SUCCESS); } NTSTATUS CConcurrentPolicy::Activate( BOOL fStartup, ULONG *pulAlternatePolicy ) { NTSTATUS Status = STATUS_SUCCESS; HANDLE hThread; HWID hwidEncrypted; LICENSE_STATUS LsStatus; LARGE_INTEGER liWait; BOOL fRet; if (NULL != pulAlternatePolicy) { // don't set an explicit alternate policy *pulAlternatePolicy = ULONG_MAX; } ReadLicensingParameters(); liWait.QuadPart = -1; // Ensure that timer is set fRet = SetWaitableTimer(g_hOkayToAdd, &liWait, // pDueTime 1, // lPeriod NULL, // pfnCompletionRoutine NULL, // lpArgToCompletionRoutine FALSE // fResume (from suspended) ); if (!fRet) { Status = GetLastError(); goto check_status; } // // Read number of licenses from LSA secret // LsStatus = GetLicenseFromStore(&g_lSessionMax, &hwidEncrypted, CURRENT_TERMINAL_SERVER_VERSION ); if (LsStatus != LICENSE_STATUS_OK) { g_lSessionMax = 0; } Status = StartCheckingGracePeriod(); if (Status == STATUS_SUCCESS) { g_rgWaitEvents[RETURN_LICENSE_START_WAITING] = CreateEvent(NULL, // SecurityAttributes, FALSE, // bManualReset FALSE, // bInitialState NULL // lpName ); if (NULL == g_rgWaitEvents[RETURN_LICENSE_START_WAITING]) { Status = GetLastError(); goto check_status; } g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY] = CreateEvent(NULL, // SecurityAttributes, FALSE, // bManualReset FALSE, // bInitialState NULL // lpName ); if (NULL == g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]) { Status = GetLastError(); CloseHandle(g_rgWaitEvents[RETURN_LICENSE_START_WAITING]); g_rgWaitEvents[RETURN_LICENSE_START_WAITING] = NULL; goto check_status; } g_rgWaitEvents[RETURN_LICENSE_EXIT] = CreateEvent(NULL, // SecurityAttributes, FALSE, // bManualReset FALSE, // bInitialState NULL // lpName ); if (NULL == g_rgWaitEvents[RETURN_LICENSE_EXIT]) { Status = GetLastError(); CloseHandle(g_rgWaitEvents[RETURN_LICENSE_START_WAITING]); g_rgWaitEvents[RETURN_LICENSE_START_WAITING] = NULL; CloseHandle(g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]); g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY] = NULL; goto check_status; } g_rgWaitEvents[RETURN_LICENSE_WAITING_DONE] = CreateWaitableTimer(NULL, // SecurityAttributes, FALSE, // bManualReset NULL // lpName ); if (NULL == g_rgWaitEvents[RETURN_LICENSE_WAITING_DONE]) { Status = GetLastError(); CloseHandle(g_rgWaitEvents[RETURN_LICENSE_START_WAITING]); g_rgWaitEvents[RETURN_LICENSE_START_WAITING] = NULL; CloseHandle(g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]); g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY] = NULL; CloseHandle(g_rgWaitEvents[RETURN_LICENSE_EXIT]); g_rgWaitEvents[RETURN_LICENSE_EXIT] = NULL; goto check_status; } hThread = CreateThread( NULL, // SecurityAttributes 0, // StackSize ReturnLicenseWorker, (LPVOID)g_rgWaitEvents, 0, // CreationFlags NULL // ThreadId ); if (NULL != hThread) { CloseHandle(hThread); } else { Status = STATUS_BAD_INITIAL_PC; CloseHandle(g_rgWaitEvents[RETURN_LICENSE_START_WAITING]); g_rgWaitEvents[RETURN_LICENSE_START_WAITING] = NULL; CloseHandle(g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]); g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY] = NULL; CloseHandle(g_rgWaitEvents[RETURN_LICENSE_EXIT]); g_rgWaitEvents[RETURN_LICENSE_EXIT] = NULL; CloseHandle(g_rgWaitEvents[RETURN_LICENSE_WAITING_DONE]); g_rgWaitEvents[RETURN_LICENSE_WAITING_DONE] = NULL; goto check_status; } } check_status: if (Status != STATUS_SUCCESS) { StopCheckingGracePeriod(); if (!fStartup) { DWORD dwRet = 0; LPTSTR lpszError; BOOL fFree = TRUE; dwRet=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, RtlNtStatusToDosError(Status), LANG_NEUTRAL, (LPTSTR)&lpszError, 0, NULL); if (dwRet == 0) { lpszError = (LPTSTR) LocalAlloc(LPTR,12 * sizeof(WCHAR)); if (NULL != lpszError) { wsprintf(lpszError,L"%#lX",RtlNtStatusToDosError(Status)); } else { lpszError = L""; fFree = FALSE; } } LicenseLogEvent(EVENTLOG_ERROR_TYPE, EVENT_LICENSING_CONCURRENT_CANT_START, 1, &lpszError ); if (fFree) { LocalFree(lpszError); } } } return Status; } NTSTATUS CConcurrentPolicy::Deactivate( BOOL fShutdown ) { NTSTATUS Status; if (fShutdown) { Status = STATUS_SUCCESS; } else { RtlAcquireResourceShared(&g_rwLockLicense,TRUE); Status = ReturnLicenseToLS(0); RtlReleaseResource(&g_rwLockLicense); if (Status != STATUS_SUCCESS) { LPTSTR lpszError; DWORD dwRet = 0; BOOL fFree = TRUE; dwRet=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, RtlNtStatusToDosError(Status), LANG_NEUTRAL, (LPTSTR)&lpszError, 0, NULL); if (dwRet == 0) { lpszError = (LPTSTR) LocalAlloc(LPTR,12 * sizeof(WCHAR)); if (NULL != lpszError) { wsprintf(lpszError,L"%#lX",RtlNtStatusToDosError(Status)); } else { lpszError = L""; fFree = FALSE; } } LicenseLogEvent(EVENTLOG_ERROR_TYPE, EVENT_LICENSING_CONCURRENT_NOT_RETURNED, 1, &lpszError ); if (fFree) { LocalFree(lpszError); } } StopCheckingGracePeriod(); } return(Status); } /* * Licensing Functions */ NTSTATUS CConcurrentPolicy::Logon( CSession& Session ) { if (!Session.IsSessionZero() && !Session.IsUserHelpAssistant()) { return LicenseClient(Session); } else { return STATUS_SUCCESS; } } NTSTATUS CConcurrentPolicy::Reconnect( CSession& Session, CSession& TemporarySession ) { UNREFERENCED_PARAMETER(Session); if (!Session.IsSessionZero() && !Session.IsUserHelpAssistant() && !Session.GetLicenseContext()->fTsLicense) { return LicenseClient(TemporarySession); } else { return STATUS_SUCCESS; } } NTSTATUS CConcurrentPolicy::Logoff( CSession& Session ) { if (!Session.IsSessionZero() && !Session.IsUserHelpAssistant()) { LONG lSessions, lLastBlock; ASSERT(Session.GetLicenseContext()->fTsLicense == TRUE); lSessions = InterlockedDecrement(&g_lSessionCount); ASSERT(lSessions >= 0); RtlAcquireResourceShared(&g_rwLockLicense,TRUE); lLastBlock = g_lSessionMax - ((g_lSessionMax / g_dwIncrement) * g_dwIncrement); if (lLastBlock == 0) lLastBlock = g_dwIncrement; if (lSessions + lLastBlock <= g_lSessionMax) { TryToReturnLicenses(g_lSessionMax-lSessions); } RtlReleaseResource(&g_rwLockLicense); } return(STATUS_SUCCESS); } /* * Private License Functions */ NTSTATUS CConcurrentPolicy::LicenseClient( CSession& Session ) { NTSTATUS Status; LONG lSessions; lSessions = InterlockedIncrement(&g_lSessionCount); RtlAcquireResourceShared(&g_rwLockLicense,TRUE); if (lSessions > g_lSessionMax) { DWORD cBlocks = (lSessions - g_lSessionMax) / g_dwIncrement; DWORD nLeftover = (lSessions - g_lSessionMax) % g_dwIncrement; if (nLeftover > 0) { cBlocks++; } TryToAddLicenses(cBlocks * g_dwIncrement + g_lSessionMax); if (lSessions > g_lSessionMax) { if (!AllowLicensingGracePeriodConnection()) { InterlockedDecrement(&g_lSessionCount); RtlReleaseResource(&g_rwLockLicense); return STATUS_CTX_LICENSE_NOT_AVAILABLE; } } } Status = CheckExpiration(); RtlReleaseResource(&g_rwLockLicense); if (Status == STATUS_SUCCESS) { Status = GetLlsLicense(Session); if (Status == STATUS_SUCCESS) { Session.GetLicenseContext()->fTsLicense = TRUE; } else { InterlockedDecrement(&g_lSessionCount); } } else { InterlockedDecrement(&g_lSessionCount); } if (Status != STATUS_SUCCESS) { // // Gina doesn't understand many error codes // Status = STATUS_CTX_LICENSE_NOT_AVAILABLE; } return(Status); } LONG CConcurrentPolicy::CheckInstalledLicenses( DWORD dwWanted ) { CONCURRENTLICENSEINFO_V1 LicenseInfo; LICENSE_STATUS LsStatus; ULONG cbSecretLen; cbSecretLen = sizeof(LicenseInfo); ZeroMemory(&LicenseInfo, cbSecretLen); // // Get the concurrent license count from the LSA secret // LsStatus = LsCsp_RetrieveSecret( CONCURRENT_LICENSE_STORE_LATEST_VERSION, (LPBYTE)&LicenseInfo, &cbSecretLen ); if ((LsStatus != LICENSE_STATUS_OK) || (cbSecretLen < sizeof(CONCURRENTLICENSEINFO_V1)) || (LicenseInfo.dwLicenseVer != CURRENT_TERMINAL_SERVER_VERSION)) { // // We determine that the license pack for this version is // not installed if: // // (1) we cannot retrieve the license info from the LSA secret // (2) we cannot read at least the size of version 1 of the license // info structure, or // (3) the license pack version is different from that requested. // return dwWanted; } else { LSERVERINFO LServerInfo; ULONG cbLServerInfo; cbLServerInfo = sizeof(LSERVERINFO); LsStatus = LsCsp_RetrieveSecret( CONCURRENT_LSERVER_STORE, (LPBYTE)&LServerInfo, &cbLServerInfo ); if (LsStatus == LICENSE_STATUS_OK) { g_ftNotAfter = LServerInfo.ftNotAfter; if (0 == TimeToHardExpiration()) { return dwWanted; } } return (dwWanted - LicenseInfo.lLicenseCount); } } VOID CConcurrentPolicy::TryToReturnLicenses( DWORD dwReturnCount ) { ASSERT(dwReturnCount != 0); if (dwReturnCount > g_dwIncrement) { // Immediately return all but one block if (NULL != g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]) SetEvent(g_rgWaitEvents[RETURN_LICENSE_IMMEDIATELY]); } // Wait before returning one block if (NULL != g_rgWaitEvents[RETURN_LICENSE_START_WAITING]) SetEvent(g_rgWaitEvents[RETURN_LICENSE_START_WAITING]); } // // Must have shared lock to call this // NTSTATUS ReturnLicenseToLS( LONG nNum ) { HANDLE hProtocol = NULL; HWID hwid; LICENSEREQUEST LicenseRequest; LICENSE_STATUS LsStatus; LONG CurrentCount; LSERVERINFO LServerInfo; ULONG cbLServerInfo; Product_Info ProductInfo; LsStatus = InitProductInfo( &ProductInfo, PRODUCT_INFO_CONCURRENT_SKU_PRODUCT_ID ); if (LsStatus != LICENSE_STATUS_OK) { return(LsStatusToNtStatus(LsStatus)); } // // Get the current license count and HWID from the store. // LsStatus = GetLicenseFromStore( &CurrentCount, &hwid, CURRENT_TERMINAL_SERVER_VERSION ); if (LsStatus == LICENSE_STATUS_OK) { if ((0 == nNum) || (nNum > CurrentCount)) { nNum = CurrentCount; } if (CurrentCount == 0) { // We don't check for status from the following calls as we don't want to fail. LsCsp_StoreSecret( CONCURRENT_LICENSE_STORE_LATEST_VERSION, NULL, 0 ); LsCsp_StoreSecret( CONCURRENT_LSERVER_STORE, NULL, 0 ); return(STATUS_SUCCESS); } } else { return(LsStatusToNtStatus(LsStatus)); } // // Initialize the license request structure. // ZeroMemory(&LicenseRequest, sizeof(LICENSEREQUEST)); LicenseRequest.pProductInfo = &ProductInfo; LicenseRequest.dwLanguageID = GetSystemDefaultLCID(); LicenseRequest.dwPlatformID = CURRENT_TERMINAL_SERVER_VERSION; LicenseRequest.cbEncryptedHwid = sizeof(HWID); LicenseRequest.pbEncryptedHwid = (PBYTE)&hwid; cbLServerInfo = sizeof(LSERVERINFO); LsStatus = LsCsp_RetrieveSecret( CONCURRENT_LSERVER_STORE, (LPBYTE)&LServerInfo, &cbLServerInfo ); if (LsStatus == LICENSE_STATUS_OK) { LsStatus = CreateProtocolContext(NULL, &hProtocol); } else { goto done; } if (LsStatus == LICENSE_STATUS_OK) { LsStatus = ReturnInternetLicense( hProtocol, LServerInfo.szServerName, &LicenseRequest, LServerInfo.ulSerialNumber, nNum ); } else { goto done; } if (LsStatus == LICENSE_STATUS_OK) { if( (CurrentCount-nNum) > 0 ) { LsStatus = SetLicenseInStore( CurrentCount-nNum, hwid, CURRENT_TERMINAL_SERVER_VERSION ); } else { // We don't check for status from the following calls as we don't want to fail. LsCsp_StoreSecret( CONCURRENT_LICENSE_STORE_LATEST_VERSION, NULL, 0 ); LsCsp_StoreSecret( CONCURRENT_LSERVER_STORE, NULL, 0 ); } RtlConvertSharedToExclusive(&g_rwLockLicense); g_lSessionMax = CurrentCount - nNum; RtlConvertExclusiveToShared(&g_rwLockLicense); } done: if (hProtocol != NULL) { DeleteProtocolContext(hProtocol); } if (ProductInfo.pbCompanyName) { LocalFree(ProductInfo.pbCompanyName); } if (ProductInfo.pbProductID) { LocalFree(ProductInfo.pbProductID); } return(LsStatusToNtStatus(LsStatus)); } DWORD CConcurrentPolicy::GenerateHwidFromComputerName( HWID *hwid ) { MD5_CTX HashState; WCHAR wszName[MAX_COMPUTERNAME_LENGTH * 9]; // buffer we hope is big enough DWORD cbName = sizeof(wszName) / sizeof(TCHAR); BOOL fRet; // // get computer name // fRet = GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, wszName, &cbName); if (!fRet) { return GetLastError(); } // // generate the hash on the data. // MD5Init( &HashState ); MD5Update( &HashState, (LPBYTE)wszName, cbName ); MD5Final( &HashState ); memcpy((LPBYTE)hwid,HashState.digest,sizeof(HashState.digest)); // fill in the rest with characters from computer name lstrcpyn((LPWSTR)(((LPBYTE)hwid)+sizeof(HashState.digest)), wszName, (sizeof(HWID)-sizeof(HashState.digest))/sizeof(WCHAR)); return ERROR_SUCCESS; } // // Must have shared lock to call this // VOID CConcurrentPolicy::TryToAddLicenses( DWORD dwTotalWanted ) { NTSTATUS Status; BOOL fRetrievedAll; // Releasing g_rwLockLicense that other threads may be sharing, avoiding // possibility of deadlock if a thread calls RtlConvertSharedToExclusive // while holding g_csAddLicenses RtlReleaseResource(&g_rwLockLicense); RtlEnterCriticalSection(&g_csAddLicenses); // Reacquiring the shared lock that was released RtlAcquireResourceShared(&g_rwLockLicense,TRUE); if (WAIT_TIMEOUT == WaitForSingleObject(g_hOkayToAdd,0)) { // We're in waiting period RtlLeaveCriticalSection(&g_csAddLicenses); return; } if (g_lSessionMax >= (LONG) dwTotalWanted) { // we already have enough RtlLeaveCriticalSection(&g_csAddLicenses); return; } Status = GetLicenseFromLS(dwTotalWanted - g_lSessionMax, FALSE, // fIgnoreCurrentCount &fRetrievedAll); if ((Status != STATUS_SUCCESS) || (!fRetrievedAll)) { LARGE_INTEGER liWait; // wait before adding more liWait.QuadPart = (__int64) g_dwWaitTimeAdd * (-10 * 1000 * 1000); SetWaitableTimer(g_hOkayToAdd, &liWait, // pDueTime g_dwWaitTimeAdd * 1000, // lPeriod NULL, // pfnCompletionRoutine NULL, // lpArgToCompletionRoutine FALSE // fResume (from suspended) ); } RtlLeaveCriticalSection(&g_csAddLicenses); } // // Must have shared lock to call this // NTSTATUS CConcurrentPolicy::GetLicenseFromLS( LONG nNumToAdd, BOOL fIgnoreCurrentCount, BOOL *pfRetrievedAll ) { BOOL fHwidSet; BOOL fRet; DWORD cbLicense; DWORD cbSecretKey; DWORD cchComputerName = MAX_COMPUTERNAME_LENGTH + 1; DWORD dwNumLicensedProduct = 0; DWORD dwStatus; HANDLE hProtocol; HWID hwid; HWID hwidEncrypted; LICENSE_STATUS LsStatus; LICENSEREQUEST LicenseRequest; LONG CurrentCount; LSERVERINFO LServerInfo; ULONG cbLServerInfo; NTSTATUS Status; PBYTE pbLicense; PBYTE pbSecretKey; PLICENSEDPRODUCT pLicensedProduct; Product_Info ProductInfo; WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1]; TCHAR *pszLicenseServerName = LServerInfo.szServerName; DWORD dwNumLicensesRetrieved = 0; if (nNumToAdd < 0) { return STATUS_INVALID_PARAMETER; } if (nNumToAdd == 0) { if (NULL != pfRetrievedAll) *pfRetrievedAll = TRUE; return STATUS_SUCCESS; } if (NULL != pfRetrievedAll) *pfRetrievedAll = FALSE; // // These variables must be initialized here, or else any of the gotos // below may cause them to be used without initialization. // hProtocol = NULL; pbLicense = NULL; pbSecretKey = NULL; pLicensedProduct = NULL; Status = STATUS_SUCCESS; ZeroMemory(&ProductInfo, sizeof(Product_Info)); // // Get the current license count and HWID from the store. Failure is not // fatal. // LsStatus = GetLicenseFromStore( &CurrentCount, &hwidEncrypted, CURRENT_TERMINAL_SERVER_VERSION ); if (LsStatus == LICENSE_STATUS_OK) { fHwidSet = TRUE; if (fIgnoreCurrentCount) { CurrentCount = 0; } } else { CurrentCount = 0; fHwidSet = FALSE; } // // Initialize the product info. // LsStatus = InitProductInfo( &ProductInfo, PRODUCT_INFO_CONCURRENT_SKU_PRODUCT_ID ); if (LsStatus == LICENSE_STATUS_OK) { // // Initialize the license request structure. // ZeroMemory(&LicenseRequest, sizeof(LicenseRequest)); LicenseRequest.pProductInfo = &ProductInfo; LicenseRequest.dwLanguageID = GetSystemDefaultLCID(); LicenseRequest.dwPlatformID = CURRENT_TERMINAL_SERVER_VERSION; LicenseRequest.cbEncryptedHwid = sizeof(HWID); } else { goto done; } if (!fHwidSet) { // // No hardware ID yet - create one // dwStatus = GenerateHwidFromComputerName(&hwid); if (dwStatus == ERROR_SUCCESS) { LsStatus = LsCsp_EncryptHwid( &hwid, (PBYTE)&hwidEncrypted, &(LicenseRequest.cbEncryptedHwid) ); if (LsStatus == LICENSE_STATUS_OK) { fHwidSet = TRUE; } else { goto done; } } else { Status = STATUS_BUFFER_TOO_SMALL; goto done; } } LicenseRequest.pbEncryptedHwid = (PBYTE)&hwidEncrypted; // // get our computer name // fRet = GetComputerName(szComputerName, &cchComputerName); if (fRet) { LsStatus = CreateProtocolContext(NULL, &hProtocol); } else { Status = STATUS_UNSUCCESSFUL; goto done; } if (0 == CurrentCount) { // any license server will do pszLicenseServerName = NULL; } else { cbLServerInfo = sizeof(LSERVERINFO); LsStatus = LsCsp_RetrieveSecret( CONCURRENT_LSERVER_STORE, (LPBYTE)&LServerInfo, &cbLServerInfo ); if (LsStatus != LICENSE_STATUS_OK) { // no license server known; any will do pszLicenseServerName = NULL; } } cbLicense = 0; if (LsStatus == LICENSE_STATUS_OK) { // // NB: even if CurrentCount>0, license server will know about // existing licenses, and do a proper upgrade // dwNumLicensesRetrieved = nNumToAdd+CurrentCount; LsStatus = RequestNewLicense( hProtocol, pszLicenseServerName, &LicenseRequest, szComputerName, szComputerName, FALSE, // bAcceptTemporaryLicense TRUE, // bAcceptFewerLicenses &dwNumLicensesRetrieved, &cbLicense, &pbLicense ); if ((NULL != pfRetrievedAll) && (LsStatus == LICENSE_STATUS_OK) && ((LONG)dwNumLicensesRetrieved == nNumToAdd+CurrentCount)) { *pfRetrievedAll = TRUE; } } else { goto done; } if (LsStatus == LICENSE_STATUS_OK) { // // Get the secret key that is used to decode the license // cbSecretKey = 0; LicenseGetSecretKey(&cbSecretKey, NULL); pbSecretKey = (PBYTE)LocalAlloc(LPTR, cbSecretKey); if (pbSecretKey != NULL) { LsStatus = LicenseGetSecretKey(&cbSecretKey, pbSecretKey); } else { Status = STATUS_NO_MEMORY; goto done; } } else { goto done; } // // Decode license issued by hydra license server certificate engine. // __try { // // Check size of decoded licenses. // LsStatus = LSVerifyDecodeClientLicense( pbLicense, cbLicense, pbSecretKey, cbSecretKey, &dwNumLicensedProduct, NULL ); if (LsStatus == LICENSE_STATUS_OK) { pLicensedProduct = (PLICENSEDPRODUCT)LocalAlloc( LPTR, sizeof(LICENSEDPRODUCT) * dwNumLicensedProduct ); } else { goto done; } if (pLicensedProduct != NULL) { // // Decode the license. // LsStatus = LSVerifyDecodeClientLicense( pbLicense, cbLicense, pbSecretKey, cbSecretKey, &dwNumLicensedProduct, pLicensedProduct ); } else { Status = STATUS_NO_MEMORY; goto done; } } __except( EXCEPTION_EXECUTE_HANDLER ) { LsStatus = LICENSE_STATUS_CANNOT_DECODE_LICENSE; } if (LsStatus == LICENSE_STATUS_OK) { ReceivedPermanentLicense(); LServerInfo.cchServerName = lstrlen(pLicensedProduct->szIssuer); lstrcpynW( LServerInfo.szServerName, pLicensedProduct->szIssuer, sizeof(LServerInfo.szServerName) / sizeof(WCHAR) ); LServerInfo.ulSerialNumber = pLicensedProduct->ulSerialNumber; LServerInfo.ftNotAfter = pLicensedProduct->NotAfter; g_ftNotAfter = LServerInfo.ftNotAfter; LsStatus = LsCsp_StoreSecret( CONCURRENT_LSERVER_STORE, (LPBYTE)&LServerInfo, sizeof(LServerInfo) ); } else { goto done; } if (LsStatus == LICENSE_STATUS_OK) { // // Adjust the license count in the local LSA store. // LsStatus = SetLicenseInStore( dwNumLicensesRetrieved, hwidEncrypted, CURRENT_TERMINAL_SERVER_VERSION ); RtlConvertSharedToExclusive(&g_rwLockLicense); g_lSessionMax = dwNumLicensesRetrieved; RtlConvertExclusiveToShared(&g_rwLockLicense); } done: if (hProtocol != NULL) { DeleteProtocolContext(hProtocol); } if (pbLicense != NULL) { LocalFree(pbLicense); } if (pbSecretKey != NULL) { LocalFree(pbSecretKey); } if (pLicensedProduct != NULL) { for (DWORD dwCount = 0; dwCount < dwNumLicensedProduct; dwCount++) { LSFreeLicensedProduct(pLicensedProduct+dwCount); } } if (ProductInfo.pbCompanyName != NULL) { LocalFree(ProductInfo.pbCompanyName); } if (ProductInfo.pbProductID != NULL) { LocalFree(ProductInfo.pbProductID); } if (Status == STATUS_SUCCESS) { return(LsStatusToNtStatus(LsStatus)); } else { return(Status); } } LICENSE_STATUS GetLicenseFromStore( PLONG pLicenseCount, PHWID phwid, DWORD dwLicenseVer ) { CONCURRENTLICENSEINFO_V1 LicenseInfo; LICENSE_STATUS LsStatus; ULONG cbSecretLen; ASSERT(pLicenseCount != NULL); ASSERT(phwid != NULL); cbSecretLen = sizeof(CONCURRENTLICENSEINFO_V1); ZeroMemory(&LicenseInfo, cbSecretLen); // // Get the license count from the LSA secret // LsStatus = LsCsp_RetrieveSecret( CONCURRENT_LICENSE_STORE_LATEST_VERSION, (LPBYTE)&LicenseInfo, &cbSecretLen ); if ((LsStatus != LICENSE_STATUS_OK) || (cbSecretLen < sizeof(CONCURRENTLICENSEINFO_V1)) || (LicenseInfo.dwLicenseVer != dwLicenseVer)) { // // We determine that the license pack for this version is // not installed if we: // // (1) cannot retrieve the license info from the LSA secret // (2) cannot read at least the size of version 1 of the license info // structure. // (3) the license pack version is different from that requested. // LsStatus = LICENSE_STATUS_NO_LICENSE_ERROR; // We don't check for status from the following as we don't want to fail. LsCsp_StoreSecret( CONCURRENT_LICENSE_STORE_LATEST_VERSION, NULL, 0 ); LsCsp_StoreSecret( CONCURRENT_LSERVER_STORE, NULL, 0 ); } else { *pLicenseCount = LicenseInfo.lLicenseCount; *phwid = LicenseInfo.hwid; } return(LsStatus); } LICENSE_STATUS SetLicenseInStore( LONG LicenseCount, HWID hwid, DWORD dwLicenseVer ) { CONCURRENTLICENSEINFO_V1 LicenseInfo; LICENSE_STATUS LsStatus; // // verify that the license count to set is not negative. // ASSERT(LicenseCount >= 0); // // initialize the license information to store // LicenseInfo.dwStructVer = CONCURRENTLICENSEINFO_TYPE_V1; LicenseInfo.dwLicenseVer = dwLicenseVer; LicenseInfo.hwid = hwid; LicenseInfo.lLicenseCount = LicenseCount; // // store the new license count // LsStatus = LsCsp_StoreSecret( CONCURRENT_LICENSE_STORE_LATEST_VERSION, (LPBYTE)&LicenseInfo, sizeof(CONCURRENTLICENSEINFO_V1) ); return(LsStatus); } /* * Private Functions */ // // Must have shared lock to call this // NTSTATUS CConcurrentPolicy::CheckExpiration( ) { DWORD dwWait = TimeToSoftExpiration(); NTSTATUS Status = STATUS_SUCCESS; if (0 == dwWait) { // Soft expiration reached, time to renew Status = GetLicenseFromLS(g_lSessionMax, TRUE, // fIgnoreCurrentCount NULL); if ((STATUS_SUCCESS != Status) && (0 == TimeToHardExpiration())) { // Couldn't renew and we're past hard expiration LicenseLogEvent(EVENTLOG_ERROR_TYPE, EVENT_LICENSING_CONCURRENT_EXPIRED, 0, NULL ); RtlConvertSharedToExclusive(&g_rwLockLicense); g_lSessionMax = 0; RtlConvertExclusiveToShared(&g_rwLockLicense); } else { Status = STATUS_SUCCESS; } } return Status; } /* * Global Static Functions */ DWORD CConcurrentPolicy::TimeToSoftExpiration( ) { SYSTEMTIME stNow; FILETIME ftNow; ULARGE_INTEGER ullNotAfterLeeway; ULARGE_INTEGER ullNow; ULARGE_INTEGER ullDiff; DWORD dwDiff = 0; GetSystemTime(&stNow); SystemTimeToFileTime(&stNow,&ftNow); ullNow.LowPart = ftNow.dwLowDateTime; ullNow.HighPart = ftNow.dwHighDateTime; ullNotAfterLeeway.LowPart = g_ftNotAfter.dwLowDateTime; ullNotAfterLeeway.HighPart = g_ftNotAfter.dwHighDateTime; ullNotAfterLeeway.QuadPart -= (__int64) LC_POLICY_CONCURRENT_EXPIRATION_LEEWAY * 10 * 1000; if (ullNotAfterLeeway.QuadPart > ullNow.QuadPart) { ullDiff.QuadPart = ullNotAfterLeeway.QuadPart - ullNow.QuadPart; ullDiff.QuadPart /= (10 * 1000); if (ullDiff.HighPart == 0) { dwDiff = ullDiff.LowPart; } else { // too big, return max dwDiff = ULONG_MAX; } } return dwDiff; } DWORD CConcurrentPolicy::TimeToHardExpiration( ) { SYSTEMTIME stNow; FILETIME ftNow; ULARGE_INTEGER ullNotAfterLeeway; ULARGE_INTEGER ullNow; ULARGE_INTEGER ullDiff; DWORD dwDiff = 0; GetSystemTime(&stNow); SystemTimeToFileTime(&stNow,&ftNow); ullNow.LowPart = ftNow.dwLowDateTime; ullNow.HighPart = ftNow.dwHighDateTime; ullNotAfterLeeway.LowPart = g_ftNotAfter.dwLowDateTime; ullNotAfterLeeway.HighPart = g_ftNotAfter.dwHighDateTime; if (ullNotAfterLeeway.QuadPart > ullNow.QuadPart) { ullDiff.QuadPart = ullNotAfterLeeway.QuadPart - ullNow.QuadPart; ullDiff.QuadPart /= (10 * 1000); if (ullDiff.HighPart == 0) { dwDiff = ullDiff.LowPart; } else { // too big, return max dwDiff = ULONG_MAX; } } return dwDiff; } VOID CConcurrentPolicy::ReadLicensingParameters( ) { HKEY hKey = NULL; DWORD dwStatus = ERROR_SUCCESS; DWORD dwBuffer; DWORD cbBuffer; g_dwIncrement = LC_POLICY_CONCURRENT_LICENSE_COUNT_INCREMENT; g_dwWaitTimeAdd = LC_POLICY_CONCURRENT_WAIT_TIME_ADD; g_dwWaitTimeRemove = LC_POLICY_CONCURRENT_WAIT_TIME_REMOVE; dwStatus =RegOpenKeyEx( HKEY_LOCAL_MACHINE, LCREG_CONCURRENTKEY, 0, KEY_READ, &hKey ); if(dwStatus == ERROR_SUCCESS) { cbBuffer = sizeof(dwBuffer); dwStatus = RegQueryValueEx( hKey, LCREG_INCREMENT, NULL, NULL, (LPBYTE)&dwBuffer, &cbBuffer ); if (dwStatus == ERROR_SUCCESS) { g_dwIncrement = max(dwBuffer, 1); } cbBuffer = sizeof(dwBuffer); dwStatus = RegQueryValueEx( hKey, LCREG_WAIT_TIME_ADD, NULL, NULL, (LPBYTE)&dwBuffer, &cbBuffer ); if (dwStatus == ERROR_SUCCESS) { g_dwWaitTimeAdd = max(dwBuffer, 1); } cbBuffer = sizeof(dwBuffer); dwStatus = RegQueryValueEx( hKey, LCREG_WAIT_TIME_REMOVE, NULL, NULL, (LPBYTE)&dwBuffer, &cbBuffer ); if (dwStatus == ERROR_SUCCESS) { g_dwWaitTimeRemove = max(dwBuffer, 1); } RegCloseKey(hKey); } }