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.

1536 lines
44 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. CONFIG.C
  5. Abstract:
  6. This file contains the routines that walk the configuration registry.
  7. Author:
  8. Rajen Shah (rajens) 1-Jul-1991
  9. Revision History:
  10. 29-Aug-1994 Danl
  11. We no longer grow log files in place. Therefore, the MaxSize value
  12. in the registery ends up being advisory only. We don't try to reserve
  13. that much memory at init time. So it could happen that when we need
  14. a larger file size that we may not have enough memory to allocate
  15. MaxSize bytes.
  16. 28-Mar-1994 Danl
  17. ReadRegistryInfo: LogFileInfo->LogFileName wasn't getting updated
  18. when using the default (generated) LogFileName.
  19. 16-Mar-1994 Danl
  20. Fixed Memory Leaks in ReadRegistryInfo(). Call to
  21. RtlDosPathNameToNtPathName allocates memory that wasn't being free'd.
  22. 03-Mar-1995 MarkBl
  23. Added GuestAccessRestriction flag initialization in ReadRegistryInfo.
  24. --*/
  25. //
  26. // INCLUDES
  27. //
  28. #include <eventp.h>
  29. #include <elfcfg.h>
  30. #include <stdlib.h>
  31. #include <malloc.h>
  32. #include <memory.h>
  33. //
  34. // STRUCTURES
  35. //
  36. //
  37. // This structure contains all the information used to setup and
  38. // for listening to registry changes in the eventlog tree.
  39. //
  40. typedef struct _REG_MONITOR_INFO
  41. {
  42. HANDLE NotifyEventHandle;
  43. DWORD Timeout;
  44. HANDLE WorkItemHandle;
  45. HANDLE RegMonitorHandle;
  46. }
  47. REG_MONITOR_INFO, *LPREG_MONITOR_INFO;
  48. //
  49. // GLOBALS
  50. //
  51. //
  52. // IMPORTANT: If NUM_KEYS_MONITORED is changed, be sure to update the initialization of GlRegMonitorInfo and
  53. // the ElfAllEventsCleared macro accordingly.
  54. //
  55. #define NUM_KEYS_MONITORED 2
  56. REG_MONITOR_INFO GlRegMonitorInfo[NUM_KEYS_MONITORED] = { {NULL, 0, NULL, NULL}, {NULL, 0, NULL, NULL} };
  57. #define ElfAllEventsCleared() (GlRegMonitorInfo[0].NotifyEventHandle == NULL && \
  58. GlRegMonitorInfo[1].NotifyEventHandle == NULL )
  59. //
  60. // LOCAL FUNCTIONS
  61. //
  62. VOID
  63. ElfRegistryMonitor(
  64. PVOID pParms,
  65. BOOLEAN fWaitStatus
  66. );
  67. BOOL
  68. ElfSetupMonitor(
  69. LPREG_MONITOR_INFO pMonitorInfo
  70. );
  71. VOID
  72. ProcessChange (
  73. HANDLE hLogFile,
  74. PUNICODE_STRING ModuleName,
  75. PUNICODE_STRING LogFileName,
  76. ULONG MaxSize,
  77. ULONG Retention,
  78. LOGPOPUP logpLogPopup,
  79. BOOL * pbAcquiredString,
  80. DWORD dwAutoBackup
  81. )
  82. /*++
  83. Routine Description:
  84. This routine is called by ProcessRegistryChanges for each log file.
  85. Arguments:
  86. Return Value:
  87. None
  88. --*/
  89. {
  90. NTSTATUS Status = STATUS_SUCCESS;
  91. PLOGMODULE pModule;
  92. PLOGFILE pLogFile;
  93. ULONG Size;
  94. PVOID BaseAddress;
  95. PUNICODE_STRING pFileNameString;
  96. LPWSTR FileName;
  97. PVOID FreeAddress;
  98. BOOL bSDChanged;
  99. LPWSTR pwsSaveCustomSDDL = NULL;
  100. PSECURITY_DESCRIPTOR pSavedSd = NULL;
  101. DWORD dwType;
  102. *pbAcquiredString = FALSE;
  103. pModule = GetModuleStruc (ModuleName);
  104. //
  105. // If this module didn't exist, this was a brand new log file and
  106. // we need to create all the structures
  107. //
  108. if (pModule == ElfDefaultLogModule &&
  109. wcscmp(ModuleName->Buffer, ELF_DEFAULT_MODULE_NAME))
  110. {
  111. ELF_LOG1(MODULES,
  112. "ProcessChange: %ws log doesn't exist -- creating\n",
  113. ModuleName->Buffer);
  114. Status = SetUpDataStruct(LogFileName,
  115. MaxSize,
  116. Retention,
  117. ModuleName,
  118. hLogFile,
  119. ElfNormalLog,
  120. logpLogPopup,
  121. dwAutoBackup);
  122. if (NT_SUCCESS(Status))
  123. *pbAcquiredString = TRUE;
  124. return;
  125. }
  126. // check for changes in the security setting
  127. pLogFile = pModule->LogFile;
  128. dwType = GetModuleType(pLogFile->LogModuleName->Buffer);
  129. RtlAcquireResourceExclusive(&pLogFile->Resource, TRUE); // Wait until available
  130. pwsSaveCustomSDDL = pLogFile->pwsCurrCustomSD;
  131. pSavedSd = pLogFile->Sd;
  132. Status = ElfpCreateLogFileObject(
  133. pLogFile,
  134. dwType,
  135. hLogFile,
  136. FALSE,
  137. &bSDChanged);
  138. RtlReleaseResource(&pLogFile->Resource);
  139. if(NT_SUCCESS(Status) && bSDChanged == TRUE)
  140. {
  141. ElfpFreeBuffer (pwsSaveCustomSDDL);
  142. RtlDeleteSecurityObject(&pSavedSd);
  143. }
  144. //
  145. // Update values
  146. //
  147. pLogFile = pModule->LogFile;
  148. pLogFile->Retention = Retention;
  149. pLogFile->logpLogPopup = logpLogPopup;
  150. pLogFile->AutoBackupLogFiles = dwAutoBackup;
  151. //
  152. // Check to see if the name has changed. If it has, and the log
  153. // hasn't been used yet, then use the new name. Be sure to free
  154. // memory that was used for the old name.
  155. //
  156. if ((wcscmp(pLogFile->LogFileName->Buffer, LogFileName->Buffer) != 0)
  157. &&
  158. (pLogFile->BeginRecord == pLogFile->EndRecord))
  159. {
  160. pFileNameString = ElfpAllocateBuffer(sizeof(UNICODE_STRING)
  161. + LogFileName->MaximumLength);
  162. if (pFileNameString != NULL)
  163. {
  164. FileName = (LPWSTR)(pFileNameString + 1);
  165. StringCchCopyW(FileName, LogFileName->MaximumLength/sizeof(WCHAR),
  166. LogFileName->Buffer);
  167. RtlInitUnicodeString(pFileNameString, FileName);
  168. ElfpFreeBuffer(pLogFile->LogFileName);
  169. pLogFile->LogFileName = pFileNameString;
  170. }
  171. }
  172. //
  173. // The log file can only be grown dynamically. To shrink it,
  174. // it has to be cleared.
  175. //
  176. if (pLogFile->ConfigMaxFileSize < ELFFILESIZE(MaxSize))
  177. {
  178. /*
  179. Description of recent changes. Problem and Solution:
  180. A couple of problems exist. (1) There is no error
  181. checking if memory can't be allocated or mapped, and
  182. therefore, no error paths exist for handling these
  183. situations. (2) Now that the eventlog is in services.exe
  184. there isn't a good way to synchronize memory allocations.
  185. Solution:
  186. I considered having some utility routines for managing
  187. memory in the eventlog. These would attempt to
  188. extend a reserved block, or get a new reserved block.
  189. However, there are so many places where that could fail,
  190. it seemed very cumbersome to support the reserved blocks.
  191. So the current design only deals with mapped views.
  192. The ConfigMaxFileSize is only used to limit the size of
  193. the mapped view, and doesn't reserve anything. This
  194. means you are not guaranteed to be operating with a file as
  195. large as the MaxSize specified in the registry. But then,
  196. you weren't guarenteed that it would even work with the
  197. original design.
  198. */
  199. ELF_LOG3(TRACE,
  200. "ProcessChange: Growing %ws log from %x bytes to %x bytes\n",
  201. ModuleName->Buffer,
  202. pLogFile->ConfigMaxFileSize,
  203. ELFFILESIZE(MaxSize));
  204. pLogFile->ConfigMaxFileSize = ELFFILESIZE(MaxSize);
  205. pLogFile->NextClearMaxFileSize = ELFFILESIZE(MaxSize);
  206. }
  207. else if (pLogFile->ConfigMaxFileSize > ELFFILESIZE(MaxSize))
  208. {
  209. //
  210. // They're shrinking the size of the log file.
  211. // Next time we clear the log file, we'll use the new size
  212. // and new retention.
  213. //
  214. ELF_LOG3(TRACE,
  215. "ProcessChange: Shrinking %ws log from %x bytes to %x bytes at next clear\n",
  216. ModuleName->Buffer,
  217. pLogFile->ConfigMaxFileSize,
  218. ELFFILESIZE(MaxSize));
  219. pLogFile->NextClearMaxFileSize = ELFFILESIZE(MaxSize);
  220. }
  221. //
  222. // Now see if they've added any new modules for this log file
  223. //
  224. SetUpModules(hLogFile, pLogFile, TRUE);
  225. return;
  226. }
  227. VOID
  228. ProcessRegistryChanges (
  229. VOID
  230. )
  231. /*++
  232. Routine Description:
  233. This routine processes that changes that have occurred in the
  234. eventlog node. It does this by rescanning the whole Eventlog node
  235. and then comparing with what it has as the current configuration.
  236. Arguments:
  237. NONE.
  238. Return Value:
  239. NONE
  240. --*/
  241. {
  242. NTSTATUS Status;
  243. HANDLE hLogFile;
  244. UNICODE_STRING SubKeyName;
  245. ULONG Index = 0;
  246. BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
  247. PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
  248. ULONG ActualSize;
  249. LOG_FILE_INFO LogFileInfo;
  250. PWCHAR SubKeyString;
  251. OBJECT_ATTRIBUTES ObjectAttributes;
  252. PLOGMODULE pModule;
  253. LOGPOPUP logpLogPopup;
  254. BOOL bAcquiredString;
  255. #if DBG
  256. ULONG ulActualSize;
  257. #endif // DBG
  258. ELF_LOG0(TRACE,
  259. "ProcessRegistryChanges: Handling change in Eventlog service key\n");
  260. //
  261. // Take the global resource so that nobody is making changes or
  262. // using the existing configured information.
  263. //
  264. GetGlobalResource (ELF_GLOBAL_SHARED);
  265. #if DBG
  266. //
  267. // See if the Debug flag changed
  268. //
  269. RtlInitUnicodeString(&SubKeyName, VALUE_DEBUG);
  270. Status = NtQueryValueKey(hEventLogNode,
  271. &SubKeyName,
  272. KeyValuePartialInformation,
  273. KeyBuffer,
  274. ELF_MAX_REG_KEY_INFO_SIZE,
  275. &ulActualSize);
  276. if (NT_SUCCESS(Status))
  277. {
  278. if (((PKEY_VALUE_PARTIAL_INFORMATION) KeyBuffer)->Type == REG_DWORD)
  279. {
  280. ElfDebugLevel = *(LPDWORD) (((PKEY_VALUE_PARTIAL_INFORMATION) KeyBuffer)->Data);
  281. }
  282. }
  283. else
  284. {
  285. ELF_LOG1(TRACE,
  286. "ProcessRegistryChanges: NtQueryValueKey for ElfDebugLevel failed %#x\n",
  287. Status);
  288. }
  289. ELF_LOG1(TRACE,
  290. "ProcessRegistryChanges: New ElfDebugLevel is %#x\n",
  291. ElfDebugLevel);
  292. #endif // DBG
  293. Status = STATUS_SUCCESS;
  294. //
  295. // Loop thru the subkeys under Eventlog and set up each logfile
  296. //
  297. while (NT_SUCCESS(Status))
  298. {
  299. Status = NtEnumerateKey(hEventLogNode,
  300. Index++,
  301. KeyNodeInformation,
  302. KeyBuffer,
  303. ELF_MAX_REG_KEY_INFO_SIZE,
  304. &ActualSize);
  305. if (NT_SUCCESS(Status))
  306. {
  307. //
  308. // It turns out the Name isn't null terminated, so we need
  309. // to copy it somewhere and null terminate it before we use it
  310. //
  311. SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength + sizeof (WCHAR));
  312. bAcquiredString = FALSE;
  313. if (!SubKeyString)
  314. {
  315. //
  316. // No one to notify, just give up till next time.
  317. //
  318. ELF_LOG0(ERROR,
  319. "ProcessRegistryChanges: Unable to allocate subkey -- returning\n");
  320. ReleaseGlobalResource();
  321. return;
  322. }
  323. memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
  324. SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;
  325. //
  326. // Open the node for this logfile and extract the information
  327. // required by SetupDataStruct, and then call it.
  328. //
  329. RtlInitUnicodeString(&SubKeyName, SubKeyString);
  330. InitializeObjectAttributes(&ObjectAttributes,
  331. &SubKeyName,
  332. OBJ_CASE_INSENSITIVE,
  333. hEventLogNode,
  334. NULL
  335. );
  336. Status = NtOpenKey(&hLogFile,
  337. KEY_READ | KEY_SET_VALUE,
  338. &ObjectAttributes);
  339. //
  340. // Should always succeed since I just enum'ed it, but if it
  341. // doesn't, just skip it
  342. //
  343. if (!NT_SUCCESS(Status))
  344. {
  345. ELF_LOG2(ERROR,
  346. "ProcessRegistryChanges: NtOpenKey for subkey %ws failed %#x\n",
  347. SubKeyName,
  348. Status);
  349. ElfpFreeBuffer(SubKeyString);
  350. Status = STATUS_SUCCESS; // to keep the enum going
  351. continue;
  352. }
  353. //
  354. // Get the updated information from the registry. Note that we
  355. // have to initialize the "log full" popup policy before doing
  356. // so since ReadRegistryInfo will compare the value found in the
  357. // registry (if there is one) to the current value.
  358. //
  359. pModule = GetModuleStruc(&SubKeyName);
  360. LogFileInfo.logpLogPopup = pModule->LogFile->logpLogPopup;
  361. Status = ReadRegistryInfo(hLogFile,
  362. &SubKeyName,
  363. &LogFileInfo);
  364. if (NT_SUCCESS(Status))
  365. {
  366. //
  367. // Now process any changes for the log file.
  368. // ProcessChange deals with any errors.
  369. //
  370. ProcessChange (
  371. hLogFile,
  372. &SubKeyName,
  373. LogFileInfo.LogFileName,
  374. LogFileInfo.MaxFileSize,
  375. LogFileInfo.Retention,
  376. LogFileInfo.logpLogPopup,
  377. &bAcquiredString,
  378. LogFileInfo.dwAutoBackup);
  379. //
  380. // Free the buffer that was allocated in ReadRegistryInfo.
  381. //
  382. ElfpFreeBuffer(LogFileInfo.LogFileName);
  383. }
  384. else
  385. {
  386. ELF_LOG2(ERROR,
  387. "ProcessRegistryChanges: ReadRegistryInfo for subkey %ws failed %#x\n",
  388. SubKeyString,
  389. Status);
  390. }
  391. if(bAcquiredString == FALSE)
  392. ElfpFreeBuffer(SubKeyString);
  393. NtClose(hLogFile);
  394. }
  395. }
  396. //
  397. // Release the global resource.
  398. //
  399. ReleaseGlobalResource();
  400. } // ProcessRegistryChanges
  401. NTSTATUS
  402. ElfCheckForComputerNameChange(
  403. )
  404. /*++
  405. Routine Description:
  406. This routine checks to determine if the computer name has changed. If
  407. it has, then it generates an event.
  408. Arguments:
  409. NONE
  410. Return Value:
  411. NONE
  412. --*/
  413. {
  414. LPWSTR Dates[2];
  415. NTSTATUS Status;
  416. UNICODE_STRING ValueName;
  417. ULONG ulActualSize;
  418. DWORD dwLen;
  419. WCHAR wElfComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  420. WCHAR wComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  421. DWORD dwComputerNameLen = MAX_COMPUTERNAME_LENGTH + 1;
  422. BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
  423. PKEY_VALUE_PARTIAL_INFORMATION ValueBuffer =
  424. (PKEY_VALUE_PARTIAL_INFORMATION) Buffer;
  425. RtlInitUnicodeString(&ValueName, VALUE_COMPUTERNAME);
  426. // Read the name that the event log stored.
  427. Status = NtQueryValueKey(hEventLogNode,
  428. &ValueName,
  429. KeyValuePartialInformation,
  430. ValueBuffer,
  431. ELF_MAX_REG_KEY_INFO_SIZE,
  432. &ulActualSize);
  433. if (!NT_SUCCESS(Status) || ValueBuffer->DataLength == 0)
  434. {
  435. ELF_LOG1(ERROR,
  436. "ElfCheckForComputerNameChange: NtQueryValueKey for current name failed %#x\n",
  437. Status);
  438. return Status;
  439. }
  440. StringCchCopyW(wElfComputerName, MAX_COMPUTERNAME_LENGTH + 1, (WCHAR *)ValueBuffer->Data);
  441. // Read the active name.
  442. Status = NtQueryValueKey(hComputerNameNode,
  443. &ValueName,
  444. KeyValuePartialInformation,
  445. ValueBuffer,
  446. ELF_MAX_REG_KEY_INFO_SIZE,
  447. &ulActualSize);
  448. if (!NT_SUCCESS(Status) || ValueBuffer->DataLength == 0)
  449. {
  450. ELF_LOG1(ERROR,
  451. "ElfCheckForComputerNameChange: NtQueryValueKey for active name failed %#x\n",
  452. Status);
  453. return Status;
  454. }
  455. StringCchCopyW(wComputerName, MAX_COMPUTERNAME_LENGTH + 1,(WCHAR *)ValueBuffer->Data);
  456. // If the names are the same, just return STATUS_SUCCESS
  457. if (!_wcsicmp(wElfComputerName, wComputerName))
  458. return STATUS_SUCCESS;
  459. Dates[0] = wElfComputerName;
  460. Dates[1] = wComputerName;
  461. ElfpCreateElfEvent(EVENT_ComputerNameChange,
  462. EVENTLOG_INFORMATION_TYPE,
  463. 0, // EventCategory
  464. 2, // NumberOfStrings
  465. Dates, // Strings
  466. NULL, // Data
  467. 0, // Datalength
  468. 0, // flags
  469. FALSE); // for security file
  470. dwLen = sizeof(WCHAR) * (wcslen(wComputerName) + 1);
  471. Status = NtSetValueKey(hEventLogNode,
  472. &ValueName,
  473. 0,
  474. REG_SZ,
  475. wComputerName,
  476. dwLen);
  477. if (!NT_SUCCESS(Status))
  478. ELF_LOG1(ERROR,
  479. "ElfCheckForComputerNameChange: NtSetValueKey failed %#x\n",
  480. Status);
  481. return Status;
  482. }
  483. VOID
  484. ElfRegistryMonitor (
  485. PVOID pParms,
  486. BOOLEAN fWaitStatus
  487. )
  488. /*++
  489. Routine Description:
  490. This is the entry point for the thread that will monitor changes in
  491. the registry. If anything changes, it will have to scan the change
  492. and then make the appropriate changes to the data structures in the
  493. service to reflect the new information.
  494. Arguments:
  495. NONE
  496. Return Value:
  497. NONE
  498. --*/
  499. {
  500. NTSTATUS ntStatus;
  501. LPREG_MONITOR_INFO pMonitorInfo = (LPREG_MONITOR_INFO)pParms;
  502. ELF_LOG0(TRACE,
  503. "ElfRegistryMonitor: Registry monitor thread waking up\n");
  504. //
  505. // Deregister the work item (must be done even if the
  506. // WT_EXECUTEONLYONCE flag is specified)
  507. //
  508. if (pMonitorInfo->WorkItemHandle != NULL)
  509. {
  510. ntStatus = RtlDeregisterWait(pMonitorInfo->WorkItemHandle);
  511. pMonitorInfo->WorkItemHandle = NULL;
  512. if (!NT_SUCCESS(ntStatus))
  513. {
  514. ELF_LOG1(ERROR,
  515. "ElfRegistryMonitor: RtlDeregisterWorkItem failed %#x\n",
  516. ntStatus);
  517. }
  518. }
  519. if (GetElState() == STOPPING)
  520. {
  521. //
  522. // If the eventlog is shutting down, then we need
  523. // to terminate this thread.
  524. //
  525. ELF_LOG0(TRACE, "ElfRegistryMonitor: Shutdown\n");
  526. //
  527. // Close the registry handle and registry event handle.
  528. //
  529. if( pMonitorInfo->NotifyEventHandle != NULL )
  530. {
  531. NtClose( pMonitorInfo->NotifyEventHandle );
  532. pMonitorInfo->NotifyEventHandle = NULL;
  533. }
  534. if( pMonitorInfo->RegMonitorHandle != NULL )
  535. {
  536. NtClose(pMonitorInfo->RegMonitorHandle);
  537. pMonitorInfo->RegMonitorHandle = NULL;
  538. }
  539. //
  540. // This thread will perform the final cleanup for the eventlog.
  541. // Cleanup is not initiated until all events have been signaled
  542. // and closed
  543. //
  544. if( ElfAllEventsCleared() )
  545. {
  546. ElfpCleanUp(EventFlags);
  547. }
  548. return;
  549. }
  550. if (fWaitStatus == TRUE)
  551. {
  552. ELF_LOG0(TRACE,
  553. "ElfRegistryMonitor: Running because of a timeout -- running queued list\n");
  554. //
  555. // Timer popped, try running the list
  556. //
  557. if (!IsListEmpty(&QueuedEventListHead))
  558. {
  559. //
  560. // There are things queued up to write, do it
  561. //
  562. WriteQueuedEvents();
  563. }
  564. //
  565. // Don't wait again
  566. //
  567. pMonitorInfo->Timeout = INFINITE;
  568. }
  569. else
  570. {
  571. ELF_LOG0(TRACE,
  572. "ElfRegistryMonitor: Running because of notification\n");
  573. ProcessRegistryChanges ();
  574. ElfCheckForComputerNameChange();
  575. }
  576. if (!ElfSetupMonitor(pMonitorInfo))
  577. {
  578. ELF_LOG0(ERROR,
  579. "ElfRegistryMonitor: ElfSetupMonitor failed -- "
  580. "no longer listening for reg changes\n");
  581. }
  582. ELF_LOG0(TRACE,
  583. "ElfRegistryMonitor: Returning\n");
  584. return;
  585. } // ElfRegistryMonitor
  586. VOID
  587. InitNotify(
  588. PVOID pData
  589. )
  590. /*++
  591. Routine Description:
  592. Arguments:
  593. Return Value:
  594. --*/
  595. {
  596. NTSTATUS NtStatus = STATUS_SUCCESS;
  597. DWORD status = NO_ERROR;
  598. DWORD Buffer;
  599. PVOID pBuffer = &Buffer;
  600. LPREG_MONITOR_INFO pMonitorInfo;
  601. static IO_STATUS_BLOCK IoStatusBlock;
  602. ELF_LOG0(TRACE,
  603. "InitNotify: Registering Eventlog key with NtNotifyChangeKey\n");
  604. pMonitorInfo = (LPREG_MONITOR_INFO)pData;
  605. NtStatus = NtNotifyChangeKey (
  606. pMonitorInfo->RegMonitorHandle,
  607. pMonitorInfo->NotifyEventHandle,
  608. NULL,
  609. NULL,
  610. &IoStatusBlock,
  611. REG_NOTIFY_CHANGE_LAST_SET |
  612. REG_NOTIFY_CHANGE_NAME,
  613. TRUE,
  614. pBuffer,
  615. 1,
  616. TRUE); // return and wait on event
  617. if (!NT_SUCCESS(NtStatus))
  618. {
  619. ELF_LOG1(ERROR,
  620. "InitNotify: NtNotifyChangeKey on Eventlog key failed %#x\n",
  621. NtStatus);
  622. status = RtlNtStatusToDosError(NtStatus);
  623. }
  624. ELF_LOG0( TRACE, "InitNotify: Returning\n" );
  625. return;
  626. } // InitNotify
  627. BOOL
  628. ElfSetupMonitor(
  629. LPREG_MONITOR_INFO pMonitorInfo
  630. )
  631. /*++
  632. Routine Description:
  633. This function submits a request for a registry NotifyChangeKey
  634. and then submits a work item to the service controller thread
  635. management system to wait for the Notification handle to become
  636. signaled.
  637. Arguments:
  638. pMonitorInfo - This is a pointer to a MONITOR_INFO structure. This
  639. function fills in the WorkItemHandle member of that structure
  640. if successfully adds a new work item.
  641. Return Value:
  642. TRUE - if successful in setting up.
  643. FALSE - if unsuccessful. A work item hasn't been submitted, and
  644. we won't be listening for registry changes.
  645. --*/
  646. {
  647. NTSTATUS Status = STATUS_SUCCESS;
  648. //
  649. // Call NtNotifyChange Key via the thread pool
  650. // and make sure the thread that created the I/O
  651. // request will always be around.
  652. //
  653. Status = RtlQueueWorkItem(InitNotify, // Callback
  654. pMonitorInfo, // pContext
  655. WT_EXECUTEONLYONCE |
  656. WT_EXECUTEINPERSISTENTIOTHREAD);
  657. if (!NT_SUCCESS(Status))
  658. {
  659. ELF_LOG1(ERROR,
  660. "ElfSetupMonitor: RtlQueueWorkItem failed %#x\n",
  661. Status);
  662. return FALSE;
  663. }
  664. //
  665. // Add the work item that is to be called when the
  666. // NotifyEventHandle is signalled.
  667. //
  668. Status = RtlRegisterWait(&pMonitorInfo->WorkItemHandle,
  669. pMonitorInfo->NotifyEventHandle, // Waitable handle
  670. ElfRegistryMonitor, // Callback
  671. pMonitorInfo, // pContext
  672. pMonitorInfo->Timeout, // Timeout
  673. WT_EXECUTEONLYONCE |
  674. WT_EXECUTEINPERSISTENTIOTHREAD);
  675. if (!NT_SUCCESS(Status))
  676. {
  677. ELF_LOG1(ERROR,
  678. "ElfSetupMonitor: RtlRegisterWait failed %#x\n",
  679. Status);
  680. return FALSE;
  681. }
  682. return TRUE;
  683. } // ElfSetupMonitor
  684. BOOL
  685. ElfStartRegistryMonitor()
  686. /*++
  687. Routine Description:
  688. This routine starts up the thread that monitors changes in the registry.
  689. This function calls ElfSetupMonitor() to register for the change
  690. notification and to submit a work item to wait for the registry
  691. change event to get signaled. When signalled, the ElfRegistryMonitor()
  692. callback function is called by a thread from the services thread pool.
  693. This callback function services the notification.
  694. Arguments:
  695. NONE
  696. Return Value:
  697. TRUE if thread creation succeeded, FALSE otherwise.
  698. Note:
  699. --*/
  700. {
  701. NTSTATUS Status = STATUS_SUCCESS;
  702. DWORD LoopCounter = 0;
  703. BOOL ReturnStatus = TRUE;
  704. DWORD LoopCount;
  705. ELF_LOG0(TRACE, "ElfStartRegistryMonitor: Setting up registry change notification\n");
  706. if (hEventLogNode == NULL)
  707. {
  708. ELF_LOG0(ERROR, "ElfStartRegistryMonitor: No Eventlog key -- exiting\n");
  709. return FALSE;
  710. }
  711. if (hComputerNameNode == NULL)
  712. {
  713. ELF_LOG0(ERROR,
  714. "ElfStartRegistryMonitor: No ComputerName key -- exiting\n");
  715. return FALSE;
  716. }
  717. GlRegMonitorInfo[0].RegMonitorHandle = hEventLogNode;
  718. GlRegMonitorInfo[1].RegMonitorHandle = hComputerNameNode;
  719. //
  720. // Create the events on which to wait
  721. //
  722. for( LoopCount = 0; LoopCount < NUM_KEYS_MONITORED; LoopCount++ )
  723. {
  724. Status = NtCreateEvent(&GlRegMonitorInfo[LoopCount].NotifyEventHandle,
  725. EVENT_ALL_ACCESS,
  726. NULL,
  727. NotificationEvent,
  728. FALSE);
  729. if (!NT_SUCCESS(Status))
  730. {
  731. ELF_LOG1(ERROR, "ElfStartRegistryMonitor: NtCreateEvent failed %#x\n",
  732. Status);
  733. GlRegMonitorInfo[LoopCount].NotifyEventHandle = NULL;
  734. break;
  735. }
  736. //
  737. // Fill in the Monitor info structure with the event handle
  738. // and a 5 minute timeout.
  739. //
  740. GlRegMonitorInfo[LoopCount].Timeout = 5 * 60 * 1000;
  741. GlRegMonitorInfo[LoopCount].WorkItemHandle = NULL;
  742. }
  743. //
  744. // Cleanup all events, its all or nothing
  745. //
  746. if(!NT_SUCCESS(Status))
  747. {
  748. for( LoopCount = 0; LoopCount < NUM_KEYS_MONITORED; LoopCount++ )
  749. {
  750. if( GlRegMonitorInfo[LoopCount].NotifyEventHandle != NULL )
  751. {
  752. NtClose( GlRegMonitorInfo[LoopCount].NotifyEventHandle );
  753. GlRegMonitorInfo[LoopCount].NotifyEventHandle = NULL;
  754. }
  755. }
  756. return FALSE;
  757. }
  758. //
  759. // Setup for the change notify and
  760. // submit the work item to the eventlog threadpool.
  761. //
  762. for( LoopCount = 0; LoopCount < NUM_KEYS_MONITORED; LoopCount++ )
  763. {
  764. if (!ElfSetupMonitor(&GlRegMonitorInfo[LoopCount]))
  765. {
  766. ELF_LOG0(ERROR,
  767. "ElfStartRegistryMonitor: ElfSetupMonitor failed -- exiting\n");
  768. //
  769. // Note that it's OK to close this handle as there's no way
  770. // the handle was used for a registered wait at this point
  771. // (since ElfSetupMonitor failed).
  772. //
  773. NtClose( GlRegMonitorInfo[LoopCount].NotifyEventHandle );
  774. GlRegMonitorInfo[LoopCount].NotifyEventHandle = NULL;
  775. return FALSE;
  776. }
  777. //
  778. //Set this flag since we have at least one success
  779. //If any startup fails, then this setting will ensure that all
  780. // started monitors are shutdown
  781. //
  782. EventFlags |= ELF_STARTED_REGISTRY_MONITOR;
  783. }
  784. ELF_LOG0(TRACE, "ElfStartRegistryMonitor: Exiting after successful call\n");
  785. return TRUE;
  786. } // ElfStartRegistryMonitor
  787. VOID
  788. StopRegistryMonitor ()
  789. /*++
  790. Routine Description:
  791. This routine wakes up the work item that has been submitted for the
  792. purpose of monitoring registry eventlog changes. The thread created
  793. to service that work item will actually do the clean-up of the monitor
  794. thread.
  795. Arguments:
  796. NONE
  797. Return Value:
  798. NONE
  799. --*/
  800. {
  801. DWORD LoopCount = 0;
  802. ELF_LOG0(TRACE, "StopRegistryMonitor: Stopping registry monitor\n");
  803. //
  804. // Wake up the RegistryMonitorThread.
  805. //
  806. for( LoopCount = 0; LoopCount < NUM_KEYS_MONITORED; LoopCount++ )
  807. {
  808. if (GlRegMonitorInfo[LoopCount].NotifyEventHandle != NULL)
  809. {
  810. SetEvent(GlRegMonitorInfo[LoopCount].NotifyEventHandle);
  811. }
  812. }
  813. return;
  814. } // StopRegistryMonitor
  815. NTSTATUS
  816. ReadRegistryValue (
  817. HANDLE hLogFile,
  818. PCWSTR pName,
  819. PKEY_VALUE_FULL_INFORMATION ValueBuffer
  820. )
  821. /*++
  822. Routine Description:
  823. This routine reads a single value from the registry. It retrys after waiting
  824. a short period if the return code is c000034. This takes care of certain
  825. race conditions.
  826. Arguments:
  827. hLogFile - A handle to the Eventlog\<somelogfile> node in the registry
  828. ValueName - Value name
  829. ValueBuffer - Where the data is to be copied
  830. Return Value:
  831. NTSTATUS
  832. --*/
  833. {
  834. NTSTATUS Status;
  835. ULONG ActualSize;
  836. UNICODE_STRING ValueName;
  837. RtlInitUnicodeString(&ValueName, pName);
  838. Status = NtQueryValueKey(hLogFile,
  839. &ValueName,
  840. KeyValueFullInformation,
  841. ValueBuffer,
  842. ELF_MAX_REG_KEY_INFO_SIZE,
  843. &ActualSize);
  844. if(Status == STATUS_OBJECT_NAME_NOT_FOUND)
  845. {
  846. if(g_dwLastDelayTickCount == 0 ||
  847. ((GetTickCount() - g_dwLastDelayTickCount) > 30000))
  848. {
  849. Sleep(2000);
  850. Status = NtQueryValueKey(hLogFile,
  851. &ValueName,
  852. KeyValueFullInformation,
  853. ValueBuffer,
  854. ELF_MAX_REG_KEY_INFO_SIZE,
  855. &ActualSize);
  856. g_dwLastDelayTickCount = GetTickCount(); // used up our kindness
  857. }
  858. }
  859. return Status;
  860. }
  861. NTSTATUS
  862. ReadRegistryInfo (
  863. HANDLE hLogFile,
  864. PUNICODE_STRING SubKeyName,
  865. PLOG_FILE_INFO LogFileInfo
  866. )
  867. /*++
  868. Routine Description:
  869. This routine reads in the information from the node pointed to by
  870. hLogFile and stores it in the a structure so that the
  871. necessary data structures can be set up for the service.
  872. ALLOCATIONS: If successful, this function allocates memory for
  873. LogFileInfo->LogFileName. It is the responsiblilty of the caller
  874. to free this memory.
  875. Arguments:
  876. hLogFile - A handle to the Eventlog\<somelogfile> node in the registry
  877. KeyName - The subkey for this logfile to open
  878. LogFileInfo - The structure to fill in with the data
  879. Return Value:
  880. NTSTATUS
  881. --*/
  882. {
  883. #define EXPAND_BUFFER_SIZE 64
  884. NTSTATUS Status;
  885. BOOLEAN RegistryCorrupt = FALSE;
  886. BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
  887. ULONG ActualSize;
  888. UNICODE_STRING ValueName;
  889. UNICODE_STRING UnexpandedName;
  890. UNICODE_STRING ExpandedName;
  891. ULONG NumberOfBytes = 0;
  892. BYTE ExpandNameBuffer[EXPAND_BUFFER_SIZE];
  893. PUNICODE_STRING FileNameString;
  894. LPWSTR FileName;
  895. BOOL ExpandedBufferWasAllocated=FALSE;
  896. PKEY_VALUE_FULL_INFORMATION ValueBuffer =
  897. (PKEY_VALUE_FULL_INFORMATION) Buffer;
  898. ASSERT(hLogFile != NULL);
  899. ELF_LOG1(TRACE,
  900. "ReadRegistryInfo: Reading information for %ws log\n",
  901. SubKeyName->Buffer);
  902. //
  903. // MaxSize
  904. //
  905. Status = ReadRegistryValue (hLogFile,
  906. VALUE_MAXSIZE,
  907. ValueBuffer);
  908. if (!NT_SUCCESS(Status))
  909. {
  910. ELF_LOG2(ERROR,
  911. "ReadRegistryInfo: Can't read MaxSize value for %ws log %#x\n",
  912. SubKeyName->Buffer,
  913. Status);
  914. LogFileInfo->MaxFileSize = ELF_DEFAULT_MAX_FILE_SIZE;
  915. RegistryCorrupt = TRUE;
  916. }
  917. else
  918. {
  919. LogFileInfo->MaxFileSize = *((PULONG)(Buffer +
  920. ValueBuffer->DataOffset));
  921. ELF_LOG2(TRACE,
  922. "ReadRegistryInfo: New MaxSize value for %ws log is %#x\n",
  923. SubKeyName->Buffer,
  924. LogFileInfo->MaxFileSize);
  925. }
  926. //
  927. // The security log has an optional warning level.
  928. //
  929. if(0 == _wcsicmp(SubKeyName->Buffer, ELF_SECURITY_MODULE_NAME))
  930. {
  931. Status = ReadRegistryValue (hLogFile,
  932. VALUE_WARNINGLEVEL,
  933. ValueBuffer);
  934. if (!NT_SUCCESS(Status))
  935. {
  936. ELF_LOG2(TRACE,
  937. "ReadRegistryInfo: Can't read WarningLevel value for %ws log %#x\n",
  938. SubKeyName->Buffer,
  939. Status);
  940. giWarningLevel= ELF_DEFAULT_WARNING_LEVEL;
  941. }
  942. else
  943. {
  944. giWarningLevel = *((PULONG)(Buffer +
  945. ValueBuffer->DataOffset));
  946. ELF_LOG2(TRACE,
  947. "ReadRegistryInfo: New WarningLevel value for %ws log is %#x\n",
  948. SubKeyName->Buffer,
  949. LogFileInfo->Retention);
  950. if(giWarningLevel < 0 || giWarningLevel > 99)
  951. {
  952. giWarningLevel = ELF_DEFAULT_WARNING_LEVEL;
  953. ELF_LOG0(ERROR,
  954. "ReadRegistryInfo: New WarningLevel is invalid, being set to 0\n");
  955. }
  956. }
  957. }
  958. //
  959. // Retention period
  960. //
  961. Status = ReadRegistryValue (hLogFile,
  962. VALUE_RETENTION,
  963. ValueBuffer);
  964. if (!NT_SUCCESS(Status))
  965. {
  966. ELF_LOG2(ERROR,
  967. "ReadRegistryInfo: Can't read Retention value for %ws log %#x\n",
  968. SubKeyName->Buffer,
  969. Status);
  970. LogFileInfo->Retention = ELF_DEFAULT_RETENTION_PERIOD;
  971. RegistryCorrupt = TRUE;
  972. }
  973. else
  974. {
  975. LogFileInfo->Retention = *((PULONG)(Buffer +
  976. ValueBuffer->DataOffset));
  977. ELF_LOG2(TRACE,
  978. "ReadRegistryInfo: New Retention value for %ws log is %#x\n",
  979. SubKeyName->Buffer,
  980. LogFileInfo->Retention);
  981. }
  982. //
  983. // Autobackup value (optional!)
  984. //
  985. Status = ReadRegistryValue (hLogFile,
  986. REGSTR_VAL_AUTOBACKUPLOGFILES,
  987. ValueBuffer);
  988. if (!NT_SUCCESS(Status))
  989. {
  990. LogFileInfo->dwAutoBackup = 0;
  991. }
  992. else
  993. {
  994. LogFileInfo->dwAutoBackup = *((PULONG)(Buffer +
  995. ValueBuffer->DataOffset));
  996. ELF_LOG2(TRACE,
  997. "ReadRegistryInfo: New autobackup value for %ws log is %#x\n",
  998. SubKeyName->Buffer,
  999. LogFileInfo->dwAutoBackup);
  1000. }
  1001. //
  1002. // Filename
  1003. //
  1004. Status = ReadRegistryValue (hLogFile,
  1005. VALUE_FILENAME,
  1006. ValueBuffer);
  1007. if (!NT_SUCCESS(Status))
  1008. {
  1009. ELF_LOG2(ERROR,
  1010. "ReadRegistryInfo: Can't read Filename value for %ws log %#x\n",
  1011. SubKeyName->Buffer,
  1012. Status);
  1013. //
  1014. // Allocate the buffer for the UNICODE_STRING for the filename and
  1015. // initialize it. (41 = \Systemroot\system32\config\xxxxxxxx.evt)
  1016. //
  1017. #define REG_NAME_SIZE 41
  1018. FileNameString = ElfpAllocateBuffer(REG_NAME_SIZE * sizeof(WCHAR) + sizeof(UNICODE_STRING));
  1019. if (!FileNameString)
  1020. {
  1021. ELF_LOG0(ERROR,
  1022. "ReadRegistryInfo: Unable to allocate FileNameString\n");
  1023. return STATUS_NO_MEMORY;
  1024. }
  1025. LogFileInfo->LogFileName = FileNameString;
  1026. FileName = (LPWSTR)(FileNameString + 1);
  1027. StringCchCopyW(FileName, REG_NAME_SIZE, L"\\Systemroot\\System32\\Config\\");
  1028. StringCchCatW(FileName, REG_NAME_SIZE, SubKeyName->Buffer);
  1029. StringCchCatW(FileName, REG_NAME_SIZE,L".evt");
  1030. RtlInitUnicodeString(FileNameString, FileName);
  1031. RegistryCorrupt = TRUE;
  1032. }
  1033. else
  1034. {
  1035. //
  1036. // If it's a REG_EXPAND_SZ expand it
  1037. //
  1038. if (ValueBuffer->Type == REG_EXPAND_SZ)
  1039. {
  1040. ELF_LOG0(TRACE,
  1041. "ReadRegistryInfo: Filename is a REG_EXPAND_SZ -- expanding\n");
  1042. //
  1043. // Initialize the UNICODE_STRING, when the string isn't null
  1044. // terminated
  1045. //
  1046. UnexpandedName.MaximumLength = UnexpandedName.Length =
  1047. (USHORT) ValueBuffer->DataLength;
  1048. UnexpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
  1049. ValueBuffer->DataOffset);
  1050. //
  1051. // Call the magic expand-o api
  1052. //
  1053. ExpandedName.Length = ExpandedName.MaximumLength = EXPAND_BUFFER_SIZE;
  1054. ExpandedName.Buffer = (LPWSTR) ExpandNameBuffer;
  1055. Status = RtlExpandEnvironmentStrings_U(NULL,
  1056. &UnexpandedName,
  1057. &ExpandedName,
  1058. &NumberOfBytes);
  1059. if (Status == STATUS_BUFFER_TOO_SMALL)
  1060. {
  1061. ELF_LOG0(TRACE,
  1062. "ReadRegistryInfo: Expansion buffer too small -- retrying\n");
  1063. //
  1064. // The default buffer wasn't big enough. Allocate a
  1065. // bigger one and try again
  1066. //
  1067. ExpandedName.Length = ExpandedName.MaximumLength = (USHORT) NumberOfBytes;
  1068. ExpandedName.Buffer = ElfpAllocateBuffer(ExpandedName.Length);
  1069. if (!ExpandedName.Buffer)
  1070. {
  1071. ELF_LOG0(ERROR,
  1072. "ReadRegistryInfo: Unable to allocate larger Filename buffer\n");
  1073. return(STATUS_NO_MEMORY);
  1074. }
  1075. ExpandedBufferWasAllocated = TRUE;
  1076. Status = RtlExpandEnvironmentStrings_U(NULL,
  1077. &UnexpandedName,
  1078. &ExpandedName,
  1079. &NumberOfBytes);
  1080. }
  1081. if (!NT_SUCCESS(Status))
  1082. {
  1083. ELF_LOG1(ERROR,
  1084. "ReadRegistryInfo: RtlExpandEnvironmentStrings_U failed %#x\n",
  1085. Status);
  1086. if (ExpandedBufferWasAllocated)
  1087. {
  1088. ElfpFreeBuffer(ExpandedName.Buffer);
  1089. }
  1090. return Status;
  1091. }
  1092. }
  1093. else
  1094. {
  1095. //
  1096. // It doesn't need to be expanded, just set up the UNICODE_STRING
  1097. // for the conversion to an NT pathname
  1098. //
  1099. ExpandedName.MaximumLength = ExpandedName.Length =
  1100. (USHORT) ValueBuffer->DataLength;
  1101. ExpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
  1102. ValueBuffer->DataOffset);
  1103. }
  1104. //
  1105. // Now convert from a DOS pathname to an NT pathname
  1106. //
  1107. // NOTE: this allocates a buffer for ValueName.Buffer.
  1108. //
  1109. if (!RtlDosPathNameToNtPathName_U(ExpandedName.Buffer,
  1110. &ValueName,
  1111. NULL,
  1112. NULL))
  1113. {
  1114. ELF_LOG0(ERROR,
  1115. "ReadRegistryInfo: RtlDosPathNameToNtPathName_U failed\n");
  1116. if (ExpandedBufferWasAllocated)
  1117. {
  1118. ElfpFreeBuffer(ExpandedName.Buffer);
  1119. }
  1120. return STATUS_UNSUCCESSFUL;
  1121. }
  1122. //
  1123. // Allocate memory for the unicode string structure and the buffer
  1124. // so that it can be free'd with a single call.
  1125. //
  1126. FileNameString = ElfpAllocateBuffer(
  1127. sizeof(UNICODE_STRING) +
  1128. ((ValueName.Length + 1) * sizeof(WCHAR)));
  1129. if (FileNameString == NULL)
  1130. {
  1131. ELF_LOG0(ERROR,
  1132. "ReadRegistryInfo: Unable to allocate copy of NT filename\n");
  1133. if (ExpandedBufferWasAllocated)
  1134. {
  1135. ElfpFreeBuffer(ExpandedName.Buffer);
  1136. }
  1137. //
  1138. // RtlDosPathNameToNtPathName_U allocates off the process heap
  1139. //
  1140. RtlFreeHeap(RtlProcessHeap(), 0, ValueName.Buffer);
  1141. return STATUS_NO_MEMORY;
  1142. }
  1143. //
  1144. // Copy the NtPathName string into the new buffer, and initialize
  1145. // the unicode string.
  1146. //
  1147. FileName = (LPWSTR)(FileNameString + 1);
  1148. wcsncpy(FileName, ValueName.Buffer, ValueName.Length);
  1149. *(FileName+ValueName.Length) = L'\0';
  1150. RtlInitUnicodeString(FileNameString, FileName);
  1151. //
  1152. // RtlDosPathNameToNtPathName_U allocates off the process heap
  1153. //
  1154. RtlFreeHeap(RtlProcessHeap(), 0, ValueName.Buffer);
  1155. //
  1156. // Clean up if I had to allocate a bigger buffer than the default
  1157. //
  1158. if (ExpandedBufferWasAllocated)
  1159. {
  1160. ElfpFreeBuffer(ExpandedName.Buffer);
  1161. }
  1162. }
  1163. //
  1164. // Add the LogFileName to the LogFileInfo structure.
  1165. //
  1166. LogFileInfo->LogFileName = FileNameString;
  1167. ELF_LOG2(TRACE,
  1168. "ReadRegistryInfo: New (expanded) Filename value for %ws log is %ws\n",
  1169. SubKeyName->Buffer,
  1170. LogFileInfo->LogFileName->Buffer);
  1171. //
  1172. // "Log full" popup policy -- never change the security log
  1173. //
  1174. if (_wcsicmp(SubKeyName->Buffer, ELF_SECURITY_MODULE_NAME) != 0)
  1175. {
  1176. RtlInitUnicodeString(&ValueName, VALUE_LOGPOPUP);
  1177. Status = NtQueryValueKey(hLogFile,
  1178. &ValueName,
  1179. KeyValueFullInformation,
  1180. ValueBuffer,
  1181. ELF_MAX_REG_KEY_INFO_SIZE,
  1182. &ActualSize);
  1183. if (NT_SUCCESS(Status))
  1184. {
  1185. LOGPOPUP logpRegValue = *(PULONG)(Buffer + ValueBuffer->DataOffset);
  1186. //
  1187. // Only update the value if this constitutes a change in the current policy
  1188. //
  1189. if (LogFileInfo->logpLogPopup == LOGPOPUP_NEVER_SHOW
  1190. ||
  1191. logpRegValue == LOGPOPUP_NEVER_SHOW)
  1192. {
  1193. LogFileInfo->logpLogPopup =
  1194. (logpRegValue == LOGPOPUP_NEVER_SHOW ? LOGPOPUP_NEVER_SHOW :
  1195. LOGPOPUP_CLEARED);
  1196. }
  1197. }
  1198. else
  1199. {
  1200. //
  1201. // TRACE rather than ERROR as this value is optional
  1202. //
  1203. ELF_LOG2(TRACE,
  1204. "ReadRegistryInfo: Can't read LogPopup value for %ws log %#x\n",
  1205. SubKeyName->Buffer,
  1206. Status);
  1207. }
  1208. }
  1209. //
  1210. // If we didn't find all the required values, tell someone
  1211. //
  1212. if (RegistryCorrupt)
  1213. {
  1214. ELF_LOG1(ERROR,
  1215. "ReadRegistryInfo: One or more registry values for %ws log invalid\n",
  1216. SubKeyName->Buffer);
  1217. }
  1218. return STATUS_SUCCESS;
  1219. }