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

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name :
  4. cal.cxx
  5. Abstract:
  6. Control licensing policy enforcement for W3 server
  7. Author:
  8. Philippe Choquier (Phillich)
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Internet Server DLL
  13. --*/
  14. #include "w3p.hxx"
  15. #include <stdio.h>
  16. #include <limits.h>
  17. #include <ole2.h>
  18. #include <imd.h>
  19. #include <mb.hxx>
  20. #include <inetinfo.h>
  21. #include <issched.hxx>
  22. #include <acache.hxx>
  23. #include <mbstring.h>
  24. extern "C" {
  25. #include <ntlsapi.h>
  26. #include <gntlsapi.h>
  27. #include <llsapi.h>
  28. }
  29. #define NO_CAL_FOR_LOCAL_ACCESS
  30. #define MULTI_CAL_PER_USER
  31. typedef LS_STATUS_CODE
  32. (LS_API_ENTRY * IISPNT_LICENSE_REQUEST_A)(
  33. LPSTR ProductName,
  34. LPSTR Version,
  35. LS_HANDLE *LicenseHandle,
  36. NT_LS_DATA *NtData);
  37. typedef NTSTATUS
  38. (NTAPI *IISPNT_LLS_PRODUCT_ENUM_W)(
  39. IN LLS_HANDLE Handle,
  40. IN DWORD Level, // Levels 0,1 supported
  41. OUT LPBYTE* bufptr,
  42. IN DWORD prefmaxlen,
  43. OUT LPDWORD EntriesRead,
  44. OUT LPDWORD TotalEntries,
  45. IN OUT LPDWORD ResumeHandle
  46. );
  47. typedef NTSTATUS
  48. (NTAPI *IISPNT_LLS_LOCAL_SERVICE_ENUM_W)(
  49. LLS_HANDLE Handle,
  50. DWORD Level,
  51. LPBYTE* bufptr,
  52. DWORD PrefMaxLen,
  53. LPDWORD EntriesRead,
  54. LPDWORD TotalEntries,
  55. LPDWORD ResumeHandle
  56. );
  57. #define CAL_NB_PERIOD 5
  58. #define BUFSTR_DEFAULT_SIZE 40
  59. #define CAL_MAX_KEY_SIZE 256
  60. #define IIS_LSAPI_NAME "IIS"
  61. #define IIS_LSAPI_VERSION "5.0"
  62. #define CAL_MIN_PERIOD (1000) // in ms
  63. #define CAL_PRODUCT L"Windows NT Server"
  64. #define CAL_KEYNAME L"FilePrint"
  65. #define CAL_DEFAULT_MAX_LICENSES 10
  66. typedef struct _CAL_ITERATOR {
  67. LIST_ENTRY* m_pNextEntry;
  68. LIST_ENTRY* m_pHeadEntry;
  69. } CAL_ITERATOR;
  70. BOOL
  71. CalExemptAddRef(
  72. LPSTR ProductName,
  73. LPSTR Version,
  74. DWORD *LicenseHandle,
  75. NT_LS_DATA *NtData
  76. );
  77. class CBufStr {
  78. public:
  79. CBufStr() { m_pDynStr = 0; m_dwSize = 0; m_achFixedSize[0] = '\0'; }
  80. ~CBufStr() { if ( m_pDynStr ) LocalFree( m_pDynStr ); }
  81. BOOL Copy( LPSTR pS, DWORD dwL );
  82. VOID Reset() { m_dwSize = 0 ; if ( m_pDynStr ) m_pDynStr[0] = '\0'; else m_achFixedSize[0]='\0'; }
  83. LPCSTR QueryStr() const { return m_pDynStr ? (LPCSTR)m_pDynStr : (LPCSTR)m_achFixedSize; }
  84. UINT QueryCCH() const { return m_dwSize; }
  85. private:
  86. CHAR m_achFixedSize[BUFSTR_DEFAULT_SIZE];
  87. DWORD m_dwMaxDynSize;
  88. DWORD m_dwSize;
  89. LPSTR m_pDynStr;
  90. } ;
  91. class CCalEntry : public HT_ELEMENT {
  92. public:
  93. CCalEntry() { m_cRefs = 1; }
  94. ~CCalEntry( VOID) { if ( m_fAcquireLicenses ) AdjustLicences( 0 ); }
  95. LPCSTR QueryKey(VOID) const
  96. { return m_strKey.QueryStr(); }
  97. DWORD QueryKeyLen(VOID) const
  98. { return m_strKey.QueryCCH(); }
  99. LONG Reference( VOID)
  100. { return InterlockedIncrement( &m_cRefs); }
  101. LONG Dereference( VOID)
  102. { return InterlockedDecrement( &m_cRefs); }
  103. BOOL IsMatch( IN LPCSTR pszKey, IN DWORD cchKey) const
  104. { return cchKey == m_strKey.QueryCCH() ? !memcmp( pszKey, m_strKey.QueryStr(), cchKey) : FALSE; }
  105. VOID Print( VOID) const;
  106. VOID IncrCnx() { if ( ++m_cCurrentCnx > m_acMaxCnxPerPeriod[m_iPeriod] ) m_acMaxCnxPerPeriod[m_iPeriod] = m_cCurrentCnx; }
  107. VOID DecrCnx() { InterlockedDecrement( &m_cCurrentCnx ); }
  108. BOOL Init( LPSTR pszKey, UINT cKey, UINT cPrefix, BOOL fSsl );
  109. DWORD NeedLicenses();
  110. BOOL AcquireLicenses( HANDLE hAccessToken, DWORD dwN );
  111. BOOL AdvancePeriod();
  112. VOID AdjustLicences( LONG cNew );
  113. public:
  114. static VOID InitCache( VOID );
  115. static VOID FreeCache( VOID );
  116. static CCalEntry * Alloc( VOID );
  117. static VOID Free( CCalEntry * pssc );
  118. LIST_ENTRY m_ListEntry;
  119. LIST_ENTRY m_FreeListEntry;
  120. static LIST_ENTRY m_FreeListHead;
  121. private:
  122. LONG m_cRefs;
  123. CBufStr m_strKey;
  124. UINT m_cKeyPrefix; // size of string before UserName in m_strKey
  125. BOOL m_fAcquireLicenses; // FALSE if (SSL or Admin) and do not call LCM to get licenses
  126. LONG m_acMaxCnxPerPeriod[CAL_NB_PERIOD];
  127. UINT m_iPeriod;
  128. LONG m_cCurrentCnx;
  129. LONG m_cCurrentLicenses;
  130. #if defined(MULTI_CAL_PER_USER)
  131. BUFFER m_bufLicenseHandles;
  132. #else
  133. LS_HANDLE m_hLicenseHandle;
  134. #endif
  135. DWORD m_dwExemptHandle;
  136. } ;
  137. class CCalHashTable : public HASH_TABLE {
  138. public:
  139. CCalHashTable( IN DWORD nBuckets,
  140. IN LPCSTR pszIdentifier,
  141. IN DWORD dwHashTableFlags
  142. ) : HASH_TABLE( nBuckets, pszIdentifier, dwHashTableFlags )
  143. {
  144. INITIALIZE_CRITICAL_SECTION( &cs );
  145. InitializeListHead( &m_ListHead );
  146. }
  147. ~CCalHashTable()
  148. {
  149. CCalEntry* pE;
  150. while ( !IsListEmpty( &m_ListHead ))
  151. {
  152. pE = CONTAINING_RECORD( m_ListHead.Flink,
  153. CCalEntry,
  154. m_ListEntry );
  155. RemoveEntryList( &pE->m_ListEntry );
  156. //
  157. // Make sure that the base class hash table object has the last remaining
  158. // reference to this CCalEntry, so that when the destructor for the base class
  159. // object is called, the CCalEntry object will get cleaned up
  160. //
  161. DBG_REQUIRE( pE->Dereference() == 1 );
  162. }
  163. DeleteCriticalSection( &cs );
  164. }
  165. VOID Lock()
  166. {
  167. EnterCriticalSection( &cs );
  168. }
  169. VOID Unlock()
  170. {
  171. LeaveCriticalSection( &cs );
  172. }
  173. BOOL Insert( CCalEntry* pE )
  174. {
  175. if ( HASH_TABLE::Insert( (HT_ELEMENT*)pE, FALSE ) )
  176. {
  177. InsertTailList( &m_ListHead, &pE->m_ListEntry );
  178. return TRUE;
  179. }
  180. return FALSE;
  181. }
  182. BOOL Delete( CCalEntry* pE )
  183. {
  184. RemoveEntryList( &pE->m_ListEntry );
  185. return HASH_TABLE::Delete( (HT_ELEMENT*)pE );
  186. }
  187. DWORD InitializeIter( CAL_ITERATOR* pI )
  188. {
  189. pI->m_pHeadEntry = &m_ListHead;
  190. pI->m_pNextEntry = m_ListHead.Flink;
  191. return 0;
  192. }
  193. DWORD NextIter( CAL_ITERATOR* pI, CCalEntry** pE )
  194. {
  195. if ( pI->m_pHeadEntry != pI->m_pNextEntry )
  196. {
  197. *pE = CONTAINING_RECORD( pI->m_pNextEntry,
  198. CCalEntry,
  199. m_ListEntry );
  200. pI->m_pNextEntry = pI->m_pNextEntry->Flink;
  201. return 0;
  202. }
  203. return ERROR_NO_MORE_ITEMS;
  204. }
  205. DWORD TerminateIter( CAL_ITERATOR* )
  206. {
  207. return 0;
  208. }
  209. private:
  210. CRITICAL_SECTION cs;
  211. LIST_ENTRY m_ListHead;
  212. } ;
  213. VOID
  214. WINAPI
  215. CalScavenger(
  216. LPVOID
  217. );
  218. //
  219. // Globals
  220. //
  221. CCalHashTable* phtAuth;
  222. CCalHashTable* phtSsl;
  223. DWORD g_dwAuthScavengerWorkItem = NULL;
  224. DWORD g_dwSslScavengerWorkItem = NULL;
  225. PSID psidAdmins;
  226. DWORD g_cSslLicences = 0; // current count of SSL licences
  227. DWORD g_cMaxLicenses = 0; // max count of licenses
  228. W3_SERVER_STATISTICS* g_pStats;
  229. DWORD g_CnxPerLicense;
  230. LIST_ENTRY CCalEntry::m_FreeListHead;
  231. IISPNT_LICENSE_REQUEST_A pfnNtLicenseRequestA = NULL;
  232. PNT_LS_FREE_HANDLE pfnNtLSFreeHandle = NULL;
  233. HINSTANCE g_hLSAPI = NULL;
  234. PGNT_LICENSE_EXEMPTION_A pfnGntLicenseExemptionA = NULL;
  235. PGNT_LS_FREE_HANDLE pfnGntLsFreeHandle = NULL;
  236. PGNT_LICENSE_REQUEST_A pfnGntLicenseRequestA = NULL;
  237. HINSTANCE g_hGNTLSAPI = NULL;
  238. BOOL g_fEnableCal;
  239. BOOL g_fEnableMtsNotification;
  240. BOOL g_fUseMtsLicense;
  241. ////////////////
  242. VOID
  243. CCalEntry::Print(
  244. ) const
  245. /*++
  246. Routine Description:
  247. Print content of entry for debugging purpose
  248. Arguments:
  249. None
  250. Return Value:
  251. Nothing
  252. --*/
  253. {
  254. }
  255. DWORD
  256. InitializeCal(
  257. W3_SERVER_STATISTICS* pStats,
  258. DWORD dwVcPerLicense,
  259. DWORD dwAuthReserve,
  260. DWORD dwSslReserve
  261. )
  262. /*++
  263. Routine Description:
  264. Initialize Cal operations
  265. Arguments:
  266. pStats - ptr to stat object to update for Cal counters
  267. Return Value:
  268. NT Status - 0 if no error otherwise error code
  269. --*/
  270. {
  271. SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
  272. DWORD dwStatus = 0;
  273. DWORD dwAuthPeriod;
  274. DWORD dwSslPeriod;
  275. HKEY hkey;
  276. CCalEntry::InitCache();
  277. phtAuth = NULL;
  278. phtSsl = NULL;
  279. g_dwAuthScavengerWorkItem = NULL;
  280. g_dwSslScavengerWorkItem = NULL;
  281. g_hLSAPI = NULL;
  282. psidAdmins = NULL;
  283. pfnGntLicenseExemptionA = NULL;
  284. pfnGntLsFreeHandle = NULL;
  285. g_hGNTLSAPI = NULL;
  286. g_fEnableCal = FALSE;
  287. g_fEnableMtsNotification = FALSE;
  288. g_fUseMtsLicense = FALSE;
  289. //
  290. // If not on server, returns status OK but all cal requests will return
  291. // immediatly w/o license checking.
  292. //
  293. if ( !InetIsNtServer( IISGetPlatformType() ) )
  294. {
  295. return 0;
  296. }
  297. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  298. W3_PARAMETERS_KEY,
  299. 0,
  300. KEY_READ,
  301. &hkey ) == NO_ERROR )
  302. {
  303. g_fEnableCal = !!ReadRegistryDword( hkey,
  304. "EnableCal",
  305. TRUE );
  306. g_fEnableMtsNotification = !!ReadRegistryDword( hkey,
  307. "EnableMtsNotification",
  308. FALSE );
  309. g_fUseMtsLicense = !!ReadRegistryDword( hkey,
  310. "UseMtsLicense",
  311. FALSE );
  312. RegCloseKey( hkey );
  313. }
  314. if ( !g_fEnableCal )
  315. {
  316. return 0;
  317. }
  318. if ( g_hLSAPI = LoadLibrary( "NTLSAPI.DLL") )
  319. {
  320. pfnNtLicenseRequestA = (IISPNT_LICENSE_REQUEST_A)GetProcAddress( g_hLSAPI, "NtLicenseRequestA" );
  321. pfnNtLSFreeHandle = (PNT_LS_FREE_HANDLE)GetProcAddress( g_hLSAPI, "NtLSFreeHandle" );
  322. if ( !pfnNtLicenseRequestA ||
  323. !pfnNtLSFreeHandle )
  324. {
  325. dwStatus = GetLastError();
  326. }
  327. }
  328. else
  329. {
  330. dwStatus = GetLastError();
  331. }
  332. if ( dwStatus == 0 )
  333. {
  334. // optional MTX ( Viper ) DLL
  335. if ( g_fEnableMtsNotification &&
  336. (g_hGNTLSAPI = LoadLibrary( "NTLSAPIX.DLL")) )
  337. {
  338. pfnGntLicenseExemptionA = (PGNT_LICENSE_EXEMPTION_A)GetProcAddress( g_hGNTLSAPI, "NtLicenseExemptionA" );
  339. pfnGntLicenseRequestA = (PGNT_LICENSE_REQUEST_A)GetProcAddress( g_hGNTLSAPI, "NtLicenseRequestA" );
  340. pfnGntLsFreeHandle = (PGNT_LS_FREE_HANDLE)GetProcAddress( g_hGNTLSAPI, "NtLSFreeHandle" );
  341. if ( !pfnGntLicenseExemptionA ||
  342. !pfnGntLicenseRequestA ||
  343. !pfnGntLsFreeHandle )
  344. {
  345. pfnGntLicenseExemptionA = NULL;
  346. pfnGntLsFreeHandle = NULL;
  347. pfnGntLicenseRequestA = NULL;
  348. FreeLibrary( g_hGNTLSAPI );
  349. g_hGNTLSAPI = NULL;
  350. }
  351. if ( g_hGNTLSAPI && g_fUseMtsLicense )
  352. {
  353. pfnNtLicenseRequestA = pfnGntLicenseRequestA;
  354. pfnNtLSFreeHandle = pfnGntLsFreeHandle;
  355. }
  356. }
  357. }
  358. if ( dwStatus == 0 )
  359. {
  360. phtAuth = new CCalHashTable( 253, "IIS AUTH CAL", 0 );
  361. phtSsl = new CCalHashTable( 253, "IIS SSL CAL", 0 );
  362. if ( !phtAuth || !phtSsl )
  363. {
  364. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  365. }
  366. }
  367. if ( dwStatus == 0 )
  368. {
  369. if ( (dwAuthPeriod = (1000 * dwAuthReserve) / (CAL_NB_PERIOD)) < CAL_MIN_PERIOD )
  370. {
  371. dwAuthPeriod = CAL_MIN_PERIOD;
  372. }
  373. if ( (dwSslPeriod = (1000 * dwSslReserve) / (CAL_NB_PERIOD)) < CAL_MIN_PERIOD )
  374. {
  375. dwSslPeriod = CAL_MIN_PERIOD;
  376. }
  377. }
  378. // initialize scavenger
  379. if ( dwStatus == 0 )
  380. {
  381. if ( !(g_dwAuthScavengerWorkItem = ScheduleWorkItem( CalScavenger,
  382. phtAuth,
  383. dwAuthPeriod,
  384. TRUE )) )
  385. {
  386. dwStatus = GetLastError();
  387. }
  388. }
  389. if ( dwStatus == 0 )
  390. {
  391. if ( !(g_dwSslScavengerWorkItem = ScheduleWorkItem( CalScavenger,
  392. phtSsl,
  393. dwSslPeriod,
  394. TRUE )) )
  395. {
  396. dwStatus = GetLastError();
  397. }
  398. }
  399. if ( dwStatus == 0 )
  400. {
  401. if ( !AllocateAndInitializeSid( &siaNt,
  402. 2,
  403. SECURITY_BUILTIN_DOMAIN_RID,
  404. DOMAIN_ALIAS_RID_ADMINS,
  405. 0,0,0,0,0,0,
  406. &psidAdmins ) )
  407. {
  408. dwStatus = GetLastError();
  409. }
  410. }
  411. if ( dwStatus == 0 )
  412. {
  413. // g_pStats = pStats;
  414. g_pStats = g_pW3Stats; // input parameter is wrong
  415. g_CnxPerLicense = dwVcPerLicense;
  416. // get #licenses from lsapi
  417. g_cMaxLicenses = CAL_DEFAULT_MAX_LICENSES;
  418. g_cSslLicences = 0;
  419. //
  420. // sample code for LLSAPI in net\svcdlls\lls\test\llscmd
  421. //
  422. LLS_HANDLE lsh;
  423. PLLS_CONNECT_INFO_0 pllsConnectInfo0;
  424. DWORD dwEntries;
  425. DWORD dwTotalEntries;
  426. DWORD dwResumeHandle = 0;
  427. HINSTANCE hLLS;
  428. PLLS_CONNECT_W pfnLlsConnectW = NULL;
  429. PLLS_CLOSE pfnLlsClose = NULL;
  430. PLLS_FREE_MEMORY pfnLlsFreeMemory = NULL;
  431. IISPNT_LLS_PRODUCT_ENUM_W pfnLlsProductEnumW = NULL;
  432. IISPNT_LLS_LOCAL_SERVICE_ENUM_W pfnLlsLocalServiceEnumW = NULL;
  433. LPBYTE pBuff;
  434. if ( hLLS = LoadLibrary( "LLSRPC.DLL") )
  435. {
  436. pfnLlsConnectW = (PLLS_CONNECT_W)GetProcAddress( hLLS, "LlsConnectW" );
  437. pfnLlsProductEnumW = (IISPNT_LLS_PRODUCT_ENUM_W)GetProcAddress( hLLS, "LlsProductEnumW" );
  438. pfnLlsLocalServiceEnumW = (IISPNT_LLS_LOCAL_SERVICE_ENUM_W)GetProcAddress( hLLS, "LlsLocalServiceEnumW" );
  439. pfnLlsFreeMemory = (PLLS_FREE_MEMORY)GetProcAddress( hLLS, "LlsFreeMemory" );
  440. pfnLlsClose = (PLLS_CLOSE)GetProcAddress( hLLS, "LlsClose" );
  441. if ( pfnLlsConnectW &&
  442. pfnLlsLocalServiceEnumW &&
  443. pfnLlsFreeMemory &&
  444. pfnLlsClose &&
  445. pfnLlsConnectW( NULL,
  446. &lsh ) == STATUS_SUCCESS )
  447. {
  448. if ( pfnLlsLocalServiceEnumW( lsh,
  449. 0,
  450. &pBuff,
  451. 4096,
  452. &dwEntries,
  453. &dwTotalEntries,
  454. &dwResumeHandle ) == STATUS_SUCCESS )
  455. {
  456. PLLS_LOCAL_SERVICE_INFO_0 pllsLocalServiceInfo0;
  457. UINT i;
  458. pllsLocalServiceInfo0 = (PLLS_LOCAL_SERVICE_INFO_0)pBuff;
  459. for ( i = 0 ; i < dwEntries ; ++i, ++pllsLocalServiceInfo0 )
  460. {
  461. if ( !memcmp( pllsLocalServiceInfo0->KeyName,
  462. CAL_KEYNAME,
  463. sizeof(CAL_KEYNAME)-sizeof(WCHAR) ) )
  464. {
  465. if ( pllsLocalServiceInfo0->Mode == LLS_LICENSE_MODE_PER_SEAT )
  466. {
  467. g_cMaxLicenses = INT_MAX - 1;
  468. }
  469. else
  470. {
  471. g_cMaxLicenses = pllsLocalServiceInfo0->ConcurrentLimit;
  472. }
  473. break;
  474. }
  475. }
  476. if ( i == dwEntries )
  477. {
  478. dwStatus = ERROR_MOD_NOT_FOUND;
  479. }
  480. pfnLlsFreeMemory( pBuff );
  481. }
  482. pfnLlsClose( lsh );
  483. }
  484. FreeLibrary( hLLS );
  485. }
  486. else
  487. {
  488. dwStatus = GetLastError();
  489. }
  490. }
  491. if ( dwStatus )
  492. {
  493. TerminateCal();
  494. }
  495. return dwStatus;
  496. }
  497. VOID
  498. TerminateCal(
  499. VOID
  500. )
  501. /*++
  502. Routine Description:
  503. Terminate Cal operations
  504. Arguments:
  505. None
  506. Return Value:
  507. Nothing
  508. --*/
  509. {
  510. if ( g_dwAuthScavengerWorkItem != NULL )
  511. {
  512. RemoveWorkItem( g_dwAuthScavengerWorkItem );
  513. g_dwAuthScavengerWorkItem = NULL;
  514. }
  515. if ( g_dwSslScavengerWorkItem != NULL )
  516. {
  517. RemoveWorkItem( g_dwSslScavengerWorkItem );
  518. g_dwSslScavengerWorkItem = NULL;
  519. }
  520. CCalEntry::FreeCache();
  521. if( psidAdmins != NULL )
  522. {
  523. FreeSid( psidAdmins );
  524. psidAdmins = NULL;
  525. }
  526. if ( phtAuth != NULL )
  527. {
  528. delete phtAuth;
  529. phtAuth = NULL;
  530. }
  531. if ( phtSsl != NULL )
  532. {
  533. delete phtSsl;
  534. phtSsl = NULL;
  535. }
  536. if ( g_hLSAPI )
  537. {
  538. FreeLibrary( g_hLSAPI );
  539. }
  540. if ( g_hGNTLSAPI )
  541. {
  542. FreeLibrary( g_hGNTLSAPI );
  543. }
  544. }
  545. // can SetLastError( ERROR_ACCESS_DENIED )
  546. BOOL
  547. CalConnect(
  548. LPSTR pszIpAddr,
  549. UINT cIpAddr,
  550. BOOL fSsl,
  551. LPSTR pszUserName,
  552. UINT cUserName,
  553. HANDLE hAccessToken,
  554. LPVOID* ppCtx
  555. )
  556. /*++
  557. Routine Description:
  558. Grant or deny access to server.
  559. Return a license context to be destroyed by CalDisconnect
  560. Arguments:
  561. psIpAddr - IP address
  562. cIpAddr - length of IP address ( w/o '\0' )
  563. fSsl - TRUE if SSL connection, otherwise FALSE
  564. pszUserName - user name, can be empty for SSL connection
  565. cUserName - length of pszUserName
  566. hAccessToken - impersonation access token for user, can be NULL for SSL connection
  567. ppCtx - updated with ptr to license context, to e destroyed by CalDisconnect
  568. Return Value:
  569. TRUE if acces granted, otherwise FALSE
  570. --*/
  571. {
  572. CHAR achKey[CAL_MAX_KEY_SIZE];
  573. CCalEntry* pCal;
  574. DWORD dwL;
  575. BOOL fSt = TRUE;
  576. CCalHashTable* pht;
  577. CHAR * pchUser;
  578. if ( g_hLSAPI == NULL
  579. || ( cIpAddr == sizeof("127.0.0.1")-1 &&
  580. !memcmp( "127.0.0.1", pszIpAddr, cIpAddr ) )
  581. )
  582. {
  583. *ppCtx = NULL;
  584. return TRUE;
  585. }
  586. // build key
  587. memcpy( achKey, pszIpAddr, cIpAddr );
  588. achKey[ cIpAddr++ ] = '|';
  589. achKey[ cIpAddr++ ] = fSsl ? 'S' : ' ';
  590. achKey[ cIpAddr++ ] = '|';
  591. //
  592. // If there's a domain, strip it and just use the username - this
  593. // allows users with the same name from different domains access
  594. // to the same CAL but that's such a corner case we'll live with it
  595. //
  596. if ( pchUser = strchr( pszUserName, '\\' ))
  597. {
  598. pchUser++;
  599. cUserName -= DIFF( pchUser - pszUserName );
  600. }
  601. else
  602. {
  603. pchUser = pszUserName;
  604. }
  605. memcpy( achKey + cIpAddr, pchUser, cUserName + 1 );
  606. //
  607. // Convert the name to lower case for later equivalency checking
  608. // Note we don't handle the corner case of users with the same
  609. // name but in different domains
  610. //
  611. IISstrlwr( (PUCHAR) achKey + cIpAddr );
  612. pht = fSsl ? phtSsl : phtAuth;
  613. // find or create entry
  614. pht->Lock();
  615. if ( !(pCal = (CCalEntry*)pht->Lookup( achKey, cIpAddr + cUserName )) )
  616. {
  617. pCal = CCalEntry::Alloc();
  618. if (pCal == NULL)
  619. {
  620. pht->Unlock();
  621. return FALSE;
  622. }
  623. pCal->Init( achKey, cIpAddr + cUserName, cIpAddr, fSsl );
  624. if ( !pht->Insert( pCal ) )
  625. {
  626. CCalEntry::Free( pCal );
  627. pht->Unlock();
  628. return FALSE;
  629. }
  630. }
  631. else
  632. {
  633. //
  634. // CCalHashTable::Lookup() calls CCalEntry::Reference()
  635. //
  636. pCal->Dereference();
  637. }
  638. // check if license necessary
  639. if ( dwL = pCal->NeedLicenses() )
  640. {
  641. fSt = pCal->AcquireLicenses( hAccessToken, dwL );
  642. }
  643. if ( fSt )
  644. {
  645. pCal->IncrCnx();
  646. *ppCtx = pCal;
  647. }
  648. else
  649. {
  650. *ppCtx = NULL;
  651. }
  652. pht->Unlock();
  653. return fSt;
  654. }
  655. BOOL
  656. CalDisconnect(
  657. LPVOID pCtx
  658. )
  659. /*++
  660. Routine Description:
  661. Destroy a license context created by CalConnect
  662. Arguments:
  663. pCtx - ptr to license context created by CalConnect
  664. Return Value:
  665. TRUE if success, otherwise FALSE
  666. --*/
  667. {
  668. if ( g_hLSAPI != NULL && pCtx )
  669. {
  670. CCalEntry* pCal = (CCalEntry*)pCtx;
  671. // decr #cnx
  672. pCal->DecrCnx();
  673. }
  674. return TRUE;
  675. }
  676. VOID
  677. WINAPI
  678. CalScavenger(
  679. LPVOID pV
  680. )
  681. /*++
  682. Routine Description:
  683. Ages licence contexts, reclaiming licenses as no longer necessary
  684. Arguments:
  685. pV - ptr to CCalHashTable to process
  686. Return Value:
  687. Nothing
  688. --*/
  689. {
  690. CAL_ITERATOR it;
  691. CCalEntry* pCal;
  692. CCalHashTable* pH = (CCalHashTable*)pV;
  693. // iterate through list of entries
  694. pH->Lock();
  695. // update # of licenses, free license & entry if necessary
  696. if ( pH->InitializeIter( &it ) == 0 )
  697. {
  698. while ( pH->NextIter( &it, &pCal) == 0 )
  699. {
  700. if ( !pCal->AdvancePeriod() )
  701. {
  702. pH->Delete( pCal );
  703. CCalEntry::Free( pCal );
  704. }
  705. }
  706. pH->TerminateIter( &it );
  707. }
  708. pH->Unlock();
  709. }
  710. VOID
  711. CCalEntry::InitCache(
  712. VOID
  713. )
  714. /*++
  715. Routine Description:
  716. Initialize allocation cache for CCalEntry
  717. Arguments:
  718. None
  719. Return Value:
  720. Nothing
  721. --*/
  722. {
  723. InitializeListHead( &m_FreeListHead );
  724. }
  725. VOID
  726. CCalEntry::FreeCache(
  727. VOID
  728. )
  729. /*++
  730. Routine Description:
  731. Free all entries in allocation cache for CCalEntry
  732. Arguments:
  733. None
  734. Return Value:
  735. Nothing
  736. --*/
  737. {
  738. LIST_ENTRY * pEntry;
  739. CCalEntry * pssc;
  740. while ( !IsListEmpty( &m_FreeListHead ))
  741. {
  742. pssc = CONTAINING_RECORD( m_FreeListHead.Flink,
  743. CCalEntry,
  744. m_FreeListEntry );
  745. RemoveEntryList( &pssc->m_FreeListEntry );
  746. delete pssc;
  747. }
  748. }
  749. //
  750. // Allocates or frees a context from cache, creating as necessary. The
  751. // lock needs to be taken before calling these
  752. //
  753. CCalEntry *
  754. CCalEntry::Alloc(
  755. VOID
  756. )
  757. /*++
  758. Routine Description:
  759. Allocate CCalEntry using allocation cache if not empty
  760. Arguments:
  761. None
  762. Return Value:
  763. CCalEntry or NULL if error
  764. --*/
  765. {
  766. CCalEntry * pssc = NULL;
  767. if ( !IsListEmpty( &m_FreeListHead ))
  768. {
  769. LIST_ENTRY * pEntry = m_FreeListHead.Flink;
  770. RemoveEntryList( pEntry );
  771. pssc = CONTAINING_RECORD( pEntry, CCalEntry, m_FreeListEntry );
  772. }
  773. else
  774. {
  775. pssc = new CCalEntry;
  776. }
  777. if ( pssc )
  778. {
  779. pssc->Reference();
  780. }
  781. return pssc;
  782. }
  783. VOID
  784. CCalEntry::Free(
  785. CCalEntry * pssc
  786. )
  787. /*++
  788. Routine Description:
  789. Put a CCalEntry on the allocation cache
  790. Arguments:
  791. pssc - CCalEntry to put on allocation cache
  792. Return Value:
  793. Nothing
  794. --*/
  795. {
  796. if ( pssc )
  797. {
  798. InsertHeadList( &m_FreeListHead,
  799. &pssc->m_FreeListEntry );
  800. }
  801. }
  802. BOOL
  803. CBufStr::Copy(
  804. LPSTR pS,
  805. DWORD dwL
  806. )
  807. /*++
  808. Routine Description:
  809. Copy a buffer to a buffered string
  810. Arguments:
  811. pS - ptr to string
  812. dwL - length of string ( w/o '\0' )
  813. Return Value:
  814. TRUE if success, otherwise FALSE
  815. --*/
  816. {
  817. if ( !m_pDynStr )
  818. {
  819. if ( dwL >= BUFSTR_DEFAULT_SIZE )
  820. {
  821. alloc_dyn:
  822. if ( !(m_pDynStr = (LPSTR)LocalAlloc( LMEM_FIXED, dwL + 1 )) )
  823. {
  824. return FALSE;
  825. }
  826. memcpy( m_pDynStr, pS, dwL + 1 );
  827. m_dwMaxDynSize = dwL;
  828. }
  829. else
  830. {
  831. memcpy( m_achFixedSize, pS, dwL + 1 );
  832. }
  833. }
  834. else
  835. {
  836. if ( dwL > m_dwMaxDynSize )
  837. {
  838. LocalFree( m_pDynStr );
  839. goto alloc_dyn;
  840. }
  841. memcpy( m_pDynStr, pS, dwL + 1 );
  842. }
  843. m_dwSize = dwL;
  844. return TRUE;
  845. }
  846. BOOL
  847. CCalEntry::Init(
  848. LPSTR pszKey,
  849. UINT cKey,
  850. UINT cPrefix,
  851. BOOL fSsl
  852. )
  853. /*++
  854. Routine Description:
  855. Initialize a CCalEntry
  856. Arguments:
  857. pszKey - key for hash table insertion
  858. cKey - length of pszKey ( w/o '\0' )
  859. cPrefix - # of chars in pszKey before user name
  860. fSsl - TRUE if SSL connection, otherwise FALSE
  861. Return Value:
  862. TRUE if success, otherwise FALSE
  863. --*/
  864. {
  865. m_cKeyPrefix = cPrefix;
  866. m_fAcquireLicenses = !fSsl;
  867. m_iPeriod = 0;
  868. m_cCurrentCnx = 0;
  869. m_cCurrentLicenses = 0;
  870. memset( m_acMaxCnxPerPeriod, '\0', sizeof(m_acMaxCnxPerPeriod) );
  871. m_dwExemptHandle = INVALID_CAL_EXEMPT_HANDLE;
  872. return m_strKey.Copy( pszKey, cKey );
  873. }
  874. DWORD
  875. CCalEntry::NeedLicenses(
  876. )
  877. /*++
  878. Routine Description:
  879. Check if new connection on this entry will require a license
  880. Arguments:
  881. None
  882. Return Value:
  883. Number of licenses to acquire to accept new connection
  884. --*/
  885. {
  886. if ( m_fAcquireLicenses )
  887. {
  888. #if defined(MULTI_CAL_PER_USER)
  889. LONG cN = (m_cCurrentCnx+g_CnxPerLicense)/g_CnxPerLicense;
  890. return cN > m_cCurrentLicenses ? cN - m_cCurrentLicenses : 0;
  891. #else
  892. return m_cCurrentLicenses ? 0 : 1;
  893. #endif
  894. }
  895. else
  896. {
  897. LONG cN = (m_cCurrentCnx+g_CnxPerLicense)/g_CnxPerLicense;
  898. return cN > m_cCurrentLicenses ? cN - m_cCurrentLicenses : 0;
  899. }
  900. }
  901. BOOL
  902. CCalEntry::AcquireLicenses(
  903. HANDLE hAccessToken,
  904. DWORD dwN
  905. )
  906. /*++
  907. Routine Description:
  908. Acquire licenses for this entry
  909. Arguments:
  910. hAccessToken - access token associated with the user name for authenticated cnx
  911. can be NULL for SSL connection.
  912. dwN - # of licenses to acquire
  913. Return Value:
  914. TRUE if success, otherwise FALSE
  915. --*/
  916. {
  917. LS_HANDLE hLicense;
  918. LS_STATUS_CODE dwLsStatus = 0;
  919. if ( m_fAcquireLicenses )
  920. {
  921. dwN = 1;
  922. NT_LS_DATA ls;
  923. ls.DataType = NT_LS_USER_NAME;
  924. ls.Data = (LPVOID)(m_strKey.QueryStr() + m_cKeyPrefix);
  925. ls.IsAdmin = FALSE;
  926. ckagain:
  927. if ( ( dwLsStatus = pfnNtLicenseRequestA( IIS_LSAPI_NAME,
  928. IIS_LSAPI_VERSION,
  929. &hLicense,
  930. &ls ) ) )
  931. {
  932. // check if admin
  933. if ( ls.IsAdmin == FALSE &&
  934. CheckTokenMembership( hAccessToken,
  935. psidAdmins,
  936. &ls.IsAdmin ))
  937. {
  938. if ( ls.IsAdmin )
  939. {
  940. goto ckagain;
  941. }
  942. }
  943. g_pStats->IncrTotalFailedCalAuth();
  944. SetLastError( ERROR_ACCESS_DENIED );
  945. return FALSE;
  946. }
  947. #if defined(MULTI_CAL_PER_USER)
  948. if ( m_bufLicenseHandles.Resize( (m_cCurrentLicenses+dwN)*sizeof(LS_HANDLE) ) )
  949. {
  950. *(LS_HANDLE*)((LPBYTE)m_bufLicenseHandles.QueryPtr()+m_cCurrentLicenses*sizeof(LS_HANDLE))
  951. = hLicense;
  952. }
  953. else
  954. {
  955. if ( dwLsStatus = pfnNtLSFreeHandle( hLicense ) )
  956. {
  957. DBGPRINTF((DBG_CONTEXT,
  958. "Status 0x%x returned from releasing license associated with CCalEntry 0x%p\n", dwLsStatus, this));
  959. }
  960. return FALSE;
  961. }
  962. #else
  963. m_hLicenseHandle = hLicense;
  964. #endif
  965. //
  966. // If this is the 1st license for this entry,
  967. // signal to MTX this user name is exempt of further licensing checks
  968. // if we are using MTS license service then no need to call CalExemptAddRef
  969. if ( !m_cCurrentLicenses && !g_fUseMtsLicense )
  970. {
  971. CalExemptAddRef( IIS_LSAPI_NAME,
  972. IIS_LSAPI_VERSION,
  973. &m_dwExemptHandle,
  974. &ls );
  975. }
  976. m_cCurrentLicenses += dwN;
  977. while ( dwN-- )
  978. {
  979. g_pStats->IncrCurrentCalAuth();
  980. }
  981. }
  982. else
  983. {
  984. if ( g_cSslLicences + dwN > g_cMaxLicenses )
  985. {
  986. g_pStats->IncrTotalFailedCalSsl();
  987. SetLastError( ERROR_ACCESS_DENIED );
  988. return FALSE;
  989. }
  990. g_cSslLicences += dwN;
  991. m_cCurrentLicenses += dwN;
  992. while ( dwN-- )
  993. {
  994. g_pStats->IncrCurrentCalSsl();
  995. }
  996. }
  997. return TRUE;
  998. }
  999. BOOL
  1000. CCalEntry::AdvancePeriod(
  1001. )
  1002. /*++
  1003. Routine Description:
  1004. Adjust number of licenses by aging # of connections in cache
  1005. Arguments:
  1006. None
  1007. Return Value:
  1008. TRUE if entry still needed ( license to be held in cache ),
  1009. FALSE if entry can be deleted.
  1010. --*/
  1011. {
  1012. LONG iM = 0;
  1013. UINT i;
  1014. for ( i = 0 ; i < CAL_NB_PERIOD ; ++i )
  1015. {
  1016. if ( m_acMaxCnxPerPeriod[i] > iM )
  1017. {
  1018. iM = m_acMaxCnxPerPeriod[i];
  1019. }
  1020. }
  1021. if ( ++m_iPeriod == CAL_NB_PERIOD )
  1022. {
  1023. m_iPeriod = 0;
  1024. }
  1025. m_acMaxCnxPerPeriod[m_iPeriod] = m_cCurrentCnx;
  1026. if ( m_fAcquireLicenses )
  1027. {
  1028. AdjustLicences( (iM+g_CnxPerLicense-1)/g_CnxPerLicense );
  1029. }
  1030. else
  1031. {
  1032. LONG cNewLicenses = (iM+g_CnxPerLicense-1)/g_CnxPerLicense;
  1033. // update global ssl count
  1034. while ( cNewLicenses < m_cCurrentLicenses )
  1035. {
  1036. g_pStats->DecrCurrentCalSsl();
  1037. --m_cCurrentLicenses;
  1038. --g_cSslLicences;
  1039. }
  1040. }
  1041. return iM;
  1042. }
  1043. VOID
  1044. CCalEntry::AdjustLicences(
  1045. LONG cNew
  1046. )
  1047. /*++
  1048. Routine Description:
  1049. Adjust number of licenses in this entry
  1050. Arguments:
  1051. cNew - new number of licenses
  1052. Return Value:
  1053. None
  1054. --*/
  1055. {
  1056. LS_STATUS_CODE dwLSStatus;
  1057. #if defined(MULTI_CAL_PER_USER)
  1058. while ( m_cCurrentLicenses > cNew )
  1059. {
  1060. if ( dwLSStatus = pfnNtLSFreeHandle(
  1061. *(LS_HANDLE*)((LPBYTE)m_bufLicenseHandles.QueryPtr()+
  1062. (m_cCurrentLicenses-1)*sizeof(LS_HANDLE)) ) )
  1063. {
  1064. DBGPRINTF((DBG_CONTEXT,"Status 0x%x returned from releasing license associated with CAL 0x%p\n", dwLSStatus, this));
  1065. }
  1066. g_pStats->DecrCurrentCalAuth();
  1067. --m_cCurrentLicenses;
  1068. }
  1069. #else
  1070. if ( !iM && m_cCurrentLicenses )
  1071. {
  1072. if ( dwLSStatus = pfnNtLSFreeHandle( m_hLicenseHandle ) )
  1073. {
  1074. DBGPRINTF((DBG_CONTEXT,"Status 0x%x returned from releasing license associated with CAL 0x%p\n", dwLSStatus, this));
  1075. }
  1076. g_pStats->DecrCurrentCalAuth();
  1077. m_cCurrentLicenses = 0;
  1078. }
  1079. #endif
  1080. //
  1081. // We don't hold any license, so if we called CalExemptAddRef
  1082. // then call release now.
  1083. //
  1084. if ( !m_cCurrentLicenses &&
  1085. m_dwExemptHandle != INVALID_CAL_EXEMPT_HANDLE )
  1086. {
  1087. CalExemptRelease( m_dwExemptHandle );
  1088. m_dwExemptHandle = INVALID_CAL_EXEMPT_HANDLE;
  1089. }
  1090. }
  1091. BOOL
  1092. CalExemptAddRef(
  1093. LPSTR pszAcct,
  1094. LPDWORD pdwHnd
  1095. )
  1096. /*++
  1097. Routine Description:
  1098. Flag an account name as exempt of further license check
  1099. for the MTX licensing package.
  1100. Arguments:
  1101. pszAcct - account name to be exempted
  1102. pdwHnd - updated with handle to exempted context, to be released with CalExemptRelease
  1103. Return Value:
  1104. TRUE if success, otherwise FALSE
  1105. LastError can be set to ERROR_MOD_NOT_FOUND if MTX licensing package not found
  1106. --*/
  1107. {
  1108. NT_LS_DATA ls;
  1109. ls.DataType = NT_LS_USER_NAME;
  1110. ls.Data = pszAcct;
  1111. ls.IsAdmin = FALSE;
  1112. return CalExemptAddRef( IIS_LSAPI_NAME, IIS_LSAPI_VERSION, pdwHnd, &ls );
  1113. }
  1114. BOOL
  1115. CalExemptAddRef(
  1116. LPSTR ProductName,
  1117. LPSTR Version,
  1118. DWORD *LicenseHandle,
  1119. NT_LS_DATA *NtData
  1120. )
  1121. /*++
  1122. Routine Description:
  1123. Flag an account name as exempt of further license check
  1124. for the MTX licensing package.
  1125. Arguments:
  1126. ProductName - product name for license usage tracking purpose
  1127. Version - product version for license usage tracking purpose
  1128. LicenseHandle - updated with handle to exempted context, to be released with CalExemptRelease
  1129. NtData - ptr to license data ( user name )
  1130. Return Value:
  1131. TRUE if success, otherwise FALSE
  1132. LastError can be set to ERROR_MOD_NOT_FOUND if MTX licensing package not found
  1133. --*/
  1134. {
  1135. if ( pfnGntLicenseExemptionA )
  1136. {
  1137. DWORD dwS = pfnGntLicenseExemptionA( ProductName,
  1138. Version,
  1139. (LS_HANDLE*)LicenseHandle,
  1140. NtData );
  1141. if ( dwS )
  1142. {
  1143. SetLastError( dwS );
  1144. return FALSE;
  1145. }
  1146. return TRUE;
  1147. }
  1148. SetLastError( ERROR_MOD_NOT_FOUND );
  1149. return FALSE;
  1150. }
  1151. BOOL
  1152. CalExemptRelease(
  1153. DWORD dwHnd
  1154. )
  1155. /*++
  1156. Routine Description:
  1157. Release a reference returned by CalExemptAddRef
  1158. Arguments:
  1159. dwHnd - handle to exempted context as returned by CalExemptAddRef
  1160. Return Value:
  1161. TRUE if success, otherwise FALSE
  1162. LastError can be set to ERROR_MOD_NOT_FOUND if MTX licensing package not found
  1163. --*/
  1164. {
  1165. if ( pfnGntLsFreeHandle )
  1166. {
  1167. DWORD dwS = pfnGntLsFreeHandle( (LS_HANDLE)dwHnd );
  1168. if ( dwS )
  1169. {
  1170. SetLastError( dwS );
  1171. return FALSE;
  1172. }
  1173. return TRUE;
  1174. }
  1175. SetLastError( ERROR_MOD_NOT_FOUND );
  1176. return FALSE;
  1177. }