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.

2607 lines
74 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. queue.cpp
  5. Abstract:
  6. queue algorithms and data structures to handle policy notifications
  7. Author:
  8. Vishnu Patankar (vishnup) 15-Aug-2000 created
  9. --*/
  10. #include "serverp.h"
  11. #include "sceutil.h"
  12. #include "queue.h"
  13. #include "sddl.h"
  14. extern HINSTANCE MyModuleHandle;
  15. static HANDLE hNotificationThread = NULL;
  16. static HANDLE ghEventNotificationQEnqueue = NULL;
  17. static HANDLE ghEventPolicyPropagation = NULL;
  18. HANDLE ghEventSamFilterAndPolicyPropExclusion = NULL;
  19. static BOOL gbShutdownForNotification = FALSE;
  20. static HANDLE hNotificationLogFile = INVALID_HANDLE_VALUE;
  21. #define SCEPOL_NOTIFY_DEBUG_LEVEL L"PolicyDebugLevel"
  22. #define SCEPOL_NOTIFY_LOG_SIZE L"PolicyLogSize"
  23. #define SCEPOL_NOTIFY_MAX_PDC_WAIT L"PolicyMaxWaitPDCSync"
  24. #define SCEPOL_NOTIFY_PDC_RETRY_INTERVAL L"PolicyPDCSyncRetryInterval"
  25. #define SCEPOL_NOTIFY_ALLOW_PDC_DIFF L"PolicyAllowedDiffTime"
  26. #define SCEPOL_NOTIFY_REQUIRE_PDC_SYNC L"PolicyRequirePDCSync"
  27. //
  28. // turn-on logging until registry queried by system thread (at least until testing)
  29. //
  30. static DWORD gdwNotificationLog = 2;
  31. DWORD gdwMaxPDCWait = 30*24*60;
  32. DWORD gdwPDCRetry = 20;
  33. DWORD gdwRequirePDCSync = 1;
  34. BOOL gbCheckSync = FALSE;
  35. BOOL bQueriedProductTypeForNotification = FALSE;
  36. static NT_PRODUCT_TYPE ProductTypeForNotification;
  37. static CRITICAL_SECTION NotificationQSync;
  38. #define SCEP_MAX_RETRY_NOTIFICATIONQ_NODES 1000
  39. #define SCEP_NOTIFICATION_RETRY_COUNT 10
  40. #define SCEP_NOTIFICATION_LOGFILE_SIZE 0x1 << 20
  41. #define SCEP_NOTIFICATION_EVENT_TIMEOUT_SECS 600
  42. #define SCEP_MINIMUM_DISK_SPACE 5 * (0x1 << 20)
  43. #define SCEP_NOTIFICATION_EVENT L"E_ScepNotificationQEnqueue"
  44. #define SCEP_POLICY_PROP_EVENT L"E_ScepPolicyPropagation"
  45. //
  46. // queue head needs to be accessed in server.cpp
  47. //
  48. PSCESRV_POLQUEUE pNotificationQHead=NULL;
  49. static PSCESRV_POLQUEUE pNotificationQTail=NULL;
  50. static DWORD gdwNumNotificationQNodes = 0;
  51. static DWORD gdwNumNotificationQRetryNodes = 0;
  52. static BOOL gbSuspendQueue=FALSE;
  53. PWSTR OpTypeTable[] = {
  54. L"Enqueue",
  55. L"Dequeue",
  56. L"Retry",
  57. L"Process"
  58. };
  59. DWORD
  60. ScepCheckAndWaitFreeDiskSpaceInSysvol();
  61. //
  62. // todo - consider exception handling in the enqueue routine (in case we claim a critsec and av)
  63. //
  64. DWORD
  65. ScepNotificationQInitialize(
  66. )
  67. /*
  68. Routine Description:
  69. This function is called when SCE server data is initialized (system startup).
  70. This function initializes data/buffer/state related to queue
  71. management, for example, the queue, critical sections, global variables, etc.
  72. This function also checks for any saved queue items from previous system
  73. shutdown and initializes the queue with the saved items.
  74. Arguments:
  75. None
  76. Return Value:
  77. Win32 error code
  78. */
  79. {
  80. DWORD rc = ERROR_SUCCESS;
  81. //
  82. // initialize head and tail to reflect an empty queue
  83. //
  84. pNotificationQHead = NULL;
  85. pNotificationQTail = NULL;
  86. //
  87. // initialize the log file on DCs only (for perf reasons no need to do so for non-DCs)
  88. // (assuming that this routine is called before ScepQueueStartSystemThread)
  89. //
  90. if ( RtlGetNtProductType( &ProductTypeForNotification ) ) {
  91. if (ProductTypeForNotification != NtProductLanManNt )
  92. gdwNotificationLog = 0;
  93. bQueriedProductTypeForNotification = TRUE;
  94. }
  95. ScepNotificationLogOpen();
  96. //
  97. // this critical section is used to sequentialize writes to the queue
  98. // reads need not be protected against/from
  99. //
  100. ScepNotifyLogPolicy(0, TRUE, L"Initialize NotificationQSync", 0, 0, NULL );
  101. InitializeCriticalSection(&NotificationQSync);
  102. //
  103. // check for any saved queue items from previous system
  104. // initialize queue with these items from some persistent store
  105. //
  106. rc = ScepNotificationQUnFlush();
  107. ScepNotifyLogPolicy(rc, FALSE, L"Unflush Notification Queue", 0, 0, NULL );
  108. return rc;
  109. }
  110. NTSTATUS
  111. ScepQueueStartSystemThread(
  112. )
  113. /*
  114. Routine Description:
  115. This function is called when SCE server is started (system startup) after
  116. RPC server starts listening.
  117. This function creates a worker thread. The worker thread manages the queue.
  118. If the thread fails to get created, an error is returned.
  119. Arguments:
  120. None
  121. Return Value:
  122. Dos error code
  123. */
  124. {
  125. DWORD rc = ERROR_SUCCESS;
  126. DWORD dwThreadId = 0;
  127. WCHAR pszMsg[MAX_PATH];
  128. //
  129. // ProductType of the machine is initialized into a thread global variable to be
  130. // used by policy filter. The type determines where to save the policy changes (DB or GP).
  131. // Based on the product type, different policy notification APIs are called.
  132. //
  133. if ( !bQueriedProductTypeForNotification && !RtlGetNtProductType( &ProductTypeForNotification ) ) {
  134. //
  135. // on failure, ProductTypeForNotification = NtProductWinNt, continue
  136. //
  137. ScepNotifyLogPolicy(ERROR_BAD_ENVIRONMENT, TRUE, L"Get product type", 0, 0, NULL );
  138. }
  139. if (ProductTypeForNotification == NtProductLanManNt ) {
  140. //
  141. // create a manual reset event whose initial state is set
  142. // this event enforces mutual exclusion between SAM filter
  143. // notifications and SCE policy propagation
  144. //
  145. ghEventSamFilterAndPolicyPropExclusion = CreateEvent(
  146. NULL,
  147. TRUE,
  148. TRUE,
  149. SCEP_SAM_FILTER_POLICY_PROP_EVENT
  150. );
  151. if ( ghEventSamFilterAndPolicyPropExclusion ) {
  152. ScepNotifyLogPolicy(0, FALSE, L"Successfully created event E_ScepSamFilterAndPolicyPropExclusion ", 0, 0, NULL );
  153. //
  154. // create an event which is signalled when a node is enqueued into the notification queue
  155. // this event helps the notification system thread to wait efficiently
  156. //
  157. ghEventNotificationQEnqueue = CreateEvent(
  158. NULL,
  159. FALSE,
  160. FALSE,
  161. SCEP_NOTIFICATION_EVENT
  162. );
  163. if ( ghEventNotificationQEnqueue ) {
  164. ScepNotifyLogPolicy(0, FALSE, L"Successfully created event E_ScepNotificationQEnqueue ", 0, 0, NULL );
  165. //
  166. // create an event for policy propagation
  167. //
  168. ghEventPolicyPropagation = CreateEvent(
  169. NULL,
  170. FALSE,
  171. FALSE,
  172. SCEP_POLICY_PROP_EVENT
  173. );
  174. if ( ghEventPolicyPropagation ) {
  175. ScepNotifyLogPolicy(0, FALSE, L"Successfully created event E_ScepPolicyPropagation", 0, 0, NULL );
  176. //
  177. // Create a worker thread that's always running in
  178. // services this thread constantly monitors notifications inserted into
  179. // the NotificationQ by LSA's threads and processes them
  180. //
  181. hNotificationThread = CreateThread(
  182. NULL,
  183. 0,
  184. (PTHREAD_START_ROUTINE)ScepNotificationQSystemThreadFunc,
  185. NULL,
  186. 0,
  187. (LPDWORD)&dwThreadId
  188. );
  189. if (hNotificationThread) {
  190. pszMsg[0] = L'\0';
  191. swprintf(pszMsg, L"Thread %x", dwThreadId);
  192. ScepNotifyLogPolicy(0, TRUE, L"Create Notification Thread Success", 0, 0, pszMsg );
  193. }
  194. else {
  195. rc = GetLastError();
  196. ScepNotifyLogPolicy(rc, TRUE, L"Create Notification Thread Failure", 0, 0, NULL );
  197. ScepNotificationQCleanup();
  198. }
  199. }
  200. else {
  201. rc = GetLastError();
  202. ScepNotifyLogPolicy(rc, FALSE, L"Error creating event E_ScepPolicyPropagation", 0, 0, NULL );
  203. ScepNotificationQCleanup();
  204. }
  205. }
  206. else {
  207. rc = GetLastError();
  208. ScepNotifyLogPolicy(rc, FALSE, L"Error creating event E_ScepNotificationQEnqueue ", 0, 0, NULL );
  209. ScepNotificationQCleanup();
  210. }
  211. }
  212. else {
  213. rc = GetLastError();
  214. ScepNotifyLogPolicy(rc, FALSE, L"Error creating event E_ScepSamFilterAndPolicyPropExclusion ", 0, 0, NULL );
  215. ScepNotificationQCleanup();
  216. }
  217. } else {
  218. ScepNotifyLogPolicy(0, FALSE, L"Policy filter is not designed for non domain controllers", 0, 0, NULL );
  219. //
  220. // if changed to DC, have to reboot so cleanup anyway
  221. //
  222. ScepNotificationQCleanup();
  223. }
  224. if (ERROR_SUCCESS != rc && hNotificationThread)
  225. {
  226. CloseHandle(hNotificationThread);
  227. hNotificationThread = NULL;
  228. }
  229. return rc;
  230. }
  231. DWORD
  232. ScepQueuePrepareShutdown(
  233. )
  234. /*
  235. Routine Description:
  236. This function is called when SCE server is requested to shut down (system
  237. shutdown) after RPC server stops listening.
  238. This function waits for SCEP_NOTIFICATION_TIMEOUT period to allow the system thread
  239. finish up with the queue items. After the timeout, it kills the worker
  240. thread and saves the pending queue items.
  241. Arguments:
  242. None
  243. Return Value:
  244. Dos error code
  245. */
  246. {
  247. ScepNotifyLogPolicy(0, TRUE, L"System shutdown", 0, 0, NULL );
  248. //
  249. // gracefully shutdown the notification system thread by setting a global
  250. //
  251. gbShutdownForNotification = TRUE;
  252. //
  253. // if notification thread is never initialized, there is no point to wait
  254. // which may delay services.exe shutdown
  255. //
  256. if ( ghEventNotificationQEnqueue ) {
  257. // sleep for 10 seconds first
  258. // then check the queue for empty
  259. Sleep( 10*000 );
  260. if ( pNotificationQHead ) {
  261. Sleep( SCEP_NUM_SHUTDOWN_SECONDS * 1000 );
  262. }
  263. }
  264. if (hNotificationThread &&
  265. WAIT_TIMEOUT == WaitForSingleObjectEx(
  266. hNotificationThread,
  267. SCEP_NUM_SHUTDOWN_SECONDS * 1000,
  268. FALSE)) {
  269. // wait for the notification thread to terminate before deleting
  270. // the critical section
  271. ScepNotifyLogPolicy(0, FALSE, L"Terminating Notification Thread", 0, 0, NULL );
  272. DWORD rc = RtlNtStatusToDosError(NtTerminateThread(
  273. hNotificationThread,
  274. STATUS_SYSTEM_SHUTDOWN
  275. ));
  276. ScepNotifyLogPolicy(rc, FALSE, L"Terminated Notification Thread", 0, 0, NULL );
  277. }
  278. DeleteCriticalSection(&NotificationQSync);
  279. if (hNotificationThread) {
  280. CloseHandle(hNotificationThread);
  281. }
  282. (void) ShutdownEvents();
  283. return ERROR_SUCCESS;
  284. }
  285. VOID
  286. ScepNotificationQDequeue(
  287. IN BOOL bAllNodes
  288. )
  289. /*
  290. Routine Description:
  291. This function pops one node from the queue.
  292. The queue is a singly linked list ->->-> which pushes at tail(rightmost) and pops
  293. from the head(leftmost)
  294. Arguments:
  295. None
  296. Return Value:
  297. None
  298. */
  299. {
  300. EnterCriticalSection(&NotificationQSync);
  301. ScepNotifyLogPolicy(0, TRUE, L"Entered NotificationQSync for Dequeueing", 0, 0, NULL );
  302. do {
  303. if ( pNotificationQHead ) {
  304. PSCESRV_POLQUEUE pQNode = pNotificationQHead;
  305. if ( pNotificationQTail == pNotificationQHead ) {
  306. //
  307. // there is only one node in the queue
  308. //
  309. pNotificationQTail = NULL;
  310. }
  311. //
  312. // move head to the next one
  313. //
  314. pNotificationQHead = pNotificationQHead->Next;
  315. //
  316. // log and free the node
  317. //
  318. ScepNotificationQNodeLog(pQNode, ScepNotificationDequeue);
  319. ScepFree(pQNode);
  320. -- gdwNumNotificationQNodes;
  321. }
  322. //
  323. // if request to dequeue all nodes, loop through the queue until head is empty
  324. //
  325. } while ( bAllNodes && pNotificationQHead );
  326. ScepNotifyLogPolicy(0, FALSE, L"Leaving NotificationQSync after Dequeueing", 0, 0, NULL );
  327. LeaveCriticalSection(&NotificationQSync);
  328. return;
  329. }
  330. DWORD
  331. ScepNotificationQEnqueue(
  332. IN SECURITY_DB_TYPE DbType,
  333. IN SECURITY_DB_DELTA_TYPE DeltaType,
  334. IN SECURITY_DB_OBJECT_TYPE ObjectType,
  335. IN PSID ObjectSid OPTIONAL,
  336. IN DWORD ExplicitLowRight,
  337. IN DWORD ExplicitHighRight,
  338. IN PSCESRV_POLQUEUE pRetryQNode OPTIONAL
  339. )
  340. /*
  341. Description:
  342. This function is called to add a notification to the queue. Note that
  343. only two notifications for the same data are added to the queue.
  344. The operation is protected from other reads/writes.
  345. Access check is already done outside of this function.
  346. Either the created threads call this routine or the system calls this routine.
  347. In the former case, pQNode = NULL
  348. Arguments:
  349. DbType - SAM or LSA
  350. DeltaType - type of change (add. delete etc.). For SAM accounts, we'll only get delete since
  351. some user rights may have to be removed.
  352. ObjectType - SECURITY_DB_OBJECT_TYPE such as SAM group, LSA account etc.
  353. ObjectSid - sid of the object being notified (might be NULL if ObjectType ==
  354. SecurityDbObjectLsaPolicy i.e. auditing info etc.)
  355. ExplicitLowRight - Bitmask of user rights (lower 32 rights)
  356. ExplicitHighRight - Bitmask of user rights (higher 32 rights)
  357. pRetryQNode - If caller is not the system thread ScepNotificationQSystemThreadFunc,
  358. this parameter is NULL since memory needs to be allocated. If caller
  359. is not the system thread, we only have to do pointer manipulations.
  360. Return Value:
  361. Win32 error code
  362. */
  363. {
  364. DWORD rc = ERROR_SUCCESS;
  365. //
  366. // on Whistler, only allow notifications on DCs
  367. // on Windows 2000, all products are allowed.
  368. //
  369. if (ProductTypeForNotification != NtProductLanManNt ) {
  370. //
  371. // what error is okay to return ?
  372. //
  373. return ERROR_SUCCESS;
  374. }
  375. //
  376. // check parameters
  377. //
  378. if ( DbType == SecurityDbLsa &&
  379. ObjectType == SecurityDbObjectLsaAccount &&
  380. ObjectSid == NULL ) {
  381. return(ERROR_INVALID_PARAMETER);
  382. }
  383. if ( DbType == SecurityDbSam &&
  384. SCEP_IS_SAM_OBJECT(ObjectType) &&
  385. ObjectSid == NULL ) {
  386. return(ERROR_INVALID_PARAMETER);
  387. }
  388. //
  389. // if the number of retried notifications is over certain limit (this is yet to be finalized)
  390. // - on a PDC we consider a full sync at reboot, stall future notifications from happening,
  391. // set some registry key to indicate that a full sync is needed at reboot
  392. // - on other DCs we continue as if nothing happened
  393. //
  394. if ( gdwNumNotificationQRetryNodes >= SCEP_MAX_RETRY_NOTIFICATIONQ_NODES ) {
  395. //
  396. // log an error
  397. // suggest to do a full sync ???
  398. //
  399. ScepNotifyLogPolicy(ERROR_NOT_ENOUGH_QUOTA, TRUE, L"Queue length is over the maximal limit.", 0, 0, NULL );
  400. return ERROR_NOT_ENOUGH_QUOTA;
  401. }
  402. EnterCriticalSection(&NotificationQSync);
  403. ScepNotifyLogPolicy(0, TRUE, L"Entered NotificationQSync for Enqueueing", 0, 0, NULL );
  404. //
  405. // if we are enqueueing due to a retry
  406. // dequeue it (adjust pointers only)
  407. // do not free the memory associated with this node (will be reused in enqueue)
  408. //
  409. if ( pRetryQNode && pNotificationQHead ) {
  410. //
  411. // this code fragment is similar to a dequeue except that the node is not freed
  412. //
  413. if ( pNotificationQTail == pNotificationQHead ) {
  414. //
  415. // there is only one node in the queue
  416. //
  417. pNotificationQTail = NULL;
  418. }
  419. //
  420. // move head to the next one
  421. //
  422. pNotificationQHead = pNotificationQHead->Next;
  423. -- gdwNumNotificationQNodes;
  424. }
  425. //
  426. // check to see if there is a duplicate notification
  427. //
  428. PSCESRV_POLQUEUE pQNode = pNotificationQHead;
  429. PSCESRV_POLQUEUE pQNodeDuplicate1 = NULL;
  430. PSCESRV_POLQUEUE pQNodeDuplicate2 = NULL;
  431. DWORD dwMatchedInstance = 0;
  432. while ( pQNode ) {
  433. //
  434. // SAM notification
  435. //
  436. if ( DbType == SecurityDbSam &&
  437. pQNode->DbType == DbType &&
  438. SCEP_IS_SAM_OBJECT(ObjectType) &&
  439. SCEP_IS_SAM_OBJECT(pQNode->ObjectType) &&
  440. pQNode->ObjectType == ObjectType &&
  441. RtlEqualSid(ObjectSid, (PSID)(pQNode->Sid))) {
  442. dwMatchedInstance++;
  443. }
  444. else if (DbType == SecurityDbSam &&
  445. pQNode->DbType == SecurityDbSam &&
  446. ObjectType == SecurityDbObjectSamDomain &&
  447. pQNode->ObjectType == SecurityDbObjectSamDomain) {
  448. dwMatchedInstance++;
  449. }
  450. //
  451. // LSA notification
  452. //
  453. else if ( DbType == SecurityDbLsa &&
  454. pQNode->DbType == DbType &&
  455. ObjectType == SecurityDbObjectLsaAccount &&
  456. pQNode->ObjectType == ObjectType &&
  457. ExplicitLowRight == pQNode->ExplicitLowRight &&
  458. ExplicitHighRight == pQNode->ExplicitHighRight &&
  459. RtlEqualSid(ObjectSid, (PSID)(pQNode->Sid))) {
  460. dwMatchedInstance++;
  461. }
  462. if ( dwMatchedInstance == 1 )
  463. pQNodeDuplicate1 = pQNode;
  464. else if ( dwMatchedInstance == 2 )
  465. pQNodeDuplicate2 = pQNode;
  466. if ( dwMatchedInstance == 2 ) {
  467. break;
  468. }
  469. pQNode = pQNode->Next;
  470. }
  471. if ( !pQNode ) {
  472. //
  473. // did not find two instances of the same kind of notification
  474. // enqueue this notification
  475. //
  476. PSCESRV_POLQUEUE pNewItem = NULL;
  477. if (pRetryQNode == NULL) {
  478. pNewItem = (PSCESRV_POLQUEUE)ScepAlloc(0, sizeof(SCESRV_POLQUEUE));
  479. if ( pNewItem ) {
  480. //
  481. // initialize the new node
  482. //
  483. pNewItem->dwPending = 1;
  484. pNewItem->DbType = DbType;
  485. pNewItem->ObjectType = ObjectType;
  486. pNewItem->DeltaType = DeltaType;
  487. pNewItem->ExplicitLowRight = ExplicitLowRight;
  488. pNewItem->ExplicitHighRight = ExplicitHighRight;
  489. pNewItem->Next = NULL;
  490. if ( ObjectSid ) {
  491. RtlCopySid (MAX_SID_LENGTH, (PSID)(pNewItem->Sid), ObjectSid);
  492. } else {
  493. RtlZeroMemory(pNewItem->Sid, MAX_SID_LENGTH);
  494. }
  495. } else {
  496. rc = ERROR_NOT_ENOUGH_MEMORY;
  497. }
  498. }
  499. else {
  500. pNewItem = pRetryQNode;
  501. pNewItem->Next = NULL;
  502. }
  503. //
  504. // enqueue the notification
  505. //
  506. if (pNewItem) {
  507. if ( pNotificationQTail ) {
  508. pNotificationQTail->Next = pNewItem;
  509. pNotificationQTail = pNewItem;
  510. } else {
  511. pNotificationQHead = pNotificationQTail = pNewItem;
  512. }
  513. //
  514. // awake the notification system thread only if queue is non-empty
  515. // multiple signalling is fine since the event stays signalled
  516. //
  517. if ( !SetEvent( ghEventNotificationQEnqueue ) ) {
  518. rc = GetLastError();
  519. ScepNotifyLogPolicy(rc, FALSE, L"Error signaling event E_ScepNotificationQEnqueue", 0, 0, NULL );
  520. }
  521. ScepNotificationQNodeLog(pNewItem, pRetryQNode ? ScepNotificationRetry : ScepNotificationEnqueue);
  522. ++ gdwNumNotificationQNodes;
  523. } else {
  524. //
  525. // log the error
  526. //
  527. ScepNotifyLogPolicy(rc, FALSE, L"Error allocating buffer for the enqueue operation.", 0, 0, NULL );
  528. }
  529. }
  530. if (pRetryQNode ) {
  531. //
  532. // if duplicates, update the retry counts (should be same for instances of
  533. // the same notification
  534. //
  535. if ( pQNodeDuplicate1 ) {
  536. // increment the retry count if it has not been
  537. if ( pQNodeDuplicate1->dwPending <= 1 ) gdwNumNotificationQRetryNodes++;
  538. pQNodeDuplicate1->dwPending = pRetryQNode->dwPending;
  539. }
  540. if ( pQNodeDuplicate2 ) {
  541. // increment the retry count if it has not been
  542. if ( pQNodeDuplicate2->dwPending <= 1 ) gdwNumNotificationQRetryNodes++;
  543. pQNodeDuplicate2->dwPending = pRetryQNode->dwPending;
  544. }
  545. }
  546. ScepNotifyLogPolicy(0, FALSE, L"Leaving NotificationQSync for Enqueueing", 0, 0, NULL );
  547. LeaveCriticalSection(&NotificationQSync);
  548. return(rc);
  549. }
  550. DWORD
  551. ScepNotificationQSystemThreadFunc(
  552. )
  553. /*
  554. Description:
  555. The thread will iterate through the notification queue to process each
  556. notification (calling existing functions). If a notification fails to be
  557. processed, the notification is added back to the end of the queue.
  558. For certain errors, such as sysvol is not ready, or hard disk is full,
  559. the system thread will sleep for some time then restart the process.
  560. Each notification node in the queue will have a retry count. After the node
  561. is retried for SCEP_NOTIFICATION_RETRY_COUNT times, the node will be removed
  562. from the queue (so that policy propagation is not blocked forever) and the
  563. error will be logged to event log.
  564. The system thread should provide logging for the operations/status (success
  565. and failure).
  566. Read/Write to the queue should be protected from other reads/writes.
  567. ProductType of the machine should be initialized into a thread global variable to
  568. be used by policy filter. The type determines where to save the policy changes
  569. (DB or GP). Based on the product type, different policy notification APIs are called.
  570. Arguments:
  571. None
  572. Return Value:
  573. Win32 error code
  574. */
  575. {
  576. //
  577. // loop through the queue
  578. // for each queue node, call the function to process
  579. // in Whistler, only process notification on DCs
  580. // in Windows 2000, process notificatioon on all products
  581. //
  582. PSCESRV_POLQUEUE pQNode = pNotificationQHead;
  583. DWORD dwMatchedInstance = 0;
  584. DWORD rc = ERROR_SUCCESS;
  585. DWORD dwLogSize=0;
  586. DWORD dwProcessedNode=0;
  587. //
  588. // this thread is always running looking to process notifications in the queue
  589. //
  590. (void) InitializeEvents(L"SceSrv");
  591. while (1) {
  592. if (pQNode) {
  593. //
  594. // query registry flag for log level (until now it is set to 2 because if
  595. // anything bad happens before this, we need to log it)
  596. // query only if first node
  597. //
  598. if (ERROR_SUCCESS != ScepRegQueryIntValue(
  599. HKEY_LOCAL_MACHINE,
  600. SCE_ROOT_PATH,
  601. SCEPOL_NOTIFY_DEBUG_LEVEL,
  602. &gdwNotificationLog
  603. )) {
  604. //
  605. // set the value to 2, in case the registry value doesn't exist
  606. //
  607. gdwNotificationLog = 2;
  608. }
  609. //
  610. // get log file size
  611. //
  612. if (ERROR_SUCCESS != ScepRegQueryIntValue(
  613. HKEY_LOCAL_MACHINE,
  614. SCE_ROOT_PATH,
  615. SCEPOL_NOTIFY_LOG_SIZE,
  616. &dwLogSize
  617. )) {
  618. dwLogSize = 0;
  619. }
  620. //
  621. // minimum log size 1MB
  622. //
  623. if ( dwLogSize > 0 ) dwLogSize = dwLogSize * (1 << 10); // number of KB
  624. if ( dwLogSize < SCEP_NOTIFICATION_LOGFILE_SIZE ) dwLogSize = SCEP_NOTIFICATION_LOGFILE_SIZE;
  625. if ( !gbCheckSync ) {
  626. //
  627. // query Maximum wait time values
  628. //
  629. if (ERROR_SUCCESS != ScepRegQueryIntValue(
  630. HKEY_LOCAL_MACHINE,
  631. SCE_ROOT_PATH,
  632. SCEPOL_NOTIFY_MAX_PDC_WAIT,
  633. &gdwMaxPDCWait
  634. )) {
  635. //
  636. // set the value to default 30 days, in case the registry value doesn't exist
  637. //
  638. gdwMaxPDCWait = 30*24*60;
  639. }
  640. if (ERROR_SUCCESS != ScepRegQueryIntValue(
  641. HKEY_LOCAL_MACHINE,
  642. SCE_ROOT_PATH,
  643. SCEPOL_NOTIFY_PDC_RETRY_INTERVAL,
  644. &gdwPDCRetry
  645. )) {
  646. //
  647. // set the value to default 20 minutes, in case the registry value doesn't exist
  648. //
  649. gdwPDCRetry = 20;
  650. }
  651. if (ERROR_SUCCESS != ScepRegQueryIntValue(
  652. HKEY_LOCAL_MACHINE,
  653. SCE_ROOT_PATH,
  654. SCEPOL_NOTIFY_REQUIRE_PDC_SYNC,
  655. &gdwRequirePDCSync
  656. )) {
  657. //
  658. // set the value to default TRUE, in case the registry value doesn't exist
  659. //
  660. gdwRequirePDCSync = 1;
  661. }
  662. }
  663. } else {
  664. //
  665. // no notifications - wait efficiently/responsively for an enqueue event
  666. // it's an auto reset event - so will get reset after it goes past when signalled
  667. //
  668. //
  669. // timeout is SCEP_NOTIFICATION_EVENT_TIMEOUT_SECS since we need to periodically
  670. // check for system shutdown if there are no enqueue events at all
  671. //
  672. while (1) {
  673. rc = WaitForSingleObjectEx(
  674. ghEventNotificationQEnqueue,
  675. SCEP_NOTIFICATION_EVENT_TIMEOUT_SECS*1000,
  676. FALSE
  677. );
  678. if ( rc == -1 )
  679. rc = GetLastError();
  680. if ( gbShutdownForNotification )
  681. break;
  682. //
  683. // if event was signalled and wait happened successfully, move on
  684. //
  685. if ( rc == WAIT_OBJECT_0 )
  686. break;
  687. //
  688. // if timeout, then continue waiting otherwise exit since some other wait status was returned
  689. //
  690. if ( rc != WAIT_TIMEOUT ) {
  691. ScepNotifyLogPolicy(rc, TRUE, L"Unexpected wait status while notification system thread waits for E_ScepNotificationQEnqueue", 0, 0, NULL );
  692. break;
  693. }
  694. }
  695. //
  696. // if error in waiting or system shutdown, break out of the outermost while
  697. // loop - system thread will eventually exit
  698. //
  699. if ( gbShutdownForNotification )
  700. break;
  701. }
  702. while ( pQNode ) {
  703. if ( gbShutdownForNotification )
  704. break;
  705. if ( dwProcessedNode >= 10 ) {
  706. if ( gdwNotificationLog && (hNotificationLogFile != INVALID_HANDLE_VALUE) ) {
  707. //
  708. // backup the log if it's over the limit and start afresh
  709. //
  710. if ( dwLogSize < GetFileSize(hNotificationLogFile, NULL) ) {
  711. ScepBackupNotificationLogFile();
  712. }
  713. else {
  714. //
  715. // GetFileSize potentially mangles the file handle - so set it back to EOF
  716. //
  717. SetFilePointer (hNotificationLogFile, 0, NULL, FILE_END);
  718. }
  719. }
  720. //
  721. // check free disk space
  722. //
  723. ScepCheckAndWaitFreeDiskSpaceInSysvol();
  724. dwProcessedNode = 0;
  725. }
  726. if ( pQNode->dwPending > 1 &&
  727. ( gdwNumNotificationQNodes == 1 ||
  728. (gdwNumNotificationQNodes == gdwNumNotificationQRetryNodes) ) ) {
  729. //
  730. // if this is a retried node, should sleep for some time before retry
  731. // if it's the only node or all nodes are retried.
  732. //
  733. ScepNotifyLogPolicy(0, FALSE, L"Retried node, taking a break.", 0, 0, NULL );
  734. Sleep( SCEP_NUM_REST_SECONDS * 1000 );
  735. }
  736. //
  737. // process this notification
  738. //
  739. BOOL bWaitTimeout=FALSE;
  740. rc = ScepNotifyProcessOneNodeDC(
  741. pQNode->DbType,
  742. pQNode->ObjectType,
  743. pQNode->DeltaType,
  744. pQNode->Sid,
  745. pQNode->ExplicitLowRight,
  746. pQNode->ExplicitHighRight,
  747. &bWaitTimeout
  748. );
  749. ScepNotificationQNodeLog(pQNode, ScepNotificationProcess);
  750. if (rc == ERROR_SUCCESS) {
  751. if (pQNode->dwPending > 1) {
  752. //
  753. // this was a retried node that is being dequeued
  754. //
  755. if ( gdwNumNotificationQRetryNodes > 0 )
  756. -- gdwNumNotificationQRetryNodes;
  757. }
  758. ScepNotificationQDequeue(FALSE);
  759. } else if ( (WAIT_TIMEOUT == rc ||
  760. ERROR_DOMAIN_CONTROLLER_NOT_FOUND == rc ||
  761. ERROR_INVALID_DOMAIN_ROLE == rc )
  762. && bWaitTimeout ) {
  763. //
  764. // The process was timed out waiting for PDC synchronization.
  765. //
  766. // Since we have waited gdwMaxPDCWait minutes for the PDC become
  767. // accessible and it still timed out (could due to network
  768. // problem), all changes in the local box are old (because they
  769. // don't synchronize with the rest of the domain anymore.
  770. //
  771. // Remove all nodes from the queue and log a big error event
  772. // for the notification dropout
  773. //
  774. ScepNotificationQDequeue(TRUE);
  775. gdwNumNotificationQRetryNodes = 0;
  776. //
  777. // logs an event and let the change go through
  778. //
  779. UINT idMsg=0;
  780. switch ( rc ) {
  781. case WAIT_TIMEOUT:
  782. idMsg = SCESRV_POLICY_ERROR_FILE_OUTOFSYNC;
  783. break;
  784. case ERROR_DOMAIN_CONTROLLER_NOT_FOUND:
  785. idMsg = SCESRV_POLICY_ERROR_LOCALDC;
  786. break;
  787. default:
  788. idMsg = SCESRV_POLICY_ERROR_PDCROLE;
  789. break;
  790. }
  791. LogEvent(MyModuleHandle,
  792. STATUS_SEVERITY_ERROR,
  793. SCEEVENT_ERROR_POLICY_PDCTIMEOUT,
  794. idMsg
  795. );
  796. } else {
  797. //
  798. // For certain errors, such as sysvol is not ready or hard disk is full,
  799. // this thread will sleep for some time then restart the process (next node).
  800. //
  801. if (rc == ERROR_FILE_NOT_FOUND ||
  802. rc == ERROR_OBJECT_NOT_FOUND ||
  803. rc == ERROR_MOD_NOT_FOUND ||
  804. rc == ERROR_EXTENDED_ERROR) {
  805. ScepNotifyLogPolicy(rc, FALSE, L"Sleeping due to processing error", 0, 0, NULL );
  806. Sleep( SCEP_NUM_NOTIFICATION_SECONDS * 1000 );
  807. }
  808. //
  809. // Each notification node in the queue will have a retry count.
  810. // After the node is retried for SCEP_NOTIFICATION_RETRY_COUNT times, the node
  811. // will be removed from the queue (so that policy propagation is not blocked
  812. // forever) and the error is logged
  813. //
  814. if ( pQNode->dwPending >= SCEP_NOTIFICATION_RETRY_COUNT) {
  815. ScepNotifyLogPolicy(0, FALSE, L"Retry count exceeded", 0, 0, NULL );
  816. //
  817. // should log to the event log
  818. //
  819. if ( (pQNode->DbType == SecurityDbLsa &&
  820. pQNode->ObjectType == SecurityDbObjectLsaAccount) ||
  821. (pQNode->DbType == SecurityDbSam &&
  822. (pQNode->ObjectType == SecurityDbObjectSamUser ||
  823. pQNode->ObjectType == SecurityDbObjectSamGroup ||
  824. pQNode->ObjectType == SecurityDbObjectSamAlias )) ) {
  825. //
  826. // user rights
  827. //
  828. UNICODE_STRING UnicodeStringSid;
  829. UnicodeStringSid.Buffer = NULL;
  830. UnicodeStringSid.Length = 0;
  831. UnicodeStringSid.MaximumLength = 0;
  832. if ( pQNode->Sid ) {
  833. RtlConvertSidToUnicodeString(&UnicodeStringSid,
  834. pQNode->Sid,
  835. TRUE );
  836. }
  837. LogEvent(MyModuleHandle,
  838. STATUS_SEVERITY_ERROR,
  839. SCEEVENT_ERROR_QUEUE_RETRY_TIMEOUT,
  840. IDS_ERROR_SAVE_POLICY_GPO_ACCOUNT,
  841. rc,
  842. UnicodeStringSid.Buffer ? UnicodeStringSid.Buffer : L""
  843. );
  844. RtlFreeUnicodeString( &UnicodeStringSid );
  845. } else {
  846. LogEvent(MyModuleHandle,
  847. STATUS_SEVERITY_ERROR,
  848. SCEEVENT_ERROR_QUEUE_RETRY_TIMEOUT,
  849. IDS_ERROR_SAVE_POLICY_GPO_OTHER,
  850. rc
  851. );
  852. }
  853. if ( gdwNumNotificationQRetryNodes > 0 )
  854. -- gdwNumNotificationQRetryNodes;
  855. ScepNotificationQDequeue(FALSE);
  856. }
  857. else {
  858. //
  859. // this node is being retried
  860. //
  861. if ( pQNode->dwPending == 1 )
  862. ++ gdwNumNotificationQRetryNodes;
  863. ++ pQNode->dwPending;
  864. ScepNotifyLogPolicy(0, FALSE, L"Retry count within bounds", 0, 0, NULL );
  865. //
  866. // no error can happen since only pointer manipulation for retry-enqueue
  867. //
  868. ScepNotificationQEnqueue(
  869. pQNode->DbType,
  870. pQNode->DeltaType,
  871. pQNode->ObjectType,
  872. pQNode->Sid,
  873. pQNode->ExplicitLowRight,
  874. pQNode->ExplicitHighRight,
  875. pQNode
  876. );
  877. }
  878. }
  879. pQNode = pNotificationQHead;
  880. dwProcessedNode++;
  881. }
  882. if ( gbShutdownForNotification )
  883. break;
  884. //
  885. // this thread has to keep being fed
  886. // - some other thread might have enqueued new notification nodes
  887. // - no other thread dequeues notification nodes (hence no need to protect reads)
  888. pQNode = pNotificationQHead;
  889. }
  890. //
  891. // should never get in here unless a shutdown happens
  892. // flush any queue items to some persistent store
  893. //
  894. rc = ScepNotificationQFlush();
  895. ScepNotifyLogPolicy(rc, TRUE, L"Flushing notification queue to disk", 0, 0, NULL );
  896. ScepNotificationQCleanup();
  897. ScepNotifyLogPolicy(0, FALSE, L"Notification thread exiting", 0, 0, NULL );
  898. ExitThread(rc);
  899. return ERROR_SUCCESS;
  900. }
  901. //
  902. // todo - this routine really has not changed from polsrv.cpp
  903. //
  904. DWORD
  905. ScepNotifyLogPolicy(
  906. IN DWORD ErrCode,
  907. IN BOOL bLogTime,
  908. IN PWSTR Msg,
  909. IN DWORD DbType,
  910. IN DWORD ObjectType,
  911. IN PWSTR ObjectName OPTIONAL
  912. )
  913. /*
  914. Description:
  915. The main logging routine that logs notification information to
  916. %windir%\\security\\logs\\scepol.log.
  917. Arguments:
  918. ErrCode - the error code to log
  919. bLogTime - if TRUE, log a timestamp
  920. Msg - the string message to log (not loczlized since it is detailed debugging)
  921. DbType - LSA/SAM
  922. ObjectType - SECURITY_DB_OBJECT_TYPE such as SAM group, LSA account etc.
  923. ObjectName - can be NULL - usually carries a message
  924. Return Value:
  925. Win32 error code
  926. */
  927. {
  928. switch ( gdwNotificationLog ) {
  929. case 0:
  930. // do not log anything
  931. return ERROR_SUCCESS;
  932. break;
  933. case 1:
  934. // log error only
  935. if ( ErrCode == 0 ) {
  936. return ERROR_SUCCESS;
  937. }
  938. break;
  939. default:
  940. break;
  941. }
  942. if (hNotificationLogFile != INVALID_HANDLE_VALUE) {
  943. //
  944. // print a time stamp
  945. //
  946. if ( bLogTime ) {
  947. LARGE_INTEGER CurrentTime;
  948. LARGE_INTEGER SysTime;
  949. TIME_FIELDS TimeFields;
  950. NTSTATUS NtStatus;
  951. NtStatus = NtQuerySystemTime(&SysTime);
  952. RtlSystemTimeToLocalTime (&SysTime,&CurrentTime);
  953. if ( NT_SUCCESS(NtStatus) &&
  954. (CurrentTime.LowPart != 0 || CurrentTime.HighPart != 0) ) {
  955. memset(&TimeFields, 0, sizeof(TIME_FIELDS));
  956. RtlTimeToTimeFields (
  957. &CurrentTime,
  958. &TimeFields
  959. );
  960. if ( TimeFields.Month > 0 && TimeFields.Month <= 12 &&
  961. TimeFields.Day > 0 && TimeFields.Day <= 31 &&
  962. TimeFields.Year > 1600 ) {
  963. ScepWriteVariableUnicodeLog(hNotificationLogFile, TRUE,
  964. L"\r\n----------------%02d/%02d/%04d %02d:%02d:%02d",
  965. TimeFields.Month,
  966. TimeFields.Day,
  967. TimeFields.Year,
  968. TimeFields.Hour,
  969. TimeFields.Minute,
  970. TimeFields.Second);
  971. } else {
  972. ScepWriteVariableUnicodeLog(hNotificationLogFile, TRUE,
  973. L"\r\n----------------%08x 08x",
  974. CurrentTime.HighPart,
  975. CurrentTime.LowPart);
  976. }
  977. } else {
  978. ScepWriteSingleUnicodeLog(hNotificationLogFile, TRUE, L"\r\n----------------Unknown time");
  979. }
  980. }
  981. //
  982. // print operation status code
  983. //
  984. if ( ErrCode ) {
  985. ScepWriteVariableUnicodeLog(hNotificationLogFile, FALSE,
  986. L"Thread %x\tError=%d",
  987. GetCurrentThreadId(),
  988. ErrCode
  989. );
  990. } else {
  991. ScepWriteVariableUnicodeLog(hNotificationLogFile, FALSE,
  992. L"Thread %x\t",
  993. GetCurrentThreadId()
  994. );
  995. }
  996. //
  997. // operation type
  998. //
  999. switch (DbType) {
  1000. case SecurityDbLsa:
  1001. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\tLSA");
  1002. break;
  1003. case SecurityDbSam:
  1004. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\tSAM");
  1005. break;
  1006. default:
  1007. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"");
  1008. break;
  1009. }
  1010. //
  1011. // print object type
  1012. //
  1013. switch (ObjectType) {
  1014. case SecurityDbObjectLsaPolicy:
  1015. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\tPolicy");
  1016. break;
  1017. case SecurityDbObjectLsaAccount:
  1018. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\tAccount");
  1019. break;
  1020. case SecurityDbObjectSamDomain:
  1021. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\tDomain");
  1022. break;
  1023. case SecurityDbObjectSamUser:
  1024. case SecurityDbObjectSamGroup:
  1025. case SecurityDbObjectSamAlias:
  1026. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\tAccount");
  1027. break;
  1028. default:
  1029. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"");
  1030. break;
  1031. }
  1032. BOOL bCRLF;
  1033. __try {
  1034. //
  1035. // print the name(s)
  1036. //
  1037. if ( Msg ) {
  1038. bCRLF = FALSE;
  1039. } else {
  1040. bCRLF = TRUE;
  1041. }
  1042. if ( ObjectName ) {
  1043. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\t");
  1044. ScepWriteSingleUnicodeLog(hNotificationLogFile, bCRLF, ObjectName);
  1045. }
  1046. if ( Msg ) {
  1047. ScepWriteSingleUnicodeLog(hNotificationLogFile, FALSE, L"\t");
  1048. ScepWriteSingleUnicodeLog(hNotificationLogFile, TRUE, Msg);
  1049. }
  1050. } __except(EXCEPTION_EXECUTE_HANDLER) {
  1051. CloseHandle( hNotificationLogFile );
  1052. hNotificationLogFile = INVALID_HANDLE_VALUE;
  1053. return(ERROR_INVALID_PARAMETER);
  1054. }
  1055. } else {
  1056. return(GetLastError());
  1057. }
  1058. return(ERROR_SUCCESS);
  1059. }
  1060. VOID
  1061. ScepNotificationQFree(
  1062. )
  1063. /*
  1064. Routine Description:
  1065. This function frees the notification queue.
  1066. Arguments:
  1067. None
  1068. Return Value:
  1069. None
  1070. */
  1071. {
  1072. EnterCriticalSection(&NotificationQSync);
  1073. ScepNotifyLogPolicy(0, TRUE, L"Entered NotificationQSync for freeing queue", 0, 0, NULL );
  1074. if ( pNotificationQHead ) {
  1075. PSCESRV_POLQUEUE pQNode = pNotificationQHead;
  1076. PSCESRV_POLQUEUE pQNodeToFree = NULL;
  1077. while ( pQNode ) {
  1078. pQNodeToFree = pQNode;
  1079. pQNode = pQNode->Next;
  1080. ScepFree(pQNodeToFree);
  1081. }
  1082. pNotificationQHead = NULL;
  1083. }
  1084. pNotificationQTail = NULL;
  1085. ScepNotifyLogPolicy(0, FALSE, L"Leaving NotificationQSync for freeing queue ", 0, 0, NULL );
  1086. LeaveCriticalSection(&NotificationQSync);
  1087. return;
  1088. }
  1089. DWORD
  1090. ScepNotificationQFlush(
  1091. )
  1092. /*
  1093. Routine Description:
  1094. This function flushes the notification queue to some persistent store.
  1095. Arguments:
  1096. None
  1097. Return Value:
  1098. None
  1099. */
  1100. {
  1101. DWORD rc = ERROR_SUCCESS;
  1102. EnterCriticalSection(&NotificationQSync);
  1103. ScepNotifyLogPolicy(0, TRUE, L"Entered NotificationQSync for flushing queue", 0, 0, NULL );
  1104. if ( pNotificationQHead ) {
  1105. PSCESRV_POLQUEUE pQNode = pNotificationQHead;
  1106. HKEY hKey = NULL;
  1107. int i=1;
  1108. HKEY hKeySub=NULL;
  1109. WCHAR SubKeyName[10];
  1110. rc = RegCreateKeyEx (HKEY_LOCAL_MACHINE,
  1111. SCE_NOTIFICATION_PATH,
  1112. 0,
  1113. NULL, // LPTSTR lpClass,
  1114. REG_OPTION_NON_VOLATILE,
  1115. KEY_READ | KEY_WRITE, // KEY_SET_VALUE,
  1116. NULL, // &SecurityAttributes,
  1117. &hKey,
  1118. NULL
  1119. );
  1120. if ( ERROR_SUCCESS == rc ) {
  1121. while ( pQNode ) {
  1122. //
  1123. // write pQNode to persistent store using available APIs
  1124. //
  1125. memset(SubKeyName, '\0', 20);
  1126. swprintf(SubKeyName, L"%9d",i);
  1127. rc = RegCreateKeyEx(
  1128. hKey,
  1129. SubKeyName,
  1130. 0,
  1131. NULL,
  1132. REG_OPTION_NON_VOLATILE,
  1133. KEY_READ | KEY_WRITE,
  1134. NULL,
  1135. &hKeySub,
  1136. NULL
  1137. );
  1138. if ( ERROR_SUCCESS == rc ) {
  1139. //
  1140. // save the node information as registry values
  1141. //
  1142. RegSetValueEx (hKeySub,
  1143. L"Pending",
  1144. 0,
  1145. REG_DWORD,
  1146. (BYTE *)&(pQNode->dwPending),
  1147. sizeof(DWORD)
  1148. );
  1149. RegSetValueEx (hKeySub,
  1150. L"DbType",
  1151. 0,
  1152. REG_DWORD,
  1153. (BYTE *)&(pQNode->DbType),
  1154. sizeof(DWORD)
  1155. );
  1156. RegSetValueEx (hKeySub,
  1157. L"ObjectType",
  1158. 0,
  1159. REG_DWORD,
  1160. (BYTE *)&(pQNode->ObjectType),
  1161. sizeof(DWORD)
  1162. );
  1163. RegSetValueEx (hKeySub,
  1164. L"DeltaType",
  1165. 0,
  1166. REG_DWORD,
  1167. (BYTE *)&(pQNode->DeltaType),
  1168. sizeof(DWORD)
  1169. );
  1170. RegSetValueEx (hKeySub,
  1171. L"LowRight",
  1172. 0,
  1173. REG_DWORD,
  1174. (BYTE *)&(pQNode->ExplicitLowRight),
  1175. sizeof(DWORD)
  1176. );
  1177. RegSetValueEx (hKeySub,
  1178. L"HighRight",
  1179. 0,
  1180. REG_DWORD,
  1181. (BYTE *)&(pQNode->ExplicitHighRight),
  1182. sizeof(DWORD)
  1183. );
  1184. RegSetValueEx (hKeySub,
  1185. L"Sid",
  1186. 0,
  1187. REG_BINARY,
  1188. (BYTE *)&(pQNode->Sid),
  1189. MAX_SID_LENGTH
  1190. );
  1191. } else {
  1192. //
  1193. // log the failure
  1194. //
  1195. ScepNotifyLogPolicy(rc, FALSE, L"Failed to save notification node.", pQNode->DbType, pQNode->ObjectType, NULL );
  1196. }
  1197. if ( hKeySub ) {
  1198. RegCloseKey( hKeySub );
  1199. hKeySub = NULL;
  1200. }
  1201. i++;
  1202. pQNode = pQNode->Next;
  1203. }
  1204. } else {
  1205. //
  1206. // log the failure
  1207. //
  1208. ScepNotifyLogPolicy(rc, FALSE, L"Failed to open notification store.", 0, 0, SCE_NOTIFICATION_PATH );
  1209. }
  1210. if ( hKey ) {
  1211. RegCloseKey(hKey);
  1212. }
  1213. } else {
  1214. //
  1215. // log the queue is empty
  1216. //
  1217. ScepNotifyLogPolicy(0, FALSE, L"Queue is empty.", 0, 0, NULL);
  1218. }
  1219. ScepNotifyLogPolicy(0, FALSE, L"Leaving NotificationQSync for flushing queue", 0, 0, NULL );
  1220. LeaveCriticalSection(&NotificationQSync);
  1221. return rc;
  1222. }
  1223. DWORD
  1224. ScepNotificationQUnFlush(
  1225. )
  1226. /*
  1227. Routine Description:
  1228. This function initializes the notification queue from some persistent store
  1229. such as registry/textfile.
  1230. Arguments:
  1231. None
  1232. Return Value:
  1233. None
  1234. */
  1235. {
  1236. DWORD rc = ERROR_SUCCESS;
  1237. DWORD DbType=0;
  1238. DWORD DeltaType=0;
  1239. DWORD ObjectType=0;
  1240. CHAR ObjectSid[MAX_SID_LENGTH];
  1241. DWORD ExplicitLowRight=0;
  1242. DWORD ExplicitHighRight=0;
  1243. EnterCriticalSection(&NotificationQSync);
  1244. ScepNotifyLogPolicy(0, FALSE, L"Entered NotificationQSync for unflushing queue", 0, 0, NULL );
  1245. memset(ObjectSid, '\0', MAX_SID_LENGTH);
  1246. HKEY hKey=NULL;
  1247. rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  1248. SCE_NOTIFICATION_PATH,
  1249. 0,
  1250. KEY_READ,
  1251. &hKey
  1252. );
  1253. if ( ERROR_SUCCESS == rc ) {
  1254. HKEY hKeySub=NULL;
  1255. DWORD dwIndex=0;
  1256. DWORD cbSubKey=10;
  1257. WCHAR SubKeyName[10];
  1258. DWORD cbData;
  1259. DWORD dwPending=0;
  1260. DWORD RegType;
  1261. //
  1262. // enumerate all subkeys and save each node
  1263. //
  1264. do {
  1265. memset(SubKeyName, '\0', 20);
  1266. cbSubKey = 10;
  1267. rc = RegEnumKeyEx (hKey,
  1268. dwIndex,
  1269. SubKeyName,
  1270. &cbSubKey,
  1271. NULL,
  1272. NULL,
  1273. NULL,
  1274. NULL
  1275. );
  1276. if ( ERROR_SUCCESS == rc ) {
  1277. dwIndex++;
  1278. //
  1279. // open the sub key
  1280. //
  1281. rc = RegOpenKeyEx (hKey,
  1282. SubKeyName,
  1283. 0,
  1284. KEY_READ,
  1285. &hKeySub
  1286. );
  1287. if ( ERROR_SUCCESS == rc ) {
  1288. //
  1289. // query all registry values
  1290. //
  1291. cbData = sizeof(DWORD);
  1292. rc = RegQueryValueEx (
  1293. hKeySub,
  1294. L"Pending",
  1295. NULL,
  1296. &RegType,
  1297. (LPBYTE)&dwPending,
  1298. &cbData
  1299. );
  1300. if ( ERROR_SUCCESS == rc ) {
  1301. cbData = sizeof(DWORD);
  1302. rc = RegQueryValueEx (
  1303. hKeySub,
  1304. L"DbType",
  1305. NULL,
  1306. &RegType,
  1307. (LPBYTE)&DbType,
  1308. &cbData
  1309. );
  1310. }
  1311. if ( ERROR_SUCCESS == rc ) {
  1312. cbData = sizeof(DWORD);
  1313. rc = RegQueryValueEx (
  1314. hKeySub,
  1315. L"ObjectType",
  1316. NULL,
  1317. &RegType,
  1318. (LPBYTE)&ObjectType,
  1319. &cbData
  1320. );
  1321. }
  1322. if ( ERROR_SUCCESS == rc ) {
  1323. cbData = sizeof(DWORD);
  1324. rc = RegQueryValueEx (
  1325. hKeySub,
  1326. L"DeltaType",
  1327. NULL,
  1328. &RegType,
  1329. (LPBYTE)&DeltaType,
  1330. &cbData
  1331. );
  1332. }
  1333. if ( ERROR_SUCCESS == rc ) {
  1334. cbData = sizeof(DWORD);
  1335. rc = RegQueryValueEx (
  1336. hKeySub,
  1337. L"LowRight",
  1338. NULL,
  1339. &RegType,
  1340. (LPBYTE)&ExplicitLowRight,
  1341. &cbData
  1342. );
  1343. }
  1344. if ( ERROR_SUCCESS == rc ) {
  1345. cbData = sizeof(DWORD);
  1346. rc = RegQueryValueEx (
  1347. hKeySub,
  1348. L"HighRight",
  1349. NULL,
  1350. &RegType,
  1351. (LPBYTE)&ExplicitHighRight,
  1352. &cbData
  1353. );
  1354. }
  1355. if ( ERROR_SUCCESS == rc ) {
  1356. cbData = MAX_SID_LENGTH;
  1357. rc = RegQueryValueEx (
  1358. hKeySub,
  1359. L"Sid",
  1360. NULL,
  1361. &RegType,
  1362. (LPBYTE)ObjectSid,
  1363. &cbData
  1364. );
  1365. }
  1366. if ( ERROR_SUCCESS == rc ) {
  1367. //
  1368. // add it to the queue
  1369. //
  1370. ScepNotificationQEnqueue(
  1371. (SECURITY_DB_TYPE)DbType,
  1372. (SECURITY_DB_DELTA_TYPE)DeltaType,
  1373. (SECURITY_DB_OBJECT_TYPE)ObjectType,
  1374. (PSID)ObjectSid,
  1375. ExplicitLowRight,
  1376. ExplicitHighRight,
  1377. NULL
  1378. );
  1379. }
  1380. }
  1381. if ( ERROR_SUCCESS != rc ) {
  1382. //
  1383. // log the error
  1384. //
  1385. ScepNotifyLogPolicy(rc, FALSE, L"Failed to query notification a node.", 0, 0, SubKeyName );
  1386. }
  1387. //
  1388. // close handle
  1389. //
  1390. if ( hKeySub ) {
  1391. RegCloseKey(hKeySub);
  1392. hKeySub = NULL;
  1393. }
  1394. }
  1395. } while ( rc != ERROR_NO_MORE_ITEMS );
  1396. } else if ( ERROR_FILE_NOT_FOUND != rc ) {
  1397. //
  1398. // log the error
  1399. //
  1400. ScepNotifyLogPolicy(rc, FALSE, L"Failed to open the notification store", 0, 0, NULL );
  1401. }
  1402. if ( ERROR_FILE_NOT_FOUND == rc ) rc = ERROR_SUCCESS;
  1403. //
  1404. // close the handle
  1405. //
  1406. if ( hKey ) {
  1407. RegCloseKey(hKey);
  1408. hKey = NULL;
  1409. }
  1410. ScepNotifyLogPolicy(0, FALSE, L"Leaving NotificationQSync for unflushing queue", 0, 0, NULL );
  1411. LeaveCriticalSection(&NotificationQSync);
  1412. return rc;
  1413. }
  1414. DWORD
  1415. ScepGetQueueInfo(
  1416. OUT DWORD *pdwInfo,
  1417. OUT PSCEP_SPLAY_TREE pRootNode OPTIONAL
  1418. )
  1419. /*
  1420. Routine Description:
  1421. Loops through all pending notifications and returns the notification type & unique
  1422. sid-list to the caller.
  1423. Arguments:
  1424. pdwInfo - bitmask of types SCE_QUEUE_INFO_SAM, SCE_QUEUE_INFO_AUDIT, SCE_QUEUE_INFO_RIGHTS
  1425. pRootNode - the root node pointing to splay tree structure.
  1426. Note: this is an optional parameter - if NULL, only pdwInfo needs to be
  1427. filled i.e. caller is looking for SCE_QUEUE_INFO_SAM only
  1428. Return Value:
  1429. Win32 error code
  1430. */
  1431. {
  1432. DWORD dwInfo = 0;
  1433. DWORD rc = ERROR_SUCCESS;
  1434. BOOL bExists;
  1435. BOOL bSamDomainInfoOnly = FALSE;
  1436. if ( pdwInfo == NULL )
  1437. return ERROR_INVALID_PARAMETER;
  1438. if (NULL == pRootNode) {
  1439. bSamDomainInfoOnly = TRUE;
  1440. }
  1441. *pdwInfo = 0;
  1442. if (ProductTypeForNotification != NtProductLanManNt ) {
  1443. //
  1444. // none DCs, the queue should always be empty.
  1445. //
  1446. ScepNotifyLogPolicy(0, TRUE, L"Wks/Srv Notification queue is empty", 0, 0, NULL );
  1447. return rc;
  1448. }
  1449. PWSTR StringSid=NULL;
  1450. ScepNotifyLogPolicy(0,
  1451. TRUE,
  1452. bSamDomainInfoOnly ? L"Building Notification queue info for SecurityDbObjectSamDomain only" : L"Building Notification queue info",
  1453. 0,
  1454. 0,
  1455. NULL );
  1456. EnterCriticalSection(&NotificationQSync);
  1457. ScepNotifyLogPolicy(0, FALSE, L"Entered NotificationQSync for building queue info", 0, 0, NULL );
  1458. if ( pNotificationQHead ) {
  1459. PSCESRV_POLQUEUE pQNode = pNotificationQHead;
  1460. while ( pQNode ) {
  1461. if ( (SCEP_IS_SAM_OBJECT(pQNode->ObjectType) ||
  1462. pQNode->ObjectType == SecurityDbObjectLsaAccount) ) {
  1463. dwInfo |= SCE_QUEUE_INFO_RIGHTS;
  1464. }
  1465. else if ( pQNode->ObjectType == SecurityDbObjectSamDomain ) {
  1466. dwInfo |= SCE_QUEUE_INFO_SAM;
  1467. if (bSamDomainInfoOnly) {
  1468. //
  1469. // at least one SAM domain notification in queue
  1470. //
  1471. break;
  1472. }
  1473. }
  1474. else if ( pQNode->ObjectType == SecurityDbObjectLsaPolicy ) {
  1475. dwInfo |= SCE_QUEUE_INFO_AUDIT;
  1476. }
  1477. if ( !bSamDomainInfoOnly ) {
  1478. if ( RtlValidSid( (PSID)pQNode->Sid )) {
  1479. rc = ScepSplayInsert( (PVOID)(pQNode->Sid), pRootNode, &bExists );
  1480. ConvertSidToStringSid( (PSID)(pQNode->Sid), &StringSid );
  1481. if ( !bExists ) {
  1482. ScepNotifyLogPolicy(rc, FALSE, L"Add SID", 0, pQNode->ObjectType, StringSid );
  1483. } else {
  1484. ScepNotifyLogPolicy(rc, FALSE, L"Duplicate SID", 0, pQNode->ObjectType, StringSid );
  1485. }
  1486. LocalFree(StringSid);
  1487. StringSid = NULL;
  1488. if (rc != ERROR_SUCCESS ) {
  1489. break;
  1490. }
  1491. } else {
  1492. ScepNotifyLogPolicy(0, FALSE, L"Add Info", 0, pQNode->ObjectType, NULL );
  1493. }
  1494. }
  1495. pQNode = pQNode->Next;
  1496. }
  1497. if ( rc != ERROR_SUCCESS ) {
  1498. if (!bSamDomainInfoOnly) {
  1499. ScepNotifyLogPolicy(rc, FALSE, L"Error building Notification queue info", 0, 0, NULL );
  1500. ScepSplayFreeTree( &pRootNode, FALSE );
  1501. }
  1502. } else {
  1503. *pdwInfo = dwInfo;
  1504. }
  1505. }
  1506. ScepNotifyLogPolicy(0, FALSE, L"Leaving NotificationQSync for building queue info", 0, 0, NULL );
  1507. LeaveCriticalSection(&NotificationQSync);
  1508. return rc;
  1509. }
  1510. VOID
  1511. ScepNotificationQNodeLog(
  1512. IN PSCESRV_POLQUEUE pQNode,
  1513. IN NOTIFICATIONQ_OPERATION_TYPE NotificationOp
  1514. )
  1515. /*
  1516. Routine Description:
  1517. Dump the node info to the log file
  1518. Arguments:
  1519. pQNode - pointer to node to dump
  1520. NotificationOp - type of queue operation
  1521. Return Value:
  1522. None
  1523. */
  1524. {
  1525. WCHAR pwszTmpBuf[MAX_PATH*2];
  1526. PWSTR pszStringSid = NULL;
  1527. pwszTmpBuf[0] = L'\0';
  1528. if ( pQNode == NULL ||
  1529. gdwNotificationLog == 0 ||
  1530. NotificationOp > ScepNotificationProcess ||
  1531. NotificationOp < ScepNotificationEnqueue) {
  1532. return;
  1533. }
  1534. switch (NotificationOp) {
  1535. case ScepNotificationEnqueue:
  1536. wcscpy(pwszTmpBuf, L"Enqueue");
  1537. break;
  1538. case ScepNotificationDequeue:
  1539. wcscpy(pwszTmpBuf, L"Dequeue");
  1540. break;
  1541. case ScepNotificationRetry:
  1542. wcscpy(pwszTmpBuf, L"Retry");
  1543. break;
  1544. case ScepNotificationProcess:
  1545. wcscpy(pwszTmpBuf, L"Process");
  1546. break;
  1547. default:
  1548. return;
  1549. }
  1550. ScepConvertSidToPrefixStringSid( (PSID)(pQNode->Sid), &pszStringSid );
  1551. swprintf(pwszTmpBuf, L"Op: %s, Num Instances: %d, Num Retry Instances: %d, Retry count: %d, LowRight: %d, HighRight: %d, Sid: %s, DbType: %d, ObjectType: %d, DeltaType: %d",
  1552. OpTypeTable[NotificationOp-1],
  1553. gdwNumNotificationQNodes,
  1554. gdwNumNotificationQRetryNodes,
  1555. pQNode->dwPending,
  1556. pQNode->ExplicitLowRight,
  1557. pQNode->ExplicitHighRight,
  1558. pszStringSid == NULL ? L"0" : pszStringSid,
  1559. pQNode->DbType,
  1560. pQNode->ObjectType,
  1561. pQNode->DeltaType);
  1562. ScepFree( pszStringSid );
  1563. ScepNotifyLogPolicy(0, FALSE, L"", 0, 0, pwszTmpBuf );
  1564. return;
  1565. }
  1566. DWORD
  1567. ScepNotificationLogOpen(
  1568. )
  1569. /* ++
  1570. Routine Description:
  1571. Open a handle to the notification log file %windir%\\security\\logs\\scepol.log
  1572. and stash it in a global handle.
  1573. Arguments:
  1574. None
  1575. Return value:
  1576. Win32 error code
  1577. -- */
  1578. {
  1579. DWORD rc=NO_ERROR;
  1580. if ( !gdwNotificationLog ) {
  1581. return(rc);
  1582. }
  1583. //
  1584. // build the log file name %windir%\security\logs\scepol.log
  1585. //
  1586. WCHAR LogName[MAX_PATH+51];
  1587. LogName[0] = L'\0';
  1588. GetSystemWindowsDirectory(LogName, MAX_PATH);
  1589. LogName[MAX_PATH] = L'\0';
  1590. wcscat(LogName, L"\\security\\logs\\scepol.log\0");
  1591. hNotificationLogFile = CreateFile(LogName,
  1592. GENERIC_WRITE,
  1593. FILE_SHARE_READ,
  1594. NULL,
  1595. OPEN_ALWAYS,
  1596. FILE_ATTRIBUTE_NORMAL,
  1597. NULL);
  1598. if ( INVALID_HANDLE_VALUE != hNotificationLogFile ) {
  1599. /*
  1600. DWORD dwBytesWritten;
  1601. SetFilePointer (hNotificationLogFile, 0, NULL, FILE_BEGIN);
  1602. CHAR TmpBuf[3];
  1603. TmpBuf[0] = (CHAR)0xFF;
  1604. TmpBuf[1] = (CHAR)0xFE;
  1605. TmpBuf[2] = '\0';
  1606. WriteFile (hNotificationLogFile, (LPCVOID)TmpBuf, 2,
  1607. &dwBytesWritten,
  1608. NULL);
  1609. */
  1610. //
  1611. // set to file end since we do not want to erase older logs unless we wrap around
  1612. //
  1613. SetFilePointer (hNotificationLogFile, 0, NULL, FILE_END);
  1614. }
  1615. if ( hNotificationLogFile == INVALID_HANDLE_VALUE ) {
  1616. rc = GetLastError();
  1617. }
  1618. return rc;
  1619. }
  1620. VOID
  1621. ScepNotificationLogClose(
  1622. )
  1623. /* ++
  1624. Routine Description:
  1625. Close the handle to the notification log file %windir%\\security\\logs\\scepol.log.
  1626. Arguments:
  1627. None
  1628. Return value:
  1629. Win32 error code
  1630. -- */
  1631. {
  1632. if ( INVALID_HANDLE_VALUE != hNotificationLogFile ) {
  1633. CloseHandle( hNotificationLogFile );
  1634. }
  1635. hNotificationLogFile = INVALID_HANDLE_VALUE;
  1636. return;
  1637. }
  1638. VOID
  1639. ScepBackupNotificationLogFile(
  1640. )
  1641. /* ++
  1642. Routine Description:
  1643. Backup the log file to %windir%\\security\\logs\\scepol.log.old and start afresh.
  1644. Arguments:
  1645. None
  1646. Return value:
  1647. None
  1648. -- */
  1649. {
  1650. //
  1651. // make sure local variables are not over the stack limit (1KB)
  1652. //
  1653. UINT cLen = GetSystemWindowsDirectory(NULL, 0);
  1654. if ( cLen == 0 ) return;
  1655. PWSTR LogName=NULL, LogNameOld=NULL;
  1656. SafeAllocaAllocate(LogName, (cLen+50)*sizeof(WCHAR));
  1657. SafeAllocaAllocate(LogNameOld, (cLen+50)*sizeof(WCHAR));
  1658. if ( LogName && LogNameOld ) {
  1659. LogName[0] = L'\0';
  1660. GetSystemWindowsDirectory(LogName, cLen+1);
  1661. LogName[cLen+1] = L'\0';
  1662. wcscpy(LogNameOld, LogName);
  1663. wcscat(LogName, L"\\security\\logs\\scepol.log\0");
  1664. wcscat(LogNameOld, L"\\security\\logs\\scepol.log.old\0");
  1665. EnterCriticalSection(&NotificationQSync);
  1666. ScepNotificationLogClose();
  1667. DWORD rc=0, rc2=0;
  1668. if ( !CopyFile( LogName, LogNameOld, FALSE ) )
  1669. rc = GetLastError();
  1670. //
  1671. // clear the file after handle is closed and then recreate the log file and handle
  1672. //
  1673. if ( !DeleteFile(LogName) )
  1674. rc2 = GetLastError();
  1675. ScepNotificationLogOpen();
  1676. LeaveCriticalSection(&NotificationQSync);
  1677. swprintf(LogName, L"Wrapping log file: Copy(%d), Delete(%d)\0", rc, rc2);
  1678. ScepNotifyLogPolicy(0, TRUE, LogName, 0, 0, NULL );
  1679. }
  1680. SafeAllocaFree(LogName);
  1681. SafeAllocaFree(LogNameOld);
  1682. return;
  1683. }
  1684. VOID
  1685. ScepNotificationQCleanup(
  1686. )
  1687. /* ++
  1688. Routine Description:
  1689. Perform cleanup operations
  1690. Arguments:
  1691. None
  1692. Return value:
  1693. None
  1694. -- */
  1695. {
  1696. ScepNotificationQFree();
  1697. if ( ghEventNotificationQEnqueue ) {
  1698. CloseHandle( ghEventNotificationQEnqueue );
  1699. ghEventNotificationQEnqueue = NULL;
  1700. }
  1701. if ( ghEventPolicyPropagation ) {
  1702. CloseHandle( ghEventPolicyPropagation );
  1703. ghEventPolicyPropagation = NULL;
  1704. }
  1705. ScepNotificationLogClose();
  1706. }
  1707. VOID
  1708. ScepNotificationQControl(
  1709. IN DWORD Flag
  1710. )
  1711. {
  1712. if (ProductTypeForNotification == NtProductLanManNt ) {
  1713. //
  1714. // only control the queue process on DCs
  1715. //
  1716. BOOL b = (Flag > 0);
  1717. if ( b != gbSuspendQueue ) {
  1718. //
  1719. // log it.
  1720. //
  1721. if ( !b ) {
  1722. gbSuspendQueue = b;
  1723. //
  1724. // if the queue should be resumed, set the event
  1725. //
  1726. if ( !SetEvent( ghEventPolicyPropagation ) ) {
  1727. DWORD rc = GetLastError();
  1728. ScepNotifyLogPolicy(rc, FALSE, L"Error signaling event E_ScepPolicyPropagation", 0, 0, NULL );
  1729. } else {
  1730. ScepNotifyLogPolicy(0, FALSE, L"Signaling event E_ScepPolicyPropagation", 0, 0, NULL );
  1731. }
  1732. } else {
  1733. //
  1734. // should reset the event before setting the global flag
  1735. //
  1736. ResetEvent( ghEventPolicyPropagation );
  1737. gbSuspendQueue = b;
  1738. ScepNotifyLogPolicy(0, FALSE, L"Resetting event E_ScepPolicyPropagation", 0, 0, NULL );
  1739. }
  1740. if ( b )
  1741. ScepNotifyLogPolicy(0, FALSE, L"Suspend flag is set.", 0, 0, NULL );
  1742. else
  1743. ScepNotifyLogPolicy(0, FALSE, L"Resume flag is set", 0, 0, NULL );
  1744. }
  1745. }
  1746. return;
  1747. }
  1748. DWORD
  1749. ScepCheckAndWaitPolicyPropFinish()
  1750. {
  1751. DWORD rc=ERROR_SUCCESS;
  1752. while (gbSuspendQueue ) {
  1753. //
  1754. // the queue should be suspended
  1755. //
  1756. rc = WaitForSingleObjectEx(
  1757. ghEventPolicyPropagation,
  1758. SCEP_NOTIFICATION_EVENT_TIMEOUT_SECS*1000,
  1759. FALSE
  1760. );
  1761. if ( rc == -1 )
  1762. rc = GetLastError();
  1763. if ( gbShutdownForNotification )
  1764. break;
  1765. //
  1766. // if event was signalled and wait happened successfully, move on
  1767. //
  1768. if ( rc == WAIT_OBJECT_0 ) {
  1769. ScepNotifyLogPolicy(0, TRUE, L"Queue process is resumed from policy propagation", 0, 0, NULL );
  1770. break;
  1771. }
  1772. //
  1773. // if timeout, then continue waiting otherwise exit since some other wait status was returned
  1774. //
  1775. if ( rc != WAIT_TIMEOUT ) {
  1776. ScepNotifyLogPolicy(rc, TRUE, L"Unexpected wait status while notification system thread waits for E_ScepPolicyPropagation", 0, 0, NULL );
  1777. break;
  1778. }
  1779. }
  1780. return rc;
  1781. }
  1782. DWORD
  1783. ScepCheckAndWaitFreeDiskSpaceInSysvol()
  1784. /*
  1785. Description:
  1786. Saving policy into sysvol requires that some amount of disk space is available.
  1787. If free disk space is below 5M, we should suspend the node processing and wait
  1788. for disk space freed up.
  1789. */
  1790. {
  1791. //
  1792. // Get the sysvol share path name in the format of \\ComputerName\Sysvol\
  1793. //
  1794. WCHAR Buffer[MAX_PATH+10];
  1795. DWORD dSize=MAX_PATH+2;
  1796. DWORD rc=ERROR_SUCCESS;
  1797. ULARGE_INTEGER BytesCaller, BytesTotal, BytesFree;
  1798. int cnt = 0;
  1799. Buffer[0] = L'\\';
  1800. Buffer[1] = L'\\';
  1801. Buffer[2] = L'\0';
  1802. if ( !GetComputerName(Buffer+2, &dSize) )
  1803. return GetLastError();
  1804. Buffer[MAX_PATH+2] = L'\0';
  1805. wcscat(Buffer, TEXT("\\sysvol\\"));
  1806. BytesCaller.QuadPart = 0;
  1807. BytesTotal.QuadPart = 0;
  1808. BytesFree.QuadPart = 0;
  1809. while ( BytesCaller.QuadPart < SCEP_MINIMUM_DISK_SPACE &&
  1810. cnt < 40 ) {
  1811. if ( !GetDiskFreeSpaceEx(Buffer, &BytesCaller, &BytesTotal, &BytesFree) ) {
  1812. rc = GetLastError();
  1813. break;
  1814. }
  1815. if ( BytesCaller.QuadPart < SCEP_MINIMUM_DISK_SPACE ) {
  1816. //
  1817. // sleep for 15 minutes then check again
  1818. //
  1819. LogEvent(MyModuleHandle,
  1820. STATUS_SEVERITY_WARNING,
  1821. SCEEVENT_WARNING_LOW_DISK_SPACE,
  1822. IDS_FREE_DISK_SPACE,
  1823. BytesCaller.LowPart
  1824. );
  1825. //
  1826. // sleep for 15 minutes
  1827. //
  1828. Sleep(15*60*1000);
  1829. }
  1830. cnt++;
  1831. }
  1832. return rc;
  1833. }
  1834. VOID
  1835. ScepDbgNotificationQDump(
  1836. )
  1837. /* ++
  1838. Routine Description:
  1839. Dump the notification queue to console - could dump to disk if needed
  1840. Arguments:
  1841. None
  1842. Return value:
  1843. None
  1844. -- */
  1845. {
  1846. EnterCriticalSection(&NotificationQSync);
  1847. DWORD dwNodeNum = 0;
  1848. wprintf(L"\nTotal no. of queue nodes = %d", gdwNumNotificationQNodes);
  1849. if ( pNotificationQHead ) {
  1850. PSCESRV_POLQUEUE pQNode = pNotificationQHead;
  1851. while ( pQNode ) {
  1852. wprintf(L"\nNode no. %d", dwNodeNum++);
  1853. ScepDbgNotificationQDumpNode(pQNode);
  1854. pQNode = pQNode->Next;
  1855. }
  1856. }
  1857. LeaveCriticalSection(&NotificationQSync);
  1858. }
  1859. VOID
  1860. ScepDbgNotificationQDumpNode(
  1861. IN PSCESRV_POLQUEUE pQNode
  1862. )
  1863. /*
  1864. Routine Description:
  1865. Dump the node info to the console
  1866. Arguments:
  1867. pQNode - pointer to node to dump
  1868. Return Value:
  1869. None
  1870. */
  1871. {
  1872. PWSTR pszStringSid = NULL;
  1873. if ( pQNode == NULL ) {
  1874. return;
  1875. }
  1876. ScepConvertSidToPrefixStringSid( (PSID)(pQNode->Sid), &pszStringSid );
  1877. wprintf( L"\nRetry count: %d, LowRight: %d, HighRight: %d, Sid: %s, DbType: %d, ObjectType: %d\n",
  1878. pQNode->dwPending,
  1879. pQNode->ExplicitLowRight,
  1880. pQNode->ExplicitHighRight,
  1881. pszStringSid == NULL ? L"0" : pszStringSid,
  1882. pQNode->DbType,
  1883. pQNode->ObjectType);
  1884. ScepFree( pszStringSid );
  1885. return;
  1886. }