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.

1044 lines
28 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. purchase.c
  5. Abstract:
  6. Author:
  7. Arthur Hanson (arth) 03-Jan-1995
  8. Revision History:
  9. Jeff Parham (jeffparh) 05-Dec-1995
  10. o Added support for uniting per seat and per server purchase models.
  11. o Added extra parameters and code to support secure certificates and
  12. certificate database.
  13. --*/
  14. #include <stdlib.h>
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <windows.h>
  19. #include <dsgetdc.h>
  20. #include <debug.h>
  21. #include "llsevent.h"
  22. #include "llsapi.h"
  23. #include "llsutil.h"
  24. #include "llssrv.h"
  25. #include "mapping.h"
  26. #include "msvctbl.h"
  27. #include "purchase.h"
  28. #include "svctbl.h"
  29. #include "perseat.h"
  30. #include "registry.h"
  31. #include "llsrpc_s.h"
  32. #include "certdb.h"
  33. #include "server.h"
  34. #include <strsafe.h> //include last
  35. //
  36. // Initialized in ReplicationInit in repl.c.
  37. //
  38. extern PLLS_CONNECT_W pLlsConnectW;
  39. extern PLLS_CLOSE pLlsClose;
  40. extern PLLS_LICENSE_ADD_W pLlsLicenseAddW;
  41. extern HANDLE g_hThrottleConnect;
  42. ULONG LicenseServiceListSize = 0;
  43. PLICENSE_SERVICE_RECORD *LicenseServiceList = NULL;
  44. ULONG PerServerLicenseServiceListSize = 0;
  45. PLICENSE_SERVICE_RECORD *PerServerLicenseServiceList = NULL;
  46. PLICENSE_PURCHASE_RECORD PurchaseList = NULL;
  47. ULONG PurchaseListSize = 0;
  48. RTL_RESOURCE LicenseListLock;
  49. static
  50. NTSTATUS
  51. LicenseAdd_UpdateQuantity(
  52. LPTSTR ServiceName,
  53. LONG Quantity,
  54. BOOL UsePerServerList,
  55. PLICENSE_SERVICE_RECORD * ppService,
  56. BOOL * pChangeLicense,
  57. LONG * pNewLicenses,
  58. PMASTER_SERVICE_RECORD * pmService
  59. );
  60. NTSTATUS
  61. ReplicationInitDelayed();
  62. /////////////////////////////////////////////////////////////////////////
  63. NTSTATUS
  64. LicenseListInit()
  65. /*++
  66. Routine Description:
  67. Arguments:
  68. None.
  69. Return Value:
  70. None.
  71. --*/
  72. {
  73. NTSTATUS status = STATUS_SUCCESS;
  74. try
  75. {
  76. RtlInitializeResource(&LicenseListLock);
  77. } except(EXCEPTION_EXECUTE_HANDLER ) {
  78. status = GetExceptionCode();
  79. }
  80. return status;
  81. } // LicenseListInit
  82. /////////////////////////////////////////////////////////////////////////
  83. int __cdecl LicenseServiceListCompare(const void *arg1, const void *arg2) {
  84. PLICENSE_SERVICE_RECORD Svc1, Svc2;
  85. Svc1 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg1);
  86. Svc2 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg2);
  87. return lstrcmpi( Svc1->ServiceName, Svc2->ServiceName);
  88. } // LicenseServiceListCompare
  89. /////////////////////////////////////////////////////////////////////////
  90. PLICENSE_SERVICE_RECORD
  91. LicenseServiceListFind(
  92. LPTSTR ServiceName,
  93. BOOL UsePerServerList
  94. )
  95. /*++
  96. Routine Description:
  97. Arguments:
  98. ServiceName -
  99. (JeffParh 95-10-31)
  100. UsePerServerList - Determines whether the license record is searched for
  101. in the per seat list (as in 3.51) or in the per server list (new for
  102. SUR). The license purchase models are now unified, so there is now
  103. a purchase history for both per seat and per server licenses.
  104. Return Value:
  105. Pointer to found service table entry or NULL if not found.
  106. --*/
  107. {
  108. LONG begin = 0;
  109. LONG end = (LONG) LicenseServiceListSize - 1;
  110. LONG cur;
  111. int match;
  112. PLICENSE_SERVICE_RECORD Service;
  113. PLICENSE_SERVICE_RECORD * l_pServiceList;
  114. #if DBG
  115. if (TraceFlags & TRACE_FUNCTION_TRACE)
  116. dprintf(TEXT("LLS TRACE: LicenseServiceListFind\n"));
  117. #endif
  118. if (ServiceName == NULL)
  119. return NULL;
  120. if ( UsePerServerList )
  121. {
  122. end = PerServerLicenseServiceListSize - 1;
  123. l_pServiceList = PerServerLicenseServiceList;
  124. }
  125. else
  126. {
  127. end = LicenseServiceListSize - 1;
  128. l_pServiceList = LicenseServiceList;
  129. }
  130. while (end >= begin) {
  131. // go halfway in-between
  132. cur = (begin + end) / 2;
  133. Service = l_pServiceList[cur];
  134. // compare the two result into match
  135. match = lstrcmpi(ServiceName, Service->ServiceName);
  136. if (match < 0)
  137. // move new begin
  138. end = cur - 1;
  139. else
  140. begin = cur + 1;
  141. if (match == 0)
  142. return Service;
  143. }
  144. return NULL;
  145. } // LicenseServiceListFind
  146. /////////////////////////////////////////////////////////////////////////
  147. PLICENSE_SERVICE_RECORD
  148. LicenseServiceListAdd(
  149. LPTSTR ServiceName,
  150. BOOL UsePerServerList
  151. )
  152. /*++
  153. Routine Description:
  154. Arguments:
  155. ServiceName -
  156. (JeffParh 95-10-31)
  157. UsePerServerList - Determines whether the license record is added to
  158. the per seat list (as in 3.51) or the per server list (new for
  159. SUR). The license purchase models are now unified, so there is now
  160. a purchase history for both per seat and per server licenses.
  161. Return Value:
  162. Pointer to added service table entry, or NULL if failed.
  163. --*/
  164. {
  165. PLICENSE_SERVICE_RECORD Service;
  166. LPTSTR NewServiceName;
  167. PLICENSE_SERVICE_RECORD ** pServiceList;
  168. LPDWORD pServiceListSize;
  169. PLICENSE_SERVICE_RECORD * pServiceListTmp;
  170. HRESULT hr;
  171. size_t cch;
  172. #if DBG
  173. if (TraceFlags & TRACE_FUNCTION_TRACE)
  174. dprintf(TEXT("LLS TRACE: LicenseServiceListAdd\n"));
  175. #endif
  176. //
  177. // We do a double check here to see if another thread just got done
  178. // adding the service, between when we checked last and actually got
  179. // the write lock.
  180. //
  181. Service = LicenseServiceListFind(ServiceName, UsePerServerList);
  182. if (Service != NULL) {
  183. return Service;
  184. }
  185. if ( UsePerServerList )
  186. {
  187. pServiceList = &PerServerLicenseServiceList;
  188. pServiceListSize = &PerServerLicenseServiceListSize;
  189. }
  190. else
  191. {
  192. pServiceList = &LicenseServiceList;
  193. pServiceListSize = &LicenseServiceListSize;
  194. }
  195. //
  196. // Allocate space for table (zero init it).
  197. //
  198. if (*pServiceList == NULL)
  199. pServiceListTmp = (PLICENSE_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1));
  200. else
  201. pServiceListTmp = (PLICENSE_SERVICE_RECORD *) LocalReAlloc(*pServiceList, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1), LHND);
  202. //
  203. // Make sure we could allocate service table
  204. //
  205. if (pServiceListTmp == NULL) {
  206. return NULL;
  207. } else {
  208. *pServiceList = pServiceListTmp;
  209. }
  210. Service = (PLICENSE_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_SERVICE_RECORD));
  211. if (Service == NULL) {
  212. ASSERT(FALSE);
  213. return NULL;
  214. }
  215. //
  216. // Create space for saving off the name.
  217. //
  218. cch = lstrlen(ServiceName) + 1;
  219. NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  220. if (NewServiceName == NULL) {
  221. ASSERT(FALSE);
  222. LocalFree(Service);
  223. return NULL;
  224. }
  225. // now copy it over...
  226. Service->ServiceName = NewServiceName;
  227. hr = StringCchCopy(NewServiceName, cch, ServiceName);
  228. ASSERT(SUCCEEDED(hr));
  229. (*pServiceList)[*pServiceListSize] = Service;
  230. Service->NumberLicenses = 0;
  231. Service->Index = *pServiceListSize;
  232. (*pServiceListSize)++;
  233. // Have added the entry - now need to sort it in order of the service names
  234. qsort((void *) *pServiceList, (size_t) *pServiceListSize, sizeof(PLICENSE_SERVICE_RECORD), LicenseServiceListCompare);
  235. return Service;
  236. } // LicenseServiceListAdd
  237. /////////////////////////////////////////////////////////////////////////
  238. ULONG
  239. ProductLicensesGet(
  240. LPTSTR ServiceName,
  241. BOOL UsePerServerList
  242. )
  243. /*++
  244. Routine Description:
  245. Arguments:
  246. ServiceName -
  247. (JeffParh 95-10-31)
  248. UsePerServerList - Determines whether the number of licenses is retirved
  249. from the per seat list (as in 3.51) or the per server list (new for
  250. SUR). The license purchase models are now unified, so there is now
  251. a purchase history for both per seat and per server licenses.
  252. Return Value:
  253. --*/
  254. {
  255. PLICENSE_SERVICE_RECORD Service;
  256. ULONG NumLicenses = 0;
  257. #if DBG
  258. if (TraceFlags & TRACE_FUNCTION_TRACE)
  259. dprintf(TEXT("LLS TRACE: ProductLicenseGet\n"));
  260. #endif
  261. //
  262. // Try to find the service.
  263. //
  264. RtlAcquireResourceShared(&LicenseListLock, TRUE);
  265. Service = LicenseServiceListFind(ServiceName, UsePerServerList);
  266. if (Service != NULL)
  267. NumLicenses = Service->NumberLicenses;
  268. RtlReleaseResource(&LicenseListLock);
  269. return NumLicenses;
  270. } // ProductLicensesGet
  271. /////////////////////////////////////////////////////////////////////////
  272. NTSTATUS
  273. LicenseAdd(
  274. LPTSTR ServiceName,
  275. LPTSTR Vendor,
  276. LONG Quantity,
  277. DWORD MaxQuantity,
  278. LPTSTR Admin,
  279. LPTSTR Comment,
  280. DWORD Date,
  281. DWORD AllowedModes,
  282. DWORD CertificateID,
  283. LPTSTR Source,
  284. DWORD ExpirationDate,
  285. LPDWORD Secrets
  286. )
  287. /*++
  288. Routine Description:
  289. Arguments:
  290. Return Value:
  291. --*/
  292. {
  293. static DWORD NullSecrets[ LLS_NUM_SECRETS ] = { 0, 0, 0, 0 };
  294. static DWORD ConnectErr = STATUS_SUCCESS;
  295. BOOL ChangeLicense = FALSE;
  296. PLICENSE_SERVICE_RECORD Service = NULL;
  297. PLICENSE_PURCHASE_RECORD PurchaseRecord;
  298. LONG NewLicenses = 0;
  299. NTSTATUS Status;
  300. BOOL PerServerChangeLicense = FALSE;
  301. PLICENSE_SERVICE_RECORD PerServerService = NULL;
  302. LONG PerServerNewLicenses = 0;
  303. LPTSTR NewName,NewComment,NewSource,NewVendor;
  304. PMASTER_SERVICE_RECORD mService = NULL;
  305. LLS_LICENSE_INFO_1 lic;
  306. DWORD dwWait;
  307. HRESULT hr;
  308. size_t cch;
  309. #if DBG
  310. if (TraceFlags & TRACE_FUNCTION_TRACE)
  311. dprintf(TEXT("LLS TRACE: LicenseAdd\n"));
  312. #endif
  313. //init
  314. ZeroMemory(&lic, sizeof(lic));
  315. if ( ( 0 == CertificateID ) && ( ServiceIsSecure( ServiceName ) ) )
  316. {
  317. return STATUS_ACCESS_DENIED;
  318. }
  319. if ( ( 0 != ExpirationDate ) && ( ExpirationDate < DateSystemGet() ) )
  320. {
  321. // certificate has expired
  322. return STATUS_ACCOUNT_EXPIRED;
  323. }
  324. if ( ( NULL == ServiceName )
  325. || ( NULL == Vendor )
  326. || ( 0 == Quantity )
  327. || ( ( 0 != CertificateID ) && ( 0 == MaxQuantity ) )
  328. || ( NULL == Admin )
  329. || ( NULL == Comment )
  330. || ( 0 == ( AllowedModes & ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ) ) )
  331. || ( NULL == Source ) )
  332. {
  333. // invalid parameter
  334. return STATUS_INVALID_PARAMETER;
  335. }
  336. if ( NULL == Secrets )
  337. {
  338. Secrets = NullSecrets;
  339. }
  340. //
  341. // ** NEW - NT 5.0 **
  342. //
  343. // All per seat purchase requests are deferred to the site license master
  344. // server. Per server is still handled individually at each server.
  345. //
  346. if ( AllowedModes & 1 ) {
  347. //
  348. // Update enterprise information, in case the site license master
  349. // has changed.
  350. //
  351. #if DELAY_INITIALIZATION
  352. EnsureInitialized();
  353. #endif
  354. ConfigInfoUpdate(NULL,FALSE);
  355. //
  356. // Connect to the site license master server, if this server is
  357. // not the master.
  358. //
  359. RtlEnterCriticalSection(&ConfigInfoLock);
  360. if ( !ConfigInfo.IsMaster && (ConfigInfo.SiteServer != NULL)) {
  361. // Make sure function pointers are initialized
  362. Status = ReplicationInitDelayed();
  363. if (STATUS_SUCCESS != ConnectErr)
  364. {
  365. dwWait = WaitForSingleObject(g_hThrottleConnect, 0);
  366. if (dwWait == WAIT_TIMEOUT)
  367. {
  368. // We've already tried in the past 15 minutes
  369. Status = ConnectErr;
  370. RtlLeaveCriticalSection(&ConfigInfoLock);
  371. return Status;
  372. }
  373. else
  374. {
  375. ConnectErr = STATUS_SUCCESS;
  376. }
  377. }
  378. if ((NOERROR == Status) && ( pLlsConnectW != NULL )) {
  379. LLS_HANDLE LlsHandle;
  380. Status = (*pLlsConnectW)(ConfigInfo.SiteServer,
  381. &LlsHandle);
  382. if ( Status == STATUS_SUCCESS ) {
  383. LLS_LICENSE_INFO_0 LicenseInfo0;
  384. LicenseInfo0.Product = ServiceName;
  385. LicenseInfo0.Quantity = Quantity;
  386. LicenseInfo0.Date = Date;
  387. LicenseInfo0.Admin = Admin;
  388. LicenseInfo0.Comment = Comment;
  389. Status = (*pLlsLicenseAddW)(LlsHandle,
  390. 0,
  391. (LPBYTE)&LicenseInfo0);
  392. (*pLlsClose)(LlsHandle);
  393. }
  394. else {
  395. //
  396. // Connection failed
  397. // Don't allow any more connections for a while
  398. //
  399. ConnectErr = Status;
  400. }
  401. }
  402. else {
  403. if (NOERROR == Status)
  404. {
  405. //
  406. // Not the best error, but we must return something should
  407. // this obscure error condition become true.
  408. //
  409. Status = STATUS_INVALID_PARAMETER;
  410. }
  411. }
  412. RtlLeaveCriticalSection(&ConfigInfoLock);
  413. return Status;
  414. }
  415. RtlLeaveCriticalSection(&ConfigInfoLock);
  416. }
  417. RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
  418. if ( 0 != CertificateID )
  419. {
  420. lic.Product = ServiceName;
  421. lic.Vendor = Vendor;
  422. lic.Quantity = Quantity;
  423. lic.MaxQuantity = MaxQuantity;
  424. lic.Admin = Admin;
  425. lic.Comment = Comment;
  426. lic.Date = Date;
  427. lic.AllowedModes = AllowedModes;
  428. lic.CertificateID = CertificateID;
  429. lic.Source = Source;
  430. lic.ExpirationDate = ExpirationDate;
  431. memcpy( lic.Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) );
  432. if ( !CertDbClaimApprove( &lic ) )
  433. {
  434. // no way, hoser!
  435. RtlReleaseResource( &LicenseListLock );
  436. return STATUS_OBJECT_NAME_EXISTS;
  437. }
  438. }
  439. // update totals for per seat / per server mode licenses
  440. Status = STATUS_SUCCESS;
  441. if ( AllowedModes & 1 )
  442. {
  443. // per seat allowed; add to per seat license tally
  444. Status = LicenseAdd_UpdateQuantity( ServiceName,
  445. Quantity,
  446. FALSE,
  447. &Service,
  448. &ChangeLicense,
  449. &NewLicenses,
  450. &mService );
  451. }
  452. if ( ( STATUS_SUCCESS == Status ) && ( AllowedModes & 2 ) )
  453. {
  454. // per server allowed; add to per server license tally
  455. Status = LicenseAdd_UpdateQuantity( ServiceName,
  456. Quantity,
  457. TRUE,
  458. &PerServerService,
  459. &PerServerChangeLicense,
  460. &PerServerNewLicenses,
  461. &mService );
  462. }
  463. if ( STATUS_SUCCESS != Status )
  464. {
  465. RtlReleaseResource( &LicenseListLock );
  466. return Status;
  467. }
  468. //
  469. // Service now points to the service table entry - now update purchase
  470. // History.
  471. //
  472. //
  473. // First allocate members, then new struct
  474. //
  475. //
  476. // Create space for saving off the Admin Name
  477. //
  478. cch = lstrlen(Admin) + 1;
  479. NewName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  480. if (NewName == NULL)
  481. {
  482. ASSERT(FALSE);
  483. RtlReleaseResource(&LicenseListLock);
  484. return STATUS_NO_MEMORY;
  485. }
  486. // now copy it over...
  487. hr = StringCchCopy(NewName, cch, Admin);
  488. ASSERT(SUCCEEDED(hr));
  489. //
  490. // Create space for saving off the Comment
  491. //
  492. cch = lstrlen(Comment) + 1;
  493. NewComment = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  494. if (NewComment == NULL)
  495. {
  496. ASSERT(FALSE);
  497. LocalFree(NewName);
  498. RtlReleaseResource(&LicenseListLock);
  499. return STATUS_NO_MEMORY;
  500. }
  501. // now copy it over...
  502. hr = StringCchCopy(NewComment, cch, Comment);
  503. ASSERT(SUCCEEDED(hr));
  504. //
  505. // Create space for saving off the Source
  506. //
  507. cch = lstrlen(Source) + 1;
  508. NewSource = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  509. if (NewSource == NULL)
  510. {
  511. ASSERT(FALSE);
  512. LocalFree(NewName);
  513. LocalFree(NewComment);
  514. RtlReleaseResource(&LicenseListLock);
  515. return STATUS_NO_MEMORY;
  516. }
  517. // now copy it over...
  518. hr = StringCchCopy(NewSource, cch, Source);
  519. ASSERT(SUCCEEDED(hr));
  520. //
  521. // Create space for saving off the Vendor
  522. //
  523. cch = lstrlen(Vendor) + 1;
  524. NewVendor = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
  525. if (NewVendor == NULL)
  526. {
  527. ASSERT(FALSE);
  528. LocalFree(NewName);
  529. LocalFree(NewComment);
  530. LocalFree(NewSource);
  531. RtlReleaseResource(&LicenseListLock);
  532. return STATUS_NO_MEMORY;
  533. }
  534. // now copy it over...
  535. hr = StringCchCopy(NewVendor, cch, Vendor);
  536. ASSERT(SUCCEEDED(hr));
  537. if (PurchaseList == NULL)
  538. PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1));
  539. else
  540. {
  541. PLICENSE_PURCHASE_RECORD PurchaseListTemp = NULL;
  542. PurchaseListTemp = (PLICENSE_PURCHASE_RECORD) LocalReAlloc(PurchaseList, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1), LHND);
  543. if (PurchaseListTemp != NULL)
  544. {
  545. PurchaseList = PurchaseListTemp;
  546. } else
  547. {
  548. ASSERT(FALSE);
  549. LocalFree(NewName);
  550. LocalFree(NewComment);
  551. LocalFree(NewSource);
  552. LocalFree(NewVendor);
  553. RtlReleaseResource(&LicenseListLock);
  554. return STATUS_NO_MEMORY;
  555. }
  556. }
  557. //
  558. // Make sure we could allocate service table
  559. //
  560. if (PurchaseList == NULL)
  561. {
  562. ASSERT(FALSE);
  563. PurchaseListSize = 0;
  564. LocalFree(NewName);
  565. LocalFree(NewComment);
  566. LocalFree(NewSource);
  567. LocalFree(NewVendor);
  568. RtlReleaseResource(&LicenseListLock);
  569. return STATUS_NO_MEMORY;
  570. }
  571. PurchaseRecord = &PurchaseList[PurchaseListSize];
  572. PurchaseRecord->Admin = NewName;
  573. PurchaseRecord->Comment = NewComment;
  574. PurchaseRecord->Source = NewSource;
  575. PurchaseRecord->Vendor = NewVendor;
  576. //
  577. // Update the rest of the stuff.
  578. //
  579. PurchaseRecord->NumberLicenses = Quantity;
  580. PurchaseRecord->MaxQuantity = MaxQuantity;
  581. PurchaseRecord->Service = Service;
  582. PurchaseRecord->PerServerService = PerServerService;
  583. PurchaseRecord->AllowedModes = AllowedModes;
  584. PurchaseRecord->CertificateID = CertificateID;
  585. PurchaseRecord->ExpirationDate = ExpirationDate;
  586. memcpy( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) );
  587. if (Date == 0)
  588. PurchaseRecord->Date = DateSystemGet();
  589. else
  590. PurchaseRecord->Date = Date;
  591. PurchaseListSize++;
  592. RtlReleaseResource(&LicenseListLock);
  593. if ( 0 != CertificateID )
  594. {
  595. // these should still be set from above
  596. ASSERT( lic.Product == ServiceName );
  597. ASSERT( lic.Vendor == Vendor );
  598. ASSERT( lic.Quantity == Quantity );
  599. ASSERT( lic.MaxQuantity == MaxQuantity );
  600. ASSERT( lic.Admin == Admin );
  601. ASSERT( lic.Comment == Comment );
  602. ASSERT( lic.Date == Date );
  603. ASSERT( lic.AllowedModes == AllowedModes );
  604. ASSERT( lic.CertificateID == CertificateID );
  605. ASSERT( lic.Source == Source );
  606. ASSERT( lic.ExpirationDate == ExpirationDate );
  607. ASSERT( 0 == memcmp( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) ) );
  608. CertDbClaimEnter( NULL, &lic, FALSE, 0 );
  609. }
  610. //
  611. // Now check if we need to re-scan the user list and update licenses
  612. //
  613. if (ChangeLicense)
  614. {
  615. if ( NewLicenses < 0 )
  616. UserListLicenseDelete( mService, NewLicenses );
  617. if ( NewLicenses > 0 )
  618. FamilyLicenseUpdate ( mService->Family );
  619. }
  620. if ( AllowedModes & 2 )
  621. {
  622. // per server licenses modified
  623. LocalServiceListConcurrentLimitSet();
  624. LocalServerServiceListUpdate();
  625. ServiceListResynch();
  626. }
  627. return STATUS_SUCCESS;
  628. } // LicenseAdd
  629. /////////////////////////////////////////////////////////////////////////
  630. static
  631. NTSTATUS
  632. LicenseAdd_UpdateQuantity(
  633. LPTSTR ServiceName,
  634. LONG Quantity,
  635. BOOL UsePerServerList,
  636. PLICENSE_SERVICE_RECORD * ppService,
  637. BOOL * pChangeLicense,
  638. LONG * pNewLicenses,
  639. PMASTER_SERVICE_RECORD * pmService
  640. )
  641. {
  642. BOOL ChangeLicense = FALSE;
  643. PLICENSE_SERVICE_RECORD Service;
  644. PMASTER_SERVICE_RECORD mService;
  645. LONG NewLicenses = 0;
  646. Service = LicenseServiceListFind( ServiceName, UsePerServerList );
  647. //
  648. // If we didn't find it we will need to add it.
  649. //
  650. if (Service == NULL)
  651. {
  652. if (Quantity < 0)
  653. {
  654. #if DBG
  655. dprintf(TEXT("Releasing Licenses from Non-existant product!\n"));
  656. #endif
  657. // ASSERT(FALSE);
  658. return STATUS_UNSUCCESSFUL;
  659. }
  660. else
  661. {
  662. Service = LicenseServiceListAdd(ServiceName, UsePerServerList);
  663. }
  664. }
  665. //
  666. // Check to make sure we found or added it. The only way we can fail is
  667. // if we couldn't alloc memory for it.
  668. //
  669. if (Service == NULL)
  670. {
  671. ASSERT(FALSE);
  672. return STATUS_NO_MEMORY;
  673. }
  674. if (((LONG) Service->NumberLicenses + Quantity) < 0)
  675. {
  676. return STATUS_UNSUCCESSFUL;
  677. }
  678. //
  679. // Update license count in service record
  680. //
  681. Service->NumberLicenses += Quantity;
  682. //
  683. // Now in master Service Record
  684. //
  685. RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
  686. mService = MasterServiceListFind(ServiceName);
  687. if (mService != NULL)
  688. {
  689. //
  690. // if we were out of license and added more, then we need to update
  691. // the user list.
  692. //
  693. if ( ( Quantity > 0 )
  694. && ( (mService->LicensesUsed > mService->Licenses) || (mService == BackOfficeRec) ) )
  695. {
  696. ChangeLicense = TRUE;
  697. }
  698. //
  699. // Can only add a number of licenses up to licensed amount
  700. //
  701. if ( ChangeLicense )
  702. {
  703. // Get current unlicensed delta
  704. NewLicenses = mService->LicensesUsed - mService->Licenses;
  705. if ((NewLicenses <= 0) || (NewLicenses > Quantity))
  706. {
  707. NewLicenses = Quantity;
  708. }
  709. }
  710. if ( UsePerServerList )
  711. {
  712. // this will be done by LicenseAdd() in LocalServerServiceListUpdate()
  713. // mService->MaxSessionCount += Quantity;
  714. }
  715. else
  716. {
  717. mService->Licenses += Quantity;
  718. }
  719. //
  720. // if we we subtracted licenses and are out of licenses, then we
  721. // need to scan the user list.
  722. //
  723. if (Quantity < 0)
  724. {
  725. if (mService->LicensesUsed > mService->Licenses)
  726. {
  727. ChangeLicense = TRUE;
  728. //
  729. // Only remove # of licenses past limit
  730. //
  731. NewLicenses = mService->Licenses - mService->LicensesUsed;
  732. if (NewLicenses < Quantity)
  733. {
  734. NewLicenses = Quantity;
  735. }
  736. }
  737. }
  738. }
  739. RtlReleaseResource(&MasterServiceListLock);
  740. ASSERT(NULL != ppService &&
  741. NULL != pChangeLicense &&
  742. NULL != pNewLicenses &&
  743. NULL != pmService);
  744. *ppService = Service;
  745. *pChangeLicense = ChangeLicense;
  746. *pNewLicenses = NewLicenses;
  747. *pmService = mService;
  748. return STATUS_SUCCESS;
  749. }
  750. #if DBG
  751. /////////////////////////////////////////////////////////////////////////
  752. VOID
  753. LicenseListDebugDump( )
  754. /*++
  755. Routine Description:
  756. Arguments:
  757. Return Value:
  758. --*/
  759. {
  760. ULONG i = 0;
  761. ULONG j = 0;
  762. HRESULT hr;
  763. //
  764. // Need to scan list so get read access.
  765. //
  766. RtlAcquireResourceShared(&LicenseListLock, TRUE);
  767. //
  768. // Dump License Service List first
  769. //
  770. dprintf(TEXT("Per Seat License Service Table, # Entries: %lu\n"), LicenseServiceListSize);
  771. if (LicenseServiceList != NULL)
  772. {
  773. for (i = 0; i < LicenseServiceListSize; i++)
  774. dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, LicenseServiceList[i]->NumberLicenses, LicenseServiceList[i]->ServiceName);
  775. }
  776. dprintf(TEXT("\nPer Server License Service Table, # Entries: %lu\n"), PerServerLicenseServiceListSize);
  777. if (PerServerLicenseServiceList != NULL)
  778. {
  779. for (i = 0; i < PerServerLicenseServiceListSize; i++)
  780. dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, PerServerLicenseServiceList[i]->NumberLicenses, PerServerLicenseServiceList[i]->ServiceName);
  781. }
  782. //
  783. // Now do purchase history
  784. //
  785. dprintf(TEXT("\nPurchase History, # Entries: %lu\n"), PurchaseListSize);
  786. if (PurchaseList != NULL)
  787. {
  788. for (i = 0; i < PurchaseListSize; i++)
  789. {
  790. TCHAR szExpirationDate[ 40 ];
  791. hr = StringCbCopy( szExpirationDate, sizeof(szExpirationDate), TimeToString( PurchaseList[i].ExpirationDate ) );
  792. ASSERT(SUCCEEDED(hr));
  793. dprintf( TEXT(" %3lu) Product : %s\n" )
  794. TEXT( " Vendor : %s\n" )
  795. TEXT( " Allowed Modes :%s%s\n" )
  796. TEXT( " Licenses : %d\n" )
  797. TEXT( " Max Licenses : %lu\n" )
  798. TEXT( " Date Entered : %s\n" )
  799. TEXT( " Date Expires : %s\n" )
  800. TEXT( " Certificate ID : %lu\n" )
  801. TEXT( " Secrets :" ),
  802. i,
  803. ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
  804. ? PurchaseList[i].Service->ServiceName
  805. : PurchaseList[i].PerServerService->ServiceName,
  806. PurchaseList[i].Vendor,
  807. ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
  808. ? TEXT(" PERSEAT")
  809. : TEXT(""),
  810. ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
  811. ? TEXT(" PERSERVER")
  812. : TEXT(""),
  813. PurchaseList[i].NumberLicenses,
  814. PurchaseList[i].MaxQuantity,
  815. TimeToString( PurchaseList[i].Date ),
  816. szExpirationDate,
  817. PurchaseList[i].CertificateID
  818. );
  819. for ( j=0; j < LLS_NUM_SECRETS; j++ )
  820. {
  821. dprintf( TEXT( " %08X" ), PurchaseList[i].Secrets[j] );
  822. }
  823. dprintf( TEXT( "\n" )
  824. TEXT( " Source : %s\n" )
  825. TEXT( " Admin : %s\n" )
  826. TEXT( " Comment : %s\n\n" ),
  827. PurchaseList[i].Source,
  828. PurchaseList[i].Admin,
  829. PurchaseList[i].Comment
  830. );
  831. }
  832. }
  833. RtlReleaseResource(&LicenseListLock);
  834. return;
  835. } // LicenseListDebugDump
  836. #endif //DBG