|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name : cal.cxx
Abstract: Control licensing policy enforcement for W3 server
Author:
Philippe Choquier (Phillich)
Environment: Win32 - User Mode
Project: Internet Server DLL
--*/
#include "w3p.hxx"
#include <stdio.h>
#include <limits.h>
#include <ole2.h>
#include <imd.h>
#include <mb.hxx>
#include <inetinfo.h>
#include <issched.hxx>
#include <acache.hxx>
#include <mbstring.h>
extern "C" { #include <ntlsapi.h>
#include <gntlsapi.h>
#include <llsapi.h>
}
#define NO_CAL_FOR_LOCAL_ACCESS
#define MULTI_CAL_PER_USER
typedef LS_STATUS_CODE (LS_API_ENTRY * IISPNT_LICENSE_REQUEST_A)( LPSTR ProductName, LPSTR Version, LS_HANDLE *LicenseHandle, NT_LS_DATA *NtData);
typedef NTSTATUS (NTAPI *IISPNT_LLS_PRODUCT_ENUM_W)( IN LLS_HANDLE Handle, IN DWORD Level, // Levels 0,1 supported
OUT LPBYTE* bufptr, IN DWORD prefmaxlen, OUT LPDWORD EntriesRead, OUT LPDWORD TotalEntries, IN OUT LPDWORD ResumeHandle );
typedef NTSTATUS (NTAPI *IISPNT_LLS_LOCAL_SERVICE_ENUM_W)( LLS_HANDLE Handle, DWORD Level, LPBYTE* bufptr, DWORD PrefMaxLen, LPDWORD EntriesRead, LPDWORD TotalEntries, LPDWORD ResumeHandle );
#define CAL_NB_PERIOD 5
#define BUFSTR_DEFAULT_SIZE 40
#define CAL_MAX_KEY_SIZE 256
#define IIS_LSAPI_NAME "IIS"
#define IIS_LSAPI_VERSION "5.0"
#define CAL_MIN_PERIOD (1000) // in ms
#define CAL_PRODUCT L"Windows NT Server"
#define CAL_KEYNAME L"FilePrint"
#define CAL_DEFAULT_MAX_LICENSES 10
typedef struct _CAL_ITERATOR { LIST_ENTRY* m_pNextEntry; LIST_ENTRY* m_pHeadEntry; } CAL_ITERATOR;
BOOL CalExemptAddRef( LPSTR ProductName, LPSTR Version, DWORD *LicenseHandle, NT_LS_DATA *NtData );
class CBufStr {
public: CBufStr() { m_pDynStr = 0; m_dwSize = 0; m_achFixedSize[0] = '\0'; } ~CBufStr() { if ( m_pDynStr ) LocalFree( m_pDynStr ); } BOOL Copy( LPSTR pS, DWORD dwL ); VOID Reset() { m_dwSize = 0 ; if ( m_pDynStr ) m_pDynStr[0] = '\0'; else m_achFixedSize[0]='\0'; } LPCSTR QueryStr() const { return m_pDynStr ? (LPCSTR)m_pDynStr : (LPCSTR)m_achFixedSize; } UINT QueryCCH() const { return m_dwSize; }
private: CHAR m_achFixedSize[BUFSTR_DEFAULT_SIZE]; DWORD m_dwMaxDynSize; DWORD m_dwSize; LPSTR m_pDynStr; } ;
class CCalEntry : public HT_ELEMENT {
public:
CCalEntry() { m_cRefs = 1; } ~CCalEntry( VOID) { if ( m_fAcquireLicenses ) AdjustLicences( 0 ); } LPCSTR QueryKey(VOID) const { return m_strKey.QueryStr(); } DWORD QueryKeyLen(VOID) const { return m_strKey.QueryCCH(); }
LONG Reference( VOID) { return InterlockedIncrement( &m_cRefs); } LONG Dereference( VOID) { return InterlockedDecrement( &m_cRefs); }
BOOL IsMatch( IN LPCSTR pszKey, IN DWORD cchKey) const { return cchKey == m_strKey.QueryCCH() ? !memcmp( pszKey, m_strKey.QueryStr(), cchKey) : FALSE; } VOID Print( VOID) const;
VOID IncrCnx() { if ( ++m_cCurrentCnx > m_acMaxCnxPerPeriod[m_iPeriod] ) m_acMaxCnxPerPeriod[m_iPeriod] = m_cCurrentCnx; } VOID DecrCnx() { InterlockedDecrement( &m_cCurrentCnx ); } BOOL Init( LPSTR pszKey, UINT cKey, UINT cPrefix, BOOL fSsl ); DWORD NeedLicenses(); BOOL AcquireLicenses( HANDLE hAccessToken, DWORD dwN ); BOOL AdvancePeriod(); VOID AdjustLicences( LONG cNew );
public: static VOID InitCache( VOID ); static VOID FreeCache( VOID ); static CCalEntry * Alloc( VOID ); static VOID Free( CCalEntry * pssc ); LIST_ENTRY m_ListEntry; LIST_ENTRY m_FreeListEntry; static LIST_ENTRY m_FreeListHead;
private: LONG m_cRefs; CBufStr m_strKey; UINT m_cKeyPrefix; // size of string before UserName in m_strKey
BOOL m_fAcquireLicenses; // FALSE if (SSL or Admin) and do not call LCM to get licenses
LONG m_acMaxCnxPerPeriod[CAL_NB_PERIOD]; UINT m_iPeriod; LONG m_cCurrentCnx; LONG m_cCurrentLicenses; #if defined(MULTI_CAL_PER_USER)
BUFFER m_bufLicenseHandles; #else
LS_HANDLE m_hLicenseHandle; #endif
DWORD m_dwExemptHandle; } ;
class CCalHashTable : public HASH_TABLE {
public: CCalHashTable( IN DWORD nBuckets, IN LPCSTR pszIdentifier, IN DWORD dwHashTableFlags ) : HASH_TABLE( nBuckets, pszIdentifier, dwHashTableFlags ) { INITIALIZE_CRITICAL_SECTION( &cs ); InitializeListHead( &m_ListHead ); } ~CCalHashTable() { CCalEntry* pE; while ( !IsListEmpty( &m_ListHead )) { pE = CONTAINING_RECORD( m_ListHead.Flink, CCalEntry, m_ListEntry );
RemoveEntryList( &pE->m_ListEntry );
//
// Make sure that the base class hash table object has the last remaining
// reference to this CCalEntry, so that when the destructor for the base class
// object is called, the CCalEntry object will get cleaned up
//
DBG_REQUIRE( pE->Dereference() == 1 );
} DeleteCriticalSection( &cs ); } VOID Lock() { EnterCriticalSection( &cs ); } VOID Unlock() { LeaveCriticalSection( &cs ); } BOOL Insert( CCalEntry* pE ) { if ( HASH_TABLE::Insert( (HT_ELEMENT*)pE, FALSE ) ) { InsertTailList( &m_ListHead, &pE->m_ListEntry ); return TRUE; } return FALSE; } BOOL Delete( CCalEntry* pE ) { RemoveEntryList( &pE->m_ListEntry ); return HASH_TABLE::Delete( (HT_ELEMENT*)pE ); } DWORD InitializeIter( CAL_ITERATOR* pI ) { pI->m_pHeadEntry = &m_ListHead; pI->m_pNextEntry = m_ListHead.Flink; return 0; } DWORD NextIter( CAL_ITERATOR* pI, CCalEntry** pE ) { if ( pI->m_pHeadEntry != pI->m_pNextEntry ) { *pE = CONTAINING_RECORD( pI->m_pNextEntry, CCalEntry, m_ListEntry ); pI->m_pNextEntry = pI->m_pNextEntry->Flink; return 0; } return ERROR_NO_MORE_ITEMS; } DWORD TerminateIter( CAL_ITERATOR* ) { return 0; } private: CRITICAL_SECTION cs; LIST_ENTRY m_ListHead; } ;
VOID WINAPI CalScavenger( LPVOID );
//
// Globals
//
CCalHashTable* phtAuth; CCalHashTable* phtSsl; DWORD g_dwAuthScavengerWorkItem = NULL; DWORD g_dwSslScavengerWorkItem = NULL; PSID psidAdmins; DWORD g_cSslLicences = 0; // current count of SSL licences
DWORD g_cMaxLicenses = 0; // max count of licenses
W3_SERVER_STATISTICS* g_pStats; DWORD g_CnxPerLicense; LIST_ENTRY CCalEntry::m_FreeListHead;
IISPNT_LICENSE_REQUEST_A pfnNtLicenseRequestA = NULL; PNT_LS_FREE_HANDLE pfnNtLSFreeHandle = NULL; HINSTANCE g_hLSAPI = NULL; PGNT_LICENSE_EXEMPTION_A pfnGntLicenseExemptionA = NULL; PGNT_LS_FREE_HANDLE pfnGntLsFreeHandle = NULL; PGNT_LICENSE_REQUEST_A pfnGntLicenseRequestA = NULL; HINSTANCE g_hGNTLSAPI = NULL; BOOL g_fEnableCal; BOOL g_fEnableMtsNotification; BOOL g_fUseMtsLicense;
////////////////
VOID CCalEntry::Print( ) const /*++
Routine Description:
Print content of entry for debugging purpose
Arguments:
None
Return Value:
Nothing
--*/ { }
DWORD InitializeCal( W3_SERVER_STATISTICS* pStats, DWORD dwVcPerLicense, DWORD dwAuthReserve, DWORD dwSslReserve ) /*++
Routine Description:
Initialize Cal operations
Arguments:
pStats - ptr to stat object to update for Cal counters
Return Value:
NT Status - 0 if no error otherwise error code
--*/ { SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; DWORD dwStatus = 0; DWORD dwAuthPeriod; DWORD dwSslPeriod; HKEY hkey; CCalEntry::InitCache(); phtAuth = NULL; phtSsl = NULL; g_dwAuthScavengerWorkItem = NULL; g_dwSslScavengerWorkItem = NULL; g_hLSAPI = NULL; psidAdmins = NULL; pfnGntLicenseExemptionA = NULL; pfnGntLsFreeHandle = NULL; g_hGNTLSAPI = NULL; g_fEnableCal = FALSE; g_fEnableMtsNotification = FALSE; g_fUseMtsLicense = FALSE;
//
// If not on server, returns status OK but all cal requests will return
// immediatly w/o license checking.
//
if ( !InetIsNtServer( IISGetPlatformType() ) ) { return 0; }
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, W3_PARAMETERS_KEY, 0, KEY_READ, &hkey ) == NO_ERROR ) { g_fEnableCal = !!ReadRegistryDword( hkey, "EnableCal", TRUE );
g_fEnableMtsNotification = !!ReadRegistryDword( hkey, "EnableMtsNotification", FALSE );
g_fUseMtsLicense = !!ReadRegistryDword( hkey, "UseMtsLicense", FALSE );
RegCloseKey( hkey ); }
if ( !g_fEnableCal ) { return 0; }
if ( g_hLSAPI = LoadLibrary( "NTLSAPI.DLL") ) { pfnNtLicenseRequestA = (IISPNT_LICENSE_REQUEST_A)GetProcAddress( g_hLSAPI, "NtLicenseRequestA" ); pfnNtLSFreeHandle = (PNT_LS_FREE_HANDLE)GetProcAddress( g_hLSAPI, "NtLSFreeHandle" ); if ( !pfnNtLicenseRequestA || !pfnNtLSFreeHandle ) { dwStatus = GetLastError(); } } else { dwStatus = GetLastError(); }
if ( dwStatus == 0 ) { // optional MTX ( Viper ) DLL
if ( g_fEnableMtsNotification && (g_hGNTLSAPI = LoadLibrary( "NTLSAPIX.DLL")) ) { pfnGntLicenseExemptionA = (PGNT_LICENSE_EXEMPTION_A)GetProcAddress( g_hGNTLSAPI, "NtLicenseExemptionA" ); pfnGntLicenseRequestA = (PGNT_LICENSE_REQUEST_A)GetProcAddress( g_hGNTLSAPI, "NtLicenseRequestA" ); pfnGntLsFreeHandle = (PGNT_LS_FREE_HANDLE)GetProcAddress( g_hGNTLSAPI, "NtLSFreeHandle" ); if ( !pfnGntLicenseExemptionA || !pfnGntLicenseRequestA || !pfnGntLsFreeHandle ) { pfnGntLicenseExemptionA = NULL; pfnGntLsFreeHandle = NULL; pfnGntLicenseRequestA = NULL; FreeLibrary( g_hGNTLSAPI ); g_hGNTLSAPI = NULL; }
if ( g_hGNTLSAPI && g_fUseMtsLicense ) { pfnNtLicenseRequestA = pfnGntLicenseRequestA; pfnNtLSFreeHandle = pfnGntLsFreeHandle; } } }
if ( dwStatus == 0 ) { phtAuth = new CCalHashTable( 253, "IIS AUTH CAL", 0 ); phtSsl = new CCalHashTable( 253, "IIS SSL CAL", 0 );
if ( !phtAuth || !phtSsl ) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; } }
if ( dwStatus == 0 ) { if ( (dwAuthPeriod = (1000 * dwAuthReserve) / (CAL_NB_PERIOD)) < CAL_MIN_PERIOD ) { dwAuthPeriod = CAL_MIN_PERIOD; }
if ( (dwSslPeriod = (1000 * dwSslReserve) / (CAL_NB_PERIOD)) < CAL_MIN_PERIOD ) { dwSslPeriod = CAL_MIN_PERIOD; } }
// initialize scavenger
if ( dwStatus == 0 ) { if ( !(g_dwAuthScavengerWorkItem = ScheduleWorkItem( CalScavenger, phtAuth, dwAuthPeriod, TRUE )) ) { dwStatus = GetLastError(); } }
if ( dwStatus == 0 ) { if ( !(g_dwSslScavengerWorkItem = ScheduleWorkItem( CalScavenger, phtSsl, dwSslPeriod, TRUE )) ) { dwStatus = GetLastError(); } }
if ( dwStatus == 0 ) { if ( !AllocateAndInitializeSid( &siaNt, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &psidAdmins ) ) { dwStatus = GetLastError(); } }
if ( dwStatus == 0 ) { // g_pStats = pStats;
g_pStats = g_pW3Stats; // input parameter is wrong
g_CnxPerLicense = dwVcPerLicense;
// get #licenses from lsapi
g_cMaxLicenses = CAL_DEFAULT_MAX_LICENSES; g_cSslLicences = 0;
//
// sample code for LLSAPI in net\svcdlls\lls\test\llscmd
//
LLS_HANDLE lsh; PLLS_CONNECT_INFO_0 pllsConnectInfo0; DWORD dwEntries; DWORD dwTotalEntries; DWORD dwResumeHandle = 0; HINSTANCE hLLS; PLLS_CONNECT_W pfnLlsConnectW = NULL; PLLS_CLOSE pfnLlsClose = NULL; PLLS_FREE_MEMORY pfnLlsFreeMemory = NULL; IISPNT_LLS_PRODUCT_ENUM_W pfnLlsProductEnumW = NULL; IISPNT_LLS_LOCAL_SERVICE_ENUM_W pfnLlsLocalServiceEnumW = NULL; LPBYTE pBuff;
if ( hLLS = LoadLibrary( "LLSRPC.DLL") ) { pfnLlsConnectW = (PLLS_CONNECT_W)GetProcAddress( hLLS, "LlsConnectW" ); pfnLlsProductEnumW = (IISPNT_LLS_PRODUCT_ENUM_W)GetProcAddress( hLLS, "LlsProductEnumW" ); pfnLlsLocalServiceEnumW = (IISPNT_LLS_LOCAL_SERVICE_ENUM_W)GetProcAddress( hLLS, "LlsLocalServiceEnumW" ); pfnLlsFreeMemory = (PLLS_FREE_MEMORY)GetProcAddress( hLLS, "LlsFreeMemory" ); pfnLlsClose = (PLLS_CLOSE)GetProcAddress( hLLS, "LlsClose" );
if ( pfnLlsConnectW && pfnLlsLocalServiceEnumW && pfnLlsFreeMemory && pfnLlsClose && pfnLlsConnectW( NULL, &lsh ) == STATUS_SUCCESS ) { if ( pfnLlsLocalServiceEnumW( lsh, 0, &pBuff, 4096, &dwEntries, &dwTotalEntries, &dwResumeHandle ) == STATUS_SUCCESS ) { PLLS_LOCAL_SERVICE_INFO_0 pllsLocalServiceInfo0; UINT i;
pllsLocalServiceInfo0 = (PLLS_LOCAL_SERVICE_INFO_0)pBuff;
for ( i = 0 ; i < dwEntries ; ++i, ++pllsLocalServiceInfo0 ) { if ( !memcmp( pllsLocalServiceInfo0->KeyName, CAL_KEYNAME, sizeof(CAL_KEYNAME)-sizeof(WCHAR) ) ) { if ( pllsLocalServiceInfo0->Mode == LLS_LICENSE_MODE_PER_SEAT ) { g_cMaxLicenses = INT_MAX - 1; } else { g_cMaxLicenses = pllsLocalServiceInfo0->ConcurrentLimit; } break; } }
if ( i == dwEntries ) { dwStatus = ERROR_MOD_NOT_FOUND; }
pfnLlsFreeMemory( pBuff ); }
pfnLlsClose( lsh ); }
FreeLibrary( hLLS ); } else { dwStatus = GetLastError(); } }
if ( dwStatus ) { TerminateCal(); }
return dwStatus; }
VOID TerminateCal( VOID ) /*++
Routine Description:
Terminate Cal operations
Arguments:
None
Return Value:
Nothing
--*/ { if ( g_dwAuthScavengerWorkItem != NULL ) { RemoveWorkItem( g_dwAuthScavengerWorkItem ); g_dwAuthScavengerWorkItem = NULL; }
if ( g_dwSslScavengerWorkItem != NULL ) { RemoveWorkItem( g_dwSslScavengerWorkItem ); g_dwSslScavengerWorkItem = NULL; }
CCalEntry::FreeCache();
if( psidAdmins != NULL ) { FreeSid( psidAdmins ); psidAdmins = NULL; }
if ( phtAuth != NULL ) { delete phtAuth; phtAuth = NULL; }
if ( phtSsl != NULL ) { delete phtSsl; phtSsl = NULL; }
if ( g_hLSAPI ) { FreeLibrary( g_hLSAPI ); }
if ( g_hGNTLSAPI ) { FreeLibrary( g_hGNTLSAPI ); } }
// can SetLastError( ERROR_ACCESS_DENIED )
BOOL CalConnect( LPSTR pszIpAddr, UINT cIpAddr, BOOL fSsl, LPSTR pszUserName, UINT cUserName, HANDLE hAccessToken, LPVOID* ppCtx ) /*++
Routine Description:
Grant or deny access to server. Return a license context to be destroyed by CalDisconnect
Arguments:
psIpAddr - IP address cIpAddr - length of IP address ( w/o '\0' ) fSsl - TRUE if SSL connection, otherwise FALSE pszUserName - user name, can be empty for SSL connection cUserName - length of pszUserName hAccessToken - impersonation access token for user, can be NULL for SSL connection ppCtx - updated with ptr to license context, to e destroyed by CalDisconnect
Return Value:
TRUE if acces granted, otherwise FALSE
--*/ { CHAR achKey[CAL_MAX_KEY_SIZE]; CCalEntry* pCal; DWORD dwL; BOOL fSt = TRUE; CCalHashTable* pht; CHAR * pchUser;
if ( g_hLSAPI == NULL || ( cIpAddr == sizeof("127.0.0.1")-1 && !memcmp( "127.0.0.1", pszIpAddr, cIpAddr ) ) ) { *ppCtx = NULL; return TRUE; }
// build key
memcpy( achKey, pszIpAddr, cIpAddr ); achKey[ cIpAddr++ ] = '|'; achKey[ cIpAddr++ ] = fSsl ? 'S' : ' '; achKey[ cIpAddr++ ] = '|';
//
// If there's a domain, strip it and just use the username - this
// allows users with the same name from different domains access
// to the same CAL but that's such a corner case we'll live with it
//
if ( pchUser = strchr( pszUserName, '\\' )) { pchUser++; cUserName -= DIFF( pchUser - pszUserName ); } else { pchUser = pszUserName; } memcpy( achKey + cIpAddr, pchUser, cUserName + 1 );
//
// Convert the name to lower case for later equivalency checking
// Note we don't handle the corner case of users with the same
// name but in different domains
//
IISstrlwr( (PUCHAR) achKey + cIpAddr );
pht = fSsl ? phtSsl : phtAuth;
// find or create entry
pht->Lock();
if ( !(pCal = (CCalEntry*)pht->Lookup( achKey, cIpAddr + cUserName )) ) { pCal = CCalEntry::Alloc();
if (pCal == NULL) { pht->Unlock(); return FALSE; }
pCal->Init( achKey, cIpAddr + cUserName, cIpAddr, fSsl ); if ( !pht->Insert( pCal ) ) { CCalEntry::Free( pCal ); pht->Unlock(); return FALSE; } } else { //
// CCalHashTable::Lookup() calls CCalEntry::Reference()
//
pCal->Dereference(); }
// check if license necessary
if ( dwL = pCal->NeedLicenses() ) { fSt = pCal->AcquireLicenses( hAccessToken, dwL ); } if ( fSt ) { pCal->IncrCnx(); *ppCtx = pCal; } else { *ppCtx = NULL; }
pht->Unlock();
return fSt; }
BOOL CalDisconnect( LPVOID pCtx ) /*++
Routine Description:
Destroy a license context created by CalConnect
Arguments:
pCtx - ptr to license context created by CalConnect
Return Value:
TRUE if success, otherwise FALSE
--*/ { if ( g_hLSAPI != NULL && pCtx ) { CCalEntry* pCal = (CCalEntry*)pCtx;
// decr #cnx
pCal->DecrCnx(); }
return TRUE; }
VOID WINAPI CalScavenger( LPVOID pV ) /*++
Routine Description:
Ages licence contexts, reclaiming licenses as no longer necessary
Arguments:
pV - ptr to CCalHashTable to process
Return Value:
Nothing
--*/ { CAL_ITERATOR it; CCalEntry* pCal; CCalHashTable* pH = (CCalHashTable*)pV;
// iterate through list of entries
pH->Lock();
// update # of licenses, free license & entry if necessary
if ( pH->InitializeIter( &it ) == 0 ) { while ( pH->NextIter( &it, &pCal) == 0 ) { if ( !pCal->AdvancePeriod() ) { pH->Delete( pCal ); CCalEntry::Free( pCal ); } }
pH->TerminateIter( &it ); }
pH->Unlock(); }
VOID CCalEntry::InitCache( VOID ) /*++
Routine Description:
Initialize allocation cache for CCalEntry
Arguments:
None
Return Value:
Nothing
--*/ { InitializeListHead( &m_FreeListHead ); }
VOID CCalEntry::FreeCache( VOID ) /*++
Routine Description:
Free all entries in allocation cache for CCalEntry
Arguments:
None
Return Value:
Nothing
--*/ { LIST_ENTRY * pEntry; CCalEntry * pssc;
while ( !IsListEmpty( &m_FreeListHead )) { pssc = CONTAINING_RECORD( m_FreeListHead.Flink, CCalEntry, m_FreeListEntry );
RemoveEntryList( &pssc->m_FreeListEntry );
delete pssc; } }
//
// Allocates or frees a context from cache, creating as necessary. The
// lock needs to be taken before calling these
//
CCalEntry * CCalEntry::Alloc( VOID ) /*++
Routine Description:
Allocate CCalEntry using allocation cache if not empty
Arguments:
None
Return Value:
CCalEntry or NULL if error
--*/ { CCalEntry * pssc = NULL;
if ( !IsListEmpty( &m_FreeListHead )) { LIST_ENTRY * pEntry = m_FreeListHead.Flink;
RemoveEntryList( pEntry );
pssc = CONTAINING_RECORD( pEntry, CCalEntry, m_FreeListEntry ); } else { pssc = new CCalEntry; }
if ( pssc ) { pssc->Reference(); }
return pssc; }
VOID CCalEntry::Free( CCalEntry * pssc ) /*++
Routine Description:
Put a CCalEntry on the allocation cache
Arguments:
pssc - CCalEntry to put on allocation cache
Return Value:
Nothing
--*/ { if ( pssc ) { InsertHeadList( &m_FreeListHead, &pssc->m_FreeListEntry ); } }
BOOL CBufStr::Copy( LPSTR pS, DWORD dwL ) /*++
Routine Description:
Copy a buffer to a buffered string
Arguments:
pS - ptr to string dwL - length of string ( w/o '\0' )
Return Value:
TRUE if success, otherwise FALSE
--*/ { if ( !m_pDynStr ) { if ( dwL >= BUFSTR_DEFAULT_SIZE ) { alloc_dyn: if ( !(m_pDynStr = (LPSTR)LocalAlloc( LMEM_FIXED, dwL + 1 )) ) { return FALSE; } memcpy( m_pDynStr, pS, dwL + 1 ); m_dwMaxDynSize = dwL; } else { memcpy( m_achFixedSize, pS, dwL + 1 ); } } else { if ( dwL > m_dwMaxDynSize ) { LocalFree( m_pDynStr ); goto alloc_dyn; } memcpy( m_pDynStr, pS, dwL + 1 ); }
m_dwSize = dwL; return TRUE; }
BOOL CCalEntry::Init( LPSTR pszKey, UINT cKey, UINT cPrefix, BOOL fSsl ) /*++
Routine Description:
Initialize a CCalEntry
Arguments:
pszKey - key for hash table insertion cKey - length of pszKey ( w/o '\0' ) cPrefix - # of chars in pszKey before user name fSsl - TRUE if SSL connection, otherwise FALSE
Return Value:
TRUE if success, otherwise FALSE
--*/ { m_cKeyPrefix = cPrefix; m_fAcquireLicenses = !fSsl; m_iPeriod = 0; m_cCurrentCnx = 0; m_cCurrentLicenses = 0; memset( m_acMaxCnxPerPeriod, '\0', sizeof(m_acMaxCnxPerPeriod) ); m_dwExemptHandle = INVALID_CAL_EXEMPT_HANDLE;
return m_strKey.Copy( pszKey, cKey ); }
DWORD CCalEntry::NeedLicenses( ) /*++
Routine Description:
Check if new connection on this entry will require a license
Arguments:
None
Return Value:
Number of licenses to acquire to accept new connection
--*/ { if ( m_fAcquireLicenses ) { #if defined(MULTI_CAL_PER_USER)
LONG cN = (m_cCurrentCnx+g_CnxPerLicense)/g_CnxPerLicense; return cN > m_cCurrentLicenses ? cN - m_cCurrentLicenses : 0; #else
return m_cCurrentLicenses ? 0 : 1; #endif
} else { LONG cN = (m_cCurrentCnx+g_CnxPerLicense)/g_CnxPerLicense; return cN > m_cCurrentLicenses ? cN - m_cCurrentLicenses : 0; } }
BOOL CCalEntry::AcquireLicenses( HANDLE hAccessToken, DWORD dwN ) /*++
Routine Description:
Acquire licenses for this entry
Arguments:
hAccessToken - access token associated with the user name for authenticated cnx can be NULL for SSL connection. dwN - # of licenses to acquire
Return Value:
TRUE if success, otherwise FALSE
--*/ { LS_HANDLE hLicense; LS_STATUS_CODE dwLsStatus = 0;
if ( m_fAcquireLicenses ) { dwN = 1;
NT_LS_DATA ls;
ls.DataType = NT_LS_USER_NAME; ls.Data = (LPVOID)(m_strKey.QueryStr() + m_cKeyPrefix); ls.IsAdmin = FALSE; ckagain: if ( ( dwLsStatus = pfnNtLicenseRequestA( IIS_LSAPI_NAME, IIS_LSAPI_VERSION, &hLicense, &ls ) ) ) { // check if admin
if ( ls.IsAdmin == FALSE && CheckTokenMembership( hAccessToken, psidAdmins, &ls.IsAdmin )) { if ( ls.IsAdmin ) { goto ckagain; } }
g_pStats->IncrTotalFailedCalAuth(); SetLastError( ERROR_ACCESS_DENIED );
return FALSE; } #if defined(MULTI_CAL_PER_USER)
if ( m_bufLicenseHandles.Resize( (m_cCurrentLicenses+dwN)*sizeof(LS_HANDLE) ) ) { *(LS_HANDLE*)((LPBYTE)m_bufLicenseHandles.QueryPtr()+m_cCurrentLicenses*sizeof(LS_HANDLE)) = hLicense; } else { if ( dwLsStatus = pfnNtLSFreeHandle( hLicense ) ) { DBGPRINTF((DBG_CONTEXT, "Status 0x%x returned from releasing license associated with CCalEntry 0x%p\n", dwLsStatus, this)); }
return FALSE; } #else
m_hLicenseHandle = hLicense; #endif
//
// If this is the 1st license for this entry,
// signal to MTX this user name is exempt of further licensing checks
// if we are using MTS license service then no need to call CalExemptAddRef
if ( !m_cCurrentLicenses && !g_fUseMtsLicense ) { CalExemptAddRef( IIS_LSAPI_NAME, IIS_LSAPI_VERSION, &m_dwExemptHandle, &ls ); }
m_cCurrentLicenses += dwN; while ( dwN-- ) { g_pStats->IncrCurrentCalAuth(); } } else { if ( g_cSslLicences + dwN > g_cMaxLicenses ) { g_pStats->IncrTotalFailedCalSsl(); SetLastError( ERROR_ACCESS_DENIED ); return FALSE; } g_cSslLicences += dwN; m_cCurrentLicenses += dwN; while ( dwN-- ) { g_pStats->IncrCurrentCalSsl(); } } return TRUE; }
BOOL CCalEntry::AdvancePeriod( ) /*++
Routine Description:
Adjust number of licenses by aging # of connections in cache
Arguments:
None
Return Value:
TRUE if entry still needed ( license to be held in cache ), FALSE if entry can be deleted.
--*/ { LONG iM = 0; UINT i;
for ( i = 0 ; i < CAL_NB_PERIOD ; ++i ) { if ( m_acMaxCnxPerPeriod[i] > iM ) { iM = m_acMaxCnxPerPeriod[i]; } } if ( ++m_iPeriod == CAL_NB_PERIOD ) { m_iPeriod = 0; } m_acMaxCnxPerPeriod[m_iPeriod] = m_cCurrentCnx;
if ( m_fAcquireLicenses ) { AdjustLicences( (iM+g_CnxPerLicense-1)/g_CnxPerLicense ); } else { LONG cNewLicenses = (iM+g_CnxPerLicense-1)/g_CnxPerLicense; // update global ssl count
while ( cNewLicenses < m_cCurrentLicenses ) { g_pStats->DecrCurrentCalSsl(); --m_cCurrentLicenses; --g_cSslLicences; } }
return iM; }
VOID CCalEntry::AdjustLicences( LONG cNew ) /*++
Routine Description:
Adjust number of licenses in this entry
Arguments:
cNew - new number of licenses
Return Value:
None
--*/ { LS_STATUS_CODE dwLSStatus;
#if defined(MULTI_CAL_PER_USER)
while ( m_cCurrentLicenses > cNew ) { if ( dwLSStatus = pfnNtLSFreeHandle( *(LS_HANDLE*)((LPBYTE)m_bufLicenseHandles.QueryPtr()+ (m_cCurrentLicenses-1)*sizeof(LS_HANDLE)) ) ) { DBGPRINTF((DBG_CONTEXT,"Status 0x%x returned from releasing license associated with CAL 0x%p\n", dwLSStatus, this)); }
g_pStats->DecrCurrentCalAuth(); --m_cCurrentLicenses; } #else
if ( !iM && m_cCurrentLicenses ) { if ( dwLSStatus = pfnNtLSFreeHandle( m_hLicenseHandle ) ) { DBGPRINTF((DBG_CONTEXT,"Status 0x%x returned from releasing license associated with CAL 0x%p\n", dwLSStatus, this)); }
g_pStats->DecrCurrentCalAuth(); m_cCurrentLicenses = 0; } #endif
//
// We don't hold any license, so if we called CalExemptAddRef
// then call release now.
//
if ( !m_cCurrentLicenses && m_dwExemptHandle != INVALID_CAL_EXEMPT_HANDLE ) { CalExemptRelease( m_dwExemptHandle ); m_dwExemptHandle = INVALID_CAL_EXEMPT_HANDLE; } }
BOOL CalExemptAddRef( LPSTR pszAcct, LPDWORD pdwHnd ) /*++
Routine Description:
Flag an account name as exempt of further license check for the MTX licensing package.
Arguments:
pszAcct - account name to be exempted pdwHnd - updated with handle to exempted context, to be released with CalExemptRelease
Return Value:
TRUE if success, otherwise FALSE LastError can be set to ERROR_MOD_NOT_FOUND if MTX licensing package not found
--*/ { NT_LS_DATA ls;
ls.DataType = NT_LS_USER_NAME; ls.Data = pszAcct; ls.IsAdmin = FALSE;
return CalExemptAddRef( IIS_LSAPI_NAME, IIS_LSAPI_VERSION, pdwHnd, &ls ); }
BOOL CalExemptAddRef( LPSTR ProductName, LPSTR Version, DWORD *LicenseHandle, NT_LS_DATA *NtData ) /*++
Routine Description:
Flag an account name as exempt of further license check for the MTX licensing package.
Arguments:
ProductName - product name for license usage tracking purpose Version - product version for license usage tracking purpose LicenseHandle - updated with handle to exempted context, to be released with CalExemptRelease NtData - ptr to license data ( user name )
Return Value:
TRUE if success, otherwise FALSE LastError can be set to ERROR_MOD_NOT_FOUND if MTX licensing package not found
--*/ { if ( pfnGntLicenseExemptionA ) { DWORD dwS = pfnGntLicenseExemptionA( ProductName, Version, (LS_HANDLE*)LicenseHandle, NtData ); if ( dwS ) { SetLastError( dwS ); return FALSE; }
return TRUE; }
SetLastError( ERROR_MOD_NOT_FOUND ); return FALSE; }
BOOL CalExemptRelease( DWORD dwHnd ) /*++
Routine Description:
Release a reference returned by CalExemptAddRef
Arguments:
dwHnd - handle to exempted context as returned by CalExemptAddRef
Return Value:
TRUE if success, otherwise FALSE LastError can be set to ERROR_MOD_NOT_FOUND if MTX licensing package not found
--*/ { if ( pfnGntLsFreeHandle ) { DWORD dwS = pfnGntLsFreeHandle( (LS_HANDLE)dwHnd ); if ( dwS ) { SetLastError( dwS ); return FALSE; }
return TRUE; }
SetLastError( ERROR_MOD_NOT_FOUND ); return FALSE; }
|