Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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