Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1502 lines
34 KiB

/*++
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;
}