You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1964 lines
50 KiB
1964 lines
50 KiB
/*
|
|
* 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 <icaevent.h>
|
|
#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);
|
|
}
|
|
}
|