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.

5401 lines
158 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. ntapi.c
  5. Abstract:
  6. This module contains the NT level entry points for the registry.
  7. Author:
  8. Bryan M. Willman (bryanwi) 26-Aug-1991
  9. Revision History:
  10. Elliot Shmukler (t-ellios) 24-Aug-1998
  11. Modified NtInitializeRegistry to handle the LKG work that needs
  12. to be done when a boot is accepted by SC.
  13. --*/
  14. #include "cmp.h"
  15. #include "safeboot.h"
  16. #include <evntrace.h>
  17. extern POBJECT_TYPE ExEventObjectType;
  18. extern POBJECT_TYPE CmpKeyObjectType;
  19. extern BOOLEAN CmFirstTime;
  20. extern BOOLEAN CmBootAcceptFirstTime;
  21. extern BOOLEAN CmpHoldLazyFlush;
  22. extern BOOLEAN CmpCannotWriteConfiguration;
  23. extern BOOLEAN CmpTraceFlag;
  24. extern BOOLEAN HvShutdownComplete;
  25. #ifdef CMP_STATS
  26. extern struct {
  27. ULONG CmpMaxKcbNo;
  28. ULONG CmpKcbNo;
  29. ULONG CmpStatNo;
  30. ULONG CmpNtCreateKeyNo;
  31. ULONG CmpNtDeleteKeyNo;
  32. ULONG CmpNtDeleteValueKeyNo;
  33. ULONG CmpNtEnumerateKeyNo;
  34. ULONG CmpNtEnumerateValueKeyNo;
  35. ULONG CmpNtFlushKeyNo;
  36. ULONG CmpNtNotifyChangeMultipleKeysNo;
  37. ULONG CmpNtOpenKeyNo;
  38. ULONG CmpNtQueryKeyNo;
  39. ULONG CmpNtQueryValueKeyNo;
  40. ULONG CmpNtQueryMultipleValueKeyNo;
  41. ULONG CmpNtRestoreKeyNo;
  42. ULONG CmpNtSaveKeyNo;
  43. ULONG CmpNtSaveMergedKeysNo;
  44. ULONG CmpNtSetValueKeyNo;
  45. ULONG CmpNtLoadKeyNo;
  46. ULONG CmpNtUnloadKeyNo;
  47. ULONG CmpNtSetInformationKeyNo;
  48. ULONG CmpNtReplaceKeyNo;
  49. ULONG CmpNtQueryOpenSubKeysNo;
  50. } CmpStatsDebug;
  51. ULONG CmpNtFakeCreate = 0;
  52. BOOLEAN CmpNtFakeCreateStarted;
  53. #endif
  54. //
  55. // Nt API helper routines
  56. //
  57. NTSTATUS
  58. CmpNameFromAttributes(
  59. IN POBJECT_ATTRIBUTES Attributes,
  60. KPROCESSOR_MODE PreviousMode,
  61. OUT PUNICODE_STRING FullName
  62. );
  63. ULONG
  64. CmpKeyInfoProbeAlingment(
  65. IN KEY_INFORMATION_CLASS KeyInformationClass
  66. );
  67. #ifdef POOL_TAGGING
  68. #define ALLOCATE_WITH_QUOTA(a,b,c) ExAllocatePoolWithQuotaTag((a)|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,b,c)
  69. #else
  70. #define ALLOCATE_WITH_QUOTA(a,b,c) ExAllocatePoolWithQuota((a)|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,b)
  71. #endif
  72. #if DBG
  73. ULONG
  74. CmpExceptionFilter(
  75. IN PEXCEPTION_POINTERS ExceptionPointers
  76. );
  77. #ifdef ALLOC_PRAGMA
  78. #pragma alloc_text(PAGE,CmpExceptionFilter)
  79. #endif
  80. #else
  81. #define CmpExceptionFilter(x) EXCEPTION_EXECUTE_HANDLER
  82. #endif
  83. #ifdef REGISTRY_LOCK_CHECKING
  84. #ifdef ALLOC_PRAGMA
  85. #pragma alloc_text(PAGE,CmpCheckLockExceptionFilter)
  86. #endif
  87. ULONG
  88. CmpCheckLockExceptionFilter(
  89. IN PEXCEPTION_POINTERS ExceptionPointers
  90. )
  91. {
  92. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CM: Registry exception %lx, ExceptionPointers = %p\n",
  93. ExceptionPointers->ExceptionRecord->ExceptionCode,
  94. ExceptionPointers));
  95. CM_BUGCHECK(REGISTRY_ERROR,CHECK_LOCK_EXCEPTION,
  96. (ULONG_PTR)ExceptionPointers->ExceptionRecord->ExceptionCode,
  97. (ULONG_PTR)ExceptionPointers->ExceptionRecord,
  98. (ULONG_PTR)ExceptionPointers->ContextRecord);
  99. return EXCEPTION_EXECUTE_HANDLER;
  100. }
  101. #endif //REGISTRY_LOCK_CHECKING
  102. VOID
  103. CmpFlushNotifiesOnKeyBodyList(
  104. IN PCM_KEY_CONTROL_BLOCK kcb
  105. );
  106. #if 0
  107. BOOLEAN
  108. CmpEnumKeyObjectCallback(
  109. IN PVOID Object,
  110. IN PUNICODE_STRING ObjectName,
  111. IN ULONG HandleCount,
  112. IN ULONG PointerCount,
  113. IN PVOID Context
  114. );
  115. #endif
  116. VOID
  117. CmpDummyApc(
  118. struct _KAPC *Apc,
  119. PVOID *SystemArgument1,
  120. PVOID *SystemArgument2
  121. );
  122. #ifdef ALLOC_PRAGMA
  123. #pragma alloc_text(PAGE,NtCreateKey)
  124. #pragma alloc_text(PAGE,NtDeleteKey)
  125. #pragma alloc_text(PAGE,NtDeleteValueKey)
  126. #pragma alloc_text(PAGE,NtEnumerateKey)
  127. #pragma alloc_text(PAGE,NtEnumerateValueKey)
  128. #pragma alloc_text(PAGE,NtFlushKey)
  129. #pragma alloc_text(PAGE,NtInitializeRegistry)
  130. #pragma alloc_text(PAGE,NtNotifyChangeKey)
  131. #pragma alloc_text(PAGE,NtNotifyChangeMultipleKeys)
  132. #pragma alloc_text(PAGE,NtOpenKey)
  133. #pragma alloc_text(PAGE,NtQueryKey)
  134. #pragma alloc_text(PAGE,NtQueryValueKey)
  135. #pragma alloc_text(PAGE,NtQueryMultipleValueKey)
  136. #pragma alloc_text(PAGE,NtRestoreKey)
  137. #pragma alloc_text(PAGE,NtSaveKey)
  138. #pragma alloc_text(PAGE,NtSaveKeyEx)
  139. #pragma alloc_text(PAGE,NtSaveMergedKeys)
  140. #pragma alloc_text(PAGE,NtSetValueKey)
  141. #pragma alloc_text(PAGE,NtLoadKey)
  142. #pragma alloc_text(PAGE,NtLoadKey2)
  143. #pragma alloc_text(PAGE,NtUnloadKey)
  144. #ifdef NT_UNLOAD_KEY_EX
  145. #pragma alloc_text(PAGE,NtUnloadKeyEx)
  146. #endif
  147. #pragma alloc_text(PAGE,NtSetInformationKey)
  148. #pragma alloc_text(PAGE,NtReplaceKey)
  149. #ifdef NT_RENAME_KEY
  150. #pragma alloc_text(PAGE,NtRenameKey)
  151. #endif
  152. #pragma alloc_text(PAGE,NtQueryOpenSubKeys)
  153. #pragma alloc_text(PAGE,NtLockRegistryKey)
  154. #pragma alloc_text(PAGE,CmpNameFromAttributes)
  155. #pragma alloc_text(PAGE,CmpAllocatePostBlock)
  156. #pragma alloc_text(PAGE,CmpFreePostBlock)
  157. #pragma alloc_text(PAGE,CmpKeyInfoProbeAlingment)
  158. #if 0
  159. #pragma alloc_text(PAGE,CmpEnumKeyObjectCallback)
  160. #endif
  161. #pragma alloc_text(PAGE,NtCompactKeys)
  162. #pragma alloc_text(PAGE,NtCompressKey)
  163. #endif
  164. //
  165. // Nt level registry API calls
  166. //
  167. NTSTATUS
  168. NtCreateKey(
  169. OUT PHANDLE KeyHandle,
  170. IN ACCESS_MASK DesiredAccess,
  171. IN POBJECT_ATTRIBUTES ObjectAttributes,
  172. IN ULONG TitleIndex,
  173. IN PUNICODE_STRING Class OPTIONAL,
  174. IN ULONG CreateOptions,
  175. OUT PULONG Disposition OPTIONAL
  176. )
  177. /*++
  178. Routine Description:
  179. An existing registry key may be opened, or a new one created,
  180. with NtCreateKey.
  181. If the specified key does not exist, an attempt is made to create it.
  182. For the create attempt to succeed, the new node must be a direct
  183. child of the node referred to by KeyHandle. If the node exists,
  184. it is opened. Its value is not affected in any way.
  185. Share access is computed from desired access.
  186. NOTE:
  187. If CreateOptions has REG_OPTION_BACKUP_RESTORE set, then
  188. DesiredAccess will be ignored. If the caller has the
  189. privilege SeBackupPrivilege asserted, a handle with
  190. KEY_READ | ACCESS_SYSTEM_SECURITY will be returned.
  191. If SeRestorePrivilege, then same but KEY_WRITE rather
  192. than KEY_READ. If both, then both access sets. If neither
  193. privilege is asserted, then the call will fail.
  194. Arguments:
  195. KeyHandle - Receives a Handle which is used to access the
  196. specified key in the Registration Database.
  197. DesiredAccess - Specifies the access rights desired.
  198. ObjectAttributes - Specifies the attributes of the key being opened.
  199. Note that a key name must be specified. If a Root Directory is
  200. specified, the name is relative to the root. The name of the
  201. object must be within the name space allocated to the Registry,
  202. that is, all names beginning "\Registry". RootHandle, if
  203. present, must be a handle to "\", or "\Registry", or a key
  204. under "\Registry".
  205. RootHandle must have been opened for KEY_CREATE_SUB_KEY access
  206. if a new node is to be created.
  207. NOTE: Object manager will capture and probe this argument.
  208. TitleIndex - Specifies the index of the localized alias for
  209. the name of the key. The title index specifies the index of
  210. the localized alias for the name. Ignored if the key
  211. already exists.
  212. Class - Specifies the object class of the key. (To the registry
  213. this is just a string.) Ignored if NULL.
  214. CreateOptions - Optional control values:
  215. REG_OPTION_VOLATILE - Object is not to be stored across boots.
  216. Disposition - This optional parameter is a pointer to a variable
  217. that will receive a value indicating whether a new Registry
  218. key was created or an existing one opened:
  219. REG_CREATED_NEW_KEY - A new Registry Key was created
  220. REG_OPENED_EXISTING_KEY - An existing Registry Key was opened
  221. Return Value:
  222. NTSTATUS - Result code from call, among the following:
  223. <TBS>
  224. --*/
  225. {
  226. NTSTATUS status;
  227. KPROCESSOR_MODE mode;
  228. CM_PARSE_CONTEXT ParseContext;
  229. PCM_KEY_BODY KeyBody;
  230. HANDLE Handle = 0;
  231. UNICODE_STRING CapturedObjectName = {0};
  232. // Start registry call tracing
  233. StartWmiCmTrace();
  234. #if !defined(BUILD_WOW6432)
  235. DesiredAccess &= (~KEY_WOW64_RES); // filter out wow64 specific access
  236. #endif
  237. PAGED_CODE();
  238. if( HvShutdownComplete == TRUE ) {
  239. //
  240. // It is forbidden to wite to the registry after it has been shutdown
  241. //
  242. if(PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_REGISTRY){
  243. //
  244. // if in clean shutdown mode all processes should have been killed and all drivers unloaded at this point
  245. //
  246. CM_BUGCHECK(REGISTRY_ERROR,INVALID_WRITE_OPERATION,1,ObjectAttributes,0);
  247. }
  248. #ifndef _CM_LDR_
  249. {
  250. PUCHAR ImageName = PsGetCurrentProcessImageFileName();
  251. if( !ImageName ) {
  252. ImageName = "Unknown";
  253. }
  254. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\n\nProcess.Thread : %p.%p (%s) is trying to create key: \n",
  255. PsGetCurrentProcessId(), PsGetCurrentThreadId(),ImageName);
  256. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tObjectAttributes = %p\n",ObjectAttributes);
  257. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"The caller should not rely on data written to the registry after shutdown...\n");
  258. }
  259. #endif //_CM_LDR_
  260. return STATUS_TOO_LATE;
  261. }
  262. #ifdef CMP_STATS
  263. CmpStatsDebug.CmpNtCreateKeyNo++;
  264. #endif
  265. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtCreateKey\n"));
  266. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tDesiredAccess=%08lx ", DesiredAccess));
  267. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tCreateOptions=%08lx\n", CreateOptions));
  268. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tObjectAttributes=%p\n", ObjectAttributes));
  269. mode = KeGetPreviousMode();
  270. try {
  271. ParseContext.Class.Length = 0;
  272. ParseContext.Class.Buffer = NULL;
  273. if (mode == UserMode) {
  274. if (ARGUMENT_PRESENT(Class)) {
  275. ParseContext.Class = ProbeAndReadUnicodeString(Class);
  276. ProbeForRead(
  277. ParseContext.Class.Buffer,
  278. ParseContext.Class.Length,
  279. sizeof(WCHAR)
  280. );
  281. }
  282. ProbeAndZeroHandle(KeyHandle);
  283. if (ARGUMENT_PRESENT(Disposition)) {
  284. ProbeForWriteUlong(Disposition);
  285. }
  286. //
  287. // probe the ObjectAttributes as we shall use it for tracing
  288. //
  289. ProbeForReadSmallStructure( ObjectAttributes,
  290. sizeof(OBJECT_ATTRIBUTES),
  291. PROBE_ALIGNMENT(OBJECT_ATTRIBUTES) );
  292. CapturedObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName);
  293. ProbeForRead(
  294. CapturedObjectName.Buffer,
  295. CapturedObjectName.Length,
  296. sizeof(WCHAR)
  297. );
  298. } else {
  299. if (ARGUMENT_PRESENT(Class)) {
  300. ParseContext.Class = *Class;
  301. }
  302. CapturedObjectName = *(ObjectAttributes->ObjectName);
  303. }
  304. //
  305. // be sure nobody will hurt himself when adding new options
  306. //
  307. ASSERT( (REG_LEGAL_OPTION & REG_OPTION_PREDEF_HANDLE) == 0 );
  308. if ((CreateOptions & (REG_LEGAL_OPTION | REG_OPTION_PREDEF_HANDLE)) != CreateOptions) {
  309. // End registry call tracing
  310. EndWmiCmTrace(STATUS_INVALID_PARAMETER,0,&CapturedObjectName,EVENT_TRACE_TYPE_REGCREATE);
  311. return STATUS_INVALID_PARAMETER;
  312. }
  313. #ifdef CMP_STATS
  314. CmpNtFakeCreateStarted = TRUE;
  315. #endif
  316. // hook it for WMI
  317. HookKcbFromHandleForWmiCmTrace(ObjectAttributes->RootDirectory);
  318. ParseContext.TitleIndex = 0;
  319. ParseContext.CreateOptions = CreateOptions;
  320. ParseContext.Disposition = 0L;
  321. ParseContext.CreateLink = FALSE;
  322. ParseContext.PredefinedHandle = NULL;
  323. status = ObOpenObjectByName(
  324. ObjectAttributes,
  325. CmpKeyObjectType,
  326. mode,
  327. NULL,
  328. DesiredAccess,
  329. (PVOID)&ParseContext,
  330. &Handle
  331. );
  332. if (status==STATUS_PREDEFINED_HANDLE) {
  333. status = ObReferenceObjectByHandle(Handle,
  334. 0,
  335. CmpKeyObjectType,
  336. KernelMode,
  337. (PVOID *)(&KeyBody),
  338. NULL);
  339. if (NT_SUCCESS(status)) {
  340. HANDLE TempHandle;
  341. //
  342. // Make sure we do the dereference and close before accessing
  343. // user space as the reference might fault.
  344. //
  345. TempHandle = (HANDLE)LongToHandle(KeyBody->Type);
  346. ObDereferenceObject((PVOID)KeyBody);
  347. NtClose(Handle);
  348. Handle = *KeyHandle = TempHandle;
  349. status = STATUS_SUCCESS;
  350. }
  351. } else
  352. if (NT_SUCCESS(status)) {
  353. *KeyHandle = Handle;
  354. // need to do this only on clean shutdown
  355. CmpAddKeyTracker(Handle,mode);
  356. }
  357. if (ARGUMENT_PRESENT(Disposition)) {
  358. *Disposition = ParseContext.Disposition;
  359. }
  360. } except (CmpExceptionFilter(GetExceptionInformation())) {
  361. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtCreateKey: code:%08lx\n", GetExceptionCode()));
  362. status = GetExceptionCode();
  363. }
  364. #ifdef CMP_STATS
  365. CmpNtFakeCreateStarted = FALSE;
  366. #endif
  367. // End registry call tracing
  368. EndWmiCmTrace(status,0,&CapturedObjectName,EVENT_TRACE_TYPE_REGCREATE);
  369. return status;
  370. }
  371. extern PCM_KEY_BODY ExpControlKey[2];
  372. //
  373. // WARNING: This should be the same as the one defined in obp.h
  374. // Remove this one when object manager guys will export
  375. // this via ob.h
  376. //
  377. #define OBJ_AUDIT_OBJECT_CLOSE 0x00000004L
  378. NTSTATUS
  379. NtDeleteKey(
  380. IN HANDLE KeyHandle
  381. )
  382. /*++
  383. Routine Description:
  384. A registry key may be marked for delete, causing it to be removed
  385. from the system. It will remain in the name space until the last
  386. handle to it is closed.
  387. Arguments:
  388. KeyHandle - Specifies the handle of the Key to delete, must have
  389. been opened for DELETE access.
  390. Return Value:
  391. NTSTATUS - Result code from call, among the following:
  392. <TBS>
  393. --*/
  394. {
  395. PCM_KEY_BODY KeyBody;
  396. NTSTATUS status;
  397. OBJECT_HANDLE_INFORMATION HandleInfo;
  398. // Start registry call tracing
  399. StartWmiCmTrace();
  400. PAGED_CODE();
  401. #ifdef CMP_STATS
  402. CmpStatsDebug.CmpNtDeleteKeyNo++;
  403. #endif
  404. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtDeleteKey\n"));
  405. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx\n", KeyHandle));
  406. status = ObReferenceObjectByHandle(KeyHandle,
  407. DELETE,
  408. CmpKeyObjectType,
  409. KeGetPreviousMode(),
  410. (PVOID *)(&KeyBody),
  411. &HandleInfo);
  412. if (NT_SUCCESS(status)) {
  413. if( CmAreCallbacksRegistered() ) {
  414. REG_DELETE_KEY_INFORMATION DeleteInfo;
  415. DeleteInfo.Object = KeyBody;
  416. status = CmpCallCallBacks(RegNtDeleteKey,&DeleteInfo);
  417. if( !NT_SUCCESS(status) ) {
  418. ObDereferenceObject((PVOID)KeyBody);
  419. return status;
  420. }
  421. }
  422. //
  423. // hook the kcb for WMI
  424. //
  425. HookKcbForWmiCmTrace(KeyBody);
  426. //
  427. // Silently fail deletes of setup key and productoptions key
  428. //
  429. if ( (ExpControlKey[0] && KeyBody->KeyControlBlock == ExpControlKey[0]->KeyControlBlock) ||
  430. (ExpControlKey[1] && KeyBody->KeyControlBlock == ExpControlKey[1]->KeyControlBlock) ) {
  431. ObDereferenceObject((PVOID)KeyBody);
  432. // End registry call tracing
  433. EndWmiCmTrace(status,0,NULL,EVENT_TRACE_TYPE_REGDELETE);
  434. return STATUS_SUCCESS;
  435. }
  436. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ||
  437. CmIsKcbReadOnly(KeyBody->KeyControlBlock->ParentKcb) ) {
  438. //
  439. // key is protected
  440. //
  441. status = STATUS_ACCESS_DENIED;
  442. } else {
  443. BEGIN_LOCK_CHECKPOINT;
  444. status = CmDeleteKey(KeyBody);
  445. END_LOCK_CHECKPOINT;
  446. }
  447. if (NT_SUCCESS(status)) {
  448. //
  449. // Audit the deletion
  450. //
  451. if ( HandleInfo.HandleAttributes & OBJ_AUDIT_OBJECT_CLOSE ) {
  452. SeDeleteObjectAuditAlarm(KeyBody,
  453. KeyHandle );
  454. }
  455. }
  456. ObDereferenceObject((PVOID)KeyBody);
  457. }
  458. // End registry call tracing
  459. EndWmiCmTrace(status,0,NULL,EVENT_TRACE_TYPE_REGDELETE);
  460. return status;
  461. }
  462. NTSTATUS
  463. NtDeleteValueKey(
  464. IN HANDLE KeyHandle,
  465. IN PUNICODE_STRING ValueName
  466. )
  467. /*++
  468. Routine Description:
  469. One of the value entries of a registry key may be removed with this
  470. call. To remove the entire key, call NtDeleteKey.
  471. The value entry with ValueName matching ValueName is removed from the key.
  472. If no such entry exists, an error is returned.
  473. Arguments:
  474. KeyHandle - Specifies the handle of the key containing the value
  475. entry of interest. Must have been opend for KEY_SET_VALUE access.
  476. ValueName - The name of the value to be deleted. NULL is a legal name.
  477. Return Value:
  478. NTSTATUS - Result code from call, among the following:
  479. <TBS>
  480. --*/
  481. {
  482. NTSTATUS status;
  483. PCM_KEY_BODY KeyBody;
  484. KPROCESSOR_MODE mode;
  485. UNICODE_STRING LocalValueName;
  486. // Start registry call tracing
  487. StartWmiCmTrace();
  488. PAGED_CODE();
  489. #ifdef CMP_STATS
  490. CmpStatsDebug.CmpNtDeleteValueKeyNo++;
  491. #endif
  492. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtDeleteValueKey\n"));
  493. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx\n", KeyHandle));
  494. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tValueName='%wZ'\n", ValueName));
  495. mode = KeGetPreviousMode();
  496. status = ObReferenceObjectByHandle(
  497. KeyHandle,
  498. KEY_SET_VALUE,
  499. CmpKeyObjectType,
  500. mode,
  501. (PVOID *)(&KeyBody),
  502. NULL
  503. );
  504. if (NT_SUCCESS(status)) {
  505. //
  506. // hook the kcb for WMI
  507. //
  508. HookKcbForWmiCmTrace(KeyBody);
  509. try {
  510. if (mode == UserMode) {
  511. LocalValueName = ProbeAndReadUnicodeString(ValueName);
  512. ProbeForRead(
  513. LocalValueName.Buffer,
  514. LocalValueName.Length,
  515. sizeof(WCHAR)
  516. );
  517. } else {
  518. LocalValueName = *ValueName;
  519. }
  520. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  521. //
  522. // key is protected
  523. //
  524. status = STATUS_ACCESS_DENIED;
  525. } else {
  526. if( CmAreCallbacksRegistered() ) {
  527. REG_DELETE_VALUE_KEY_INFORMATION DeleteInfo;
  528. DeleteInfo.Object = KeyBody;
  529. DeleteInfo.ValueName = &LocalValueName;
  530. status = CmpCallCallBacks(RegNtDeleteValueKey,&DeleteInfo);
  531. }
  532. if( NT_SUCCESS(status) ) {
  533. BEGIN_LOCK_CHECKPOINT;
  534. status = CmDeleteValueKey(
  535. KeyBody->KeyControlBlock,
  536. LocalValueName
  537. );
  538. END_LOCK_CHECKPOINT;
  539. }
  540. }
  541. } except (EXCEPTION_EXECUTE_HANDLER) {
  542. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtDeleteValueKey: code:%08lx\n", GetExceptionCode()));
  543. status = GetExceptionCode();
  544. }
  545. ObDereferenceObject((PVOID)KeyBody);
  546. } else {
  547. LocalValueName.Buffer = NULL;
  548. LocalValueName.Length = 0;
  549. }
  550. // End registry call tracing
  551. EndWmiCmTrace(status,0,&LocalValueName,EVENT_TRACE_TYPE_REGDELETEVALUE);
  552. return status;
  553. }
  554. NTSTATUS
  555. NtEnumerateKey(
  556. IN HANDLE KeyHandle,
  557. IN ULONG Index,
  558. IN KEY_INFORMATION_CLASS KeyInformationClass,
  559. IN PVOID KeyInformation,
  560. IN ULONG Length,
  561. IN PULONG ResultLength
  562. )
  563. /*++
  564. Routine Description:
  565. The sub keys of an open key may be enumerated with NtEnumerateKey.
  566. NtEnumerateKey returns the name of the Index'th sub key of the open
  567. key specified by KeyHandle. The value STATUS_NO_MORE_ENTRIES will be
  568. returned if value of Index is larger than the number of sub keys.
  569. Note that Index is simply a way to select among child keys. Two calls
  570. to NtEnumerateKey with the same Index are NOT guaranteed to return
  571. the same results.
  572. If KeyInformation is not long enough to hold all requested data,
  573. STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
  574. set to the number of bytes actually required.
  575. Arguments:
  576. KeyHandle - Handle of the key whose sub keys are to be enumerated. Must
  577. be open for KEY_ENUMERATE_SUB_KEY access.
  578. Index - Specifies the (0-based) number of the sub key to be returned.
  579. KeyInformationClass - Specifies the type of information returned in
  580. Buffer. One of the following types:
  581. KeyBasicInformation - return last write time, title index, and name.
  582. (see KEY_BASIC_INFORMATION structure)
  583. KeyNodeInformation - return last write time, title index, name, class.
  584. (see KEY_NODE_INFORMATION structure)
  585. KeyInformation -Supplies pointer to buffer to receive the data.
  586. Length - Length of KeyInformation in bytes.
  587. ResultLength - Number of bytes actually written into KeyInformation.
  588. Return Value:
  589. NTSTATUS - Result code from call, among the following:
  590. <TBS>
  591. --*/
  592. {
  593. NTSTATUS status;
  594. PCM_KEY_BODY KeyBody;
  595. KPROCESSOR_MODE mode;
  596. // Start registry call tracing
  597. StartWmiCmTrace();
  598. PAGED_CODE();
  599. #ifdef CMP_STATS
  600. CmpStatsDebug.CmpNtEnumerateKeyNo++;
  601. #endif
  602. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtEnumerateKey\n"));
  603. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx Index=%08lx\n", KeyHandle, Index));
  604. if ((KeyInformationClass != KeyBasicInformation) &&
  605. (KeyInformationClass != KeyNodeInformation) &&
  606. (KeyInformationClass != KeyFullInformation))
  607. {
  608. //
  609. // hook the kcb for WMI
  610. //
  611. HookKcbFromHandleForWmiCmTrace(KeyHandle);
  612. // End registry call tracing
  613. EndWmiCmTrace(STATUS_INVALID_PARAMETER,Index,NULL,EVENT_TRACE_TYPE_REGENUMERATEKEY);
  614. return STATUS_INVALID_PARAMETER;
  615. }
  616. mode = KeGetPreviousMode();
  617. status = ObReferenceObjectByHandle(
  618. KeyHandle,
  619. KEY_ENUMERATE_SUB_KEYS,
  620. CmpKeyObjectType,
  621. mode,
  622. (PVOID *)(&KeyBody),
  623. NULL
  624. );
  625. if (NT_SUCCESS(status)) {
  626. //
  627. // hook the kcb for WMI
  628. //
  629. HookKcbForWmiCmTrace(KeyBody);
  630. try {
  631. if (mode == UserMode) {
  632. ProbeForWrite(
  633. KeyInformation,
  634. Length,
  635. sizeof(ULONG)
  636. );
  637. ProbeForWriteUlong(ResultLength);
  638. }
  639. } except (EXCEPTION_EXECUTE_HANDLER) {
  640. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtEnumerateKey: code:%08lx\n", GetExceptionCode()));
  641. status = GetExceptionCode();
  642. }
  643. if( NT_SUCCESS(status)) {
  644. if( CmAreCallbacksRegistered() ) {
  645. REG_ENUMERATE_KEY_INFORMATION EnumerateInfo;
  646. EnumerateInfo.Object = KeyBody;
  647. EnumerateInfo.Index = Index;
  648. EnumerateInfo.KeyInformationClass = KeyInformationClass;
  649. EnumerateInfo.KeyInformation = KeyInformation;
  650. EnumerateInfo.Length = Length;
  651. EnumerateInfo.ResultLength = ResultLength;
  652. status = CmpCallCallBacks(RegNtEnumerateKey,&EnumerateInfo);
  653. }
  654. if( NT_SUCCESS(status)) {
  655. //
  656. // CmEnumerateKey is protected to user mode buffer exceptions
  657. // all other exceptions are cm internals and should result in a bugcheck
  658. //
  659. BEGIN_LOCK_CHECKPOINT;
  660. status = CmEnumerateKey(
  661. KeyBody->KeyControlBlock,
  662. Index,
  663. KeyInformationClass,
  664. KeyInformation,
  665. Length,
  666. ResultLength
  667. );
  668. END_LOCK_CHECKPOINT;
  669. }
  670. }
  671. ObDereferenceObject((PVOID)KeyBody);
  672. }
  673. // End registry call tracing
  674. EndWmiCmTrace(status,Index,NULL,EVENT_TRACE_TYPE_REGENUMERATEKEY);
  675. return status;
  676. }
  677. NTSTATUS
  678. NtEnumerateValueKey(
  679. IN HANDLE KeyHandle,
  680. IN ULONG Index,
  681. IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
  682. IN PVOID KeyValueInformation,
  683. IN ULONG Length,
  684. OUT PULONG ResultLength
  685. )
  686. /*++
  687. Routine Description:
  688. The value entries of an open key may be enumerated
  689. with NtEnumerateValueKey.
  690. NtEnumerateValueKey returns the name of the Index'th value
  691. entry of the open key specified by KeyHandle. The value
  692. STATUS_NO_MORE_ENTRIES will be returned if value of Index is
  693. larger than the number of sub keys.
  694. Note that Index is simply a way to select among value
  695. entries. Two calls to NtEnumerateValueKey with the same Index
  696. are NOT guaranteed to return the same results.
  697. If KeyValueInformation is not long enough to hold all requested data,
  698. STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
  699. set to the number of bytes actually required.
  700. Arguments:
  701. KeyHandle - Handle of the key whose value entries are to be enumerated.
  702. Must have been opened with KEY_QUERY_VALUE access.
  703. Index - Specifies the (0-based) number of the sub key to be returned.
  704. KeyValueInformationClass - Specifies the type of information returned
  705. in Buffer. One of the following types:
  706. KeyValueBasicInformation - return time of last write,
  707. title index, and name. (See KEY_VALUE_BASIC_INFORMATION)
  708. KeyValueFullInformation - return time of last write,
  709. title index, name, class. (See KEY_VALUE_FULL_INFORMATION)
  710. KeyValueInformation -Supplies pointer to buffer to receive the data.
  711. Length - Length of KeyValueInformation in bytes.
  712. ResultLength - Number of bytes actually written into KeyValueInformation.
  713. Return Value:
  714. NTSTATUS - Result code from call, among the following:
  715. <TBS>
  716. --*/
  717. {
  718. NTSTATUS status;
  719. PCM_KEY_BODY KeyBody;
  720. KPROCESSOR_MODE mode;
  721. // Start registry call tracing
  722. StartWmiCmTrace();
  723. PAGED_CODE();
  724. #ifdef CMP_STATS
  725. CmpStatsDebug.CmpNtEnumerateValueKeyNo++;
  726. #endif
  727. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtEnumerateValueKey\n"));
  728. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx Index=%08lx\n", KeyHandle, Index));
  729. if ((KeyValueInformationClass != KeyValueBasicInformation) &&
  730. (KeyValueInformationClass != KeyValueFullInformation) &&
  731. (KeyValueInformationClass != KeyValuePartialInformation))
  732. {
  733. //
  734. // hook the kcb for WMI
  735. //
  736. HookKcbFromHandleForWmiCmTrace(KeyHandle);
  737. // End registry call tracing
  738. EndWmiCmTrace(STATUS_INVALID_PARAMETER,Index,NULL,EVENT_TRACE_TYPE_REGENUMERATEVALUEKEY);
  739. return STATUS_INVALID_PARAMETER;
  740. }
  741. mode = KeGetPreviousMode();
  742. status = ObReferenceObjectByHandle(
  743. KeyHandle,
  744. KEY_QUERY_VALUE,
  745. CmpKeyObjectType,
  746. mode,
  747. (PVOID *)(&KeyBody),
  748. NULL
  749. );
  750. if (NT_SUCCESS(status)) {
  751. //
  752. // hook the kcb for WMI
  753. //
  754. HookKcbForWmiCmTrace(KeyBody);
  755. try {
  756. if (mode == UserMode) {
  757. ProbeForWrite(
  758. KeyValueInformation,
  759. Length,
  760. sizeof(ULONG)
  761. );
  762. ProbeForWriteUlong(ResultLength);
  763. }
  764. } except (EXCEPTION_EXECUTE_HANDLER) {
  765. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtEnumerateValueKey: code:%08lx\n", GetExceptionCode()));
  766. status = GetExceptionCode();
  767. }
  768. if( NT_SUCCESS(status)) {
  769. if( CmAreCallbacksRegistered() ) {
  770. REG_ENUMERATE_VALUE_KEY_INFORMATION EnumerateValueInfo;
  771. EnumerateValueInfo.Object = KeyBody;
  772. EnumerateValueInfo.Index = Index;
  773. EnumerateValueInfo.KeyValueInformationClass = KeyValueInformationClass;
  774. EnumerateValueInfo.KeyValueInformation = KeyValueInformation;
  775. EnumerateValueInfo.Length = Length;
  776. EnumerateValueInfo.ResultLength = ResultLength;
  777. status = CmpCallCallBacks(RegNtEnumerateValueKey,&EnumerateValueInfo);
  778. }
  779. if( NT_SUCCESS(status)) {
  780. //
  781. // CmEnumerateValueKey is protected to user mode buffer exceptions
  782. // all other exceptions are cm internals and should result in a bugcheck
  783. //
  784. BEGIN_LOCK_CHECKPOINT;
  785. status = CmEnumerateValueKey(
  786. KeyBody->KeyControlBlock,
  787. Index,
  788. KeyValueInformationClass,
  789. KeyValueInformation,
  790. Length,
  791. ResultLength
  792. );
  793. END_LOCK_CHECKPOINT;
  794. }
  795. }
  796. ObDereferenceObject((PVOID)KeyBody);
  797. }
  798. // End registry call tracing
  799. EndWmiCmTrace(status,Index,NULL,EVENT_TRACE_TYPE_REGENUMERATEVALUEKEY);
  800. return status;
  801. }
  802. NTSTATUS
  803. NtFlushKey(
  804. IN HANDLE KeyHandle
  805. )
  806. /*++
  807. Routine Description:
  808. Changes made by NtCreateKey or NtSetKey may be flushed to disk with
  809. NtFlushKey.
  810. NtFlushKey will not return to its caller until any changed data
  811. associated with KeyHandle has been written to permanent store.
  812. WARNING: NtFlushKey will flush the entire registry tree, and thus will
  813. burn cycles and I/O.
  814. Arguments:
  815. KeyHandle - Handle of open key to be flushed.
  816. Return Value:
  817. NTSTATUS - Result code from call, among the following:
  818. <TBS>
  819. --*/
  820. {
  821. PCM_KEY_BODY KeyBody;
  822. NTSTATUS status;
  823. // Start registry call tracing
  824. StartWmiCmTrace();
  825. PAGED_CODE();
  826. #ifdef CMP_STATS
  827. CmpStatsDebug.CmpNtFlushKeyNo++;
  828. #endif
  829. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtFlushKey\n"));
  830. status = ObReferenceObjectByHandle(
  831. KeyHandle,
  832. 0,
  833. CmpKeyObjectType,
  834. KeGetPreviousMode(),
  835. (PVOID *)(&KeyBody),
  836. NULL
  837. );
  838. if (NT_SUCCESS(status)) {
  839. //
  840. // hook the kcb for WMI
  841. //
  842. HookKcbForWmiCmTrace(KeyBody);
  843. BEGIN_LOCK_CHECKPOINT;
  844. CmpLockRegistry();
  845. if (KeyBody->KeyControlBlock->Delete) {
  846. status = STATUS_KEY_DELETED;
  847. } else {
  848. //
  849. // call the worker to do the flush
  850. //
  851. status = CmFlushKey(KeyBody->KeyControlBlock->KeyHive, KeyBody->KeyControlBlock->KeyCell);
  852. }
  853. CmpUnlockRegistry();
  854. END_LOCK_CHECKPOINT;
  855. ObDereferenceObject((PVOID)KeyBody);
  856. }
  857. // End registry call tracing
  858. EndWmiCmTrace(status,0,NULL,EVENT_TRACE_TYPE_REGFLUSH);
  859. return status;
  860. }
  861. NTSTATUS
  862. NtInitializeRegistry(
  863. IN USHORT BootCondition
  864. )
  865. /*++
  866. Routine Description:
  867. This routine is called in 2 situations:
  868. 1) It is called from SM after autocheck (chkdsk) has
  869. run and the paging files have been opened. It's function is
  870. to bind in memory hives to their files, and to open any other
  871. files yet to be used.
  872. 2) It is called from SC after the current boot has been accepted
  873. and the control set used for the boot process should be saved
  874. as the LKG control set.
  875. After this routine accomplishes the work of situation #1 and
  876. #2, further requests for such work will not be carried out.
  877. Arguments:
  878. BootCondition -
  879. REG_INIT_BOOT_SM - The routine has been called from SM
  880. in situation #1.
  881. REG_INIT_BOOT_SETUP - The routine has been called to perform
  882. situation #1 work but has been called
  883. from setup and needs to do some special
  884. work.
  885. REG_INIT_BOOT_ACCEPTED_BASE + Num
  886. (where 0 < Num < 1000) - The routine has been called
  887. in situation #2. "Num" is the
  888. number of the control set
  889. to which the boot control set
  890. should be saved.
  891. Return Value:
  892. NTSTATUS - Result code from call, among the following:
  893. STATUS_SUCCESS - it worked
  894. STATUS_ACCESS_DENIED - the routine has already done the work
  895. requested and will not do it again.
  896. --*/
  897. {
  898. BOOLEAN SetupBoot;
  899. NTSTATUS Status;
  900. PAGED_CODE();
  901. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtInitializeRegistry()\n"));
  902. //
  903. // Force previous mode to be KernelMode
  904. //
  905. if (KeGetPreviousMode() == UserMode) {
  906. return ZwInitializeRegistry(BootCondition);
  907. } else {
  908. //
  909. // Check for a valid BootCondition value
  910. //
  911. if(BootCondition > REG_INIT_MAX_VALID_CONDITION)
  912. return STATUS_INVALID_PARAMETER;
  913. //
  914. // Check for a Boot acceptance
  915. //
  916. if((BootCondition >= REG_INIT_BOOT_ACCEPTED_BASE) &&
  917. (BootCondition <= REG_INIT_BOOT_ACCEPTED_MAX))
  918. {
  919. //
  920. // Make sure the Boot can be accepted only once
  921. //
  922. if(!CmBootAcceptFirstTime)
  923. return STATUS_ACCESS_DENIED;
  924. CmBootAcceptFirstTime = FALSE;
  925. //
  926. // Calculate the control set we want to save
  927. // the boot control set to
  928. //
  929. BootCondition -= REG_INIT_BOOT_ACCEPTED_BASE;
  930. if(BootCondition)
  931. {
  932. //
  933. // OK, this is a good boot for the purposes of LKG, and we have
  934. // a valid control set number passed into us. We save off our
  935. // control set and then notify everyone who wants to do post
  936. // WinLogon work.
  937. //
  938. // Note that none of this happens during Safe Mode boot!
  939. //
  940. Status = CmpSaveBootControlSet(BootCondition);
  941. //
  942. // Mark the boot good for the Hal. This lets the Hal do things
  943. // like optimize reboot performance.
  944. //
  945. HalEndOfBoot();
  946. //
  947. // Notify prefetcher of boot progress.
  948. //
  949. CcPfBeginBootPhase(PfBootAcceptedRegistryInitPhase);
  950. //
  951. // inform the user in the event one of the system core hives have been self healed
  952. //
  953. CmpRaiseSelfHealWarningForSystemHives();
  954. //
  955. // enable lazy flusher
  956. //
  957. CmpHoldLazyFlush = FALSE;
  958. CmpLazyFlush();
  959. return Status;
  960. }
  961. else
  962. {
  963. //
  964. // 0 passed in as a control set number.
  965. // That is not valid, fail.
  966. //
  967. return STATUS_INVALID_PARAMETER;
  968. }
  969. }
  970. // called from setup?
  971. SetupBoot = (BootCondition == REG_INIT_BOOT_SETUP ? TRUE : FALSE);
  972. //
  973. // Fail if not first time called for situation #1 work.
  974. //
  975. if (CmFirstTime != TRUE) {
  976. return STATUS_ACCESS_DENIED;
  977. }
  978. CmFirstTime = FALSE;
  979. //
  980. // Notify WMI.
  981. //
  982. WmiBootPhase1();
  983. //
  984. // Notify prefetcher of boot progress.
  985. //
  986. CcPfBeginBootPhase(PfSMRegistryInitPhase);
  987. //
  988. // Call the worker to init cm globals
  989. //
  990. CmpLockRegistryExclusive();
  991. CmpCmdInit(SetupBoot);
  992. CmpSetVersionData();
  993. CmpUnlockRegistry();
  994. //
  995. // Notify PO that the volumes are usabled
  996. //
  997. PoInitHiberServices(SetupBoot);
  998. if (!SetupBoot) {
  999. IopCopyBootLogRegistryToFile();
  1000. }
  1001. return STATUS_SUCCESS;
  1002. }
  1003. }
  1004. NTSTATUS
  1005. NtNotifyChangeKey(
  1006. IN HANDLE KeyHandle,
  1007. IN HANDLE Event OPTIONAL,
  1008. IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
  1009. IN PVOID ApcContext OPTIONAL,
  1010. OUT PIO_STATUS_BLOCK IoStatusBlock,
  1011. IN ULONG CompletionFilter,
  1012. IN BOOLEAN WatchTree,
  1013. OUT PVOID Buffer,
  1014. IN ULONG BufferSize,
  1015. IN BOOLEAN Asynchronous
  1016. )
  1017. /*++
  1018. Routine Description:
  1019. Notification of key creation, deletion, and modification may be
  1020. obtained by calling NtNotifyChangeKey.
  1021. NtNotifyChangeKey monitors changes to a key - if the key or
  1022. subtree specified by KeyHandle are modified, the service notifies
  1023. its caller. It also returns the name(s) of the key(s) that changed.
  1024. All names are specified relative to the key that the handle represents
  1025. (therefore a NULL name represents that key). The service completes
  1026. once the key or subtree has been modified based on the supplied
  1027. CompletionFilter. The service is a "single shot" and therefore
  1028. needs to be reinvoked to watch the key for further changes.
  1029. The operation of this service begins by opening a key for KEY_NOTIFY
  1030. access. Once the handle is returned, the NtNotifyChangeKey service
  1031. may be invoked to begin watching the values and subkeys of the
  1032. specified key for changes. The first time the service is invoked,
  1033. the BufferSize parameter supplies not only the size of the user's
  1034. Buffer, but also the size of the buffer that will be used by the
  1035. Registry to store names of keys that have changed. Likewise, the
  1036. CompletionFilter and WatchTree parameters on the first call indicate
  1037. how notification should operate for all calls using the supplied
  1038. KeyHandle. These two parameters are ignored on subsequent calls
  1039. to the API with the same instance of KeyHandle.
  1040. Once a modification is made that should be reported, the Registry will
  1041. complete the service. The names of the files that have changed since
  1042. the last time the service was called will be placed into the caller's
  1043. output Buffer. The Information field of IoStatusBlock will contain
  1044. the number of bytes placed in Buffer, or zero if too many keys have
  1045. changed since the last time the service was called, in which case
  1046. the application must Query and Enumerate the key and sub keys to
  1047. discover changes. The Status field of IoStatusBlock will contain
  1048. the actual status of the call.
  1049. If Asynchronous is TRUE, then Event, if specified, will be set to
  1050. the Signaled state. If no Event parameter was specified, then
  1051. KeyHandle will be set to the Signaled state. If an ApcRoutine
  1052. was specified, it is invoked with the ApcContext and the address of the
  1053. IoStatusBlock as its arguments. If Asynchronous is FALSE, Event,
  1054. ApcRoutine, and ApcContext are ignored.
  1055. This service requires KEY_NOTIFY access to the key that was
  1056. actually modified
  1057. The notify "session" is terminated by closing KeyHandle.
  1058. Arguments:
  1059. KeyHandle-- Supplies a handle to an open key. This handle is
  1060. effectively the notify handle, because only one set of
  1061. notify parameters may be set against it.
  1062. Event - An optional handle to an event to be set to the
  1063. Signaled state when the operation completes.
  1064. ApcRoutine - An optional procedure to be invoked once the
  1065. operation completes. For more information about this
  1066. parameter see the NtReadFile system service description.
  1067. If PreviousMode == Kernel, this parameter is an optional
  1068. pointer to a WORK_QUEUE_ITEM to be queued when the notify
  1069. is signaled.
  1070. ApcContext - A pointer to pass as an argument to the ApcRoutine,
  1071. if one was specified, when the operation completes. This
  1072. argument is required if an ApcRoutine was specified.
  1073. If PreviousMode == Kernel, this parameter is an optional
  1074. WORK_QUEUE_TYPE describing the queue to be used. This argument
  1075. is required if an ApcRoutine was specified.
  1076. IoStatusBlock - A variable to receive the final completion status.
  1077. For more information about this parameter see the NtCreateFile
  1078. system service description.
  1079. CompletionFilter -- Specifies a set of flags that indicate the
  1080. types of operations on the key or its value that cause the
  1081. call to complete. The following are valid flags for this parameter:
  1082. REG_NOTIFY_CHANGE_NAME -- Specifies that the call should be
  1083. completed if a subkey is added or deleted.
  1084. REG_NOTIFY_CHANGE_ATTRIBUTES -- Specifies that the call should
  1085. be completed if the attributes (e.g.: ACL) of the key or
  1086. any subkey are changed.
  1087. REG_NOTIFY_CHANGE_LAST_SET -- Specifies that the call should be
  1088. completed if the lastWriteTime of the key or any of its
  1089. subkeys is changed. (Ie. if the value of the key or any
  1090. subkey is changed).
  1091. REG_NOTIFY_CHANGE_SECURITY -- Specifies that the call should be
  1092. completed if the security information (e.g. ACL) on the key
  1093. or any subkey is changed.
  1094. WatchTree -- A BOOLEAN value that, if TRUE, specifies that all
  1095. changes in the subtree of this key should also be reported.
  1096. If FALSE, only changes to this key, its value, and its immediate
  1097. subkeys (but not their values nor their subkeys) are reported.
  1098. Buffer -- A variable to receive the name(s) of the key(s) that
  1099. changed. See REG_NOTIFY_INFORMATION.
  1100. BufferSize -- Specifies the length of Buffer.
  1101. Asynchronous -- If FALSE, call will not return until
  1102. complete (synchronous) if TRUE, call may return STATUS_PENDING.
  1103. Obs:
  1104. Since NtNotifyChangeMultipleKeys, this routine is kept only for bacwards compatibility
  1105. Return Value:
  1106. NTSTATUS - Result code from call, among the following:
  1107. <TBS>
  1108. --*/
  1109. {
  1110. PAGED_CODE();
  1111. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtNotifyChangeKey\n"));
  1112. // Just call the wiser routine
  1113. return NtNotifyChangeMultipleKeys(
  1114. KeyHandle,
  1115. 0,
  1116. NULL,
  1117. Event,
  1118. ApcRoutine,
  1119. ApcContext,
  1120. IoStatusBlock,
  1121. CompletionFilter,
  1122. WatchTree,
  1123. Buffer,
  1124. BufferSize,
  1125. Asynchronous
  1126. );
  1127. }
  1128. NTSTATUS
  1129. NtNotifyChangeMultipleKeys(
  1130. IN HANDLE MasterKeyHandle,
  1131. IN ULONG Count,
  1132. IN OBJECT_ATTRIBUTES SlaveObjects[],
  1133. IN HANDLE Event OPTIONAL,
  1134. IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
  1135. IN PVOID ApcContext OPTIONAL,
  1136. OUT PIO_STATUS_BLOCK IoStatusBlock,
  1137. IN ULONG CompletionFilter,
  1138. IN BOOLEAN WatchTree,
  1139. OUT PVOID Buffer,
  1140. IN ULONG BufferSize,
  1141. IN BOOLEAN Asynchronous
  1142. )
  1143. /*++
  1144. Routine Description:
  1145. Notificaion of creation, deletion and modification on multiple keys
  1146. may be obtained with NtNotifyChangeMultipleKeys.
  1147. NtNotifyMultipleKeys monitors changes to any of the MasterKeyHandle
  1148. or one of SlaveObjects and/or their subtrees, whichever occurs first.
  1149. When an event on these keys is triggered, the notification is considered
  1150. fulfilled, and has to be "armed" again, in order to watch for further
  1151. changes.
  1152. The mechanism is similar to the one described in NtNotifyChangeKey.
  1153. The MasterKeyHandle key, give the caller control over the lifetime
  1154. of the notification. The notification will live as long as the caller
  1155. keeps the MasterKeyHandle open, or an event is triggered.
  1156. The caller doesn't have to open the SlaveKeys. He will provide the
  1157. routine with an array of OBJECT_ATTRIBUTES, describing the slave objects.
  1158. The routine will open the objects, and ensure keep a reference on them
  1159. untill the back-end side will close them.
  1160. The notify "session" is terminated by closing MasterKeyHandle.
  1161. Obs:
  1162. For the time being, the routine supports only one slave object. When more
  1163. than one slave object is provided, the routine will signal an error of
  1164. STATUS_INVALID_PARAMETER.
  1165. However, the interface is designed for future enhancements (taking an
  1166. array of slave objects), that may be provided with future versions(w2001).
  1167. When no slave object is supplied (i.e. Count == 0) we have the identical
  1168. behavior as for NtNotifyChangeKey.
  1169. Arguments:
  1170. MasterKeyHandle - Supplies a handle to an open key. This handle is
  1171. the "master handle". It has control overthe lifetime of the
  1172. notification.
  1173. Count - Number of slave objects. For the time being, this should be 1
  1174. SlaveObjects - Array of slave objects. Only the attributes of the
  1175. objects are provided, so the caller doesn't have to take care
  1176. of them.
  1177. Event,ApcRoutine,ApcContext,IoStatusBlock,CompletionFilter,WatchTree,
  1178. Buffer,BufferSize,Asynchronous - same as for NtNotifyChangeKey
  1179. Return Value:
  1180. NTSTATUS - Result code from call, among the following:
  1181. <TBS>
  1182. --*/
  1183. {
  1184. NTSTATUS status;
  1185. NTSTATUS WaitStatus;
  1186. KPROCESSOR_MODE PreviousMode;
  1187. PCM_KEY_BODY MasterKeyBody;
  1188. PCM_KEY_BODY SlaveKeyBody;
  1189. PKEVENT UserEvent=NULL;
  1190. PCM_POST_BLOCK MasterPostBlock;
  1191. PCM_POST_BLOCK SlavePostBlock;
  1192. KIRQL OldIrql;
  1193. HANDLE SlaveKeyHandle;
  1194. POST_BLOCK_TYPE PostType = PostSynchronous;
  1195. BOOLEAN SlavePresent = FALSE; // assume that we are in the NtNotifyChangeKey case
  1196. #if defined(_WIN64)
  1197. BOOLEAN UseIosb32=FALSE; // If the caller is a 32bit process on sundown and previous mode
  1198. // is user mode, use a 32bit IoSb.
  1199. #endif
  1200. PAGED_CODE();
  1201. #ifdef CMP_STATS
  1202. CmpStatsDebug.CmpNtNotifyChangeMultipleKeysNo++;
  1203. #endif
  1204. BEGIN_LOCK_CHECKPOINT;
  1205. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtNotifyChangeMultipleKeys\n"));
  1206. if( HvShutdownComplete == TRUE ) {
  1207. //
  1208. // too late to do registry operations.
  1209. //
  1210. return STATUS_TOO_LATE;
  1211. }
  1212. if(Count > 1) {
  1213. //
  1214. // This version supports only one slave object
  1215. //
  1216. return STATUS_INVALID_PARAMETER;
  1217. }
  1218. if(Count == 1) {
  1219. //
  1220. // We have one slave, so we are in the NtNotifyChangeMultipleKeys case
  1221. //
  1222. SlavePresent = TRUE;
  1223. }
  1224. #if DBG
  1225. if (SlavePresent) {
  1226. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"NtNotifyChangeMultipleKeys(%d,slave = %p, Asynchronous = %d)\n",MasterKeyHandle,SlaveObjects,(int)Asynchronous));
  1227. }
  1228. #endif
  1229. //
  1230. // Threads that are attached give us real grief, so disallow it.
  1231. //
  1232. if (KeIsAttachedProcess()) {
  1233. CM_BUGCHECK(REGISTRY_ERROR,BAD_NOTIFY_CONTEXT,1,0,0);
  1234. }
  1235. //
  1236. // Probe user buffer parameters.
  1237. //
  1238. PreviousMode = KeGetPreviousMode();
  1239. if (PreviousMode != KernelMode) {
  1240. #if defined(_WIN64)
  1241. // Process is 32bit if Wow64 is not NULL.
  1242. UseIosb32 = (PsGetCurrentProcess()->Wow64Process != NULL);
  1243. #endif
  1244. try {
  1245. ProbeForWrite(
  1246. IoStatusBlock,
  1247. #if defined(_WIN64)
  1248. UseIosb32 ? sizeof(IO_STATUS_BLOCK32) : sizeof(IO_STATUS_BLOCK),
  1249. #else
  1250. sizeof(IO_STATUS_BLOCK),
  1251. #endif
  1252. sizeof(ULONG)
  1253. );
  1254. ProbeForWrite(Buffer, BufferSize, sizeof(ULONG));
  1255. //
  1256. // Initialize IOSB
  1257. //
  1258. CmpSetIoStatus(IoStatusBlock, STATUS_PENDING, 0, UseIosb32);
  1259. } except(EXCEPTION_EXECUTE_HANDLER) {
  1260. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtChangeNotifyMultipleKeys: code:%08lx\n", GetExceptionCode()));
  1261. return GetExceptionCode();
  1262. }
  1263. if (Asynchronous) {
  1264. PostType = PostAsyncUser;
  1265. }
  1266. } else {
  1267. if (Asynchronous) {
  1268. PostType = PostAsyncKernel;
  1269. if( Count > 0 ) {
  1270. //
  1271. // we don't allow multiple asyncronous kernel notifications
  1272. //
  1273. return STATUS_INVALID_PARAMETER;
  1274. }
  1275. }
  1276. }
  1277. //
  1278. // Check filter
  1279. //
  1280. if (CompletionFilter != (CompletionFilter & REG_LEGAL_CHANGE_FILTER)) {
  1281. return STATUS_INVALID_PARAMETER;
  1282. }
  1283. //
  1284. // Reference the Master Key handle
  1285. //
  1286. status = ObReferenceObjectByHandle(
  1287. MasterKeyHandle,
  1288. KEY_NOTIFY,
  1289. CmpKeyObjectType,
  1290. PreviousMode,
  1291. (PVOID *)(&MasterKeyBody),
  1292. NULL
  1293. );
  1294. if (!NT_SUCCESS(status)) {
  1295. return status;
  1296. }
  1297. if(SlavePresent) {
  1298. //
  1299. // Open the slave object and add a reference to it.
  1300. //
  1301. try {
  1302. status = ObOpenObjectByName(SlaveObjects,
  1303. CmpKeyObjectType,
  1304. PreviousMode,
  1305. NULL,
  1306. KEY_NOTIFY,
  1307. NULL,
  1308. &SlaveKeyHandle);
  1309. if (NT_SUCCESS(status)) {
  1310. status = ObReferenceObjectByHandle(SlaveKeyHandle,
  1311. KEY_NOTIFY,
  1312. CmpKeyObjectType,
  1313. PreviousMode,
  1314. (PVOID *)&SlaveKeyBody,
  1315. NULL);
  1316. NtClose(SlaveKeyHandle);
  1317. }
  1318. } except (CmpExceptionFilter(GetExceptionInformation())) {
  1319. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtNotifyChangeMultipleKeys: code:%08lx\n", GetExceptionCode()));
  1320. status = GetExceptionCode();
  1321. }
  1322. if (!NT_SUCCESS(status)) {
  1323. ObDereferenceObject(MasterKeyBody);
  1324. return status;
  1325. }
  1326. //
  1327. // Reject calls setting with keys on the same hive as they could lead to obscure deadlocks
  1328. //
  1329. if( MasterKeyBody->KeyControlBlock->KeyHive == SlaveKeyBody->KeyControlBlock->KeyHive ) {
  1330. ObDereferenceObject(SlaveKeyBody);
  1331. ObDereferenceObject(MasterKeyBody);
  1332. return STATUS_INVALID_PARAMETER;
  1333. }
  1334. }
  1335. //
  1336. // Allocate master and slave post blocks, and init it. Do NOT put it on the chain,
  1337. // CmNotifyChangeKey will do that while holding a mutex.
  1338. //
  1339. // WARNING: PostBlocks MUST BE ALLOCATED from Pool, since back side
  1340. // of Notify will free it!
  1341. //
  1342. MasterPostBlock = CmpAllocateMasterPostBlock(PostType);
  1343. if (MasterPostBlock == NULL) {
  1344. if(SlavePresent) {
  1345. ObDereferenceObject(SlaveKeyBody);
  1346. }
  1347. ObDereferenceObject(MasterKeyBody);
  1348. return STATUS_INSUFFICIENT_RESOURCES;
  1349. }
  1350. #ifdef CM_NOTIFY_CHANGED_KCB_FULLPATH
  1351. MasterPostBlock->CallerBuffer = Buffer;
  1352. MasterPostBlock->CallerBufferSize = BufferSize;
  1353. #endif //CM_NOTIFY_CHANGED_KCB_FULLPATH
  1354. #if DBG
  1355. MasterPostBlock->TraceIntoDebugger = TRUE;
  1356. #endif
  1357. if(SlavePresent) {
  1358. SlavePostBlock = CmpAllocateSlavePostBlock(PostType,SlaveKeyBody,MasterPostBlock);
  1359. if (SlavePostBlock == NULL) {
  1360. ObDereferenceObject(SlaveKeyBody);
  1361. ObDereferenceObject(MasterKeyBody);
  1362. CmpFreePostBlock(MasterPostBlock);
  1363. return STATUS_INSUFFICIENT_RESOURCES;
  1364. }
  1365. #ifdef CM_NOTIFY_CHANGED_KCB_FULLPATH
  1366. SlavePostBlock->CallerBuffer = Buffer;
  1367. SlavePostBlock->CallerBufferSize = BufferSize;
  1368. #endif //CM_NOTIFY_CHANGED_KCB_FULLPATH
  1369. #if DBG
  1370. SlavePostBlock->TraceIntoDebugger = TRUE;
  1371. #endif
  1372. }
  1373. if ((PostType == PostAsyncUser) ||
  1374. (PostType == PostAsyncKernel)) {
  1375. //
  1376. // If event is present, reference it, save its address, and set
  1377. // it to the not signaled state.
  1378. //
  1379. if (ARGUMENT_PRESENT(Event)) {
  1380. status = ObReferenceObjectByHandle(
  1381. Event,
  1382. EVENT_MODIFY_STATE,
  1383. ExEventObjectType,
  1384. PreviousMode,
  1385. (PVOID *)(&UserEvent),
  1386. NULL
  1387. );
  1388. if (!NT_SUCCESS(status)) {
  1389. if(SlavePresent) {
  1390. CmpFreePostBlock(SlavePostBlock);
  1391. // SlaveKeyBody is dereferenced in CmpFreePostBlock(SlavePostBlock)
  1392. }
  1393. CmpFreePostBlock(MasterPostBlock);
  1394. ObDereferenceObject(MasterKeyBody);
  1395. return status;
  1396. } else {
  1397. KeClearEvent(UserEvent);
  1398. }
  1399. }
  1400. if (PostType == PostAsyncUser) {
  1401. KPROCESSOR_MODE ApcMode;
  1402. MasterPostBlock->u->AsyncUser.IoStatusBlock = IoStatusBlock;
  1403. MasterPostBlock->u->AsyncUser.UserEvent = UserEvent;
  1404. //
  1405. // Initialize APC. May or may not be a user apc, will always
  1406. // be a kernel apc.
  1407. //
  1408. ApcMode = PreviousMode;
  1409. if( ApcRoutine == NULL ) {
  1410. ApcRoutine = (PIO_APC_ROUTINE)CmpDummyApc;
  1411. ApcMode = KernelMode;
  1412. }
  1413. KeInitializeApc(MasterPostBlock->u->AsyncUser.Apc,
  1414. KeGetCurrentThread(),
  1415. CurrentApcEnvironment,
  1416. (PKKERNEL_ROUTINE)CmpPostApc,
  1417. (PKRUNDOWN_ROUTINE)CmpPostApcRunDown,
  1418. (PKNORMAL_ROUTINE)ApcRoutine,
  1419. ApcMode,
  1420. ApcContext);
  1421. } else {
  1422. MasterPostBlock->u->AsyncKernel.Event = UserEvent;
  1423. MasterPostBlock->u->AsyncKernel.WorkItem = (PWORK_QUEUE_ITEM)ApcRoutine;
  1424. MasterPostBlock->u->AsyncKernel.QueueType = (WORK_QUEUE_TYPE)((ULONG_PTR)ApcContext);
  1425. }
  1426. }
  1427. //
  1428. // Exclusively lock the registry; We want nobody to mess with it while we are doing the
  1429. // post/notify list manipulation; what else could be safer than that :-)
  1430. //
  1431. CmpLockRegistryExclusive();
  1432. #ifdef CHECK_REGISTRY_USECOUNT
  1433. CmpCheckRegistryUseCount();
  1434. #endif //CHECK_REGISTRY_USECOUNT
  1435. //
  1436. // Call worker for master
  1437. //
  1438. status = CmpNotifyChangeKey(
  1439. MasterKeyBody,
  1440. MasterPostBlock,
  1441. CompletionFilter,
  1442. WatchTree,
  1443. Buffer,
  1444. BufferSize,
  1445. MasterPostBlock
  1446. );
  1447. if( !NT_SUCCESS(status)) {
  1448. //
  1449. // it didn't work, clean up for error path
  1450. //
  1451. CmpUnlockRegistry();
  1452. if (UserEvent != NULL) {
  1453. ObDereferenceObject(UserEvent);
  1454. }
  1455. if(SlavePresent) {
  1456. CmpFreePostBlock(SlavePostBlock);
  1457. // SlaveKeyBody is dereferenced in CmpFreePostBlock(SlavePostBlock)
  1458. }
  1459. // MasterPostBlock if freed by CmpNotifyChangeKey !!!
  1460. ObDereferenceObject(MasterKeyBody);
  1461. return status;
  1462. }
  1463. ASSERT(status == STATUS_PENDING || status == STATUS_SUCCESS);
  1464. if(SlavePresent) {
  1465. if( status == STATUS_SUCCESS ) {
  1466. //
  1467. // The notify has already been triggered for the master, there is no point to set one for the slave too
  1468. // Clean up the mess we made for the slave object and signal as there is no slave present
  1469. //
  1470. CmpFreePostBlock(SlavePostBlock);
  1471. SlavePresent = FALSE;
  1472. } else {
  1473. //
  1474. // Call worker for slave
  1475. //
  1476. status = CmpNotifyChangeKey(
  1477. SlaveKeyBody,
  1478. SlavePostBlock,
  1479. CompletionFilter,
  1480. WatchTree,
  1481. Buffer,
  1482. BufferSize,
  1483. MasterPostBlock
  1484. );
  1485. if(!NT_SUCCESS(status)) {
  1486. //
  1487. // if we are here, the slave key has been deleted in between or there was no memory available to allocate
  1488. // a notify block for the slave key. We do the cleanup here since we already hold the registry lock
  1489. // exclusively and we don't want to give a anybody else a chance to trigger the notification on master post
  1490. // (otherwise we could end up freeing it twice). The master post block and the user event are cleaned later,
  1491. // covering both single and multiple notifications cases
  1492. //
  1493. // Use Cmp variant to protect for multiple deletion of the same object
  1494. CmpRemoveEntryList(&(MasterPostBlock->NotifyList));
  1495. KeRaiseIrql(APC_LEVEL, &OldIrql);
  1496. // Use Cmp variant to protect for multiple deletion of the same object
  1497. CmpRemoveEntryList(&(MasterPostBlock->ThreadList));
  1498. KeLowerIrql(OldIrql);
  1499. }
  1500. }
  1501. }
  1502. //
  1503. // postblocks are now on various lists, so we can die without losing them
  1504. //
  1505. CmpUnlockRegistry();
  1506. if (NT_SUCCESS(status)) {
  1507. //
  1508. // success. wait for event if sync.
  1509. // do NOT deref User event, back side of notify will do that.
  1510. //
  1511. ASSERT(status == STATUS_PENDING || status == STATUS_SUCCESS);
  1512. if (PostType == PostSynchronous) {
  1513. WaitStatus = KeWaitForSingleObject(MasterPostBlock->u->Sync.SystemEvent,
  1514. Executive,
  1515. PreviousMode,
  1516. TRUE,
  1517. NULL);
  1518. if ((WaitStatus==STATUS_ALERTED) || (WaitStatus == STATUS_USER_APC)) {
  1519. //
  1520. // The wait was aborted, clean up and return.
  1521. //
  1522. // 1. Remove the PostBlocks from the notify list. This
  1523. // is normally done by the back end of notify, but
  1524. // we have to do it here since the back end is not
  1525. // involved.
  1526. // 2. Delist and free the post blocks
  1527. //
  1528. CmpLockRegistryExclusive();
  1529. #ifdef CHECK_REGISTRY_USECOUNT
  1530. CmpCheckRegistryUseCount();
  1531. #endif //CHECK_REGISTRY_USECOUNT
  1532. KeRaiseIrql(APC_LEVEL, &OldIrql);
  1533. if(SlavePresent) {
  1534. if (SlavePostBlock->NotifyList.Flink != NULL) {
  1535. // Use Cmp variant to protect for multiple deletion of the same object
  1536. CmpRemoveEntryList(&(SlavePostBlock->NotifyList));
  1537. }
  1538. // Use Cmp variant to protect for multiple deletion of the same object
  1539. CmpRemoveEntryList(&(SlavePostBlock->ThreadList));
  1540. }
  1541. if (MasterPostBlock->NotifyList.Flink != NULL) {
  1542. // Use Cmp variant to protect for multiple deletion of the same object
  1543. CmpRemoveEntryList(&(MasterPostBlock->NotifyList));
  1544. }
  1545. // Use Cmp variant to protect for multiple deletion of the same object
  1546. CmpRemoveEntryList(&(MasterPostBlock->ThreadList));
  1547. KeLowerIrql(OldIrql);
  1548. CmpUnlockRegistry();
  1549. if(SlavePresent) {
  1550. CmpFreePostBlock(SlavePostBlock);
  1551. }
  1552. CmpFreePostBlock(MasterPostBlock);
  1553. status = WaitStatus;
  1554. } else {
  1555. //
  1556. // The wait was satisfied, which means the back end has
  1557. // already removed the postblock from the notify list.
  1558. // We just have to delist and free the post block.
  1559. //
  1560. //
  1561. // Aquire the registry lock exclusive to enter the post block rule prerequisites
  1562. //
  1563. CmpLockRegistryExclusive();
  1564. #ifdef CHECK_REGISTRY_USECOUNT
  1565. CmpCheckRegistryUseCount();
  1566. #endif //CHECK_REGISTRY_USECOUNT
  1567. KeRaiseIrql(APC_LEVEL, &OldIrql);
  1568. if(SlavePresent) {
  1569. if (SlavePostBlock->NotifyList.Flink != NULL) {
  1570. // Use Cmp variant to protect for multiple deletion of the same object
  1571. CmpRemoveEntryList(&(SlavePostBlock->NotifyList));
  1572. }
  1573. // Use Cmp variant to protect for multiple deletion of the same object
  1574. CmpRemoveEntryList(&(SlavePostBlock->ThreadList));
  1575. #ifdef CM_NOTIFY_CHANGED_KCB_FULLPATH
  1576. if( IsMasterPostBlock(SlavePostBlock) ) {
  1577. //
  1578. // slave has been promoted to master; it stores the full qualified changed kcb name
  1579. // in it's private kernel mode buffer; old master has been downgraded to slave
  1580. //
  1581. ASSERT( !IsMasterPostBlock(MasterPostBlock) );
  1582. ASSERT( MasterPostBlock->ChangedKcbFullName == NULL );
  1583. //
  1584. // fill the caller buffer (if any) - we are in the same process now.
  1585. //
  1586. CmpFillCallerBuffer(SlavePostBlock,SlavePostBlock->ChangedKcbFullName);
  1587. }
  1588. #endif //CM_NOTIFY_CHANGED_KCB_FULLPATH
  1589. }
  1590. if (MasterPostBlock->NotifyList.Flink != NULL) {
  1591. // Use Cmp variant to protect for multiple deletion of the same object
  1592. CmpRemoveEntryList(&(MasterPostBlock->NotifyList));
  1593. }
  1594. #ifdef CM_NOTIFY_CHANGED_KCB_FULLPATH
  1595. if( IsMasterPostBlock(MasterPostBlock) ) {
  1596. //
  1597. // fill the caller buffer (if any) - we are in the same process now.
  1598. //
  1599. CmpFillCallerBuffer(MasterPostBlock,MasterPostBlock->ChangedKcbFullName);
  1600. }
  1601. #endif //CM_NOTIFY_CHANGED_KCB_FULLPATH
  1602. // Use Cmp variant to protect for multiple deletion of the same object
  1603. CmpRemoveEntryList(&(MasterPostBlock->ThreadList));
  1604. KeLowerIrql(OldIrql);
  1605. CmpUnlockRegistry();
  1606. status = MasterPostBlock->u->Sync.Status;
  1607. try {
  1608. CmpSetIoStatus(IoStatusBlock, status, 0, UseIosb32);
  1609. } except (EXCEPTION_EXECUTE_HANDLER) {
  1610. status = GetExceptionCode();
  1611. }
  1612. if(SlavePresent) {
  1613. CmpFreePostBlock(SlavePostBlock);
  1614. }
  1615. CmpFreePostBlock(MasterPostBlock);
  1616. }
  1617. }
  1618. } else {
  1619. CmpFreePostBlock(MasterPostBlock);
  1620. //
  1621. // it didn't work, clean up for error path
  1622. //
  1623. if (UserEvent != NULL) {
  1624. ObDereferenceObject(UserEvent);
  1625. }
  1626. }
  1627. ObDereferenceObject(MasterKeyBody);
  1628. //
  1629. // Don't dereference SlaveKeyBody!!! => Back-end routine will do that !!!
  1630. //
  1631. END_LOCK_CHECKPOINT;
  1632. return status;
  1633. }
  1634. NTSTATUS
  1635. NtOpenKey(
  1636. OUT PHANDLE KeyHandle,
  1637. IN ACCESS_MASK DesiredAccess,
  1638. IN POBJECT_ATTRIBUTES ObjectAttributes
  1639. )
  1640. /*++
  1641. Routine Description:
  1642. A registry key which already exists may be opened with NtOpenKey.
  1643. Share access is computed from desired access.
  1644. Arguments:
  1645. KeyHandle - Receives a Handle which is used to access the
  1646. specified key in the Registration Database.
  1647. DesiredAccess - Specifies the access rights desired.
  1648. ObjectAttributes - Specifies the attributes of the key being opened.
  1649. Note that a key name must be specified. If a Root Directory
  1650. is specified, the name is relative to the root. The name of
  1651. the object must be within the name space allocated to the
  1652. Registry, that is, all names beginning "\Registry". RootHandle,
  1653. if present, must be a handle to "\", or "\Registry", or a
  1654. key under "\Registry". If the specified key does not exist, or
  1655. access requested is not allowed, the operation will fail.
  1656. NOTE: Object manager will capture and probe this argument.
  1657. Return Value:
  1658. NTSTATUS - Result code from call, among the following:
  1659. <TBS>
  1660. --*/
  1661. {
  1662. NTSTATUS status = STATUS_SUCCESS;
  1663. KPROCESSOR_MODE mode;
  1664. PCM_KEY_BODY KeyBody;
  1665. HANDLE Handle =0;
  1666. UNICODE_STRING CapturedObjectName = {0};
  1667. // Start registry call tracing
  1668. StartWmiCmTrace();
  1669. #if !defined(BUILD_WOW6432)
  1670. DesiredAccess &= (~KEY_WOW64_RES); // filter out wow64 specific access
  1671. #endif
  1672. PAGED_CODE();
  1673. if( HvShutdownComplete == TRUE ) {
  1674. //
  1675. // it is now too late to do registry operations
  1676. //
  1677. return STATUS_TOO_LATE;
  1678. }
  1679. #ifdef CMP_STATS
  1680. CmpStatsDebug.CmpNtOpenKeyNo++;
  1681. #endif
  1682. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtOpenKey\n"));
  1683. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tDesiredAccess=%08lx ", DesiredAccess));
  1684. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tObjectAttributes=%p\n", ObjectAttributes));
  1685. mode = KeGetPreviousMode();
  1686. try {
  1687. if (mode == UserMode) {
  1688. ProbeAndZeroHandle(KeyHandle);
  1689. //
  1690. // probe the ObjectAttributes as we shall use it for tracing
  1691. //
  1692. ProbeForReadSmallStructure( ObjectAttributes,
  1693. sizeof(OBJECT_ATTRIBUTES),
  1694. PROBE_ALIGNMENT(OBJECT_ATTRIBUTES) );
  1695. CapturedObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName);
  1696. ProbeForRead(
  1697. CapturedObjectName.Buffer,
  1698. CapturedObjectName.Length,
  1699. sizeof(WCHAR)
  1700. );
  1701. } else {
  1702. CapturedObjectName = *(ObjectAttributes->ObjectName);
  1703. }
  1704. // hook it for WMI
  1705. HookKcbFromHandleForWmiCmTrace(ObjectAttributes->RootDirectory);
  1706. } except (CmpExceptionFilter(GetExceptionInformation())) {
  1707. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtOpenKey: code:%08lx\n", GetExceptionCode()));
  1708. status = GetExceptionCode();
  1709. }
  1710. if( NT_SUCCESS(status) ) {
  1711. //
  1712. // this should not be inside the try/except as we captured the buffer
  1713. //
  1714. status = ObOpenObjectByName(
  1715. ObjectAttributes,
  1716. CmpKeyObjectType,
  1717. mode,
  1718. NULL,
  1719. DesiredAccess,
  1720. NULL,
  1721. &Handle
  1722. );
  1723. //
  1724. // need to protect against attacks to KeyHandle usermode pointer
  1725. //
  1726. try {
  1727. if (status==STATUS_PREDEFINED_HANDLE) {
  1728. status = ObReferenceObjectByHandle( Handle,
  1729. 0,
  1730. CmpKeyObjectType,
  1731. KernelMode,
  1732. (PVOID *)(&KeyBody),
  1733. NULL);
  1734. if (NT_SUCCESS(status)) {
  1735. *KeyHandle = (HANDLE)LongToHandle(KeyBody->Type);
  1736. ObDereferenceObject((PVOID)KeyBody);
  1737. //
  1738. // disallow attempts to return NULL handles
  1739. //
  1740. if( *KeyHandle ) {
  1741. status = STATUS_SUCCESS;
  1742. } else {
  1743. status = STATUS_OBJECT_NAME_NOT_FOUND;
  1744. }
  1745. }
  1746. NtClose(Handle);
  1747. Handle = *KeyHandle;
  1748. } else if (NT_SUCCESS(status)) {
  1749. *KeyHandle = Handle;
  1750. // need to do this only on clean shutdown
  1751. CmpAddKeyTracker(Handle,mode);
  1752. }
  1753. } except (EXCEPTION_EXECUTE_HANDLER) {
  1754. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtOpenKey: code:%08lx\n", GetExceptionCode()));
  1755. status = GetExceptionCode();
  1756. }
  1757. }
  1758. // End registry call tracing
  1759. EndWmiCmTrace(status,0,&CapturedObjectName,EVENT_TRACE_TYPE_REGOPEN);
  1760. return status;
  1761. }
  1762. NTSTATUS
  1763. NtQueryKey(
  1764. IN HANDLE KeyHandle,
  1765. IN KEY_INFORMATION_CLASS KeyInformationClass,
  1766. IN PVOID KeyInformation,
  1767. IN ULONG Length,
  1768. OUT PULONG ResultLength
  1769. )
  1770. /*++
  1771. Routine Description:
  1772. Data about the class of a key, and the numbers and sizes of its
  1773. children and value entries may be queried with NtQueryKey.
  1774. If KeyValueInformation is not long enough to hold all requested data,
  1775. STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
  1776. set to the number of bytes actually required.
  1777. NOTE: The returned lengths are guaranteed to be at least as
  1778. long as the described values, but may be longer in
  1779. some circumstances.
  1780. Arguments:
  1781. KeyHandle - Handle of the key to query data for. Must have been
  1782. opened for KEY_QUERY_KEY access.
  1783. KeyInformationClass - Specifies the type of information
  1784. returned in Buffer. One of the following types:
  1785. KeyBasicInformation - return last write time, title index, and name.
  1786. (See KEY_BASIC_INFORMATION)
  1787. KeyNodeInformation - return last write time, title index, name, class.
  1788. (See KEY_NODE_INFORMATION)
  1789. KeyFullInformation - return all data except for name and security.
  1790. (See KEY_FULL_INFORMATION)
  1791. KeyInformation -Supplies pointer to buffer to receive the data.
  1792. Length - Length of KeyInformation in bytes.
  1793. ResultLength - Number of bytes actually written into KeyInformation.
  1794. Return Value:
  1795. NTSTATUS - Result code from call, among the following:
  1796. <TBS>
  1797. --*/
  1798. {
  1799. NTSTATUS status;
  1800. PCM_KEY_BODY KeyBody;
  1801. KPROCESSOR_MODE mode;
  1802. // Start registry call tracing
  1803. StartWmiCmTrace();
  1804. PAGED_CODE();
  1805. #ifdef CMP_STATS
  1806. CmpStatsDebug.CmpNtQueryKeyNo++;
  1807. #endif
  1808. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtQueryKey\n"));
  1809. if ((KeyInformationClass != KeyBasicInformation) &&
  1810. (KeyInformationClass != KeyNodeInformation) &&
  1811. (KeyInformationClass != KeyFullInformation) &&
  1812. (KeyInformationClass != KeyNameInformation) &&
  1813. (KeyInformationClass != KeyCachedInformation) &&
  1814. (KeyInformationClass != KeyFlagsInformation)
  1815. )
  1816. {
  1817. // hook it for WMI
  1818. HookKcbFromHandleForWmiCmTrace(KeyHandle);
  1819. // End registry call tracing
  1820. EndWmiCmTrace(STATUS_INVALID_PARAMETER,KeyInformationClass,NULL,EVENT_TRACE_TYPE_REGQUERY);
  1821. return STATUS_INVALID_PARAMETER;
  1822. }
  1823. mode = KeGetPreviousMode();
  1824. if( KeyInformationClass == KeyNameInformation ){
  1825. //
  1826. // special case: name information is available regardless of the access level
  1827. // you have on the key (provided that you have some ...)
  1828. //
  1829. OBJECT_HANDLE_INFORMATION HandleInfo;
  1830. // reference with "no access required"
  1831. status = ObReferenceObjectByHandle(
  1832. KeyHandle,
  1833. 0,
  1834. CmpKeyObjectType,
  1835. mode,
  1836. (PVOID *)(&KeyBody),
  1837. &HandleInfo
  1838. );
  1839. if( NT_SUCCESS(status) ) {
  1840. if( HandleInfo.GrantedAccess == 0 ) {
  1841. //
  1842. // no access is granted on the handle; bad luck!
  1843. //
  1844. ObDereferenceObject((PVOID)KeyBody);
  1845. status = STATUS_ACCESS_DENIED;
  1846. }
  1847. }
  1848. } else {
  1849. status = ObReferenceObjectByHandle(
  1850. KeyHandle,
  1851. KEY_QUERY_VALUE,
  1852. CmpKeyObjectType,
  1853. mode,
  1854. (PVOID *)(&KeyBody),
  1855. NULL
  1856. );
  1857. }
  1858. if (NT_SUCCESS(status)) {
  1859. //
  1860. // hook the kcb for WMI
  1861. //
  1862. HookKcbForWmiCmTrace(KeyBody);
  1863. try {
  1864. if (mode == UserMode) {
  1865. ProbeForWrite(
  1866. KeyInformation,
  1867. Length,
  1868. CmpKeyInfoProbeAlingment(KeyInformationClass)
  1869. );
  1870. ProbeForWriteUlong(ResultLength);
  1871. }
  1872. if( NT_SUCCESS(status)) {
  1873. if( CmAreCallbacksRegistered() ) {
  1874. REG_QUERY_KEY_INFORMATION QueryKeyInfo;
  1875. QueryKeyInfo.Object = KeyBody;
  1876. QueryKeyInfo.KeyInformationClass = KeyInformationClass;
  1877. QueryKeyInfo.KeyInformation = KeyInformation;
  1878. QueryKeyInfo.Length = Length;
  1879. QueryKeyInfo.ResultLength = ResultLength;
  1880. status = CmpCallCallBacks(RegNtQueryKey,&QueryKeyInfo);
  1881. }
  1882. if( NT_SUCCESS(status)) {
  1883. //
  1884. // CmQueryKey is writting to user-mode buffer
  1885. //
  1886. status = CmQueryKey(
  1887. KeyBody->KeyControlBlock,
  1888. KeyInformationClass,
  1889. KeyInformation,
  1890. Length,
  1891. ResultLength
  1892. );
  1893. }
  1894. }
  1895. } except (EXCEPTION_EXECUTE_HANDLER) {
  1896. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtQueryKey: code:%08lx\n", GetExceptionCode()));
  1897. status = GetExceptionCode();
  1898. }
  1899. ObDereferenceObject((PVOID)KeyBody);
  1900. }
  1901. // End registry call tracing
  1902. EndWmiCmTrace(status,KeyInformationClass,NULL,EVENT_TRACE_TYPE_REGQUERY);
  1903. return status;
  1904. }
  1905. NTSTATUS
  1906. NtQueryValueKey(
  1907. IN HANDLE KeyHandle,
  1908. IN PUNICODE_STRING ValueName,
  1909. IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
  1910. IN PVOID KeyValueInformation,
  1911. IN ULONG Length,
  1912. IN PULONG ResultLength
  1913. )
  1914. /*++
  1915. Routine Description:
  1916. The ValueName, TitleIndex, Type, and Data for any one of a key's
  1917. value entries may be queried with NtQueryValueKey.
  1918. If KeyValueInformation is not long enough to hold all requested data,
  1919. STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
  1920. set to the number of bytes actually required.
  1921. Arguments:
  1922. KeyHandle - Handle of the key whose value entries are to be
  1923. enumerated. Must be open for KEY_QUERY_VALUE access.
  1924. ValueName - The name of the value entry to return data for.
  1925. KeyValueInformationClass - Specifies the type of information
  1926. returned in KeyValueInformation. One of the following types:
  1927. KeyValueBasicInformation - return time of last write, title
  1928. index, and name. (See KEY_VALUE_BASIC_INFORMATION)
  1929. KeyValueFullInformation - return time of last write, title
  1930. index, name, class. (See KEY_VALUE_FULL_INFORMATION)
  1931. KeyValueInformation -Supplies pointer to buffer to receive the data.
  1932. Length - Length of KeyValueInformation in bytes.
  1933. ResultLength - Number of bytes actually written into KeyValueInformation.
  1934. Return Value:
  1935. NTSTATUS - Result code from call, among the following:
  1936. <TBS>
  1937. TMP: The IopQueryRegsitryValues() routine in the IO system assumes
  1938. STATUS_OBJECT_NAME_NOT_FOUND is returned if the value being queried
  1939. for does not exist.
  1940. --*/
  1941. {
  1942. NTSTATUS status;
  1943. PCM_KEY_BODY KeyBody;
  1944. KPROCESSOR_MODE mode;
  1945. UNICODE_STRING LocalValueName;
  1946. // Start registry call tracing
  1947. StartWmiCmTrace();
  1948. PAGED_CODE();
  1949. #ifdef CMP_STATS
  1950. CmpStatsDebug.CmpNtQueryValueKeyNo++;
  1951. #endif
  1952. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtQueryValueKey\n"));
  1953. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx\n", KeyHandle));
  1954. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tValueName='%wZ'\n", ValueName));
  1955. if ((KeyValueInformationClass != KeyValueBasicInformation) &&
  1956. (KeyValueInformationClass != KeyValueFullInformation) &&
  1957. (KeyValueInformationClass != KeyValueFullInformationAlign64) &&
  1958. (KeyValueInformationClass != KeyValuePartialInformationAlign64) &&
  1959. (KeyValueInformationClass != KeyValuePartialInformation))
  1960. {
  1961. // hook it for WMI
  1962. HookKcbFromHandleForWmiCmTrace(KeyHandle);
  1963. // End registry call tracing
  1964. EndWmiCmTrace(STATUS_INVALID_PARAMETER,KeyValueInformationClass,NULL,EVENT_TRACE_TYPE_REGQUERYVALUE);
  1965. return STATUS_INVALID_PARAMETER;
  1966. }
  1967. mode = KeGetPreviousMode();
  1968. status = ObReferenceObjectByHandle(
  1969. KeyHandle,
  1970. KEY_QUERY_VALUE,
  1971. CmpKeyObjectType,
  1972. mode,
  1973. (PVOID *)(&KeyBody),
  1974. NULL
  1975. );
  1976. if (NT_SUCCESS(status)) {
  1977. //
  1978. // hook the kcb for WMI
  1979. //
  1980. HookKcbForWmiCmTrace(KeyBody);
  1981. try {
  1982. if (mode == UserMode) {
  1983. LocalValueName = ProbeAndReadUnicodeString(ValueName);
  1984. ProbeForRead(LocalValueName.Buffer,
  1985. LocalValueName.Length,
  1986. sizeof(WCHAR));
  1987. //
  1988. // We only probe the output buffer for Read to avoid touching
  1989. // all the pages. Some people like to pass in gigantic buffers
  1990. // Just In Case. The actual copy into the buffer is done under
  1991. // an exception handler.
  1992. //
  1993. ProbeForRead(KeyValueInformation,
  1994. Length,
  1995. sizeof(ULONG));
  1996. ProbeForWriteUlong(ResultLength);
  1997. } else {
  1998. LocalValueName = *ValueName;
  1999. }
  2000. //
  2001. // do NOT allow trailing NULLs at the end of the ValueName
  2002. //
  2003. while( (LocalValueName.Length > 0) && (LocalValueName.Buffer[LocalValueName.Length/sizeof(WCHAR)-1] == UNICODE_NULL) ) {
  2004. LocalValueName.Length -= sizeof(WCHAR);
  2005. }
  2006. } except (EXCEPTION_EXECUTE_HANDLER) {
  2007. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtQueryValueKey: code:%08lx\n", GetExceptionCode()));
  2008. status = GetExceptionCode();
  2009. }
  2010. //
  2011. // CmQueryValueKey is protected to user mode buffer exceptions
  2012. // all other exceptions are cm internals and should result in a bugcheck
  2013. //
  2014. if( NT_SUCCESS(status)) {
  2015. if( CmAreCallbacksRegistered() ) {
  2016. REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
  2017. QueryValueKeyInfo.Object = KeyBody;
  2018. QueryValueKeyInfo.ValueName = &LocalValueName;
  2019. QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
  2020. QueryValueKeyInfo.KeyValueInformation = KeyValueInformation;
  2021. QueryValueKeyInfo.Length = Length;
  2022. QueryValueKeyInfo.ResultLength = ResultLength;
  2023. status = CmpCallCallBacks(RegNtQueryValueKey,&QueryValueKeyInfo);
  2024. }
  2025. if( NT_SUCCESS(status)) {
  2026. BEGIN_LOCK_CHECKPOINT;
  2027. status = CmQueryValueKey(KeyBody->KeyControlBlock,
  2028. LocalValueName,
  2029. KeyValueInformationClass,
  2030. KeyValueInformation,
  2031. Length,
  2032. ResultLength);
  2033. END_LOCK_CHECKPOINT;
  2034. }
  2035. }
  2036. ObDereferenceObject((PVOID)KeyBody);
  2037. } else {
  2038. LocalValueName.Buffer = NULL;
  2039. LocalValueName.Length = 0;
  2040. }
  2041. // End registry call tracing
  2042. EndWmiCmTrace(status,KeyValueInformationClass,&LocalValueName,EVENT_TRACE_TYPE_REGQUERYVALUE);
  2043. return status;
  2044. }
  2045. NTSTATUS
  2046. NtRestoreKey(
  2047. IN HANDLE KeyHandle,
  2048. IN HANDLE FileHandle,
  2049. IN ULONG Flags
  2050. )
  2051. /*++
  2052. Routine Description:
  2053. A file in the format created by NtSaveKey may be loaded into
  2054. the system's active registry with NtRestoreKey. An entire subtree
  2055. is created in the active registry as a result. All of the
  2056. data for the new sub-tree, including such things as security
  2057. descriptors, will be read from the source file. The data will
  2058. not be interpreted in any way.
  2059. This call (unlike NtLoadKey, see below) copies the data. The
  2060. system will NOT be using the source file after the call returns.
  2061. If the flag REG_WHOLE_HIVE_VOLATILE is specified, a new hive
  2062. can be created. It will be a memory only copy. The restore
  2063. must be done to the root of a hive (e.g. \registry\user\<name>)
  2064. If the flag is NOT set, then the target of the restore must
  2065. be an existing hive. The restore can be done to an arbitrary
  2066. location within an existing hive.
  2067. Caller must have SeRestorePrivilege privilege.
  2068. If the flag REG_REFRESH_HIVE is set (must be only flag) then the
  2069. the Hive will be restored to its state as of the last flush.
  2070. The hive must be marked NOLAZY_FLUSH, and the caller must have
  2071. TCB privilege, and the handle must point to the root of the hive.
  2072. If the refresh fails, the hive will be corrupt, and the system
  2073. will bugcheck. Notifies are flushed. The hive file will be resized,
  2074. the log will not. If there is any volatile space in the hive
  2075. being refreshed, STATUS_UNSUCCESSFUL will be returned. (It's much
  2076. too obscure a failure to warrant a new error code.)
  2077. If the flag REG_FORCE_RESTORE is set, the restore operation is done
  2078. even if the KeyHandle has open subkeys by other applications
  2079. Arguments:
  2080. KeyHandle - refers to the Key in the registry which is to be the
  2081. root of the new tree read from the disk. This key
  2082. will be replaced.
  2083. FileHandle - refers to file to restore from, must have read access.
  2084. Flags - If REG_WHOLE_HIVE_VOLATILE is set, then the copy will
  2085. exist only in memory, and disappear when the machine
  2086. is rebooted. No hive file will be created on disk.
  2087. Normally, a hive file will be created on disk.
  2088. Return Value:
  2089. NTSTATUS - values TBS.
  2090. --*/
  2091. {
  2092. NTSTATUS status;
  2093. PCM_KEY_BODY KeyBody;
  2094. KPROCESSOR_MODE mode;
  2095. PAGED_CODE();
  2096. #ifdef CMP_STATS
  2097. CmpStatsDebug.CmpNtRestoreKeyNo++;
  2098. #endif
  2099. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtRestoreKey\n"));
  2100. mode = KeGetPreviousMode();
  2101. //
  2102. // Check to see if the caller has the privilege to make this call.
  2103. //
  2104. if (!SeSinglePrivilegeCheck(SeRestorePrivilege, mode)) {
  2105. return(STATUS_PRIVILEGE_NOT_HELD);
  2106. }
  2107. //
  2108. // Force previous mode to be KernelMode so we can call filesystems
  2109. //
  2110. if (mode == UserMode) {
  2111. return ZwRestoreKey(KeyHandle, FileHandle, Flags);
  2112. } else {
  2113. status = ObReferenceObjectByHandle(
  2114. KeyHandle,
  2115. 0,
  2116. CmpKeyObjectType,
  2117. mode,
  2118. (PVOID *)(&KeyBody),
  2119. NULL
  2120. );
  2121. if (NT_SUCCESS(status)) {
  2122. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  2123. //
  2124. // key is protected
  2125. //
  2126. status = STATUS_ACCESS_DENIED;
  2127. } else {
  2128. BEGIN_LOCK_CHECKPOINT;
  2129. status = CmRestoreKey(
  2130. KeyBody->KeyControlBlock,
  2131. FileHandle,
  2132. Flags
  2133. );
  2134. END_LOCK_CHECKPOINT;
  2135. }
  2136. ObDereferenceObject((PVOID)KeyBody);
  2137. }
  2138. }
  2139. return status;
  2140. }
  2141. NTSTATUS
  2142. NtSaveKey(
  2143. IN HANDLE KeyHandle,
  2144. IN HANDLE FileHandle
  2145. )
  2146. /*++
  2147. Routine Description:
  2148. A subtree of the active registry may be written to a file in a
  2149. format suitable for use with NtRestoreKey. All of the data in the
  2150. subtree, including such things as security descriptors will be written
  2151. out.
  2152. Caller must have SeBackupPrivilege privilege.
  2153. This function will always save the hive in HSYS_MINOR format. For saving
  2154. in other format (latest - 1.5) NtSaveKeyEx is provided.
  2155. Arguments:
  2156. KeyHandle - refers to the Key in the registry which is the
  2157. root of the tree to be written to disk. The specified
  2158. node will be included in the data written out.
  2159. FileHandle - a file handle with write access to the target file
  2160. of interest.
  2161. Return Value:
  2162. NTSTATUS - values TBS
  2163. --*/
  2164. {
  2165. NTSTATUS status;
  2166. PCM_KEY_BODY KeyBody;
  2167. KPROCESSOR_MODE mode;
  2168. PAGED_CODE();
  2169. #ifdef CMP_STATS
  2170. CmpStatsDebug.CmpNtSaveKeyNo++;
  2171. #endif
  2172. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtSaveKey\n"));
  2173. mode = KeGetPreviousMode();
  2174. //
  2175. // Check to see if the caller has the privilege to make this call.
  2176. //
  2177. if (!SeSinglePrivilegeCheck(SeBackupPrivilege, mode)) {
  2178. return(STATUS_PRIVILEGE_NOT_HELD);
  2179. }
  2180. //
  2181. // Force previous mode to be KernelMode
  2182. //
  2183. if (mode == UserMode) {
  2184. return ZwSaveKey(KeyHandle, FileHandle);
  2185. } else {
  2186. status = ObReferenceObjectByHandle(
  2187. KeyHandle,
  2188. 0,
  2189. CmpKeyObjectType,
  2190. mode,
  2191. (PVOID *)(&KeyBody),
  2192. NULL
  2193. );
  2194. if (NT_SUCCESS(status)) {
  2195. BEGIN_LOCK_CHECKPOINT;
  2196. status = CmSaveKey(
  2197. KeyBody->KeyControlBlock,
  2198. FileHandle,
  2199. HSYS_MINOR
  2200. );
  2201. END_LOCK_CHECKPOINT;
  2202. ObDereferenceObject((PVOID)KeyBody);
  2203. }
  2204. }
  2205. return status;
  2206. }
  2207. NTSTATUS
  2208. NtSaveKeyEx(
  2209. IN HANDLE KeyHandle,
  2210. IN HANDLE FileHandle,
  2211. IN ULONG Format
  2212. )
  2213. /*++
  2214. Routine Description:
  2215. A subtree of the active registry may be written to a file in a
  2216. format suitable for use with NtRestoreKey. All of the data in the
  2217. subtree, including such things as security descriptors will be written
  2218. out.
  2219. Caller must have SeBackupPrivilege privilege.
  2220. Arguments:
  2221. KeyHandle - refers to the Key in the registry which is the
  2222. root of the tree to be written to disk. The specified
  2223. node will be included in the data written out.
  2224. FileHandle - a file handle with write access to the target file
  2225. of interest.
  2226. Format - specifies whether in which the file will be saved
  2227. Can be:
  2228. HIVE_VERSION_STANDARD ==> 1.3
  2229. HIVE_VERSION_LATEST ==> 1.4
  2230. Return Value:
  2231. NTSTATUS - values TBS
  2232. --*/
  2233. {
  2234. NTSTATUS status;
  2235. PCM_KEY_BODY KeyBody;
  2236. KPROCESSOR_MODE mode;
  2237. ULONG HiveVersion;
  2238. PAGED_CODE();
  2239. #ifdef CMP_STATS
  2240. CmpStatsDebug.CmpNtSaveKeyNo++;
  2241. #endif
  2242. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtSaveKeyEx\n"));
  2243. mode = KeGetPreviousMode();
  2244. //
  2245. // Check to see if the caller has the privilege to make this call.
  2246. //
  2247. if (!SeSinglePrivilegeCheck(SeBackupPrivilege, mode)) {
  2248. return(STATUS_PRIVILEGE_NOT_HELD);
  2249. }
  2250. //
  2251. // param validation
  2252. //
  2253. if( (Format != REG_STANDARD_FORMAT) && (Format != REG_LATEST_FORMAT) && (Format != REG_NO_COMPRESSION) ) {
  2254. return STATUS_INVALID_PARAMETER;
  2255. }
  2256. //
  2257. // Force previous mode to be KernelMode
  2258. //
  2259. if (mode == UserMode) {
  2260. return ZwSaveKeyEx(KeyHandle, FileHandle,Format);
  2261. } else {
  2262. status = ObReferenceObjectByHandle(
  2263. KeyHandle,
  2264. 0,
  2265. CmpKeyObjectType,
  2266. mode,
  2267. (PVOID *)(&KeyBody),
  2268. NULL
  2269. );
  2270. if (NT_SUCCESS(status)) {
  2271. BEGIN_LOCK_CHECKPOINT;
  2272. if( Format == REG_NO_COMPRESSION ) {
  2273. status = CmDumpKey(
  2274. KeyBody->KeyControlBlock,
  2275. FileHandle
  2276. );
  2277. } else {
  2278. HiveVersion = HSYS_MINOR;
  2279. if( Format == REG_LATEST_FORMAT ) {
  2280. HiveVersion = HSYS_WHISTLER;
  2281. }
  2282. status = CmSaveKey(
  2283. KeyBody->KeyControlBlock,
  2284. FileHandle,
  2285. HiveVersion
  2286. );
  2287. }
  2288. END_LOCK_CHECKPOINT;
  2289. ObDereferenceObject((PVOID)KeyBody);
  2290. }
  2291. }
  2292. return status;
  2293. }
  2294. NTSTATUS
  2295. NtSaveMergedKeys(
  2296. IN HANDLE HighPrecedenceKeyHandle,
  2297. IN HANDLE LowPrecedenceKeyHandle,
  2298. IN HANDLE FileHandle
  2299. )
  2300. /*++
  2301. Routine Description:
  2302. Two subtrees of the registry can be merged. The resulting subtree may
  2303. be written to a file in a format suitable for use with NtRestoreKey.
  2304. All of the data in the subtree, including such things as security
  2305. descriptors will be written out.
  2306. Caller must have SeBackupPrivilege privilege.
  2307. Arguments:
  2308. HighPrecedenceKeyHandle - refers to the key in the registry which is the
  2309. root of the HighPrecedence tree. I.e., when a key is present in
  2310. both trees headded by the two keys, the key underneath HighPrecedence
  2311. tree will always prevail. The specified
  2312. node will be included in the data written out.
  2313. LowPrecedenceKeyHandle - referrs to the key in the registry which is the
  2314. root of the "second choice" tree. Keys from this trees get saved
  2315. when there is no equivalent key in the tree headded by HighPrecedenceKey
  2316. FileHandle - a file handle with write access to the target file
  2317. of interest.
  2318. Return Value:
  2319. NTSTATUS - values TBS
  2320. --*/
  2321. {
  2322. NTSTATUS status;
  2323. PCM_KEY_BODY HighKeyBody;
  2324. PCM_KEY_BODY LowKeyBody;
  2325. KPROCESSOR_MODE mode;
  2326. PAGED_CODE();
  2327. #ifdef CMP_STATS
  2328. CmpStatsDebug.CmpNtSaveMergedKeysNo++;
  2329. #endif
  2330. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtSaveMergedKeys\n"));
  2331. mode = KeGetPreviousMode();
  2332. //
  2333. // Check to see if the caller has the privilege to make this call.
  2334. //
  2335. if (!SeSinglePrivilegeCheck(SeBackupPrivilege, mode)) {
  2336. return(STATUS_PRIVILEGE_NOT_HELD);
  2337. }
  2338. //
  2339. // Force previous mode to be KernelMode
  2340. //
  2341. if (mode == UserMode) {
  2342. return ZwSaveMergedKeys(HighPrecedenceKeyHandle, LowPrecedenceKeyHandle, FileHandle);
  2343. } else {
  2344. status = ObReferenceObjectByHandle(
  2345. HighPrecedenceKeyHandle,
  2346. 0,
  2347. CmpKeyObjectType,
  2348. mode,
  2349. (PVOID *)(&HighKeyBody),
  2350. NULL
  2351. );
  2352. if (NT_SUCCESS(status)) {
  2353. status = ObReferenceObjectByHandle(
  2354. LowPrecedenceKeyHandle,
  2355. 0,
  2356. CmpKeyObjectType,
  2357. mode,
  2358. (PVOID *)(&LowKeyBody),
  2359. NULL
  2360. );
  2361. if (NT_SUCCESS(status)) {
  2362. BEGIN_LOCK_CHECKPOINT;
  2363. status = CmSaveMergedKeys(
  2364. HighKeyBody->KeyControlBlock,
  2365. LowKeyBody->KeyControlBlock,
  2366. FileHandle
  2367. );
  2368. END_LOCK_CHECKPOINT;
  2369. ObDereferenceObject((PVOID)LowKeyBody);
  2370. }
  2371. ObDereferenceObject((PVOID)HighKeyBody);
  2372. }
  2373. }
  2374. return status;
  2375. }
  2376. NTSTATUS
  2377. NtSetValueKey(
  2378. IN HANDLE KeyHandle,
  2379. IN PUNICODE_STRING ValueName,
  2380. IN ULONG TitleIndex OPTIONAL,
  2381. IN ULONG Type,
  2382. IN PVOID Data,
  2383. IN ULONG DataSize
  2384. )
  2385. /*++
  2386. Routine Description:
  2387. A value entry may be created or replaced with NtSetValueKey.
  2388. If a value entry with a Value ID (i.e. name) matching the
  2389. one specified by ValueName exists, it is deleted and replaced
  2390. with the one specified. If no such value entry exists, a new
  2391. one is created. NULL is a legal Value ID. While Value IDs must
  2392. be unique within any given key, the same Value ID may appear
  2393. in many different keys.
  2394. Arguments:
  2395. KeyHandle - Handle of the key whose for which a value entry is
  2396. to be set. Must be opened for KEY_SET_VALUE access.
  2397. ValueName - The unique (relative to the containing key) name
  2398. of the value entry. May be NULL.
  2399. TitleIndex - Supplies the title index for ValueName. The title
  2400. index specifies the index of the localized alias for the ValueName.
  2401. Type - The integer type number of the value entry.
  2402. Data - Pointer to buffer with actual data for the value entry.
  2403. DataSize - Size of Data buffer.
  2404. Return Value:
  2405. NTSTATUS - Result code from call, among the following:
  2406. <TBS>
  2407. --*/
  2408. {
  2409. NTSTATUS status;
  2410. PCM_KEY_BODY KeyBody;
  2411. KPROCESSOR_MODE mode;
  2412. UNICODE_STRING LocalValueName;
  2413. PWSTR CapturedName=NULL;
  2414. // Start registry call tracing
  2415. StartWmiCmTrace();
  2416. PAGED_CODE();
  2417. #ifdef CMP_STATS
  2418. CmpStatsDebug.CmpNtSetValueKeyNo++;
  2419. #endif
  2420. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtSetValueKey\n"));
  2421. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx\n", KeyHandle));
  2422. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tValueName='%wZ'n", ValueName));
  2423. mode = KeGetPreviousMode();
  2424. status = ObReferenceObjectByHandle(
  2425. KeyHandle,
  2426. KEY_SET_VALUE,
  2427. CmpKeyObjectType,
  2428. mode,
  2429. (PVOID *)(&KeyBody),
  2430. NULL
  2431. );
  2432. if (NT_SUCCESS(status)) {
  2433. //
  2434. // hook the kcb for WMI
  2435. //
  2436. HookKcbForWmiCmTrace(KeyBody);
  2437. if (mode == UserMode) {
  2438. try {
  2439. LocalValueName = ProbeAndReadUnicodeString(ValueName);
  2440. ProbeForRead(Data,
  2441. DataSize,
  2442. sizeof(UCHAR));
  2443. //
  2444. // Sanity check for ValueName length
  2445. //
  2446. if(LocalValueName.Length > REG_MAX_KEY_VALUE_NAME_LENGTH) {
  2447. status = STATUS_INVALID_PARAMETER;
  2448. goto Exit;
  2449. }
  2450. //
  2451. // Capture the name buffer. Note that a zero-length name is valid, that is the
  2452. // "Default" value.
  2453. //
  2454. if (LocalValueName.Length > 0) {
  2455. ProbeForRead(LocalValueName.Buffer,
  2456. LocalValueName.Length,
  2457. sizeof(WCHAR));
  2458. CapturedName = ExAllocatePoolWithQuotaTag(PagedPool, LocalValueName.Length, 'nVmC');
  2459. if (CapturedName == NULL) {
  2460. status = STATUS_INSUFFICIENT_RESOURCES;
  2461. goto Exit;
  2462. }
  2463. RtlCopyMemory(CapturedName, LocalValueName.Buffer, LocalValueName.Length);
  2464. } else {
  2465. CapturedName = NULL;
  2466. }
  2467. LocalValueName.Buffer = CapturedName;
  2468. } except (CmpExceptionFilter(GetExceptionInformation())) {
  2469. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtSetValueKey: code:%08lx [1]\n", GetExceptionCode()));
  2470. status = GetExceptionCode();
  2471. goto Exit;
  2472. }
  2473. } else {
  2474. LocalValueName = *ValueName;
  2475. CapturedName = NULL;
  2476. //
  2477. // Sanity check for ValueName length
  2478. //
  2479. if(LocalValueName.Length > REG_MAX_KEY_VALUE_NAME_LENGTH) {
  2480. status = STATUS_INVALID_PARAMETER;
  2481. goto Exit;
  2482. }
  2483. }
  2484. //
  2485. // do NOT allow trailing NULLs at the end of the ValueName
  2486. //
  2487. try {
  2488. while( (LocalValueName.Length > 0) && (LocalValueName.Buffer[LocalValueName.Length/sizeof(WCHAR)-1] == UNICODE_NULL) ) {
  2489. LocalValueName.Length -= sizeof(WCHAR);
  2490. }
  2491. } except (CmpExceptionFilter(GetExceptionInformation())) {
  2492. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtSetValueKey: code:%08lx [2]\n", GetExceptionCode()));
  2493. status = GetExceptionCode();
  2494. goto Exit;
  2495. }
  2496. if( DataSize > 0x80000000 ) {
  2497. status = STATUS_INVALID_PARAMETER;
  2498. goto Exit;
  2499. }
  2500. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  2501. //
  2502. // key is protected
  2503. //
  2504. status = STATUS_ACCESS_DENIED;
  2505. } else {
  2506. if( CmAreCallbacksRegistered() ) {
  2507. REG_SET_VALUE_KEY_INFORMATION SetValueInfo;
  2508. SetValueInfo.Object = KeyBody;
  2509. SetValueInfo.ValueName = &LocalValueName;
  2510. SetValueInfo.TitleIndex = TitleIndex;
  2511. SetValueInfo.Type = Type;
  2512. SetValueInfo.Data = Data;
  2513. SetValueInfo.DataSize = DataSize;
  2514. status = CmpCallCallBacks(RegNtSetValueKey,&SetValueInfo);
  2515. }
  2516. if( NT_SUCCESS(status) ) {
  2517. BEGIN_LOCK_CHECKPOINT;
  2518. status = CmSetValueKey(KeyBody->KeyControlBlock,
  2519. &LocalValueName,
  2520. Type,
  2521. Data,
  2522. DataSize);
  2523. END_LOCK_CHECKPOINT;
  2524. }
  2525. }
  2526. Exit:
  2527. // End registry call tracing
  2528. EndWmiCmTrace(status,0,&LocalValueName,EVENT_TRACE_TYPE_REGSETVALUE);
  2529. if (CapturedName != NULL) {
  2530. ExFreePool(CapturedName);
  2531. }
  2532. ObDereferenceObject((PVOID)KeyBody);
  2533. }
  2534. return status;
  2535. }
  2536. NTSTATUS
  2537. NtLoadKey(
  2538. IN POBJECT_ATTRIBUTES TargetKey,
  2539. IN POBJECT_ATTRIBUTES SourceFile
  2540. )
  2541. /*++
  2542. Routine Description:
  2543. A hive (file in the format created by NtSaveKey) may be linked
  2544. into the active registry with this call. UNLIKE NtRestoreKey,
  2545. the file specified to NtLoadKey will become the actual backing
  2546. store of part of the registry (that is, it will NOT be copied.)
  2547. The file may have an associated .log file.
  2548. If the hive file is marked as needing a .log file, and one is
  2549. not present, the call will fail.
  2550. The name specified by SourceFile must be such that ".log" can
  2551. be appended to it to generate the name of the log file. Thus,
  2552. on FAT file systems, the hive file may not have an extension.
  2553. Caller must have SeRestorePrivilege privilege.
  2554. This call is used by logon to make the user's profile available
  2555. in the registry. It is not intended for use doing backup,
  2556. restore, etc. Use NtRestoreKey for that.
  2557. Arguments:
  2558. TargetKey - specifies the path to a key to link the hive to.
  2559. path must be of the form "\registry\user\<username>"
  2560. SourceFile - specifies a file. while file could be remote,
  2561. that is strongly discouraged.
  2562. Return Value:
  2563. NTSTATUS - values TBS.
  2564. --*/
  2565. {
  2566. return(NtLoadKey2(TargetKey, SourceFile, 0));
  2567. }
  2568. NTSTATUS
  2569. NtLoadKey2(
  2570. IN POBJECT_ATTRIBUTES TargetKey,
  2571. IN POBJECT_ATTRIBUTES SourceFile,
  2572. IN ULONG Flags
  2573. )
  2574. /*++
  2575. Routine Description:
  2576. A hive (file in the format created by NtSaveKey) may be linked
  2577. into the active registry with this call. UNLIKE NtRestoreKey,
  2578. the file specified to NtLoadKey will become the actual backing
  2579. store of part of the registry (that is, it will NOT be copied.)
  2580. The file may have an associated .log file.
  2581. If the hive file is marked as needing a .log file, and one is
  2582. not present, the call will fail.
  2583. The name specified by SourceFile must be such that ".log" can
  2584. be appended to it to generate the name of the log file. Thus,
  2585. on FAT file systems, the hive file may not have an extension.
  2586. Caller must have SeRestorePrivilege privilege.
  2587. This call is used by logon to make the user's profile available
  2588. in the registry. It is not intended for use doing backup,
  2589. restore, etc. Use NtRestoreKey for that.
  2590. Arguments:
  2591. TargetKey - specifies the path to a key to link the hive to.
  2592. path must be of the form "\registry\user\<username>"
  2593. SourceFile - specifies a file. while file could be remote,
  2594. that is strongly discouraged.
  2595. Flags - specifies any flags that should be used for the load operation.
  2596. The only valid flag is REG_NO_LAZY_FLUSH.
  2597. Return Value:
  2598. NTSTATUS - values TBS.
  2599. --*/
  2600. {
  2601. OBJECT_ATTRIBUTES File;
  2602. OBJECT_ATTRIBUTES Key;
  2603. KPROCESSOR_MODE PreviousMode;
  2604. UNICODE_STRING CapturedKeyName;
  2605. UNICODE_STRING FileName;
  2606. USHORT Maximum;
  2607. NTSTATUS Status;
  2608. PWSTR KeyBuffer;
  2609. PAGED_CODE();
  2610. #ifdef CMP_STATS
  2611. CmpStatsDebug.CmpNtLoadKeyNo++;
  2612. #endif
  2613. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtLoadKey\n"));
  2614. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tTargetKey = %p\n", TargetKey));
  2615. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tSourceFile= %p\n", SourceFile));
  2616. //
  2617. // Check for illegal flags
  2618. //
  2619. if (Flags & ~REG_NO_LAZY_FLUSH) {
  2620. return(STATUS_INVALID_PARAMETER);
  2621. }
  2622. FileName.Buffer = NULL;
  2623. KeyBuffer = NULL;
  2624. //
  2625. // The way we do this is a cronk, but at least it's the same cronk we
  2626. // use for all the registry I/O.
  2627. //
  2628. // The file needs to be opened in the worker thread's context, since
  2629. // the resulting handle must be valid when we poke him to go read/write
  2630. // from. So we just capture the object attributes for the hive file
  2631. // here, then poke the worker thread to go do the rest of the work.
  2632. //
  2633. PreviousMode = KeGetPreviousMode();
  2634. //
  2635. // Check to see if the caller has the privilege to make this call.
  2636. //
  2637. if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode)) {
  2638. return(STATUS_PRIVILEGE_NOT_HELD);
  2639. }
  2640. //
  2641. // CmpNameFromAttributes will probe and capture as necessary.
  2642. //
  2643. KeEnterCriticalRegion();
  2644. Status = CmpNameFromAttributes(SourceFile,
  2645. PreviousMode,
  2646. &FileName);
  2647. if (!NT_SUCCESS(Status)) {
  2648. KeLeaveCriticalRegion();
  2649. return(Status);
  2650. }
  2651. try {
  2652. //
  2653. // Probe the object attributes if necessary.
  2654. //
  2655. if (PreviousMode == UserMode) {
  2656. ProbeForReadSmallStructure(TargetKey,
  2657. sizeof(OBJECT_ATTRIBUTES),
  2658. sizeof(ULONG));
  2659. }
  2660. //
  2661. // Capture the object attributes.
  2662. //
  2663. Key = *TargetKey;
  2664. //
  2665. // Capture the object name.
  2666. //
  2667. if (PreviousMode == UserMode) {
  2668. CapturedKeyName = ProbeAndReadUnicodeString(Key.ObjectName);
  2669. ProbeForRead(CapturedKeyName.Buffer,
  2670. CapturedKeyName.Length,
  2671. sizeof(WCHAR));
  2672. } else {
  2673. CapturedKeyName = *(TargetKey->ObjectName);
  2674. }
  2675. File.ObjectName = &FileName;
  2676. File.SecurityDescriptor = NULL;
  2677. Maximum = (USHORT)(CapturedKeyName.Length);
  2678. KeyBuffer = ALLOCATE_WITH_QUOTA(PagedPool, Maximum, CM_POOL_TAG);
  2679. if (KeyBuffer == NULL) {
  2680. ExFreePool(FileName.Buffer);
  2681. KeLeaveCriticalRegion();
  2682. return(STATUS_INSUFFICIENT_RESOURCES);
  2683. }
  2684. RtlCopyMemory(KeyBuffer, CapturedKeyName.Buffer, Maximum);
  2685. CapturedKeyName.Length = Maximum;
  2686. CapturedKeyName.Buffer = KeyBuffer;
  2687. Key.ObjectName = &CapturedKeyName;
  2688. Key.SecurityDescriptor = NULL;
  2689. } except (EXCEPTION_EXECUTE_HANDLER) {
  2690. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtLoadKey: code:%08lx\n", GetExceptionCode()));
  2691. Status = GetExceptionCode();
  2692. }
  2693. //
  2694. // Clean up if there was an exception while probing and copying user data
  2695. //
  2696. if (!NT_SUCCESS(Status)) {
  2697. if (FileName.Buffer != NULL) {
  2698. ExFreePool(FileName.Buffer);
  2699. }
  2700. if (KeyBuffer != NULL) {
  2701. ExFreePool(KeyBuffer);
  2702. }
  2703. KeLeaveCriticalRegion();
  2704. return(Status);
  2705. }
  2706. BEGIN_LOCK_CHECKPOINT;
  2707. Status = CmLoadKey(&Key, &File, Flags);
  2708. END_LOCK_CHECKPOINT;
  2709. ExFreePool(FileName.Buffer);
  2710. ExFreePool(KeyBuffer);
  2711. KeLeaveCriticalRegion();
  2712. return(Status);
  2713. }
  2714. NTSTATUS
  2715. NtUnloadKey(
  2716. IN POBJECT_ATTRIBUTES TargetKey
  2717. )
  2718. /*++
  2719. Routine Description:
  2720. Drop a subtree (hive) out of the registry.
  2721. Will fail if applied to anything other than the root of a hive.
  2722. Cannot be applied to core system hives (HARDWARE, SYSTEM, etc.)
  2723. Can be applied to user hives loaded via NtRestoreKey or NtLoadKey.
  2724. If there are handles open to the hive being dropped, this call
  2725. will fail. Terminate relevent processes so that handles are
  2726. closed.
  2727. This call will flush the hive being dropped.
  2728. Caller must have SeRestorePrivilege privilege.
  2729. Arguments:
  2730. TargetKey - specifies the path to a key to link the hive to.
  2731. path must be of the form "\registry\user\<username>"
  2732. Return Value:
  2733. NTSTATUS - values TBS.
  2734. --*/
  2735. {
  2736. HANDLE KeyHandle;
  2737. NTSTATUS Status;
  2738. PCM_KEY_BODY KeyBody;
  2739. PHHIVE Hive;
  2740. HCELL_INDEX Cell;
  2741. KPROCESSOR_MODE PreviousMode;
  2742. CM_PARSE_CONTEXT ParseContext;
  2743. PAGED_CODE();
  2744. #ifdef CMP_STATS
  2745. CmpStatsDebug.CmpNtUnloadKeyNo++;
  2746. #endif
  2747. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtUnloadKey\n"));
  2748. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tTargetKey ='%p'\n", TargetKey));
  2749. PreviousMode = KeGetPreviousMode();
  2750. if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode)) {
  2751. return(STATUS_PRIVILEGE_NOT_HELD);
  2752. }
  2753. try {
  2754. ParseContext.TitleIndex = 0;
  2755. ParseContext.Class.Length = 0;
  2756. ParseContext.Class.Buffer = NULL;
  2757. ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE;
  2758. ParseContext.Disposition = 0L;
  2759. ParseContext.CreateLink = FALSE;
  2760. ParseContext.PredefinedHandle = NULL;
  2761. Status = ObOpenObjectByName(TargetKey,
  2762. CmpKeyObjectType,
  2763. PreviousMode,
  2764. NULL,
  2765. KEY_WRITE,
  2766. &ParseContext,
  2767. &KeyHandle);
  2768. if (NT_SUCCESS(Status)) {
  2769. Status = ObReferenceObjectByHandle(KeyHandle,
  2770. KEY_WRITE,
  2771. CmpKeyObjectType,
  2772. PreviousMode,
  2773. (PVOID *)&KeyBody,
  2774. NULL);
  2775. NtClose(KeyHandle);
  2776. }
  2777. } except (EXCEPTION_EXECUTE_HANDLER) {
  2778. Status = GetExceptionCode();
  2779. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtUnloadKey: code:%08lx\n", Status));
  2780. }
  2781. if (NT_SUCCESS(Status)) {
  2782. BEGIN_LOCK_CHECKPOINT;
  2783. CmpLockRegistryExclusive();
  2784. #ifdef CHECK_REGISTRY_USECOUNT
  2785. CmpCheckRegistryUseCount();
  2786. #endif //CHECK_REGISTRY_USECOUNT
  2787. if( KeyBody->KeyControlBlock->Delete ) {
  2788. Status = STATUS_KEY_DELETED;
  2789. } else {
  2790. Hive = KeyBody->KeyControlBlock->KeyHive;
  2791. Cell = KeyBody->KeyControlBlock->KeyCell;
  2792. #ifdef NT_UNLOAD_KEY_EX
  2793. if( !IsHiveFrozen((PCMHIVE)Hive) ) {
  2794. #endif //NT_UNLOAD_KEY_EX
  2795. //
  2796. // Report the notify here, because the KCB won't be around later.
  2797. //
  2798. CmpReportNotify(KeyBody->KeyControlBlock,
  2799. Hive,
  2800. Cell,
  2801. REG_NOTIFY_CHANGE_LAST_SET);
  2802. //
  2803. // post any waiting notifies
  2804. //
  2805. CmpFlushNotify(KeyBody);
  2806. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  2807. //
  2808. // key is protected
  2809. //
  2810. Status = STATUS_ACCESS_DENIED;
  2811. } else {
  2812. Status = CmUnloadKey(Hive, Cell, KeyBody->KeyControlBlock);
  2813. }
  2814. if (NT_SUCCESS(Status)) {
  2815. //
  2816. // Mark this kcb as deleted so that it won't get put on the delayed close list.
  2817. //
  2818. KeyBody->KeyControlBlock->Delete = TRUE;
  2819. //
  2820. // If the parent has the subkey info or hint cached, free it.
  2821. //
  2822. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  2823. CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
  2824. CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
  2825. }
  2826. #ifdef NT_UNLOAD_KEY_EX
  2827. } else {
  2828. //
  2829. // don't let them hurt themselves by calling it twice
  2830. //
  2831. Status = STATUS_TOO_LATE;
  2832. }
  2833. #endif //NT_UNLOAD_KEY_EX
  2834. }
  2835. #ifdef CM_CHECK_FOR_ORPHANED_KCBS
  2836. if( NT_SUCCESS(Status) ) {
  2837. CmpCheckForOrphanedKcbs(Hive);
  2838. }
  2839. #endif //CM_CHECK_FOR_ORPHANED_KCBS
  2840. #ifdef CHECK_REGISTRY_USECOUNT
  2841. CmpCheckRegistryUseCount();
  2842. #endif //CHECK_REGISTRY_USECOUNT
  2843. CmpUnlockRegistry();
  2844. END_LOCK_CHECKPOINT;
  2845. ObDereferenceObject((PVOID)KeyBody);
  2846. }
  2847. return(Status);
  2848. }
  2849. #ifdef NT_UNLOAD_KEY_EX
  2850. NTSTATUS
  2851. NtUnloadKeyEx(
  2852. IN POBJECT_ATTRIBUTES TargetKey,
  2853. IN HANDLE Event OPTIONAL
  2854. )
  2855. /*++
  2856. Routine Description:
  2857. Drop a subtree (hive) out of the registry.
  2858. Will fail if applied to anything other than the root of a hive.
  2859. Cannot be applied to core system hives (HARDWARE, SYSTEM, etc.)
  2860. Can be applied to user hives loaded via NtRestoreKey or NtLoadKey.
  2861. If there are handles open to the hive being dropped, the hive will be
  2862. frozen and all calls to CmDeleteKey will be watched as when the last handle
  2863. inside this hive is closed, the hive will be unloaded.
  2864. Caller must have SeRestorePrivilege privilege.
  2865. Arguments:
  2866. TargetKey - specifies the path to a key to link the hive to.
  2867. path must be of the form "\registry\user\<username>"
  2868. Return Value:
  2869. STATUS_SUCCESS - hive successfully unloaded - no late-unloading needed
  2870. STATUS_PENDING - hive has been frozen and the event (if any) will be signaled
  2871. when the hive unloads
  2872. <other> - an error occured, no action
  2873. --*/
  2874. {
  2875. HANDLE KeyHandle;
  2876. NTSTATUS Status;
  2877. PCM_KEY_BODY KeyBody;
  2878. PHHIVE Hive;
  2879. HCELL_INDEX Cell;
  2880. KPROCESSOR_MODE PreviousMode;
  2881. CM_PARSE_CONTEXT ParseContext;
  2882. PKEVENT UserEvent = NULL;
  2883. PAGED_CODE();
  2884. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtUnloadKeyEx\n"));
  2885. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tTargetKey = %p \tEvent = %p\n", TargetKey,Event));
  2886. PreviousMode = KeGetPreviousMode();
  2887. if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode)) {
  2888. return(STATUS_PRIVILEGE_NOT_HELD);
  2889. }
  2890. try {
  2891. ParseContext.TitleIndex = 0;
  2892. ParseContext.Class.Length = 0;
  2893. ParseContext.Class.Buffer = NULL;
  2894. ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE;
  2895. ParseContext.Disposition = 0L;
  2896. ParseContext.CreateLink = FALSE;
  2897. ParseContext.PredefinedHandle = NULL;
  2898. Status = ObOpenObjectByName(TargetKey,
  2899. CmpKeyObjectType,
  2900. PreviousMode,
  2901. NULL,
  2902. KEY_WRITE,
  2903. &ParseContext,
  2904. &KeyHandle);
  2905. if (NT_SUCCESS(Status)) {
  2906. Status = ObReferenceObjectByHandle(KeyHandle,
  2907. KEY_WRITE,
  2908. CmpKeyObjectType,
  2909. PreviousMode,
  2910. (PVOID *)&KeyBody,
  2911. NULL);
  2912. NtClose(KeyHandle);
  2913. if (ARGUMENT_PRESENT(Event)) {
  2914. Status = ObReferenceObjectByHandle(
  2915. Event,
  2916. EVENT_MODIFY_STATE,
  2917. ExEventObjectType,
  2918. PreviousMode,
  2919. (PVOID *)(&UserEvent),
  2920. NULL
  2921. );
  2922. if (NT_SUCCESS(Status)) {
  2923. KeClearEvent(UserEvent);
  2924. }
  2925. }
  2926. }
  2927. } except (EXCEPTION_EXECUTE_HANDLER) {
  2928. Status = GetExceptionCode();
  2929. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtUnloadKeyEx: code:%08lx\n", Status));
  2930. }
  2931. if (NT_SUCCESS(Status)) {
  2932. BEGIN_LOCK_CHECKPOINT;
  2933. CmpLockRegistryExclusive();
  2934. #ifdef CHECK_REGISTRY_USECOUNT
  2935. CmpCheckRegistryUseCount();
  2936. #endif //CHECK_REGISTRY_USECOUNT
  2937. if( KeyBody->KeyControlBlock->Delete ) {
  2938. Status = STATUS_KEY_DELETED;
  2939. } else {
  2940. Hive = KeyBody->KeyControlBlock->KeyHive;
  2941. Cell = KeyBody->KeyControlBlock->KeyCell;
  2942. //
  2943. // Report the notify here, because the KCB won't be around later.
  2944. //
  2945. CmpReportNotify(KeyBody->KeyControlBlock,
  2946. Hive,
  2947. Cell,
  2948. REG_NOTIFY_CHANGE_LAST_SET);
  2949. //
  2950. // post any waiting notifies
  2951. //
  2952. CmpFlushNotify(KeyBody);
  2953. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  2954. //
  2955. // key is protected
  2956. //
  2957. Status = STATUS_ACCESS_DENIED;
  2958. } else {
  2959. Status = CmUnloadKeyEx(KeyBody->KeyControlBlock,UserEvent);
  2960. }
  2961. if (Status == STATUS_SUCCESS) {
  2962. //
  2963. // Mark this kcb as deleted so that it won't get put on the delayed close list.
  2964. //
  2965. KeyBody->KeyControlBlock->Delete = TRUE;
  2966. //
  2967. // If the parent has the subkey info or hint cached, free it.
  2968. //
  2969. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  2970. CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
  2971. CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
  2972. }
  2973. }
  2974. #ifdef CM_CHECK_FOR_ORPHANED_KCBS
  2975. if( Status == STATUS_SUCCESS ) {
  2976. CmpCheckForOrphanedKcbs(Hive);
  2977. }
  2978. #endif //CM_CHECK_FOR_ORPHANED_KCBS
  2979. #ifdef CHECK_REGISTRY_USECOUNT
  2980. CmpCheckRegistryUseCount();
  2981. #endif //CHECK_REGISTRY_USECOUNT
  2982. CmpUnlockRegistry();
  2983. END_LOCK_CHECKPOINT;
  2984. //
  2985. // if hive was successfully unloaded (or something wrong happened,
  2986. // we need to deref user event otherwise the back-end routine will deref it after signaling
  2987. //
  2988. if( (Status != STATUS_PENDING) && (UserEvent != NULL) ) {
  2989. ObDereferenceObject(UserEvent);
  2990. }
  2991. ObDereferenceObject((PVOID)KeyBody);
  2992. }
  2993. return(Status);
  2994. }
  2995. #endif NT_UNLOAD_KEY_EX
  2996. NTSTATUS
  2997. NtSetInformationKey(
  2998. IN HANDLE KeyHandle,
  2999. IN KEY_SET_INFORMATION_CLASS KeySetInformationClass,
  3000. IN PVOID KeySetInformation,
  3001. IN ULONG KeySetInformationLength
  3002. )
  3003. {
  3004. NTSTATUS status;
  3005. PCM_KEY_BODY KeyBody;
  3006. KPROCESSOR_MODE mode;
  3007. LARGE_INTEGER LocalWriteTime;
  3008. ULONG LocalUserFlags;
  3009. // Start registry call tracing
  3010. StartWmiCmTrace();
  3011. PAGED_CODE();
  3012. #ifdef CMP_STATS
  3013. CmpStatsDebug.CmpNtSetInformationKeyNo++;
  3014. #endif
  3015. BEGIN_LOCK_CHECKPOINT;
  3016. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtSetInformationKey\n"));
  3017. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx\n", KeyHandle));
  3018. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tInfoClass=%08x\n", KeySetInformationClass));
  3019. mode = KeGetPreviousMode();
  3020. //
  3021. // check arg validity and probe
  3022. //
  3023. switch (KeySetInformationClass) {
  3024. case KeyWriteTimeInformation:
  3025. if (KeySetInformationLength != sizeof( KEY_WRITE_TIME_INFORMATION )) {
  3026. // hook it for WMI
  3027. HookKcbFromHandleForWmiCmTrace(KeyHandle);
  3028. // End registry call tracing
  3029. EndWmiCmTrace(STATUS_INFO_LENGTH_MISMATCH,0,NULL,EVENT_TRACE_TYPE_REGSETINFORMATION);
  3030. return STATUS_INFO_LENGTH_MISMATCH;
  3031. }
  3032. try {
  3033. if (mode == UserMode) {
  3034. LocalWriteTime = ProbeAndReadLargeInteger(
  3035. (PLARGE_INTEGER) KeySetInformation );
  3036. } else {
  3037. LocalWriteTime = *(PLARGE_INTEGER)KeySetInformation;
  3038. }
  3039. } except (EXCEPTION_EXECUTE_HANDLER) {
  3040. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtSetInformationKey: code:%08lx\n", GetExceptionCode()));
  3041. return GetExceptionCode();
  3042. }
  3043. break;
  3044. case KeyUserFlagsInformation:
  3045. if (KeySetInformationLength != sizeof( KEY_USER_FLAGS_INFORMATION )) {
  3046. // hook it for WMI
  3047. HookKcbFromHandleForWmiCmTrace(KeyHandle);
  3048. // End registry call tracing
  3049. EndWmiCmTrace(STATUS_INFO_LENGTH_MISMATCH,0,NULL,EVENT_TRACE_TYPE_REGSETINFORMATION);
  3050. return STATUS_INFO_LENGTH_MISMATCH;
  3051. }
  3052. try {
  3053. if (mode == UserMode) {
  3054. LocalUserFlags = ProbeAndReadUlong( (PULONG) KeySetInformation );
  3055. } else {
  3056. LocalUserFlags = *(PULONG)KeySetInformation;
  3057. }
  3058. } except (EXCEPTION_EXECUTE_HANDLER) {
  3059. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtSetInformationKey: code:%08lx\n", GetExceptionCode()));
  3060. return GetExceptionCode();
  3061. }
  3062. break;
  3063. default:
  3064. // hook it for WMI
  3065. HookKcbFromHandleForWmiCmTrace(KeyHandle);
  3066. // End registry call tracing
  3067. EndWmiCmTrace(STATUS_INVALID_INFO_CLASS,0,NULL,EVENT_TRACE_TYPE_REGSETINFORMATION);
  3068. return STATUS_INVALID_INFO_CLASS;
  3069. }
  3070. status = ObReferenceObjectByHandle(
  3071. KeyHandle,
  3072. KEY_SET_VALUE,
  3073. CmpKeyObjectType,
  3074. mode,
  3075. (PVOID *)(&KeyBody),
  3076. NULL
  3077. );
  3078. if (NT_SUCCESS(status)) {
  3079. //
  3080. // hook the kcb for WMI
  3081. //
  3082. HookKcbForWmiCmTrace(KeyBody);
  3083. if( CmAreCallbacksRegistered() ) {
  3084. REG_SET_INFORMATION_KEY_INFORMATION SetInfo;
  3085. SetInfo.Object = KeyBody;
  3086. SetInfo.KeySetInformationClass = KeySetInformationClass;
  3087. SetInfo.KeySetInformation = KeySetInformation;
  3088. SetInfo.KeySetInformationLength = KeySetInformationLength;
  3089. status = CmpCallCallBacks(RegNtSetInformationKey,&SetInfo);
  3090. if( !NT_SUCCESS(status) ) {
  3091. return status;
  3092. }
  3093. }
  3094. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  3095. //
  3096. // key is protected
  3097. //
  3098. status = STATUS_ACCESS_DENIED;
  3099. } else {
  3100. switch (KeySetInformationClass) {
  3101. case KeyWriteTimeInformation:
  3102. if( NT_SUCCESS(status)) {
  3103. //
  3104. // not in try ... except! we want to bugcheck here if something wrong in the registry
  3105. //
  3106. status = CmSetLastWriteTimeKey(
  3107. KeyBody->KeyControlBlock,
  3108. &LocalWriteTime
  3109. );
  3110. }
  3111. break;
  3112. case KeyUserFlagsInformation:
  3113. if( NT_SUCCESS(status)) {
  3114. //
  3115. // not in try ... except! we want to bugcheck here if something wrong in the registry
  3116. //
  3117. status = CmSetKeyUserFlags(
  3118. KeyBody->KeyControlBlock,
  3119. LocalUserFlags
  3120. );
  3121. }
  3122. break;
  3123. default:
  3124. // we shouldn't go through here
  3125. ASSERT( FALSE );
  3126. }
  3127. }
  3128. ObDereferenceObject((PVOID)KeyBody);
  3129. }
  3130. END_LOCK_CHECKPOINT;
  3131. // End registry call tracing
  3132. EndWmiCmTrace(status,0,NULL,EVENT_TRACE_TYPE_REGSETINFORMATION);
  3133. return status;
  3134. }
  3135. NTSTATUS
  3136. NtReplaceKey(
  3137. IN POBJECT_ATTRIBUTES NewFile,
  3138. IN HANDLE TargetHandle,
  3139. IN POBJECT_ATTRIBUTES OldFile
  3140. )
  3141. /*++
  3142. Routine Description:
  3143. A hive file may be "replaced" under a running system, such
  3144. that the new file will be the one actually used at next
  3145. boot, with this call.
  3146. This routine will:
  3147. Open newfile, and verify that it is a valid Hive file.
  3148. Rename the Hive file backing TargetHandle to OldFile.
  3149. All handles will remain open, and the system will continue
  3150. to use the file until rebooted.
  3151. Rename newfile to match the name of the hive file
  3152. backing TargetHandle.
  3153. .log and .alt files are ignored
  3154. The system must be rebooted for any useful effect to be seen.
  3155. Caller must have SeRestorePrivilege.
  3156. Arguments:
  3157. NewFile - specifies the new file to use. must not be just
  3158. a handle, since NtReplaceKey will insist on
  3159. opening the file for exclusive access (which it
  3160. will hold until the system is rebooted.)
  3161. TargetHandle - handle to a registry hive root
  3162. OldFile - name of file to apply to current hive, which will
  3163. become old hive
  3164. Return Value:
  3165. NTSTATUS - values TBS.
  3166. --*/
  3167. {
  3168. KPROCESSOR_MODE PreviousMode;
  3169. UNICODE_STRING NewHiveName;
  3170. UNICODE_STRING OldFileName;
  3171. NTSTATUS Status;
  3172. PCM_KEY_BODY KeyBody;
  3173. PAGED_CODE();
  3174. #ifdef CMP_STATS
  3175. CmpStatsDebug.CmpNtReplaceKeyNo++;
  3176. #endif
  3177. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtReplaceKey\n"));
  3178. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tNewFile =%p\n", NewFile));
  3179. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tOldFile =%p\n", OldFile));
  3180. PreviousMode = KeGetPreviousMode();
  3181. //
  3182. // Check to see if the caller has the privilege to make this call.
  3183. //
  3184. if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode)) {
  3185. return(STATUS_PRIVILEGE_NOT_HELD);
  3186. }
  3187. KeEnterCriticalRegion();
  3188. Status = CmpNameFromAttributes(NewFile,
  3189. PreviousMode,
  3190. &NewHiveName);
  3191. if (!NT_SUCCESS(Status)) {
  3192. KeLeaveCriticalRegion();
  3193. return(Status);
  3194. }
  3195. Status = CmpNameFromAttributes(OldFile,
  3196. PreviousMode,
  3197. &OldFileName);
  3198. if (!NT_SUCCESS(Status)) {
  3199. ExFreePool(NewHiveName.Buffer);
  3200. KeLeaveCriticalRegion();
  3201. return(Status);
  3202. }
  3203. Status = ObReferenceObjectByHandle(TargetHandle,
  3204. 0,
  3205. CmpKeyObjectType,
  3206. PreviousMode,
  3207. (PVOID *)&KeyBody,
  3208. NULL);
  3209. if (NT_SUCCESS(Status)) {
  3210. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  3211. //
  3212. // key is protected
  3213. //
  3214. Status = STATUS_ACCESS_DENIED;
  3215. } else {
  3216. BEGIN_LOCK_CHECKPOINT;
  3217. Status = CmReplaceKey(KeyBody->KeyControlBlock->KeyHive,
  3218. KeyBody->KeyControlBlock->KeyCell,
  3219. &NewHiveName,
  3220. &OldFileName);
  3221. END_LOCK_CHECKPOINT;
  3222. }
  3223. ObDereferenceObject((PVOID)KeyBody);
  3224. }
  3225. ExFreePool(OldFileName.Buffer);
  3226. ExFreePool(NewHiveName.Buffer);
  3227. KeLeaveCriticalRegion();
  3228. return(Status);
  3229. }
  3230. NTSYSAPI
  3231. NTSTATUS
  3232. NTAPI
  3233. NtQueryMultipleValueKey(
  3234. IN HANDLE KeyHandle,
  3235. IN PKEY_VALUE_ENTRY ValueEntries,
  3236. IN ULONG EntryCount,
  3237. OUT PVOID ValueBuffer,
  3238. IN OUT PULONG BufferLength,
  3239. OUT OPTIONAL PULONG RequiredBufferLength
  3240. )
  3241. /*++
  3242. Routine Description:
  3243. Multiple values of any key may be queried atomically with
  3244. this api.
  3245. Arguments:
  3246. KeyHandle - Supplies the key to be queried.
  3247. ValueNames - Supplies an array of value names to be queried
  3248. ValueEntries - Returns an array of KEY_VALUE_ENTRY structures, one for each value.
  3249. EntryCount - Supplies the number of entries in the ValueNames and ValueEntries arrays
  3250. ValueBuffer - Returns the value data for each value.
  3251. BufferLength - Supplies the length of the ValueBuffer array in bytes.
  3252. Returns the length of the ValueBuffer array that was filled in.
  3253. RequiredBufferLength - if present, Returns the length in bytes of the ValueBuffer
  3254. array required to return all the values of this key.
  3255. Return Value:
  3256. NTSTATUS
  3257. --*/
  3258. {
  3259. KPROCESSOR_MODE PreviousMode;
  3260. NTSTATUS Status;
  3261. PCM_KEY_BODY KeyBody;
  3262. ULONG i;
  3263. ULONG LocalBufferLength;
  3264. // Start registry call tracing
  3265. StartWmiCmTrace();
  3266. PAGED_CODE();
  3267. #ifdef CMP_STATS
  3268. CmpStatsDebug.CmpNtQueryMultipleValueKeyNo++;
  3269. #endif
  3270. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtQueryMultipleValueKey\n"));
  3271. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx\n", KeyHandle));
  3272. PreviousMode = KeGetPreviousMode();
  3273. Status = ObReferenceObjectByHandle(KeyHandle,
  3274. KEY_QUERY_VALUE,
  3275. CmpKeyObjectType,
  3276. PreviousMode,
  3277. (PVOID *)(&KeyBody),
  3278. NULL);
  3279. if (NT_SUCCESS(Status)) {
  3280. //
  3281. // hook the kcb for WMI
  3282. //
  3283. HookKcbForWmiCmTrace(KeyBody);
  3284. try {
  3285. if (PreviousMode == UserMode) {
  3286. LocalBufferLength = ProbeAndReadUlong(BufferLength);
  3287. //
  3288. // Probe the output buffers
  3289. //
  3290. // Put an arbitrary 64K limit on the number of entries to
  3291. // prevent bogus apps from passing an EntryCount large enough
  3292. // to overflow the EntryCount * sizeof(KEY_VALUE_ENTRY) calculation.
  3293. //
  3294. if (EntryCount > 0x10000) {
  3295. ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
  3296. }
  3297. ProbeForWrite(ValueEntries,
  3298. EntryCount * sizeof(KEY_VALUE_ENTRY),
  3299. sizeof(ULONG));
  3300. if (ARGUMENT_PRESENT(RequiredBufferLength)) {
  3301. ProbeForWriteUlong(RequiredBufferLength);
  3302. }
  3303. ProbeForWrite(ValueBuffer,
  3304. LocalBufferLength,
  3305. sizeof(ULONG));
  3306. } else {
  3307. LocalBufferLength = *BufferLength;
  3308. }
  3309. if( NT_SUCCESS(Status)) {
  3310. if( CmAreCallbacksRegistered() ) {
  3311. REG_QUERY_MULTIPLE_VALUE_KEY_INFORMATION QueryMultipleValueInfo;
  3312. QueryMultipleValueInfo.Object = KeyBody;
  3313. QueryMultipleValueInfo.ValueEntries = ValueEntries;
  3314. QueryMultipleValueInfo.EntryCount = EntryCount;
  3315. QueryMultipleValueInfo.ValueBuffer = ValueBuffer;
  3316. QueryMultipleValueInfo.BufferLength = BufferLength;
  3317. QueryMultipleValueInfo.RequiredBufferLength = RequiredBufferLength;
  3318. Status = CmpCallCallBacks(RegNtQueryMultipleValueKey,&QueryMultipleValueInfo);
  3319. }
  3320. if( NT_SUCCESS(Status)) {
  3321. // not here because we want to catch user buffer misalignments
  3322. //BEGIN_LOCK_CHECKPOINT;
  3323. Status = CmQueryMultipleValueKey(KeyBody->KeyControlBlock,
  3324. ValueEntries,
  3325. EntryCount,
  3326. ValueBuffer,
  3327. &LocalBufferLength,
  3328. RequiredBufferLength);
  3329. //END_LOCK_CHECKPOINT;
  3330. // anybody messed with BufferLength in between?
  3331. *BufferLength = LocalBufferLength;
  3332. }
  3333. }
  3334. } except(EXCEPTION_EXECUTE_HANDLER) {
  3335. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtQueryMultipleValueKey: code:%08lx\n",GetExceptionCode()));
  3336. Status = GetExceptionCode();
  3337. }
  3338. ObDereferenceObject((PVOID)KeyBody);
  3339. }
  3340. // End registry call tracing
  3341. EndWmiCmTrace(Status,EntryCount,NULL,EVENT_TRACE_TYPE_REGQUERYMULTIPLEVALUE);
  3342. return(Status);
  3343. }
  3344. NTSTATUS
  3345. CmpNameFromAttributes(
  3346. IN POBJECT_ATTRIBUTES Attributes,
  3347. KPROCESSOR_MODE PreviousMode,
  3348. OUT PUNICODE_STRING FullName
  3349. )
  3350. /*++
  3351. Routine Description:
  3352. This is a helper routine that converts OBJECT_ATTRIBUTES into a
  3353. full object pathname. This is needed because we cannot pass handles
  3354. to the worker thread, since it runs in a different process.
  3355. This routine will also probe and capture the attributes based on
  3356. PreviousMode.
  3357. Storage for the string buffer is allocated from paged pool, and should
  3358. be freed by the caller.
  3359. Arguments:
  3360. Attributes - Supplies the object attributes to be converted to a pathname
  3361. PreviousMode - Supplies the previous mode.
  3362. Name - Returns the object pathname.
  3363. Return Value:
  3364. NTSTATUS
  3365. --*/
  3366. {
  3367. OBJECT_ATTRIBUTES CapturedAttributes;
  3368. UNICODE_STRING FileName;
  3369. UNICODE_STRING RootName;
  3370. NTSTATUS Status;
  3371. ULONG ObjectNameLength;
  3372. UCHAR ObjectNameInfo[512];
  3373. POBJECT_NAME_INFORMATION ObjectName;
  3374. PWSTR End;
  3375. PUNICODE_STRING CapturedObjectName;
  3376. ULONG Length;
  3377. PAGED_CODE();
  3378. FullName->Buffer = NULL; // so we know whether to free it in our exception handler
  3379. try {
  3380. //
  3381. // Probe the object attributes if necessary.
  3382. //
  3383. if (PreviousMode == UserMode) {
  3384. ProbeForReadSmallStructure(Attributes,
  3385. sizeof(OBJECT_ATTRIBUTES),
  3386. sizeof(ULONG));
  3387. CapturedObjectName = Attributes->ObjectName;
  3388. FileName = ProbeAndReadUnicodeString(CapturedObjectName);
  3389. ProbeForRead(FileName.Buffer,
  3390. FileName.Length,
  3391. sizeof(WCHAR));
  3392. } else {
  3393. FileName = *(Attributes->ObjectName);
  3394. }
  3395. CapturedAttributes = *Attributes;
  3396. if (CapturedAttributes.RootDirectory != NULL) {
  3397. if ((FileName.Buffer != NULL) &&
  3398. (FileName.Length >= sizeof(WCHAR)) &&
  3399. (*(FileName.Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
  3400. return(STATUS_OBJECT_PATH_SYNTAX_BAD);
  3401. }
  3402. //
  3403. // Find the name of the root directory and append the
  3404. // name of the relative object to it.
  3405. //
  3406. Status = ZwQueryObject(CapturedAttributes.RootDirectory,
  3407. ObjectNameInformation,
  3408. &ObjectNameInfo,
  3409. sizeof(ObjectNameInfo),
  3410. &ObjectNameLength);
  3411. ObjectName = (POBJECT_NAME_INFORMATION)ObjectNameInfo;
  3412. if (!NT_SUCCESS(Status)) {
  3413. return(Status);
  3414. }
  3415. RootName = ObjectName->Name;
  3416. FullName->Length = 0;
  3417. Length = RootName.Length+FileName.Length+sizeof(WCHAR);
  3418. //
  3419. // Overflow test: If Length overflows the USHRT_MAX value
  3420. // cleanup and return STATUS_OBJECT_PATH_INVALID
  3421. //
  3422. if( Length>0xFFFF ) {
  3423. return STATUS_OBJECT_PATH_INVALID;
  3424. }
  3425. FullName->MaximumLength = (USHORT)Length;
  3426. FullName->Buffer = ALLOCATE_WITH_QUOTA(PagedPool, FullName->MaximumLength, CM_POOL_TAG);
  3427. if (FullName->Buffer == NULL) {
  3428. return STATUS_INSUFFICIENT_RESOURCES;
  3429. }
  3430. Status = RtlAppendUnicodeStringToString(FullName, &RootName);
  3431. ASSERT(NT_SUCCESS(Status));
  3432. //
  3433. // Append a trailing separator if necessary.
  3434. //
  3435. if( FullName->Length != 0 ) {
  3436. End = (PWSTR)((PUCHAR)FullName->Buffer + FullName->Length) - 1;
  3437. if (*End != OBJ_NAME_PATH_SEPARATOR) {
  3438. ++End;
  3439. *End = OBJ_NAME_PATH_SEPARATOR;
  3440. FullName->Length += sizeof(WCHAR);
  3441. }
  3442. }
  3443. Status = RtlAppendUnicodeStringToString(FullName, &FileName);
  3444. ASSERT(NT_SUCCESS(Status));
  3445. } else {
  3446. //
  3447. // RootDirectory is NULL, so just use the name.
  3448. //
  3449. FullName->Length = FileName.Length;
  3450. FullName->MaximumLength = FileName.Length;
  3451. FullName->Buffer = ALLOCATE_WITH_QUOTA(PagedPool, FileName.Length, CM_POOL_TAG);
  3452. if (FullName->Buffer == NULL) {
  3453. Status = STATUS_INSUFFICIENT_RESOURCES;
  3454. } else {
  3455. RtlCopyMemory(FullName->Buffer,
  3456. FileName.Buffer,
  3457. FileName.Length);
  3458. Status = STATUS_SUCCESS;
  3459. }
  3460. }
  3461. } except (EXCEPTION_EXECUTE_HANDLER) {
  3462. Status = GetExceptionCode();
  3463. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmpNameFromAttributes: code %08lx\n", Status));
  3464. if (FullName->Buffer != NULL) {
  3465. ExFreePool(FullName->Buffer);
  3466. }
  3467. }
  3468. return(Status);
  3469. }
  3470. VOID
  3471. CmpFreePostBlock(
  3472. IN PCM_POST_BLOCK PostBlock
  3473. )
  3474. /*++
  3475. Routine Description:
  3476. Frees the various bits of pool that were allocated for a postblock
  3477. Arguments:
  3478. None
  3479. Return Value:
  3480. None.
  3481. --*/
  3482. {
  3483. #if DBG
  3484. if(PostBlock->TraceIntoDebugger) {
  3485. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_FLOW,"[CM]CmpFreePostBlock: PostBlock:%p\t", PostBlock));
  3486. if( PostBlock->NotifyType&REG_NOTIFY_MASTER_POST) {
  3487. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_FLOW,"--MasterBlock\n"));
  3488. } else {
  3489. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_FLOW,"--SlaveBlock\n"));
  3490. }
  3491. }
  3492. #endif
  3493. #ifdef CMP_ENTRYLIST_MANIPULATION
  3494. // check if the post block has been removed from the notify and thread list(s)
  3495. if((PostBlock->NotifyList.Flink != NULL) || (PostBlock->NotifyList.Blink != NULL)) {
  3496. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFreePostBlock: Attempt to free post block %08lx not removed from notify list\n",PostBlock);
  3497. DbgBreakPoint();
  3498. }
  3499. if((PostBlock->ThreadList.Flink != NULL) || (PostBlock->ThreadList.Blink != NULL)) {
  3500. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFreePostBlock: Attempt to free post block %08lx not removed from thread list\n",PostBlock);
  3501. DbgBreakPoint();
  3502. }
  3503. #endif //CMP_ENTRYLIST_MANIPULATION
  3504. // Protect for multiple deletion of the same object
  3505. CmpClearListEntry(&(PostBlock->CancelPostList));
  3506. //
  3507. // Cleanup for objects referenced by NtNotifyMultipleKeys
  3508. //
  3509. if( PostBlock->PostKeyBody) {
  3510. //
  3511. // If we have a PostKeyBody, the attached key body must not be NULL
  3512. //
  3513. ASSERT(PostBlock->PostKeyBody->KeyBody);
  3514. //
  3515. // KeyBodyList must be used only in CmpPostBlock implementation for the delayed dereferencing mechanism.
  3516. //
  3517. ASSERT(IsListEmpty(&(PostBlock->PostKeyBody->KeyBodyList)));
  3518. //
  3519. // dereference the actual keybody
  3520. //
  3521. ObDereferenceObject(PostBlock->PostKeyBody->KeyBody);
  3522. //
  3523. // Free the PostKeyBody structure
  3524. //
  3525. ExFreePool(PostBlock->PostKeyBody);
  3526. }
  3527. if( IsMasterPostBlock(PostBlock) ) {
  3528. //
  3529. // this members are allocated only for master post blocks
  3530. //
  3531. switch (PostBlockType(PostBlock)) {
  3532. case PostSynchronous:
  3533. ExFreePool(PostBlock->u->Sync.SystemEvent);
  3534. break;
  3535. case PostAsyncUser:
  3536. ExFreePool(PostBlock->u->AsyncUser.Apc);
  3537. break;
  3538. case PostAsyncKernel:
  3539. break;
  3540. }
  3541. ExFreePool(PostBlock->u);
  3542. }
  3543. #ifdef CM_NOTIFY_CHANGED_KCB_FULLPATH
  3544. //
  3545. // get rid of the kcb name allocated in CmpPostNotify
  3546. //
  3547. if( PostBlock->ChangedKcbFullName != NULL ) {
  3548. ExFreePoolWithTag(PostBlock->ChangedKcbFullName,CM_FIND_LEAK_TAG43);
  3549. }
  3550. #endif //CM_NOTIFY_CHANGED_KCB_FULLPATH
  3551. #ifdef CMP_ENTRYLIST_MANIPULATION
  3552. RtlZeroMemory((PVOID)PostBlock, sizeof(CM_POST_BLOCK));
  3553. #endif //CMP_ENTRYLIST_MANIPULATION
  3554. // and the storage for the Post object
  3555. ExFreePool(PostBlock);
  3556. }
  3557. PCM_POST_BLOCK
  3558. CmpAllocatePostBlock(
  3559. IN POST_BLOCK_TYPE BlockType,
  3560. IN ULONG PostFlags,
  3561. IN PCM_KEY_BODY KeyBody,
  3562. IN PCM_POST_BLOCK MasterBlock
  3563. )
  3564. /*++
  3565. Routine Description:
  3566. Allocates a post block from pool. The non-pagable stuff comes from
  3567. NonPagedPool, the pagable stuff from paged pool. Quota will be
  3568. charged.
  3569. Arguments:
  3570. BlockType - specifies the type of the post block to be allocated
  3571. i.e. : PostSyncrhronous, PostAsyncUser, PostAsyncKernel
  3572. PostFlags - specifies the flags to be set on the allocated post block
  3573. vallid flags:
  3574. - REG_NOTIFY_MASTER_POST - the post block to be allocated
  3575. is a master post block.
  3576. KeyBody - The Key object to whom this post block is attached. On master blocks
  3577. this is NULL. When the post object is freed, the KeyBody object is
  3578. dereferenced (if not NULL - i.e. for slave blocks). This allow us to
  3579. perform back-end cleanup for "fake-slave" keys opened by NtNotifyMultipleKeys
  3580. MasterBlock - the post block to be allocated is a slave of this master block.
  3581. valid only when PostFlags == REG_NOTIFY_MASTER_POST
  3582. Obs: The Sync.SystemEvent and AsyncUser.Apc members are allocated only for master post blocks
  3583. Return Value:
  3584. Pointer to the CM_POST_BLOCK if successful
  3585. NULL if there were not enough resources available.
  3586. --*/
  3587. {
  3588. PCM_POST_BLOCK PostBlock;
  3589. // protection against outrageous calls
  3590. ASSERT( !PostFlags || (!MasterBlock && !KeyBody) );
  3591. PostBlock = ALLOCATE_WITH_QUOTA(PagedPool, sizeof(CM_POST_BLOCK),CM_POSTBLOCK_TAG);
  3592. if (PostBlock==NULL) {
  3593. return(NULL);
  3594. }
  3595. #ifdef CMP_ENTRYLIST_MANIPULATION
  3596. RtlZeroMemory((PVOID)PostBlock, sizeof(CM_POST_BLOCK));
  3597. #endif //CMP_ENTRYLIST_MANIPULATION
  3598. #if DBG
  3599. PostBlock->TraceIntoDebugger = FALSE;
  3600. #endif
  3601. PostBlock->NotifyType = (ULONG)BlockType;
  3602. PostBlock->NotifyType |= PostFlags;
  3603. #ifdef CM_NOTIFY_CHANGED_KCB_FULLPATH
  3604. PostBlock->ChangedKcbFullName = NULL;
  3605. PostBlock->CallerBuffer = NULL;
  3606. PostBlock->CallerBufferSize = 0;
  3607. #endif //CM_NOTIFY_CHANGED_KCB_FULLPATH
  3608. if(IsMasterPostBlock(PostBlock)) {
  3609. PostBlock->PostKeyBody = NULL;
  3610. //
  3611. // master post block ==> allocate the storage
  3612. //
  3613. PostBlock->u = ALLOCATE_WITH_QUOTA(NonPagedPool,
  3614. sizeof(CM_POST_BLOCK_UNION),
  3615. CM_FIND_LEAK_TAG44);
  3616. if (PostBlock->u == NULL) {
  3617. ExFreePool(PostBlock);
  3618. return(NULL);
  3619. }
  3620. switch (BlockType) {
  3621. case PostSynchronous:
  3622. PostBlock->u->Sync.SystemEvent = ALLOCATE_WITH_QUOTA(NonPagedPool,
  3623. sizeof(KEVENT),
  3624. CM_POSTEVENT_TAG);
  3625. if (PostBlock->u->Sync.SystemEvent == NULL) {
  3626. ExFreePool(PostBlock->u);
  3627. ExFreePool(PostBlock);
  3628. return(NULL);
  3629. }
  3630. KeInitializeEvent(PostBlock->u->Sync.SystemEvent,
  3631. SynchronizationEvent,
  3632. FALSE);
  3633. break;
  3634. case PostAsyncUser:
  3635. PostBlock->u->AsyncUser.Apc = ALLOCATE_WITH_QUOTA(NonPagedPool,
  3636. sizeof(KAPC),
  3637. CM_POSTAPC_TAG);
  3638. if (PostBlock->u->AsyncUser.Apc==NULL) {
  3639. ExFreePool(PostBlock->u);
  3640. ExFreePool(PostBlock);
  3641. return(NULL);
  3642. }
  3643. break;
  3644. case PostAsyncKernel:
  3645. RtlZeroMemory(&PostBlock->u->AsyncKernel, sizeof(CM_ASYNC_KERNEL_POST_BLOCK));
  3646. break;
  3647. }
  3648. } else {
  3649. //
  3650. // Slave post block ==> copy storage allocated for the master post block
  3651. //
  3652. PostBlock->u = MasterBlock->u;
  3653. //
  3654. // allocate a PostKeyBody which will hold this KeyBody, and initialize the head of its KeyBodyList
  3655. //
  3656. PostBlock->PostKeyBody = ALLOCATE_WITH_QUOTA(PagedPool| POOL_COLD_ALLOCATION, sizeof(CM_POST_KEY_BODY),CM_FIND_LEAK_TAG45);
  3657. if (PostBlock->PostKeyBody == NULL) {
  3658. ExFreePool(PostBlock);
  3659. return(NULL);
  3660. }
  3661. PostBlock->PostKeyBody->KeyBody = KeyBody;
  3662. InitializeListHead(&(PostBlock->PostKeyBody->KeyBodyList));
  3663. }
  3664. return(PostBlock);
  3665. }
  3666. #if DBG
  3667. #ifdef DRAGOSS_PRIVATE_DEBUG
  3668. LOGICAL CmpExceptionBreak = TRUE;
  3669. #else
  3670. LOGICAL CmpExceptionBreak = FALSE;
  3671. #endif //DRAGOSS_PRIVATE_DEBUG
  3672. ULONG
  3673. CmpExceptionFilter(
  3674. IN PEXCEPTION_POINTERS ExceptionPointers
  3675. )
  3676. /*++
  3677. Routine Description:
  3678. Debug code to find registry exceptions that are being swallowed
  3679. Return Value:
  3680. EXCEPTION_EXECUTE_HANDLER
  3681. --*/
  3682. {
  3683. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CM: Registry exception %lx, ExceptionPointers = %p\n",
  3684. ExceptionPointers->ExceptionRecord->ExceptionCode,
  3685. ExceptionPointers));
  3686. if (CmpExceptionBreak == TRUE) {
  3687. try {
  3688. DbgBreakPoint();
  3689. } except (EXCEPTION_EXECUTE_HANDLER) {
  3690. //
  3691. // no debugger enabled, just keep going
  3692. //
  3693. }
  3694. }
  3695. return(EXCEPTION_EXECUTE_HANDLER);
  3696. }
  3697. #endif
  3698. ULONG CmpOpenSubKeys;
  3699. #if 0
  3700. BOOLEAN
  3701. CmpEnumKeyObjectCallback(
  3702. IN PVOID Object,
  3703. IN PUNICODE_STRING ObjectName,
  3704. IN ULONG HandleCount,
  3705. IN ULONG PointerCount,
  3706. IN PVOID Context
  3707. )
  3708. {
  3709. PCM_KEY_BODY KeyBody;
  3710. PHHIVE Hive;
  3711. KeyBody = (PCM_KEY_BODY)Object;
  3712. Hive = (PHHIVE)Context;
  3713. if( KeyBody->KeyControlBlock->KeyHive == Hive ) {
  3714. //
  3715. // that's and open subkey inside of the hive
  3716. //
  3717. #ifndef _CM_LDR_
  3718. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Key %wZ (HandleCount = %lu PointerCount = %lu) is opened by process %lx\n",
  3719. ObjectName,HandleCount,PointerCount,KeyBody->Process);
  3720. #endif //_CM_LDR_
  3721. // count it
  3722. CmpOpenSubKeys++;
  3723. }
  3724. return TRUE;
  3725. }
  3726. #endif
  3727. NTSTATUS
  3728. NtQueryOpenSubKeys(
  3729. IN POBJECT_ATTRIBUTES TargetKey,
  3730. OUT PULONG HandleCount
  3731. )
  3732. /*++
  3733. Routine Description:
  3734. Dumps all the subkeys of the target key that are kept open by some other
  3735. process; Returns the number of open subkeys
  3736. Arguments:
  3737. TargetKey - specifies the path to a key to link the hive to.
  3738. path must be of the form "\registry\user\<username>"
  3739. Return Value:
  3740. NTSTATUS - values TBS.
  3741. --*/
  3742. {
  3743. HANDLE KeyHandle;
  3744. NTSTATUS Status;
  3745. PCM_KEY_BODY KeyBody;
  3746. PHHIVE Hive;
  3747. HCELL_INDEX Cell;
  3748. KPROCESSOR_MODE PreviousMode;
  3749. UNICODE_STRING HiveName;
  3750. PAGED_CODE();
  3751. #ifdef CMP_STATS
  3752. CmpStatsDebug.CmpNtQueryOpenSubKeysNo++;
  3753. #endif
  3754. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtQueryOpenSubKeys\n"));
  3755. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tTargetKey =%p\n", TargetKey));
  3756. PreviousMode = KeGetPreviousMode();
  3757. try {
  3758. if (PreviousMode == UserMode) {
  3759. ProbeForWriteUlong(HandleCount);
  3760. }
  3761. Status = ObOpenObjectByName(TargetKey,
  3762. CmpKeyObjectType,
  3763. PreviousMode,
  3764. NULL,
  3765. KEY_READ,
  3766. NULL,
  3767. &KeyHandle);
  3768. if (NT_SUCCESS(Status)) {
  3769. Status = ObReferenceObjectByHandle(KeyHandle,
  3770. KEY_READ,
  3771. CmpKeyObjectType,
  3772. PreviousMode,
  3773. (PVOID *)&KeyBody,
  3774. NULL);
  3775. NtClose(KeyHandle);
  3776. }
  3777. } except (EXCEPTION_EXECUTE_HANDLER) {
  3778. Status = GetExceptionCode();
  3779. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtQueryOpenSubKeys: code:%08lx\n", Status));
  3780. }
  3781. if (NT_SUCCESS(Status)) {
  3782. //
  3783. // lock registry exclusive so nobody messes with it while we're around
  3784. //
  3785. BEGIN_LOCK_CHECKPOINT;
  3786. CmpLockRegistryExclusive();
  3787. #ifdef CHECK_REGISTRY_USECOUNT
  3788. CmpCheckRegistryUseCount();
  3789. #endif //CHECK_REGISTRY_USECOUNT
  3790. if( KeyBody->KeyControlBlock->Delete ) {
  3791. CmpUnlockRegistry();
  3792. ObDereferenceObject((PVOID)KeyBody);
  3793. return(STATUS_KEY_DELETED);
  3794. }
  3795. Hive = KeyBody->KeyControlBlock->KeyHive;
  3796. Cell = KeyBody->KeyControlBlock->KeyCell;
  3797. //
  3798. // Make sure the cell passed in is the root cell of the hive.
  3799. //
  3800. if (Cell != Hive->BaseBlock->RootCell) {
  3801. CmpUnlockRegistry();
  3802. ObDereferenceObject((PVOID)KeyBody);
  3803. return(STATUS_INVALID_PARAMETER);
  3804. }
  3805. //
  3806. // Dump the hive name and hive address
  3807. //
  3808. RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName);
  3809. #ifndef _CM_LDR_
  3810. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\n Subkeys open inside the hive (%p) (%.*S) :\n\n",Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer);
  3811. #endif //_CM_LDR_
  3812. //
  3813. // dump open subkeys (if any)
  3814. //
  3815. CmpOpenSubKeys = CmpSearchForOpenSubKeys(KeyBody->KeyControlBlock,SearchAndCount);
  3816. #if 0
  3817. //
  3818. // use a global var to count the number of subkeys, as this is the only
  3819. // way interfere with the Enum callback; It is safe to use as this will
  3820. // be the only thread working on this global var (registry is locked exclusively)
  3821. //
  3822. CmpOpenSubKeys = 0;
  3823. ObEnumerateObjectsByType(
  3824. CmpKeyObjectType,
  3825. CmpEnumKeyObjectCallback,
  3826. Hive
  3827. );
  3828. #endif
  3829. #ifdef CHECK_REGISTRY_USECOUNT
  3830. CmpCheckRegistryUseCount();
  3831. #endif //CHECK_REGISTRY_USECOUNT
  3832. CmpUnlockRegistry();
  3833. END_LOCK_CHECKPOINT;
  3834. ObDereferenceObject((PVOID)KeyBody);
  3835. try {
  3836. //
  3837. // protect user mode memory
  3838. //
  3839. *HandleCount = CmpOpenSubKeys;
  3840. } except (EXCEPTION_EXECUTE_HANDLER) {
  3841. Status = GetExceptionCode();
  3842. }
  3843. }
  3844. return(Status);
  3845. }
  3846. #ifdef NT_RENAME_KEY
  3847. NTSTATUS
  3848. NtRenameKey(
  3849. IN HANDLE KeyHandle,
  3850. IN PUNICODE_STRING NewName
  3851. )
  3852. /*++
  3853. Routine Description:
  3854. Renames the key specified by Handle.
  3855. Arguments:
  3856. NewFile - specifies the key to be renamed
  3857. NewName - the new name the key will have if the API succeedes
  3858. Return Value:
  3859. NTSTATUS - values TBS.
  3860. --*/
  3861. {
  3862. KPROCESSOR_MODE PreviousMode;
  3863. UNICODE_STRING LocalKeyName;
  3864. NTSTATUS status;
  3865. PCM_KEY_BODY KeyBody;
  3866. KPROCESSOR_MODE mode;
  3867. PAGED_CODE();
  3868. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtRenameKey\n"));
  3869. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tKeyHandle=%08lx\n", KeyHandle));
  3870. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tNewName='%wZ'\n", NewName));
  3871. mode = KeGetPreviousMode();
  3872. status = ObReferenceObjectByHandle(
  3873. KeyHandle,
  3874. KEY_WRITE,
  3875. CmpKeyObjectType,
  3876. mode,
  3877. (PVOID *)(&KeyBody),
  3878. NULL
  3879. );
  3880. if (NT_SUCCESS(status)) {
  3881. try {
  3882. if (mode == UserMode) {
  3883. LocalKeyName = ProbeAndReadUnicodeString(NewName);
  3884. ProbeForRead(
  3885. LocalKeyName.Buffer,
  3886. LocalKeyName.Length,
  3887. sizeof(WCHAR)
  3888. );
  3889. } else {
  3890. LocalKeyName = *NewName;
  3891. }
  3892. } except (EXCEPTION_EXECUTE_HANDLER) {
  3893. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtRenameKey: code:%08lx\n", GetExceptionCode()));
  3894. status = GetExceptionCode();
  3895. }
  3896. //
  3897. // we really need exclusive access here
  3898. //
  3899. BEGIN_LOCK_CHECKPOINT;
  3900. CmpLockRegistryExclusive();
  3901. #ifdef CHECK_REGISTRY_USECOUNT
  3902. CmpCheckRegistryUseCount();
  3903. #endif //CHECK_REGISTRY_USECOUNT
  3904. //
  3905. // flush notifications for all open objects on this key
  3906. //
  3907. CmpFlushNotifiesOnKeyBodyList(KeyBody->KeyControlBlock);
  3908. if( NT_SUCCESS(status) ) {
  3909. if( CmIsKcbReadOnly(KeyBody->KeyControlBlock) ) {
  3910. //
  3911. // key is protected
  3912. //
  3913. status = STATUS_ACCESS_DENIED;
  3914. } else {
  3915. if( CmAreCallbacksRegistered() ) {
  3916. REG_RENAME_KEY_INFORMATION RenameKeyInfo;
  3917. RenameKeyInfo.Object = KeyBody;
  3918. RenameKeyInfo.NewName = &LocalKeyName;
  3919. status = CmpCallCallBacks(RegNtRenameKey,&RenameKeyInfo);
  3920. }
  3921. if( NT_SUCCESS(status) ) {
  3922. status = CmRenameKey(KeyBody->KeyControlBlock,LocalKeyName);
  3923. }
  3924. }
  3925. }
  3926. #ifdef CHECK_REGISTRY_USECOUNT
  3927. CmpCheckRegistryUseCount();
  3928. #endif //CHECK_REGISTRY_USECOUNT
  3929. //
  3930. // we need to release just here, after the kcb has been kicked out of cache
  3931. //
  3932. CmpUnlockRegistry();
  3933. END_LOCK_CHECKPOINT;
  3934. ObDereferenceObject((PVOID)KeyBody);
  3935. } else {
  3936. //
  3937. // just in case we want to add wmi tracing for this API
  3938. //
  3939. LocalKeyName.Buffer = NULL;
  3940. LocalKeyName.Length = 0;
  3941. }
  3942. return status;
  3943. }
  3944. #endif
  3945. ULONG
  3946. CmpKeyInfoProbeAlingment(
  3947. IN KEY_INFORMATION_CLASS KeyInformationClass
  3948. )
  3949. {
  3950. switch(KeyInformationClass)
  3951. {
  3952. case KeyBasicInformation:
  3953. return PROBE_ALIGNMENT(KEY_BASIC_INFORMATION);
  3954. case KeyNodeInformation:
  3955. return PROBE_ALIGNMENT(KEY_NODE_INFORMATION);
  3956. case KeyFullInformation:
  3957. return PROBE_ALIGNMENT(KEY_FULL_INFORMATION);
  3958. case KeyNameInformation:
  3959. return PROBE_ALIGNMENT(KEY_NAME_INFORMATION);
  3960. case KeyCachedInformation:
  3961. return PROBE_ALIGNMENT(KEY_CACHED_INFORMATION);
  3962. case KeyFlagsInformation:
  3963. return PROBE_ALIGNMENT(KEY_FLAGS_INFORMATION);
  3964. default:
  3965. ASSERT(FALSE);
  3966. }
  3967. return PROBE_ALIGNMENT(ULONG);
  3968. }
  3969. NTSTATUS
  3970. NtCompactKeys(
  3971. IN ULONG Count,
  3972. IN HANDLE KeyArray[]
  3973. )
  3974. /*++
  3975. Routine Description:
  3976. Compacts the keys in the given array together, so they will
  3977. end up in the same bin (or adjacent)
  3978. Arguments:
  3979. Count - number of keys in the array
  3980. KeyArray - array of keys to be compacted.
  3981. Return Value:
  3982. NTSTATUS - values TBS.
  3983. --*/
  3984. {
  3985. NTSTATUS status = STATUS_SUCCESS;
  3986. NTSTATUS status2;
  3987. PCM_KEY_BODY *KeyBodyArray = NULL;
  3988. ULONG i;
  3989. PHHIVE KeyHive;
  3990. PCMHIVE CmHive;
  3991. KPROCESSOR_MODE mode;
  3992. PAGED_CODE();
  3993. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtCompactKeys\n"));
  3994. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI_ARGS,"\tCount=%08lx\n", Count));
  3995. mode = KeGetPreviousMode();
  3996. if( Count == 0 ) {
  3997. //
  3998. // noop
  3999. //
  4000. return STATUS_SUCCESS;
  4001. }
  4002. if( Count >= (((ULONG)0xFFFFFFFF)/sizeof(PCM_KEY_BODY)) ) {
  4003. return STATUS_INVALID_PARAMETER;
  4004. }
  4005. if (mode == UserMode) {
  4006. try {
  4007. ProbeForRead(KeyArray,
  4008. Count * sizeof(HANDLE),
  4009. sizeof(ULONG));
  4010. } except (EXCEPTION_EXECUTE_HANDLER) {
  4011. status = GetExceptionCode();
  4012. return status;
  4013. }
  4014. }
  4015. KeyBodyArray = ExAllocatePool(PagedPool,Count * sizeof(PCM_KEY_BODY));
  4016. if( KeyBodyArray == NULL ) {
  4017. return STATUS_INSUFFICIENT_RESOURCES;
  4018. }
  4019. //
  4020. // reference each handle and make sure they are inside the same hive
  4021. //
  4022. try {
  4023. for(i=0;i<Count;i++) {
  4024. status = ObReferenceObjectByHandle(
  4025. KeyArray[i],
  4026. KEY_WRITE,
  4027. CmpKeyObjectType,
  4028. mode,
  4029. (PVOID *)(&(KeyBodyArray[i])),
  4030. NULL
  4031. );
  4032. if(!NT_SUCCESS(status)) {
  4033. //
  4034. // cleanup
  4035. //
  4036. for(;i;i--) {
  4037. ObDereferenceObject((PVOID)(KeyBodyArray[i-1]));
  4038. }
  4039. ExFreePool(KeyBodyArray);
  4040. return status;
  4041. }
  4042. }
  4043. } except (EXCEPTION_EXECUTE_HANDLER) {
  4044. status = GetExceptionCode();
  4045. //
  4046. // cleanup
  4047. //
  4048. for(;i;i--) {
  4049. ObDereferenceObject((PVOID)(KeyBodyArray[i-1]));
  4050. }
  4051. ExFreePool(KeyBodyArray);
  4052. return status;
  4053. }
  4054. BEGIN_LOCK_CHECKPOINT;
  4055. CmpLockRegistryExclusive();
  4056. for(i=0;i<Count;i++) {
  4057. if( (KeyBodyArray[i])->KeyControlBlock->Delete ) {
  4058. status = STATUS_KEY_DELETED;
  4059. goto Exit;
  4060. }
  4061. if( i > 0 ) {
  4062. if( KeyHive != (KeyBodyArray[i])->KeyControlBlock->KeyHive ) {
  4063. //
  4064. // Ooops, not same hive
  4065. //
  4066. status = STATUS_INVALID_PARAMETER;
  4067. goto Exit;
  4068. }
  4069. } else {
  4070. KeyHive = (KeyBodyArray[i])->KeyControlBlock->KeyHive;
  4071. }
  4072. }
  4073. //
  4074. // set the hive into "Grow Only mode"
  4075. //
  4076. CmHive = (PCMHIVE)CONTAINING_RECORD(KeyHive, CMHIVE, Hive);
  4077. CmHive->GrowOnlyMode = TRUE;
  4078. CmHive->GrowOffset = KeyHive->Storage[Stable].Length;
  4079. //KdPrint(("Length before compaction=%08lx\n", CmHive->GrowOffset));
  4080. //
  4081. // truncate to the CM_VIEW_SIZE segment
  4082. //
  4083. CmHive->GrowOffset += HBLOCK_SIZE;
  4084. CmHive->GrowOffset &= (~(CM_VIEW_SIZE - 1));
  4085. if( CmHive->GrowOffset ) {
  4086. CmHive->GrowOffset -= HBLOCK_SIZE;
  4087. }
  4088. //
  4089. // move each kcb at offset > HiveLength
  4090. //
  4091. for(i=0;i<Count;i++) {
  4092. status2 = CmMoveKey((KeyBodyArray[i])->KeyControlBlock);
  4093. if( !NT_SUCCESS(status2) && NT_SUCCESS(status)) {
  4094. //
  4095. // record the status and go on with the remaining
  4096. //
  4097. status = status2;
  4098. }
  4099. }
  4100. //KdPrint(("Length after compaction=%08lx\n", KeyHive->Storage[Stable].Length));
  4101. //
  4102. // reset the "Grow Only mode" to normal
  4103. //
  4104. CmHive->GrowOnlyMode = FALSE;
  4105. CmHive->GrowOffset = 0;
  4106. Exit:
  4107. CmpUnlockRegistry();
  4108. END_LOCK_CHECKPOINT;
  4109. //
  4110. // cleanup
  4111. //
  4112. for(i=0;i<Count;i++) {
  4113. ObDereferenceObject((PVOID)(KeyBodyArray[i]));
  4114. }
  4115. ExFreePool(KeyBodyArray);
  4116. return status;
  4117. }
  4118. NTSTATUS
  4119. NtCompressKey(
  4120. IN HANDLE Key
  4121. )
  4122. /*++
  4123. Routine Description:
  4124. Compresses the specified key (must be the root of a hive),
  4125. by simulating an "in-place" SaveKey.
  4126. Arguments:
  4127. Key - root of the hive to be compressed.
  4128. Return Value:
  4129. NTSTATUS - values TBS.
  4130. --*/
  4131. {
  4132. NTSTATUS status;
  4133. PCM_KEY_BODY KeyBody;
  4134. KPROCESSOR_MODE mode;
  4135. PAGED_CODE();
  4136. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_NTAPI,"NtCompressKey\n"));
  4137. mode = KeGetPreviousMode();
  4138. //
  4139. // DRAGOSS: see if we need this
  4140. //
  4141. // Check to see if the caller has the privilege to make this call.
  4142. //
  4143. //if (!SeSinglePrivilegeCheck(SeBackupPrivilege, mode)) {
  4144. // return(STATUS_PRIVILEGE_NOT_HELD);
  4145. //}
  4146. status = ObReferenceObjectByHandle(
  4147. Key,
  4148. KEY_WRITE,
  4149. CmpKeyObjectType,
  4150. mode,
  4151. (PVOID *)(&KeyBody),
  4152. NULL
  4153. );
  4154. if(NT_SUCCESS(status)) {
  4155. BEGIN_LOCK_CHECKPOINT;
  4156. CmpLockRegistryExclusive();
  4157. //
  4158. // no edits, on keys marked for deletion
  4159. //
  4160. if (KeyBody->KeyControlBlock->Delete) {
  4161. status = STATUS_KEY_DELETED;
  4162. } else if( KeyBody->KeyControlBlock->KeyCell != KeyBody->KeyControlBlock->KeyHive->BaseBlock->RootCell ) {
  4163. status = STATUS_INVALID_PARAMETER;
  4164. } else {
  4165. status = CmCompressKey(KeyBody->KeyControlBlock->KeyHive);
  4166. }
  4167. CmpUnlockRegistry();
  4168. END_LOCK_CHECKPOINT;
  4169. ObDereferenceObject((PVOID)KeyBody);
  4170. }
  4171. return status;
  4172. }
  4173. NTSTATUS
  4174. NtLockRegistryKey(
  4175. IN HANDLE KeyHandle
  4176. )
  4177. /*++
  4178. Routine Description:
  4179. Locks the specified registry key for writing
  4180. Arguments:
  4181. KeyHandle - Handle of the key to be locked.
  4182. Return Value:
  4183. NTSTATUS - values TBS.
  4184. --*/
  4185. {
  4186. KPROCESSOR_MODE PreviousMode;
  4187. NTSTATUS status;
  4188. PCM_KEY_BODY KeyBody;
  4189. PAGED_CODE();
  4190. PreviousMode = KeGetPreviousMode();
  4191. if( (PreviousMode != KernelMode) ||
  4192. !SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) {
  4193. return(STATUS_PRIVILEGE_NOT_HELD);
  4194. }
  4195. status = ObReferenceObjectByHandle(
  4196. KeyHandle,
  4197. KEY_WRITE,
  4198. CmpKeyObjectType,
  4199. PreviousMode,
  4200. (PVOID *)(&KeyBody),
  4201. NULL
  4202. );
  4203. if (NT_SUCCESS(status)) {
  4204. //
  4205. // we only need shared access
  4206. //
  4207. BEGIN_LOCK_CHECKPOINT;
  4208. CmpLockRegistry();
  4209. status = CmLockKcbForWrite(KeyBody->KeyControlBlock);
  4210. CmpUnlockRegistry();
  4211. END_LOCK_CHECKPOINT;
  4212. ObDereferenceObject((PVOID)KeyBody);
  4213. }
  4214. return status;
  4215. }