/*++ 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 #include #include #include #include #include #include #include #include extern "C" { #include #include #include } #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; }