Leaked source code of windows server 2003
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.

1200 lines
32 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. svctbl.c
  5. Abstract:
  6. Service Table routines. Handles all access to service table
  7. for keeping track of running services and session counts to those
  8. services.
  9. Author:
  10. Arthur Hanson (arth) 07-Dec-1994
  11. Revision History:
  12. Jeff Parham (jeffparh) 05-Dec-1995
  13. o Integrated per seat and per server purchase models for secure
  14. certificates.
  15. o Added logging of per server license rejections.
  16. --*/
  17. #include <stdlib.h>
  18. #include <nt.h>
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <windows.h>
  22. #include <lm.h>
  23. #include <dsgetdc.h>
  24. #include "llsapi.h"
  25. #include "debug.h"
  26. #include "llssrv.h"
  27. #include "registry.h"
  28. #include "ntlsapi.h"
  29. #include "mapping.h"
  30. #include "msvctbl.h"
  31. #include "svctbl.h"
  32. #include "perseat.h"
  33. #include "llsevent.h"
  34. #include "llsutil.h"
  35. #include "purchase.h"
  36. #include <strsafe.h> //include last
  37. //
  38. // Must have ending space for version number placeholder!
  39. //
  40. #define FILE_PRINT "FilePrint "
  41. #define FILE_PRINT_BASE "FilePrint"
  42. #define FILE_PRINT_VERSION_NDX ( 9 )
  43. #define REMOTE_ACCESS "REMOTE_ACCESS "
  44. #define REMOTE_ACCESS_BASE "REMOTE_ACCESS"
  45. #define THIRTY_MINUTES (30 * 60) // 30 minutes in seconds
  46. #define TWELVE_HOURS (12 * 60 * 60) // 12 hours in seconds
  47. #define MACHINE_ACCOUNT_NO_CAL_NEEDED 0xFFFFFFFE
  48. extern ULONG NumFilePrintEntries;
  49. extern LPTSTR *FilePrintTable;
  50. extern DWORD PotentialAttackCounter;
  51. ULONG ServiceListSize = 0;
  52. PSERVICE_RECORD *ServiceList = NULL;
  53. static PSERVICE_RECORD *ServiceFreeList = NULL;
  54. static DWORD gdwLastWarningTime = 0;
  55. RTL_RESOURCE ServiceListLock;
  56. DWORD AssessPerServerLicenseCapacity(
  57. ULONG cLicensesPurchased,
  58. ULONG cLicensesConsumed);
  59. int __cdecl MServiceRecordCompare(
  60. const void *arg1,
  61. const void *arg2);
  62. DWORD GetUserNameFromSID(
  63. PSID UserSID,
  64. DWORD ccFullUserName,
  65. TCHAR szFullUserName[]);
  66. /////////////////////////////////////////////////////////////////////////
  67. NTSTATUS
  68. ServiceListInit()
  69. /*++
  70. Routine Description:
  71. Creates the service table, used for tracking the services and session
  72. count. This will pull the initial services from the registry.
  73. The table is linear so a binary search can be used on the table, so
  74. some extra records are initialized so that each time we add a new
  75. service we don't have to do a realloc. We also assume that adding
  76. new services is a relatively rare occurance, since we need to sort
  77. it each time.
  78. The service table is guarded by a read and write semaphore. Multiple
  79. reads can occur, but a write blocks everything.
  80. The service table has two default entries for FilePrint and REMOTE_ACCESS.
  81. Arguments:
  82. None.
  83. Return Value:
  84. None.
  85. --*/
  86. {
  87. BOOL PerSeatLicensing;
  88. ULONG SessionLimit;
  89. PSERVICE_RECORD Service;
  90. NTSTATUS status = STATUS_SUCCESS;
  91. try
  92. {
  93. RtlInitializeResource(&ServiceListLock);
  94. } except(EXCEPTION_EXECUTE_HANDLER ) {
  95. status = GetExceptionCode();
  96. }
  97. if (!NT_SUCCESS(status))
  98. return status;
  99. //
  100. // Just need to init FilePrint values...
  101. //
  102. Service = ServiceListAdd(TEXT(FILE_PRINT), FILE_PRINT_VERSION_NDX );
  103. RegistryInitValues(TEXT(FILE_PRINT_BASE), &PerSeatLicensing, &SessionLimit);
  104. //
  105. // Need to init RAS separatly as it uses File/Print Licenses.
  106. //
  107. Service = ServiceListAdd(TEXT(REMOTE_ACCESS), lstrlen(TEXT(REMOTE_ACCESS)) - 1);
  108. if (Service != NULL) {
  109. Service->MaxSessionCount = SessionLimit;
  110. Service->PerSeatLicensing = PerSeatLicensing;
  111. }
  112. return STATUS_SUCCESS;
  113. } // ServiceListInit
  114. /////////////////////////////////////////////////////////////////////////
  115. int __cdecl ServiceListCompare(const void *arg1, const void *arg2) {
  116. PSERVICE_RECORD Svc1, Svc2;
  117. Svc1 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg1);
  118. Svc2 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg2);
  119. return lstrcmpi( Svc1->Name, Svc2->Name);
  120. } // ServiceListCompare
  121. PSERVICE_RECORD
  122. ServiceListFind(
  123. LPTSTR ServiceName
  124. )
  125. /*++
  126. Routine Description:
  127. Internal routine to actually do binary search on ServiceList, this
  128. does not do any locking as we expect the wrapper routine to do this.
  129. The search is a simple binary search.
  130. Arguments:
  131. ServiceName -
  132. Return Value:
  133. Pointer to found service table entry or NULL if not found.
  134. --*/
  135. {
  136. LONG begin = 0;
  137. LONG end = (LONG) ServiceListSize - 1;
  138. LONG cur;
  139. int match;
  140. PSERVICE_RECORD Service;
  141. #if DBG
  142. if (TraceFlags & TRACE_FUNCTION_TRACE)
  143. dprintf(TEXT("LLS TRACE: ServiceListFind\n"));
  144. #endif
  145. if ((ServiceName == NULL) || (ServiceListSize == 0))
  146. return NULL;
  147. while (end >= begin) {
  148. // go halfway in-between
  149. cur = (begin + end) / 2;
  150. Service = ServiceList[cur];
  151. // compare the two result into match
  152. match = lstrcmpi(ServiceName, Service->Name);
  153. if (match < 0)
  154. // move new begin
  155. end = cur - 1;
  156. else
  157. begin = cur + 1;
  158. if (match == 0)
  159. return Service;
  160. }
  161. return NULL;
  162. } // ServiceListFind
  163. /////////////////////////////////////////////////////////////////////////
  164. DWORD
  165. VersionToDWORD(LPTSTR Version)
  166. /*++
  167. Routine Description:
  168. Arguments:
  169. Return Value:
  170. --*/
  171. {
  172. LPSTR pVer;
  173. DWORD Ver = 0;
  174. char tmpStr[12]; // two extra chars for null-termination just in case
  175. #if DBG
  176. if (TraceFlags & TRACE_FUNCTION_TRACE)
  177. dprintf(TEXT("LLS TRACE: VersionToDWORD\n"));
  178. #endif
  179. if ((Version == NULL) || (*Version == TEXT('\0')))
  180. return Ver;
  181. //
  182. // Do major version number
  183. //
  184. ZeroMemory(tmpStr, sizeof(tmpStr));
  185. if (0 ==WideCharToMultiByte(CP_ACP, 0, Version, -1, tmpStr, 10, NULL, NULL))
  186. {
  187. // Error
  188. return 0;
  189. }
  190. Ver = (ULONG) atoi(tmpStr);
  191. Ver *= 0x10000;
  192. //
  193. // Now minor - look for period
  194. //
  195. pVer = tmpStr;
  196. while ((*pVer != '\0') && (*pVer != '.'))
  197. pVer++;
  198. if (*pVer == '.') {
  199. pVer++;
  200. Ver += atoi(pVer);
  201. }
  202. return Ver;
  203. } // VersionToDWORD
  204. /////////////////////////////////////////////////////////////////////////
  205. PSERVICE_RECORD
  206. ServiceListAdd(
  207. LPTSTR ServiceName,
  208. ULONG VersionIndex
  209. )
  210. /*++
  211. Routine Description:
  212. Adds a service to the service table. This will also cause a poll of
  213. the registry to get the initial values for session limits and the
  214. type of licensing being used.
  215. Arguments:
  216. ServiceName -
  217. Return Value:
  218. Pointer to added service table entry, or NULL if failed.
  219. --*/
  220. {
  221. ULONG i;
  222. ULONG SessionLimit = 0;
  223. BOOL PerSeatLicensing = FALSE;
  224. PSERVICE_RECORD NewService;
  225. LPTSTR NewServiceName, pDisplayName, pFamilyDisplayName;
  226. PSERVICE_RECORD CurrentRecord = NULL;
  227. PMASTER_SERVICE_RECORD mService;
  228. NTSTATUS status;
  229. PSERVICE_RECORD *pServiceListTmp, *pServiceFreeListTmp;
  230. HRESULT hr;
  231. size_t cch;
  232. #if DBG
  233. if (TraceFlags & TRACE_FUNCTION_TRACE)
  234. dprintf(TEXT("LLS TRACE: ServiceListAdd\n"));
  235. #endif
  236. //
  237. // We do a double check here to see if another thread just got done
  238. // adding the service, between when we checked last and actually got
  239. // the write lock.
  240. //
  241. CurrentRecord = ServiceListFind(ServiceName);
  242. if (CurrentRecord != NULL) {
  243. return CurrentRecord;
  244. }
  245. if (VersionIndex >= (ULONG)lstrlen(ServiceName))
  246. {
  247. // Invalid VersionIndex
  248. return NULL;
  249. }
  250. //
  251. // Allocate space for table (zero init it).
  252. //
  253. if (ServiceList == NULL) {
  254. pServiceListTmp = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
  255. pServiceFreeListTmp = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
  256. } else {
  257. pServiceListTmp = (PSERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
  258. pServiceFreeListTmp = (PSERVICE_RECORD *) LocalReAlloc(ServiceFreeList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
  259. }
  260. //
  261. // Make sure we could allocate service table
  262. //
  263. if ((pServiceListTmp == NULL) || (pServiceFreeListTmp == NULL)) {
  264. if (pServiceListTmp != NULL)
  265. LocalFree(pServiceListTmp);
  266. if (pServiceFreeListTmp != NULL)
  267. LocalFree(pServiceFreeListTmp);
  268. return NULL;
  269. } else {
  270. ServiceList = pServiceListTmp;
  271. ServiceFreeList = pServiceFreeListTmp;
  272. }
  273. //
  274. // Allocate space for saving off Service Name - we will take a space, then
  275. // the version string onto the end of the Product Name. Therefore the
  276. // product name will be something like "Microsoft SQL 4.2a". We maintain
  277. // a pointer to the version, so that we can convert the space to a NULL
  278. // and then get the product and version string separatly. Keeping them
  279. // together simplifies the qsort and binary search routines.
  280. //
  281. NewService = (PSERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVICE_RECORD));
  282. if (NewService == NULL) {
  283. ASSERT(FALSE);
  284. return NULL;
  285. }
  286. ServiceList[ServiceListSize] = NewService;
  287. ServiceFreeList[ServiceListSize] = NewService;
  288. cch = lstrlen(ServiceName) + 1;
  289. NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  290. if (NewServiceName == NULL) {
  291. ASSERT(FALSE);
  292. LocalFree(NewService);
  293. return NULL;
  294. }
  295. // now copy it over...
  296. NewService->Name = NewServiceName;
  297. hr = StringCchCopy(NewService->Name, cch, ServiceName);
  298. ASSERT(SUCCEEDED(hr));
  299. //
  300. // Allocate space for Root Name
  301. //
  302. NewService->Name[VersionIndex] = TEXT('\0');
  303. cch = lstrlen(NewService->Name) + 1;
  304. NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  305. if (NewServiceName == NULL) {
  306. ASSERT(FALSE);
  307. LocalFree(NewService->Name);
  308. LocalFree(NewService);
  309. return NULL;
  310. }
  311. hr = StringCchCopy(NewServiceName, cch, NewService->Name);
  312. ASSERT(SUCCEEDED(hr));
  313. NewService->Name[VersionIndex] = TEXT(' ');
  314. // point service structure to it...
  315. NewService->FamilyName = NewServiceName;
  316. //
  317. // Allocate space for Display Name
  318. //
  319. RegistryDisplayNameGet(NewService->FamilyName, NewService->Name, &pDisplayName);
  320. if (pDisplayName == NULL) {
  321. ASSERT(FALSE);
  322. LocalFree(NewService->Name);
  323. LocalFree(NewService->FamilyName);
  324. LocalFree(NewService);
  325. return NULL;
  326. }
  327. // point service structure to it...
  328. NewService->DisplayName = pDisplayName;
  329. RegistryFamilyDisplayNameGet(NewService->FamilyName, NewService->DisplayName, &pFamilyDisplayName);
  330. if (pFamilyDisplayName == NULL) {
  331. ASSERT(FALSE);
  332. LocalFree(NewService->Name);
  333. LocalFree(NewService->FamilyName);
  334. LocalFree(NewService->DisplayName);
  335. LocalFree(NewService);
  336. return NULL;
  337. }
  338. // point service structure to it...
  339. NewService->FamilyDisplayName = pFamilyDisplayName;
  340. //
  341. // Update table size and init entry, including reading init values
  342. // from registry.
  343. //
  344. NewService->Version = VersionToDWORD(&ServiceName[VersionIndex + 1]);
  345. // Init values from registry...
  346. RegistryInitService(NewService->FamilyName, &PerSeatLicensing, &SessionLimit);
  347. if ( PerSeatLicensing )
  348. {
  349. // per seat mode
  350. NewService->MaxSessionCount = 0;
  351. }
  352. else if ( ServiceIsSecure( NewService->DisplayName ) )
  353. {
  354. // per server mode with a secure product; requires certificate
  355. NewService->MaxSessionCount = ProductLicensesGet( NewService->DisplayName, TRUE );
  356. }
  357. else
  358. {
  359. // per server mode with an unsecure product; use limit from registry
  360. NewService->MaxSessionCount = SessionLimit;
  361. }
  362. NewService->PerSeatLicensing = PerSeatLicensing;
  363. NewService->SessionCount = 0;
  364. NewService->Index = ServiceListSize;
  365. status = RtlInitializeCriticalSection(&NewService->ServiceLock);
  366. if (!NT_SUCCESS(status))
  367. {
  368. LocalFree(NewService->Name);
  369. LocalFree(NewService->FamilyName);
  370. LocalFree(NewService->DisplayName);
  371. LocalFree(NewService);
  372. return NULL;
  373. }
  374. if (lstrcmpi(ServiceName, TEXT(REMOTE_ACCESS))) {
  375. RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
  376. mService = MasterServiceListAdd( NewService->FamilyDisplayName, NewService->DisplayName, NewService->Version);
  377. if (mService == NULL) {
  378. ASSERT(FALSE);
  379. } else {
  380. NewService->MasterService = mService;
  381. //
  382. // In case this got added from the local service list table and we
  383. // didn't have a version # yet.
  384. //
  385. if (mService->Version == 0) {
  386. PMASTER_SERVICE_ROOT ServiceRoot = NULL;
  387. //
  388. // Fixup next pointer chain
  389. //
  390. ServiceRoot = mService->Family;
  391. i = 0;
  392. while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < NewService->Version))
  393. i++;
  394. mService->next = 0;
  395. mService->Version = NewService->Version;
  396. if (i > 0) {
  397. if (MasterServiceTable[ServiceRoot->Services[i - 1]]->next == mService->Index + 1)
  398. mService->next = 0;
  399. else
  400. mService->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
  401. if (MasterServiceTable[ServiceRoot->Services[i - 1]] != mService)
  402. MasterServiceTable[ServiceRoot->Services[i - 1]]->next = mService->Index + 1;
  403. }
  404. // Resort it in order of the versions
  405. qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(ULONG), MServiceRecordCompare);
  406. }
  407. }
  408. RtlReleaseResource(&MasterServiceListLock);
  409. }
  410. ServiceListSize++;
  411. // Have added the entry - now need to sort it in order of the service names
  412. qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVICE_RECORD), ServiceListCompare);
  413. return NewService;
  414. } // ServiceListAdd
  415. /////////////////////////////////////////////////////////////////////////
  416. VOID
  417. ServiceListResynch( )
  418. /*++
  419. Routine Description:
  420. Arguments:
  421. Return Value:
  422. --*/
  423. {
  424. PSERVICE_RECORD Service;
  425. BOOL PerSeatLicensing;
  426. ULONG SessionLimit;
  427. ULONG i = 0;
  428. PSERVICE_RECORD FilePrintService;
  429. #if DBG
  430. if (TraceFlags & TRACE_FUNCTION_TRACE)
  431. dprintf(TEXT("LLS TRACE: ServiceListReSynch\n"));
  432. #endif
  433. if (ServiceList == NULL)
  434. return;
  435. //
  436. // Need to update list so get exclusive access.
  437. //
  438. RtlAcquireResourceExclusive(&ServiceListLock, TRUE);
  439. for (i = 0; i < ServiceListSize; i++) {
  440. //
  441. // Note: We will init REMOTE_ACCESS with bogus values here, but we
  442. // reset it to the correct values below. Since we have exclusive access
  443. // to the table, this is fine (and faster than always checking for
  444. // REMOTE_ACCESS).
  445. //
  446. RegistryInitService((ServiceList[i])->FamilyName, &PerSeatLicensing, &SessionLimit);
  447. if ( PerSeatLicensing )
  448. {
  449. // per seat mode
  450. (ServiceList[i])->MaxSessionCount = 0;
  451. }
  452. else if ( ServiceIsSecure( (ServiceList[i])->DisplayName ) )
  453. {
  454. // per server mode with a secure product; requires certificate
  455. (ServiceList[i])->MaxSessionCount = ProductLicensesGet( (ServiceList[i])->DisplayName, TRUE );
  456. }
  457. else
  458. {
  459. // per server mode with an unsecure product; use limit from registry
  460. (ServiceList[i])->MaxSessionCount = SessionLimit;
  461. }
  462. (ServiceList[i])->PerSeatLicensing = PerSeatLicensing;
  463. }
  464. //
  465. // Need to init RAS separatly as it uses File/Print Licenses.
  466. //
  467. Service = ServiceListFind(TEXT(REMOTE_ACCESS));
  468. FilePrintService = ServiceListFind(TEXT(FILE_PRINT));
  469. ASSERT( NULL != Service );
  470. ASSERT( NULL != FilePrintService );
  471. if ( ( NULL != Service ) && ( NULL != FilePrintService ) )
  472. {
  473. Service->MaxSessionCount = FilePrintService->MaxSessionCount;
  474. Service->PerSeatLicensing = FilePrintService->PerSeatLicensing;
  475. }
  476. RtlReleaseResource(&ServiceListLock);
  477. return;
  478. } // ServiceListResynch
  479. /////////////////////////////////////////////////////////////////////////
  480. NTSTATUS
  481. DispatchRequestLicense(
  482. ULONG DataType,
  483. PVOID Data,
  484. LPTSTR ServiceID,
  485. ULONG VersionIndex,
  486. BOOL IsAdmin,
  487. ULONG *Handle
  488. )
  489. /*++
  490. Routine Description:
  491. Arguments:
  492. ServiceID -
  493. IsAdmin -
  494. Handle -
  495. Return Value:
  496. --*/
  497. {
  498. #define FULL_USERNAME_LENGTH (MAX_DOMAINNAME_LENGTH + \
  499. MAX_USERNAME_LENGTH + 3)
  500. LPWSTR apszSubString[ 2 ];
  501. NTSTATUS Status = STATUS_SUCCESS;
  502. PSERVICE_RECORD Service;
  503. ULONG SessionCount;
  504. ULONG TableEntry;
  505. LPTSTR pServiceID;
  506. BOOL NoLicense = FALSE;
  507. BOOL PerSeat;
  508. #if DBG
  509. if (TraceFlags & TRACE_FUNCTION_TRACE)
  510. dprintf(TEXT("LLS TRACE: DispatchRequestLicense\n"));
  511. #endif
  512. if (DataType == NT_LS_USER_NAME)
  513. {
  514. WCHAR *wszAccount = (WCHAR *) Data;
  515. int cchAccount = lstrlen(wszAccount);
  516. // machine accounts must end in a dollar sign
  517. if (L'$' == wszAccount[(cchAccount-1)])
  518. {
  519. //
  520. // Not all accounts ending in $ are a machine account
  521. // but for the sake of efficiency we'll make that
  522. // assumption
  523. //
  524. // Calling NetUserGetInfo is too heavyweight - causes
  525. // overload on the DCs
  526. //
  527. //
  528. // don't allocate a CAL
  529. //
  530. ASSERT(NULL != Handle);
  531. *Handle = MACHINE_ACCOUNT_NO_CAL_NEEDED;
  532. return STATUS_SUCCESS;
  533. }
  534. }
  535. *Handle = 0xFFFFFFFF;
  536. pServiceID = ServiceID;
  537. // we only need read access since we aren't adding at this point
  538. RtlAcquireResourceShared( &ServiceListLock, TRUE );
  539. // check if in FilePrint table, if so then we use FilePrint as the name
  540. ServiceID[ VersionIndex ] = TEXT('\0');
  541. if ( ServiceFindInTable( ServiceID, FilePrintTable, NumFilePrintEntries, &TableEntry ) )
  542. {
  543. pServiceID = TEXT(FILE_PRINT);
  544. }
  545. ServiceID[ VersionIndex ] = TEXT(' ');
  546. Service = ServiceListFind( pServiceID );
  547. if (Service == NULL)
  548. {
  549. // couldn't find service in list, so add it
  550. RtlConvertSharedToExclusive(&ServiceListLock);
  551. Service = ServiceListAdd( pServiceID, VersionIndex );
  552. RtlConvertExclusiveToShared(&ServiceListLock);
  553. }
  554. if (Service != NULL)
  555. {
  556. // service found or added successfully
  557. *Handle = (ULONG) Service->Index;
  558. RtlEnterCriticalSection(&Service->ServiceLock);
  559. SessionCount = Service->SessionCount + 1;
  560. #if DBG
  561. if (TraceFlags & TRACE_LICENSE_REQUEST)
  562. dprintf(TEXT("LLS: [0x%lX] %s License: %ld of %ld\n"), Service, Service->Name, SessionCount, Service->MaxSessionCount);
  563. #endif
  564. if (SessionCount > Service->HighMark)
  565. {
  566. Service->HighMark = SessionCount;
  567. }
  568. PerSeat = Service->PerSeatLicensing;
  569. if ( !PerSeat ) {
  570. if ( !IsAdmin ) {
  571. TCHAR szFullUserName[ FULL_USERNAME_LENGTH ] = TEXT("");
  572. DWORD dwCapacityState;
  573. DWORD dwError=0; //init to avoid W4 complaint, use uninit in LogEvent
  574. DWORD dwInsertsCount=0; //init to avoid W4 complaint, use uninit in LogEvent
  575. DWORD dwMessageID=0; //init to avoid W4 complaint, use uninit in LogEvent
  576. dwCapacityState = AssessPerServerLicenseCapacity(
  577. Service->MaxSessionCount,
  578. SessionCount);
  579. if ( dwCapacityState == LICENSE_CAPACITY_NORMAL ) {
  580. //
  581. // Within normal capacity.
  582. //
  583. Service->SessionCount++;
  584. }
  585. else if ( dwCapacityState == LICENSE_CAPACITY_NEAR_MAXIMUM ) {
  586. //
  587. // Within the threshold of near 100% capacity.
  588. //
  589. dwInsertsCount = 1;
  590. apszSubString[ 0 ] = Service->DisplayName;
  591. dwMessageID = LLS_EVENT_LOG_PER_SERVER_NEAR_MAX;
  592. dwError = ERROR_SUCCESS;
  593. Service->SessionCount++;
  594. }
  595. else if ( dwCapacityState == LICENSE_CAPACITY_AT_MAXIMUM ) {
  596. //
  597. // Exceeding 100% capacity, but still within the grace range.
  598. //
  599. dwInsertsCount = 1;
  600. apszSubString[ 0 ] = Service->DisplayName;
  601. dwMessageID = LLS_EVENT_LOG_PER_SERVER_AT_MAX;
  602. dwError = ERROR_SUCCESS;
  603. Service->SessionCount++;
  604. }
  605. else {
  606. //
  607. // License maximum exceeded. Zero tolerance for exceeding
  608. // limits on concurrent licenses
  609. //
  610. if ( NT_LS_USER_NAME == DataType )
  611. {
  612. apszSubString[ 0 ] = (LPWSTR) Data;
  613. dwError = ERROR_SUCCESS;
  614. }
  615. else
  616. {
  617. dwError = GetUserNameFromSID((PSID)Data,
  618. sizeof(szFullUserName)/sizeof(szFullUserName[0]),
  619. szFullUserName);
  620. apszSubString[ 0 ] = szFullUserName;
  621. }
  622. dwInsertsCount = 2;
  623. apszSubString[ 1 ] = ServiceID;
  624. dwMessageID = LLS_EVENT_USER_NO_LICENSE;
  625. NoLicense = TRUE;
  626. }
  627. if ( dwCapacityState != LICENSE_CAPACITY_NORMAL ) {
  628. if (!SBSPerServerHotfix || 0 != PotentialAttackCounter) {
  629. //
  630. // Log warning and put up warning dialog locally.
  631. // Limit the log/ui warning to a low frequency. Specifically:
  632. // Once per every 12 hours for warnings.
  633. // Every 30 minutes when the license maximum is exceeded
  634. // causing licenses to no longer be provided.
  635. //
  636. LARGE_INTEGER liTime;
  637. DWORD dwCurrentTime;
  638. NtQuerySystemTime(&liTime);
  639. RtlTimeToSecondsSince1970(&liTime, &dwCurrentTime);
  640. if ( dwCurrentTime - gdwLastWarningTime >
  641. (DWORD)(NoLicense ? THIRTY_MINUTES : TWELVE_HOURS) ) {
  642. LogEvent(dwMessageID, dwInsertsCount, apszSubString,
  643. dwError);
  644. LicenseCapacityWarningDlg(dwCapacityState);
  645. gdwLastWarningTime = dwCurrentTime;
  646. }
  647. } else {
  648. //
  649. // SBS mods (bug# 505640). This code modified to log an event in the case of something going
  650. // wrong under ALL conditions.
  651. //
  652. LogEvent(dwMessageID, dwInsertsCount, apszSubString,
  653. dwError);
  654. LicenseCapacityWarningDlg(dwCapacityState);
  655. //
  656. // end SBS mods
  657. //
  658. }
  659. }
  660. }
  661. else {
  662. Service->SessionCount++;
  663. }
  664. }
  665. else {
  666. Service->SessionCount++;
  667. }
  668. RtlLeaveCriticalSection(&Service->ServiceLock);
  669. RtlReleaseResource(&ServiceListLock);
  670. if ( PerSeat )
  671. {
  672. // per node ("per seat") license
  673. // translate REMOTE_ACCESS into FILE_PRINT before adding to
  674. // per seat license records
  675. if ( !lstrcmpi( ServiceID, TEXT( REMOTE_ACCESS ) ) )
  676. {
  677. RtlAcquireResourceShared(&ServiceListLock, TRUE);
  678. Service = ServiceListFind(TEXT(FILE_PRINT));
  679. RtlReleaseResource(&ServiceListLock);
  680. ASSERT(Service != NULL);
  681. }
  682. if (Service == NULL)
  683. {
  684. // shouldn't ever happen
  685. *Handle = 0xFFFFFFFF;
  686. return LS_UNKNOWN_STATUS;
  687. }
  688. UserListUpdate( DataType, Data, Service );
  689. }
  690. else
  691. {
  692. // concurrent use ("per server") license
  693. if (NoLicense)
  694. {
  695. Status = LS_INSUFFICIENT_UNITS;
  696. *Handle = 0xFFFFFFFF;
  697. }
  698. }
  699. }
  700. else
  701. {
  702. // could neither find nor create service entry
  703. RtlReleaseResource(&ServiceListLock);
  704. #if DBG
  705. dprintf( TEXT( "DispatchRequestLicense(): Could neither find nor create service entry.\n" ) );
  706. #endif
  707. }
  708. return Status;
  709. } // DispatchRequestLicense
  710. /////////////////////////////////////////////////////////////////////////
  711. VOID
  712. DispatchFreeLicense(
  713. ULONG Handle
  714. )
  715. /*++
  716. Routine Description:
  717. Arguments:
  718. Handle -
  719. Return Value:
  720. None.
  721. --*/
  722. {
  723. PSERVICE_RECORD Service;
  724. #if DBG
  725. if (TraceFlags & TRACE_FUNCTION_TRACE)
  726. dprintf(TEXT("LLS TRACE: DispatchFreeLicense\n"));
  727. #endif
  728. if (MACHINE_ACCOUNT_NO_CAL_NEEDED == Handle)
  729. {
  730. return;
  731. }
  732. //
  733. // We only need read access since we aren't adding at this point.
  734. //
  735. RtlAcquireResourceShared(&ServiceListLock, TRUE);
  736. #if DBG
  737. if (TraceFlags & TRACE_LICENSE_FREE)
  738. dprintf(TEXT("Free Handle: 0x%lX\n"), Handle);
  739. #endif
  740. if (Handle < ServiceListSize) {
  741. Service = ServiceFreeList[Handle];
  742. RtlEnterCriticalSection(&Service->ServiceLock);
  743. if (Service->SessionCount > 0)
  744. Service->SessionCount--;
  745. RtlLeaveCriticalSection(&Service->ServiceLock);
  746. } else {
  747. #if DBG
  748. dprintf(TEXT("Passed invalid Free Handle: 0x%lX\n"), Handle);
  749. #endif
  750. }
  751. RtlReleaseResource(&ServiceListLock);
  752. } // DispatchFreeLicense
  753. /////////////////////////////////////////////////////////////////////////
  754. DWORD
  755. GetUserNameFromSID(
  756. PSID UserSID,
  757. DWORD ccFullUserName,
  758. TCHAR szFullUserName[]
  759. )
  760. /*++
  761. Routine Description:
  762. Arguments:
  763. UserSID -
  764. ccFullUserName -
  765. szFullUserName -
  766. Return Value:
  767. None.
  768. --*/
  769. {
  770. TCHAR szUserName[ MAX_USERNAME_LENGTH + 1 ];
  771. TCHAR szDomainName[ MAX_DOMAINNAME_LENGTH + 1 ];
  772. DWORD Status = ERROR_SUCCESS;
  773. DWORD cbUserName;
  774. DWORD cbDomainName;
  775. SID_NAME_USE snu;
  776. HRESULT hr;
  777. cbUserName = sizeof( szUserName );
  778. cbDomainName = sizeof( szDomainName );
  779. if ((UserSID == NULL) || (!IsValidSid(UserSID))) {
  780. return ERROR_INVALID_PARAMETER;
  781. }
  782. if ( LookupAccountSid( NULL,
  783. UserSID,
  784. szUserName,
  785. &cbUserName,
  786. szDomainName,
  787. &cbDomainName,
  788. &snu ) )
  789. {
  790. if ( ccFullUserName >=
  791. ( cbUserName + cbDomainName + sizeof(TEXT("\\")) ) /
  792. sizeof(TCHAR) ) {
  793. hr = StringCchCopy( szFullUserName, ccFullUserName, szDomainName );
  794. ASSERT(SUCCEEDED(hr));
  795. hr = StringCchCat( szFullUserName, ccFullUserName, TEXT( "\\" ) );
  796. ASSERT(SUCCEEDED(hr));
  797. hr = StringCchCat( szFullUserName, ccFullUserName, szUserName );
  798. ASSERT(SUCCEEDED(hr));
  799. }
  800. else {
  801. Status = ERROR_INSUFFICIENT_BUFFER;
  802. }
  803. }
  804. else {
  805. Status = GetLastError();
  806. }
  807. return(Status);
  808. }
  809. /////////////////////////////////////////////////////////////////////////
  810. DWORD
  811. AssessPerServerLicenseCapacity(
  812. ULONG cLicensesPurchased,
  813. ULONG cLicensesConsumed
  814. )
  815. /*++
  816. Routine Description:
  817. Determine the license capacity state given the number of licenses
  818. purchased and used according to the following table:
  819. # of Purchased Warning Message Violation Message License not provided
  820. Licenses Sent at % Sent at % at %
  821. -------------- --------------- ----------------- --------------------
  822. 0-9 # of licenses - 1 # of licenses + 1 # of licenses + 2
  823. 10-500 90% <= x <= 100% 100% < x <= 110% x > 110%
  824. 501+ 95% <= x <= 100% 100% < x <= 105% x > 105%
  825. Arguments:
  826. cLicensesPurchased -- License purchase total
  827. cLicensesConsumed -- Number of licenses used
  828. Return Value:
  829. Returned state.
  830. --*/
  831. {
  832. ULONG GracePercentage;
  833. ULONG Divisor;
  834. if ( cLicensesPurchased == 0 ) {
  835. Divisor = 0;
  836. }
  837. else if ( cLicensesPurchased < 10 ) {
  838. Divisor = 1;
  839. }
  840. else if ( cLicensesPurchased <= 500 ) {
  841. Divisor = 10;
  842. }
  843. else {
  844. Divisor = 20;
  845. }
  846. GracePercentage = Divisor > 1 ? cLicensesPurchased / Divisor : Divisor;
  847. if ( cLicensesConsumed <= cLicensesPurchased ) {
  848. if ( cLicensesConsumed >= cLicensesPurchased - GracePercentage ) {
  849. return LICENSE_CAPACITY_NEAR_MAXIMUM;
  850. }
  851. else {
  852. return LICENSE_CAPACITY_NORMAL;
  853. }
  854. }
  855. else {
  856. if ( cLicensesConsumed > cLicensesPurchased + GracePercentage ) {
  857. return LICENSE_CAPACITY_EXCEEDED;
  858. }
  859. else {
  860. return LICENSE_CAPACITY_AT_MAXIMUM;
  861. }
  862. }
  863. } // AssessPerServerLicenseCapacity
  864. #if DBG
  865. /////////////////////////////////////////////////////////////////////////
  866. VOID
  867. ServiceListDebugDump( )
  868. /*++
  869. Routine Description:
  870. Arguments:
  871. Return Value:
  872. --*/
  873. {
  874. ULONG i = 0;
  875. //
  876. // Need to scan list so get read access.
  877. //
  878. RtlAcquireResourceShared(&ServiceListLock, TRUE);
  879. dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
  880. if (ServiceList == NULL)
  881. goto ServiceListDebugDumpExit;
  882. for (i = 0; i < ServiceListSize; i++) {
  883. if ((ServiceList[i])->PerSeatLicensing)
  884. dprintf(TEXT("%3lu) PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  885. i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
  886. else
  887. dprintf(TEXT("%3lu) PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  888. i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
  889. }
  890. ServiceListDebugDumpExit:
  891. RtlReleaseResource(&ServiceListLock);
  892. return;
  893. } // ServiceListDebugDump
  894. /////////////////////////////////////////////////////////////////////////
  895. VOID
  896. ServiceListDebugInfoDump( PVOID Data )
  897. /*++
  898. Routine Description:
  899. Arguments:
  900. Return Value:
  901. --*/
  902. {
  903. PSERVICE_RECORD CurrentRecord = NULL;
  904. dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
  905. if (lstrlen((LPWSTR) Data) > 0) {
  906. CurrentRecord = ServiceListFind((LPWSTR) Data);
  907. if (CurrentRecord != NULL) {
  908. if (CurrentRecord->PerSeatLicensing)
  909. dprintf(TEXT(" PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  910. CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
  911. else
  912. dprintf(TEXT(" PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  913. CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
  914. }
  915. }
  916. } // ServiceListDebugInfoDump
  917. #endif //DBG