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.

1873 lines
64 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. regutil.c
  5. Abstract:
  6. This file contains support routines for accessing the registry.
  7. Author:
  8. Steve Wood (stevewo) 15-Apr-1992
  9. Revision History:
  10. --*/
  11. #include "ntrtlp.h"
  12. #include <ctype.h>
  13. NTSTATUS
  14. RtlpGetRegistryHandle(
  15. IN ULONG RelativeTo,
  16. IN PCWSTR KeyName,
  17. IN BOOLEAN WriteAccess,
  18. OUT PHANDLE Key
  19. );
  20. NTSTATUS
  21. RtlpQueryRegistryDirect(
  22. IN ULONG ValueType,
  23. IN PVOID ValueData,
  24. IN ULONG ValueLength,
  25. IN OUT PVOID Destination
  26. );
  27. NTSTATUS
  28. RtlpCallQueryRegistryRoutine(
  29. IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
  30. IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
  31. IN OUT PULONG PKeyValueInfoLength,
  32. IN PVOID Context,
  33. IN PVOID Environment OPTIONAL
  34. );
  35. PVOID
  36. RtlpAllocDeallocQueryBuffer(
  37. IN OUT SIZE_T *PAllocLength OPTIONAL,
  38. IN PVOID OldKeyValueInformation OPTIONAL,
  39. IN SIZE_T OldAllocLength OPTIONAL,
  40. OUT NTSTATUS *pStatus OPTIONAL
  41. );
  42. NTSTATUS
  43. RtlpInitCurrentUserString(
  44. OUT PUNICODE_STRING UserString
  45. );
  46. NTSTATUS
  47. RtlpGetTimeZoneInfoHandle(
  48. IN BOOLEAN WriteAccess,
  49. OUT PHANDLE Key
  50. );
  51. #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  52. #pragma alloc_text(PAGE,RtlpGetRegistryHandle)
  53. #pragma alloc_text(PAGE,RtlpQueryRegistryDirect)
  54. #pragma alloc_text(PAGE,RtlpCallQueryRegistryRoutine)
  55. #pragma alloc_text(PAGE,RtlpAllocDeallocQueryBuffer)
  56. #pragma alloc_text(PAGE,RtlQueryRegistryValues)
  57. #pragma alloc_text(PAGE,RtlWriteRegistryValue)
  58. #pragma alloc_text(PAGE,RtlCheckRegistryKey)
  59. #pragma alloc_text(PAGE,RtlCreateRegistryKey)
  60. #pragma alloc_text(PAGE,RtlDeleteRegistryValue)
  61. #pragma alloc_text(PAGE,RtlExpandEnvironmentStrings_U)
  62. #pragma alloc_text(PAGE,RtlFormatCurrentUserKeyPath)
  63. #pragma alloc_text(PAGE,RtlGetNtGlobalFlags)
  64. #pragma alloc_text(PAGE,RtlpInitCurrentUserString)
  65. #pragma alloc_text(PAGE,RtlOpenCurrentUser)
  66. #pragma alloc_text(PAGE,RtlpGetTimeZoneInfoHandle)
  67. #pragma alloc_text(PAGE,RtlQueryTimeZoneInformation)
  68. #pragma alloc_text(PAGE,RtlSetTimeZoneInformation)
  69. #pragma alloc_text(PAGE,RtlSetActiveTimeBias)
  70. #endif
  71. extern const PWSTR RtlpRegistryPaths[ RTL_REGISTRY_MAXIMUM ];
  72. NTSTATUS
  73. RtlpGetRegistryHandle(
  74. IN ULONG RelativeTo,
  75. IN PCWSTR KeyName,
  76. IN BOOLEAN WriteAccess,
  77. OUT PHANDLE Key
  78. )
  79. {
  80. NTSTATUS Status;
  81. OBJECT_ATTRIBUTES ObjectAttributes;
  82. WCHAR KeyPathBuffer[ MAXIMUM_FILENAME_LENGTH+6 ];
  83. UNICODE_STRING KeyPath;
  84. UNICODE_STRING CurrentUserKeyPath;
  85. BOOLEAN OptionalPath;
  86. if (RelativeTo & RTL_REGISTRY_HANDLE) {
  87. *Key = (HANDLE)KeyName;
  88. return STATUS_SUCCESS;
  89. }
  90. if (RelativeTo & RTL_REGISTRY_OPTIONAL) {
  91. RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
  92. OptionalPath = TRUE;
  93. } else {
  94. OptionalPath = FALSE;
  95. }
  96. if (RelativeTo >= RTL_REGISTRY_MAXIMUM) {
  97. return STATUS_INVALID_PARAMETER;
  98. }
  99. KeyPath.Buffer = KeyPathBuffer;
  100. KeyPath.Length = 0;
  101. KeyPath.MaximumLength = sizeof( KeyPathBuffer );
  102. if (RelativeTo != RTL_REGISTRY_ABSOLUTE) {
  103. if (RelativeTo == RTL_REGISTRY_USER &&
  104. NT_SUCCESS( RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath ) )
  105. ) {
  106. Status = RtlAppendUnicodeStringToString( &KeyPath, &CurrentUserKeyPath );
  107. RtlFreeUnicodeString( &CurrentUserKeyPath );
  108. } else {
  109. Status = RtlAppendUnicodeToString( &KeyPath, RtlpRegistryPaths[ RelativeTo ] );
  110. }
  111. if (!NT_SUCCESS( Status )) {
  112. return Status;
  113. }
  114. Status = RtlAppendUnicodeToString( &KeyPath, L"\\" );
  115. if (!NT_SUCCESS( Status )) {
  116. return Status;
  117. }
  118. }
  119. Status = RtlAppendUnicodeToString( &KeyPath, KeyName );
  120. if (!NT_SUCCESS( Status )) {
  121. return Status;
  122. }
  123. //
  124. // Use a kernel-mode handle for the registry key to prevent
  125. // malicious apps from hijacking it.
  126. //
  127. InitializeObjectAttributes( &ObjectAttributes,
  128. &KeyPath,
  129. OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
  130. NULL,
  131. NULL
  132. );
  133. if (WriteAccess) {
  134. Status = ZwCreateKey( Key,
  135. GENERIC_WRITE,
  136. &ObjectAttributes,
  137. 0,
  138. (PUNICODE_STRING) NULL,
  139. 0,
  140. NULL
  141. );
  142. } else {
  143. Status = ZwOpenKey( Key,
  144. MAXIMUM_ALLOWED | GENERIC_READ,
  145. &ObjectAttributes
  146. );
  147. }
  148. return Status;
  149. }
  150. //
  151. // This is the maximum MaximumLength for a UNICODE_STRING.
  152. //
  153. #define MAX_USTRING ( sizeof(WCHAR) * (MAXUSHORT/sizeof(WCHAR)) )
  154. //
  155. // This is the maximum MaximumLength for a UNICODE_STRING that still leaves
  156. // room for a UNICODE_NULL.
  157. //
  158. #define MAX_NONNULL_USTRING ( MAX_USTRING - sizeof(UNICODE_NULL) )
  159. //
  160. // Return a registry value for RTL_QUERY_REGISTRY_DIRECT.
  161. // For string values, ValueLength includes the UNICODE_NULL.
  162. // Truncate string values if they don't fit within a UNICODE_STRING.
  163. //
  164. NTSTATUS
  165. RtlpQueryRegistryDirect(
  166. IN ULONG ValueType,
  167. IN PVOID ValueData,
  168. IN ULONG ValueLength,
  169. IN OUT PVOID Destination
  170. )
  171. {
  172. if (ValueType == REG_SZ ||
  173. ValueType == REG_EXPAND_SZ ||
  174. ValueType == REG_MULTI_SZ
  175. ) {
  176. PUNICODE_STRING DestinationString;
  177. USHORT TruncValueLength;
  178. //
  179. // Truncate ValueLength to be represented in a UNICODE_STRING
  180. //
  181. if ( ValueLength <= MAX_USTRING ) {
  182. TruncValueLength = (USHORT)ValueLength;
  183. } else {
  184. TruncValueLength = MAX_USTRING;
  185. #if DBG
  186. DbgPrint("RtlpQueryRegistryDirect: truncating SZ Value length: %x -> %x\n",
  187. ValueLength, TruncValueLength);
  188. #endif //DBG
  189. }
  190. DestinationString = (PUNICODE_STRING)Destination;
  191. if (DestinationString->Buffer == NULL) {
  192. DestinationString->Buffer = RtlAllocateStringRoutine( TruncValueLength );
  193. if (!DestinationString->Buffer) {
  194. return STATUS_NO_MEMORY;
  195. }
  196. DestinationString->MaximumLength = TruncValueLength;
  197. } else if (TruncValueLength > DestinationString->MaximumLength) {
  198. return STATUS_BUFFER_TOO_SMALL;
  199. }
  200. RtlCopyMemory( DestinationString->Buffer, ValueData, TruncValueLength );
  201. DestinationString->Length = (TruncValueLength - sizeof(UNICODE_NULL));
  202. } else if (ValueLength <= sizeof( ULONG )) {
  203. RtlCopyMemory( Destination, ValueData, ValueLength );
  204. } else {
  205. PULONG DestinationLength;
  206. DestinationLength = (PULONG)Destination;
  207. if ((LONG)*DestinationLength < 0) {
  208. ULONG n = -(LONG)*DestinationLength;
  209. if (n < ValueLength) {
  210. return STATUS_BUFFER_TOO_SMALL;
  211. }
  212. RtlCopyMemory( DestinationLength, ValueData, ValueLength );
  213. } else {
  214. if (*DestinationLength < (2 * sizeof(*DestinationLength) + ValueLength)) {
  215. return STATUS_BUFFER_TOO_SMALL;
  216. }
  217. *DestinationLength++ = ValueLength;
  218. *DestinationLength++ = ValueType;
  219. RtlCopyMemory( DestinationLength, ValueData, ValueLength );
  220. }
  221. }
  222. return STATUS_SUCCESS;
  223. }
  224. #define QuadAlignPtr(P) ( \
  225. (PVOID)((((ULONG_PTR)(P)) + 7) & (-8)) \
  226. )
  227. NTSTATUS
  228. RtlpCallQueryRegistryRoutine(
  229. IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
  230. IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
  231. IN OUT PULONG PKeyValueInfoLength,
  232. IN PVOID Context,
  233. IN PVOID Environment OPTIONAL
  234. )
  235. /*++
  236. Routine Description:
  237. This function implements the caller out the a caller specified
  238. routine. It is reponsible for capturing the arguments for the
  239. routine and then calling it. If not specifically disabled, this
  240. routine will converted REG_EXPAND_SZ Registry values to REG_SZ by
  241. calling RtlExpandEnvironmentStrings_U prior to calling the routine.
  242. It will also converted REG_MULTI_SZ registry values into multiple
  243. REG_SZ calls to the specified routine.
  244. N.B. UNICODE_STRINGs cannot handle strings exceeding MAX_USTRING bytes. This creates
  245. issues both for expansion and for returning queries. Whenever this limitation
  246. is a encountered, we punt as best we can -- often returning an unexpanded, or perhaps
  247. truncated stream -- since this seems to create fewer problems for our callers than
  248. if we unexpectedly fail.
  249. Arguments:
  250. QueryTable - specifies the current query table entry.
  251. KeyValueInformation - points to a buffer that contains the information
  252. about the current registry value.
  253. PKeyValueInfoLength - pointer to the maximum length of the KeyValueInformation
  254. buffer. This function will use the
  255. unused portion at the end of this buffer for storing null terminated
  256. value name strings and the expanded version of REG_EXPAND_SZ values.
  257. PKeyValueInfoLength returns an estimate of the space required if
  258. STATUS_BUFFER_TOO_SMALL is returned. This estimate can be used to retry
  259. with a larger buffer. Two retries may be required if REG_EXPAND_SZ is specified.
  260. Context - specifies a 32-bit quantity that is passed uninterpreted to
  261. each QueryRoutine called.
  262. Environment - optional parameter, that if specified is the environment
  263. used when expanding variable values in REG_EXPAND_SZ registry
  264. values.
  265. Return Value:
  266. Status of the operation.
  267. --*/
  268. {
  269. NTSTATUS Status;
  270. ULONG ValueType;
  271. PWSTR ValueName;
  272. PVOID ValueData;
  273. ULONG ValueLength;
  274. PWSTR s;
  275. PCHAR FreeMem;
  276. PCHAR EndFreeMem;
  277. LONG FreeMemSize;
  278. ULONG KeyValueInfoLength;
  279. int retries;
  280. //
  281. // Return 0 length unless we return STATUS_BUFFER_TOO_SMALL.
  282. //
  283. KeyValueInfoLength = *PKeyValueInfoLength;
  284. *PKeyValueInfoLength = 0;
  285. //
  286. // the registry has signaled no data for this value
  287. //
  288. if( KeyValueInformation->DataOffset == (ULONG)-1 ) {
  289. //
  290. // Return success unless this is a required value.
  291. //
  292. if ( QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED ) {
  293. return STATUS_OBJECT_NAME_NOT_FOUND;
  294. } else {
  295. return STATUS_SUCCESS;
  296. }
  297. }
  298. //
  299. // Initially assume the entire KeyValueInformation buffer is unused.
  300. //
  301. FreeMem = (PCHAR)KeyValueInformation;
  302. FreeMemSize = KeyValueInfoLength;
  303. EndFreeMem = FreeMem + FreeMemSize;
  304. if (KeyValueInformation->Type == REG_NONE ||
  305. (KeyValueInformation->DataLength == 0 &&
  306. KeyValueInformation->Type == QueryTable->DefaultType)
  307. ) {
  308. //
  309. // If there is no registry value then see if they want to default
  310. // this value.
  311. //
  312. if (QueryTable->DefaultType == REG_NONE) {
  313. //
  314. // No default value specified. Return success unless this is
  315. // a required value.
  316. //
  317. if ( QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED ) {
  318. return STATUS_OBJECT_NAME_NOT_FOUND;
  319. } else {
  320. return STATUS_SUCCESS;
  321. }
  322. }
  323. //
  324. // Default requested. Setup the value data pointers from the
  325. // information in the table entry.
  326. //
  327. ValueName = QueryTable->Name,
  328. ValueType = QueryTable->DefaultType;
  329. ValueData = QueryTable->DefaultData;
  330. ValueLength = QueryTable->DefaultLength;
  331. if (ValueLength == 0) {
  332. //
  333. // If the length of the value is zero, then calculate the
  334. // actual length for REG_SZ, REG_EXPAND_SZ and REG_MULTI_SZ
  335. // value types.
  336. //
  337. s = (PWSTR)ValueData;
  338. if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ) {
  339. while (*s++ != UNICODE_NULL) {
  340. }
  341. ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData);
  342. } else if (ValueType == REG_MULTI_SZ) {
  343. while (*s != UNICODE_NULL) {
  344. while (*s++ != UNICODE_NULL) {
  345. }
  346. }
  347. ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData) + sizeof( UNICODE_NULL );
  348. }
  349. }
  350. } else {
  351. if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)) {
  352. LONG ValueSpaceNeeded;
  353. //
  354. // There is a registry value. Calculate a pointer to the
  355. // free memory at the end of the value information buffer,
  356. // and its size.
  357. //
  358. if (KeyValueInformation->DataLength) {
  359. FreeMem += KeyValueInformation->DataOffset +
  360. KeyValueInformation->DataLength;
  361. } else {
  362. FreeMem += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
  363. KeyValueInformation->NameLength;
  364. }
  365. FreeMem = (PCHAR)QuadAlignPtr(FreeMem);
  366. FreeMemSize = (ULONG) (EndFreeMem - FreeMem);
  367. //
  368. // See if there is room in the free memory area for a null
  369. // terminated copy of the value name string. If not return
  370. // the length we require (so far) and an error.
  371. //
  372. ValueSpaceNeeded = KeyValueInformation->NameLength + sizeof(UNICODE_NULL);
  373. if ( FreeMemSize < ValueSpaceNeeded ) {
  374. *PKeyValueInfoLength = (ULONG)(((PCHAR)FreeMem - (PCHAR)KeyValueInformation) + ValueSpaceNeeded);
  375. return STATUS_BUFFER_TOO_SMALL;
  376. }
  377. //
  378. // There is room, so copy the string, and null terminate it.
  379. //
  380. ValueName = (PWSTR)FreeMem;
  381. RtlCopyMemory( ValueName,
  382. KeyValueInformation->Name,
  383. KeyValueInformation->NameLength
  384. );
  385. *(PWSTR)((PCHAR)ValueName + KeyValueInformation->NameLength) = UNICODE_NULL;
  386. //
  387. // Update the free memory pointer and size to reflect the space we
  388. // just used for the null terminated value name.
  389. //
  390. FreeMem += ValueSpaceNeeded;
  391. FreeMem = (PCHAR)QuadAlignPtr(FreeMem);
  392. FreeMemSize = (LONG) (EndFreeMem - FreeMem);
  393. } else {
  394. ValueName = QueryTable->Name;
  395. }
  396. //
  397. // Get the remaining data for the registry value.
  398. //
  399. ValueType = KeyValueInformation->Type;
  400. ValueData = (PCHAR)KeyValueInformation + KeyValueInformation->DataOffset;
  401. ValueLength = KeyValueInformation->DataLength;
  402. }
  403. //
  404. // Unless specifically disabled for this table entry, preprocess
  405. // registry values of type REG_EXPAND_SZ and REG_MULTI_SZ
  406. //
  407. if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND)) {
  408. if (ValueType == REG_MULTI_SZ) {
  409. PWSTR ValueEnd;
  410. //
  411. // For REG_MULTI_SZ value type, call the query routine once
  412. // for each null terminated string in the registry value. Fake
  413. // like this is multiple REG_SZ values with the same value name.
  414. //
  415. Status = STATUS_SUCCESS;
  416. ValueEnd = (PWSTR)((PCHAR)ValueData + ValueLength) - sizeof(UNICODE_NULL);
  417. s = (PWSTR)ValueData;
  418. while (s < ValueEnd) {
  419. while (*s++ != UNICODE_NULL) {
  420. }
  421. ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData);
  422. if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
  423. Status = RtlpQueryRegistryDirect( REG_SZ,
  424. ValueData,
  425. ValueLength,
  426. QueryTable->EntryContext
  427. );
  428. (PUNICODE_STRING)(QueryTable->EntryContext) += 1;
  429. } else {
  430. Status = (QueryTable->QueryRoutine)( ValueName,
  431. REG_SZ,
  432. ValueData,
  433. ValueLength,
  434. Context,
  435. QueryTable->EntryContext
  436. );
  437. }
  438. //
  439. // We ignore failures where the buffer is too small.
  440. //
  441. if (Status == STATUS_BUFFER_TOO_SMALL) {
  442. Status = STATUS_SUCCESS;
  443. }
  444. if (!NT_SUCCESS( Status )) {
  445. break;
  446. }
  447. ValueData = (PVOID)s;
  448. }
  449. return Status;
  450. }
  451. //
  452. // If requested, expand the Value -- but only if the unexpanded value
  453. // can be represented with a UNICODE_STRING.
  454. //
  455. if ((ValueType == REG_EXPAND_SZ) &&
  456. (ValueLength >= sizeof(WCHAR)) &&
  457. (ValueLength <= MAX_NONNULL_USTRING)) {
  458. //
  459. // For REG_EXPAND_SZ value type, expand any environment variable
  460. // references in the registry value string using the Rtl function.
  461. //
  462. UNICODE_STRING Source;
  463. UNICODE_STRING Destination;
  464. PWCHAR Src;
  465. ULONG SrcLength;
  466. ULONG RequiredLength;
  467. BOOLEAN PercentFound;
  468. //
  469. // Don't expand unless we have to since expansion doubles buffer usage.
  470. //
  471. PercentFound = FALSE;
  472. SrcLength = ValueLength - sizeof(WCHAR);
  473. Src = (PWSTR)ValueData;
  474. while (SrcLength) {
  475. if (*Src == L'%') {
  476. PercentFound = TRUE;
  477. break;
  478. }
  479. Src++;
  480. SrcLength -= sizeof(WCHAR);
  481. }
  482. if ( PercentFound ) {
  483. Source.Buffer = (PWSTR)ValueData;
  484. Source.MaximumLength = (USHORT)ValueLength;
  485. Source.Length = (USHORT)(Source.MaximumLength - sizeof(UNICODE_NULL));
  486. Destination.Buffer = (PWSTR)FreeMem;
  487. Destination.Length = 0;
  488. if (FreeMemSize <= 0) {
  489. Destination.MaximumLength = 0;
  490. } else if (FreeMemSize <= MAX_USTRING) {
  491. Destination.MaximumLength = (USHORT)FreeMemSize;
  492. Destination.Buffer[FreeMemSize/sizeof(WCHAR) - 1] = UNICODE_NULL;
  493. } else {
  494. Destination.MaximumLength = MAX_USTRING;
  495. Destination.Buffer[MAX_USTRING/sizeof(WCHAR) - 1] = UNICODE_NULL;
  496. }
  497. Status = RtlExpandEnvironmentStrings_U( Environment,
  498. &Source,
  499. &Destination,
  500. &RequiredLength
  501. );
  502. ValueType = REG_SZ;
  503. if ( NT_SUCCESS(Status) ) {
  504. ValueData = Destination.Buffer;
  505. ValueLength = Destination.Length + sizeof( UNICODE_NULL );
  506. } else {
  507. if (Status == STATUS_BUFFER_TOO_SMALL) {
  508. *PKeyValueInfoLength = (ULONG)((PCHAR)FreeMem - (PCHAR)KeyValueInformation) + RequiredLength;
  509. }
  510. //#if DBG
  511. if (Status == STATUS_BUFFER_TOO_SMALL) {
  512. DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx Size %x > %x <%x>\n",
  513. &Source, Status, *PKeyValueInfoLength, KeyValueInfoLength,
  514. Destination.MaximumLength );
  515. } else {
  516. DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx\n", &Source, Status );
  517. }
  518. //#endif // DBG
  519. if ( Status == STATUS_BUFFER_OVERFLOW ||
  520. Status == STATUS_BUFFER_TOO_SMALL &&
  521. ( Destination.MaximumLength == MAX_USTRING
  522. || RequiredLength > MAX_NONNULL_USTRING ) ) {
  523. // We can't do variable expansion because the required buffer can't be described
  524. // by a UNICODE_STRING, so we silently ignore expansion.
  525. //#if DBG
  526. DbgPrint("RtlpCallQueryRegistryRoutine: skipping expansion. Status=%x RequiredLength=%x\n",
  527. Status, RequiredLength);
  528. //#endif //DBG
  529. } else {
  530. return Status;
  531. }
  532. }
  533. }
  534. }
  535. //#if DBG
  536. else if (ValueType == REG_EXPAND_SZ && ValueLength > MAX_NONNULL_USTRING) {
  537. DbgPrint("RtlpCallQueryRegistryRoutine: skipping environment expansion. ValueLength=%x\n",
  538. ValueLength);
  539. }
  540. //#endif //DBG
  541. }
  542. //
  543. // No special process of the registry value required so just call
  544. // the query routine.
  545. //
  546. if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
  547. Status = RtlpQueryRegistryDirect( ValueType,
  548. ValueData,
  549. ValueLength,
  550. QueryTable->EntryContext
  551. );
  552. } else {
  553. Status = (QueryTable->QueryRoutine)( ValueName,
  554. ValueType,
  555. ValueData,
  556. ValueLength,
  557. Context,
  558. QueryTable->EntryContext
  559. );
  560. }
  561. //
  562. // At this point we fail silently if the buffer is too small.
  563. //
  564. if (Status == STATUS_BUFFER_TOO_SMALL) {
  565. Status = STATUS_SUCCESS;
  566. }
  567. return Status;
  568. }
  569. //
  570. // Most of the registry queries in the kernel are small (40-50 bytes).
  571. // User queries use ZwAllocateVirtualMemory, so nothing less than a page will do.
  572. //
  573. #ifdef NTOS_KERNEL_RUNTIME
  574. #if defined(ALLOC_DATA_PRAGMA)
  575. #pragma const_seg("PAGECONST")
  576. #endif
  577. const SIZE_T RtlpRegistryQueryInitialBuffersize = 0x80 + sizeof(PVOID);
  578. #else
  579. const SIZE_T RtlpRegistryQueryInitialBuffersize = PAGE_SIZE;
  580. #endif
  581. //
  582. // Allocate, Free, or Free/Allocate space for registry queries.
  583. //
  584. PVOID
  585. RtlpAllocDeallocQueryBuffer(
  586. IN OUT SIZE_T *PAllocLength OPTIONAL,
  587. IN PVOID OldKeyValueInformation OPTIONAL,
  588. IN SIZE_T OldAllocLength OPTIONAL,
  589. OUT NTSTATUS *pStatus OPTIONAL
  590. )
  591. {
  592. PVOID Ptr = NULL;
  593. NTSTATUS Status = STATUS_SUCCESS;
  594. #ifdef NTOS_KERNEL_RUNTIME
  595. //
  596. // Kernel version
  597. //
  598. UNREFERENCED_PARAMETER( OldAllocLength );
  599. if ( ARGUMENT_PRESENT(OldKeyValueInformation) ) {
  600. ExFreePool( OldKeyValueInformation );
  601. }
  602. if ( ARGUMENT_PRESENT(PAllocLength) ) {
  603. Ptr = ExAllocatePoolWithTag( PagedPool, *PAllocLength, 'vrqR' );
  604. if (Ptr == NULL) {
  605. Status = STATUS_NO_MEMORY;
  606. }
  607. }
  608. #else
  609. //
  610. // User version
  611. //
  612. if ( ARGUMENT_PRESENT(OldKeyValueInformation) ) {
  613. Status = ZwFreeVirtualMemory( NtCurrentProcess(),
  614. &OldKeyValueInformation,
  615. &OldAllocLength,
  616. MEM_RELEASE );
  617. }
  618. if ( ARGUMENT_PRESENT(PAllocLength) ) {
  619. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  620. &Ptr,
  621. 0,
  622. PAllocLength,
  623. MEM_COMMIT,
  624. PAGE_READWRITE );
  625. if (!NT_SUCCESS(Status)) {
  626. Ptr = NULL;
  627. }
  628. }
  629. #endif
  630. if ( ARGUMENT_PRESENT(pStatus) ) {
  631. *pStatus = Status;
  632. }
  633. return Ptr;
  634. }
  635. NTSTATUS
  636. RtlQueryRegistryValues(
  637. IN ULONG RelativeTo,
  638. IN PCWSTR Path,
  639. IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
  640. IN PVOID Context,
  641. IN PVOID Environment OPTIONAL
  642. )
  643. /*++
  644. Routine Description:
  645. This function allows the caller to query multiple values from the registry
  646. sub-tree with a single call. The caller specifies an initial key path,
  647. and a table. The table contains one or more entries that describe the
  648. key values and subkey names the caller is interested in. This function
  649. starts at the initial key and enumerates the entries in the table. For
  650. each entry that specifies a value name or subkey name that exists in
  651. the registry, this function calls the caller's query routine associated
  652. with each table entry. The caller's query routine is passed the value
  653. name, type, data and data length, to do with what they wish.
  654. Arguments:
  655. RelativeTo - specifies that the Path parameter is either an absolute
  656. registry path, or a path relative to a predefined key path. The
  657. following values are defined:
  658. RTL_REGISTRY_ABSOLUTE - Path is an absolute registry path
  659. RTL_REGISTRY_SERVICES - Path is relative to \Registry\Machine\System\CurrentControlSet\Services
  660. RTL_REGISTRY_CONTROL - Path is relative to \Registry\Machine\System\CurrentControlSet\Control
  661. RTL_REGISTRY_WINDOWS_NT - Path is relative to \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
  662. RTL_REGISTRY_DEVICEMAP - Path is relative to \Registry\Machine\Hardware\DeviceMap
  663. RTL_REGISTRY_USER - Path is relative to \Registry\User\CurrentUser
  664. RTL_REGISTRY_OPTIONAL - Bit that specifies the key referenced by
  665. this parameter and the Path parameter is
  666. optional.
  667. RTL_REGISTRY_HANDLE - Bit that specifies that the Path parameter
  668. is actually a registry handle to use.
  669. optional.
  670. Path - specifies either an absolute registry path, or a path relative to the
  671. known location specified by the RelativeTo parameter. If the the
  672. RTL_REGISTRY_HANDLE flag is specified, then this parameter is a
  673. registry handle to use directly.
  674. QueryTable - specifies a table of one or more value names and subkey names
  675. that the caller is interested. Each table entry contains a query routine
  676. that will be called for each value name that exists in the registry.
  677. The table is terminated when a NULL table entry is reached. A NULL
  678. table entry is defined as a table entry with a NULL QueryRoutine
  679. and a NULL Name field.
  680. QueryTable entry fields:
  681. PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine - This routine is
  682. called with the name, type, data and data length of a
  683. registry value. If this field is NULL, then it marks the
  684. end of the table.
  685. ULONG Flags - These flags control how the following fields are
  686. interpreted. The following flags are defined:
  687. RTL_QUERY_REGISTRY_SUBKEY - says the Name field of this
  688. table entry is another path to a registry key and all
  689. following table entries are for that key rather than the
  690. key specified by the Path parameter. This change in
  691. focus lasts until the end of the table or another
  692. RTL_QUERY_REGISTRY_SUBKEY entry is seen or
  693. RTL_QUERY_REGISTRY_TOPKEY entry is seen. Each such
  694. entry must specify a path that is relative to the Path
  695. specified on the call to this function.
  696. RTL_QUERY_REGISTRY_TOPKEY - resets the current registry key
  697. handle to the original one specified by the RelativeTo
  698. and Path parameters. Useful for getting back to the
  699. original node after descending into subkeys with the
  700. RTL_QUERY_REGISTRY_SUBKEY flag.
  701. RTL_QUERY_REGISTRY_REQUIRED - specifies that this value is
  702. required and if not found then STATUS_OBJECT_NAME_NOT_FOUND
  703. is returned. For a table entry that specifies a NULL
  704. name so that this function will enumerate all of the
  705. value names under a key, STATUS_OBJECT_NAME_NOT_FOUND
  706. will be returned only if there are no value keys under
  707. the current key.
  708. RTL_QUERY_REGISTRY_NOVALUE - specifies that even though
  709. there is no Name field for this table entry, all the
  710. caller wants is a call back, it does NOT want to
  711. enumerate all the values under the current key. The
  712. query routine is called with NULL for ValueData,
  713. REG_NONE for ValueType and zero for ValueLength.
  714. RTL_QUERY_REGISTRY_NOEXPAND - specifies that if the value
  715. type of this registry value is REG_EXPAND_SZ or
  716. REG_MULTI_SZ, then this function is NOT to do any
  717. preprocessing of the registry values prior to calling
  718. the query routine. Default behavior is to expand
  719. environment variable references in REG_EXPAND_SZ
  720. values and to enumerate the NULL terminated strings
  721. in a REG_MULTI_SZ value and call the query routine
  722. once for each, making it look like multiple REG_SZ
  723. values with the same ValueName.
  724. RTL_QUERY_REGISTRY_DIRECT QueryRoutine field ignored.
  725. EntryContext field points to location to store value.
  726. For null terminated strings, EntryContext points to
  727. UNICODE_STRING structure that that describes maximum
  728. size of buffer. If .Buffer field is NULL then a buffer
  729. is allocated.
  730. RTL_QUERY_REGISTRY_DELETE Used to delete value keys after
  731. they are queried.
  732. PWSTR Name - This field gives the name of a Value the caller
  733. wants to query the value of. If this field is NULL, then
  734. the QueryRoutine specified for this table entry is called
  735. for all values associated with the current registry key.
  736. PVOID EntryContext - This field is an arbitrary 32-bit field
  737. that is passed uninterpreted to each QueryRoutine called.
  738. ULONG DefaultType
  739. PVOID DefaultData
  740. ULONG DefaultLength If there is no value name that matches the
  741. name given by the Name field, and the DefaultType field is
  742. not REG_NONE, then the QueryRoutine for this table entry is
  743. called with the contents of the following fields as if the
  744. value had been found in the registry. If the DefaultType is
  745. REG_SZ, REG_EXPANDSZ or REG_MULTI_SZ and the DefaultLength
  746. is 0 then the value of DefaultLength will be computed based
  747. on the length of unicode string pointed to by DefaultData
  748. Context - specifies a 32-bit quantity that is passed uninterpreted to
  749. each QueryRoutine called.
  750. Environment - optional parameter, that if specified is the environment
  751. used when expanding variable values in REG_EXPAND_SZ registry
  752. values.
  753. Return Value:
  754. Status of the operation.
  755. --*/
  756. {
  757. NTSTATUS Status;
  758. OBJECT_ATTRIBUTES ObjectAttributes;
  759. UNICODE_STRING KeyPath, KeyValueName;
  760. HANDLE Key, Key1;
  761. PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
  762. SIZE_T KeyValueInfoLength;
  763. ULONG ValueIndex;
  764. SIZE_T AllocLength;
  765. ULONG KeyResultLength;
  766. int retries;
  767. RTL_PAGED_CODE();
  768. KeyValueInformation = NULL;
  769. Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
  770. if (!NT_SUCCESS( Status )) {
  771. return Status;
  772. }
  773. if ((RelativeTo & RTL_REGISTRY_HANDLE) == 0) {
  774. RtlInitUnicodeString(&KeyPath, Path);
  775. } else {
  776. RtlInitUnicodeString(&KeyPath, NULL);
  777. }
  778. AllocLength = RtlpRegistryQueryInitialBuffersize;
  779. KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength, NULL, 0, &Status );
  780. if ( KeyValueInformation == NULL ) {
  781. if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
  782. ZwClose( Key );
  783. }
  784. return Status;
  785. }
  786. KeyValueInformation->DataOffset = 0;
  787. KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
  788. Key1 = Key;
  789. while (QueryTable->QueryRoutine != NULL ||
  790. (QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_DIRECT))
  791. ) {
  792. if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) &&
  793. (QueryTable->Name == NULL ||
  794. (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) ||
  795. QueryTable->QueryRoutine != NULL)
  796. ) {
  797. Status = STATUS_INVALID_PARAMETER;
  798. break;
  799. }
  800. if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY | RTL_QUERY_REGISTRY_SUBKEY)) {
  801. if (Key1 != Key) {
  802. NtClose( Key1 );
  803. Key1 = Key;
  804. }
  805. }
  806. if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) {
  807. if (QueryTable->Name == NULL) {
  808. Status = STATUS_INVALID_PARAMETER;
  809. } else {
  810. RtlInitUnicodeString( &KeyPath, QueryTable->Name );
  811. //
  812. // Use a kernel-mode handle for the registry key to prevent
  813. // malicious apps from hijacking it.
  814. //
  815. InitializeObjectAttributes( &ObjectAttributes,
  816. &KeyPath,
  817. OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
  818. Key,
  819. NULL
  820. );
  821. Status = ZwOpenKey( &Key1,
  822. MAXIMUM_ALLOWED,
  823. &ObjectAttributes
  824. );
  825. if (NT_SUCCESS( Status )) {
  826. if (QueryTable->QueryRoutine != NULL) {
  827. goto enumvalues;
  828. }
  829. }
  830. }
  831. } else if (QueryTable->Name != NULL) {
  832. RtlInitUnicodeString( &KeyValueName, QueryTable->Name );
  833. retries = 0;
  834. retryqueryvalue:
  835. //
  836. // A maximum of two retries is expected. If we see more we must
  837. // have miscomputed how much is required for the query buffer.
  838. //
  839. if (retries++ > 4) {
  840. //#if DBG
  841. DbgPrint("RtlQueryRegistryValues: Miscomputed buffer size at line %d\n", __LINE__);
  842. //#endif
  843. break;
  844. }
  845. Status = ZwQueryValueKey( Key1,
  846. &KeyValueName,
  847. KeyValueFullInformation,
  848. KeyValueInformation,
  849. (ULONG) KeyValueInfoLength,
  850. &KeyResultLength
  851. );
  852. //
  853. // ZwQueryValueKey returns overflow even though the problem is that
  854. // the specified buffer was too small, so we fix that up here so we
  855. // can decide correctly whether to retry or not below.
  856. //
  857. if (Status == STATUS_BUFFER_OVERFLOW) {
  858. Status = STATUS_BUFFER_TOO_SMALL;
  859. }
  860. if (!NT_SUCCESS( Status )) {
  861. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  862. KeyValueInformation->Type = REG_NONE;
  863. KeyValueInformation->DataLength = 0;
  864. KeyResultLength = (ULONG)KeyValueInfoLength;
  865. Status = RtlpCallQueryRegistryRoutine( QueryTable,
  866. KeyValueInformation,
  867. &KeyResultLength,
  868. Context,
  869. Environment
  870. );
  871. }
  872. if (Status == STATUS_BUFFER_TOO_SMALL) {
  873. //
  874. // Try to allocate a larger buffer as this is one humongous
  875. // value.
  876. //
  877. AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
  878. KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
  879. KeyValueInformation,
  880. AllocLength,
  881. &Status
  882. );
  883. if ( KeyValueInformation == NULL) {
  884. break;
  885. }
  886. KeyValueInformation->DataOffset = 0;
  887. KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
  888. goto retryqueryvalue;
  889. }
  890. } else {
  891. //
  892. // KeyResultLength holds the length of the data returned by ZwQueryKeyValue.
  893. // If this is a MULTI_SZ value, catenate a NUL.
  894. //
  895. if ( KeyValueInformation->Type == REG_MULTI_SZ ) {
  896. *(PWCHAR) ((PUCHAR)KeyValueInformation + KeyResultLength) = UNICODE_NULL;
  897. KeyValueInformation->DataLength += sizeof(UNICODE_NULL);
  898. }
  899. KeyResultLength = (ULONG)KeyValueInfoLength;
  900. Status = RtlpCallQueryRegistryRoutine( QueryTable,
  901. KeyValueInformation,
  902. &KeyResultLength,
  903. Context,
  904. Environment
  905. );
  906. if ( Status == STATUS_BUFFER_TOO_SMALL ) {
  907. //
  908. // Try to allocate a larger buffer as this is one humongous
  909. // value.
  910. //
  911. AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
  912. KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
  913. KeyValueInformation,
  914. AllocLength,
  915. &Status
  916. );
  917. if ( KeyValueInformation == NULL) {
  918. break;
  919. }
  920. KeyValueInformation->DataOffset = 0;
  921. KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
  922. goto retryqueryvalue;
  923. }
  924. //
  925. // If requested, delete the value key after it has been successfully queried.
  926. //
  927. if (NT_SUCCESS( Status ) && QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
  928. ZwDeleteValueKey (Key1, &KeyValueName);
  929. }
  930. }
  931. } else if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE) {
  932. Status = (QueryTable->QueryRoutine)( NULL,
  933. REG_NONE,
  934. NULL,
  935. 0,
  936. Context,
  937. QueryTable->EntryContext
  938. );
  939. } else {
  940. enumvalues:
  941. retries = 0;
  942. for (ValueIndex = 0; TRUE; ValueIndex++) {
  943. Status = ZwEnumerateValueKey( Key1,
  944. ValueIndex,
  945. KeyValueFullInformation,
  946. KeyValueInformation,
  947. (ULONG) KeyValueInfoLength,
  948. &KeyResultLength
  949. );
  950. //
  951. // ZwEnumerateValueKey returns overflow even though the problem is that
  952. // the specified buffer was too small, so we fix that up here so we
  953. // can decide correctly whether to retry or not below.
  954. //
  955. if (Status == STATUS_BUFFER_OVERFLOW) {
  956. Status = STATUS_BUFFER_TOO_SMALL;
  957. }
  958. if (Status == STATUS_NO_MORE_ENTRIES) {
  959. if (ValueIndex == 0 && (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)) {
  960. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  961. } else {
  962. Status = STATUS_SUCCESS;
  963. }
  964. break;
  965. }
  966. if ( NT_SUCCESS( Status ) ) {
  967. KeyResultLength = (ULONG)KeyValueInfoLength;
  968. Status = RtlpCallQueryRegistryRoutine( QueryTable,
  969. KeyValueInformation,
  970. &KeyResultLength,
  971. Context,
  972. Environment
  973. );
  974. }
  975. if (Status == STATUS_BUFFER_TOO_SMALL) {
  976. //
  977. // Allocate a larger buffer and try again.
  978. //
  979. AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
  980. KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
  981. KeyValueInformation,
  982. AllocLength,
  983. &Status
  984. );
  985. if (KeyValueInformation == NULL) {
  986. break;
  987. }
  988. KeyValueInformation->DataOffset = 0;
  989. KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
  990. ValueIndex -= 1;
  991. //
  992. // A maximum of two retries is expected per loop iteration.
  993. // If we see more we must have miscomputed
  994. // how much is required for the query buffer.
  995. //
  996. if (retries++ <= 4) {
  997. continue;
  998. }
  999. //#if DBG
  1000. DbgPrint("RtlQueryRegistryValues: Miscomputed buffer size at line %d\n", __LINE__);
  1001. //#endif
  1002. break;
  1003. }
  1004. if (!NT_SUCCESS( Status )) {
  1005. break;
  1006. }
  1007. retries = 0;
  1008. //
  1009. // If requested, delete the value key after it has been successfully queried.
  1010. // After deletion the current ValueIndex is for the next sub-key, so adjust it.
  1011. // KeyValueInformation->NameLength should fit in a USHORT, but we don't check since
  1012. // it only harms our caller.
  1013. //
  1014. if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
  1015. KeyValueName.Buffer = KeyValueInformation->Name;
  1016. KeyValueName.Length = (USHORT)KeyValueInformation->NameLength;
  1017. KeyValueName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
  1018. Status = ZwDeleteValueKey( Key1,
  1019. &KeyValueName
  1020. );
  1021. if (NT_SUCCESS( Status )) {
  1022. ValueIndex -= 1;
  1023. }
  1024. }
  1025. }
  1026. }
  1027. if (!NT_SUCCESS( Status )) {
  1028. break;
  1029. }
  1030. QueryTable++;
  1031. }
  1032. if (Key != NULL && !(RelativeTo & RTL_REGISTRY_HANDLE)) {
  1033. ZwClose( Key );
  1034. }
  1035. if (Key1 != NULL && Key1 != Key) {
  1036. ZwClose( Key1 );
  1037. }
  1038. //
  1039. // Free any query buffer we allocated.
  1040. //
  1041. (void) RtlpAllocDeallocQueryBuffer( NULL, KeyValueInformation, AllocLength, NULL );
  1042. return Status;
  1043. }
  1044. NTSTATUS
  1045. RtlWriteRegistryValue(
  1046. IN ULONG RelativeTo,
  1047. IN PCWSTR Path,
  1048. IN PCWSTR ValueName,
  1049. IN ULONG ValueType,
  1050. IN PVOID ValueData,
  1051. IN ULONG ValueLength
  1052. )
  1053. {
  1054. NTSTATUS Status;
  1055. UNICODE_STRING KeyValueName;
  1056. HANDLE Key;
  1057. RTL_PAGED_CODE();
  1058. Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
  1059. if (!NT_SUCCESS( Status )) {
  1060. return Status;
  1061. }
  1062. RtlInitUnicodeString( &KeyValueName, ValueName );
  1063. Status = ZwSetValueKey( Key,
  1064. &KeyValueName,
  1065. 0,
  1066. ValueType,
  1067. ValueData,
  1068. ValueLength
  1069. );
  1070. if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
  1071. ZwClose( Key );
  1072. }
  1073. return Status;
  1074. }
  1075. NTSTATUS
  1076. RtlCheckRegistryKey(
  1077. IN ULONG RelativeTo,
  1078. IN PWSTR Path
  1079. )
  1080. {
  1081. NTSTATUS Status;
  1082. HANDLE Key;
  1083. RTL_PAGED_CODE();
  1084. Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
  1085. if (!NT_SUCCESS( Status )) {
  1086. return Status;
  1087. }
  1088. ZwClose( Key );
  1089. return STATUS_SUCCESS;
  1090. }
  1091. NTSTATUS
  1092. RtlCreateRegistryKey(
  1093. IN ULONG RelativeTo,
  1094. IN PWSTR Path
  1095. )
  1096. {
  1097. NTSTATUS Status;
  1098. HANDLE Key;
  1099. RTL_PAGED_CODE();
  1100. Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
  1101. if (!NT_SUCCESS( Status )) {
  1102. return Status;
  1103. }
  1104. if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
  1105. ZwClose( Key );
  1106. }
  1107. return STATUS_SUCCESS;
  1108. }
  1109. NTSTATUS
  1110. RtlDeleteRegistryValue(
  1111. IN ULONG RelativeTo,
  1112. IN PCWSTR Path,
  1113. IN PCWSTR ValueName
  1114. )
  1115. {
  1116. NTSTATUS Status;
  1117. UNICODE_STRING KeyValueName;
  1118. HANDLE Key;
  1119. RTL_PAGED_CODE();
  1120. Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
  1121. if (!NT_SUCCESS( Status )) {
  1122. return Status;
  1123. }
  1124. RtlInitUnicodeString( &KeyValueName, ValueName );
  1125. Status = ZwDeleteValueKey( Key, &KeyValueName );
  1126. if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
  1127. ZwClose( Key );
  1128. }
  1129. return Status;
  1130. }
  1131. NTSTATUS
  1132. RtlExpandEnvironmentStrings_U(
  1133. IN PVOID Environment OPTIONAL,
  1134. IN PUNICODE_STRING Source,
  1135. OUT PUNICODE_STRING Destination,
  1136. OUT PULONG ReturnedLength OPTIONAL
  1137. )
  1138. {
  1139. NTSTATUS Status, Status1;
  1140. PWCHAR Src, Src1, Dst;
  1141. UNICODE_STRING VariableName, VariableValue;
  1142. ULONG SrcLength, DstLength, VarLength, RequiredLength;
  1143. RTL_PAGED_CODE();
  1144. Src = Source->Buffer;
  1145. SrcLength = Source->Length;
  1146. Dst = Destination->Buffer;
  1147. DstLength = Destination->MaximumLength;
  1148. Status = STATUS_SUCCESS;
  1149. RequiredLength = 0;
  1150. while (SrcLength >= sizeof(WCHAR)) {
  1151. if (*Src == L'%') {
  1152. Src1 = Src + 1;
  1153. VarLength = 0;
  1154. VariableName.Length = 0;
  1155. VariableName.Buffer = Src1;
  1156. while (VarLength < (SrcLength - sizeof(WCHAR))) {
  1157. if (*Src1 == L'%') {
  1158. if (VarLength) {
  1159. VariableName.Length = (USHORT)VarLength;
  1160. VariableName.MaximumLength = (USHORT)VarLength;
  1161. }
  1162. break;
  1163. }
  1164. Src1++;
  1165. VarLength += sizeof(WCHAR);
  1166. }
  1167. if (VariableName.Length) {
  1168. VariableValue.Buffer = Dst;
  1169. VariableValue.Length = 0;
  1170. VariableValue.MaximumLength = (USHORT)DstLength;
  1171. Status1 = RtlQueryEnvironmentVariable_U( Environment,
  1172. &VariableName,
  1173. &VariableValue
  1174. );
  1175. if (NT_SUCCESS( Status1 ) || Status1 == STATUS_BUFFER_TOO_SMALL) {
  1176. RequiredLength += VariableValue.Length;
  1177. Src = Src1 + 1;
  1178. SrcLength -= (VarLength + 2*sizeof(WCHAR));
  1179. if (NT_SUCCESS( Status1 )) {
  1180. DstLength -= VariableValue.Length;
  1181. Dst += VariableValue.Length / sizeof(WCHAR);
  1182. } else {
  1183. Status = Status1;
  1184. }
  1185. continue;
  1186. }
  1187. }
  1188. }
  1189. if (NT_SUCCESS( Status )) {
  1190. if (DstLength > sizeof(WCHAR)) {
  1191. DstLength -= sizeof(WCHAR);
  1192. *Dst++ = *Src;
  1193. } else {
  1194. Status = STATUS_BUFFER_TOO_SMALL;
  1195. }
  1196. }
  1197. RequiredLength += sizeof(WCHAR);
  1198. SrcLength -= sizeof(WCHAR);
  1199. Src++;
  1200. }
  1201. if (NT_SUCCESS( Status )) {
  1202. if (DstLength) {
  1203. DstLength -= sizeof(WCHAR);
  1204. *Dst = UNICODE_NULL;
  1205. } else {
  1206. Status = STATUS_BUFFER_TOO_SMALL;
  1207. }
  1208. }
  1209. RequiredLength += sizeof(WCHAR);
  1210. if (ARGUMENT_PRESENT( ReturnedLength )) {
  1211. *ReturnedLength = RequiredLength;
  1212. }
  1213. if (NT_SUCCESS( Status )) {
  1214. Destination->Length = (USHORT)(RequiredLength - sizeof(WCHAR));
  1215. }
  1216. return Status;
  1217. }
  1218. ULONG
  1219. RtlGetNtGlobalFlags( VOID )
  1220. {
  1221. #ifdef NTOS_KERNEL_RUNTIME
  1222. return NtGlobalFlag;
  1223. #else
  1224. return NtCurrentPeb()->NtGlobalFlag;
  1225. #endif
  1226. }
  1227. //
  1228. // Maximum size of TOKEN_USER information.
  1229. //
  1230. #define SIZE_OF_TOKEN_INFORMATION \
  1231. sizeof( TOKEN_USER ) \
  1232. + sizeof( SID ) \
  1233. + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES
  1234. NTSTATUS
  1235. RtlFormatCurrentUserKeyPath(
  1236. OUT PUNICODE_STRING CurrentUserKeyPath
  1237. )
  1238. /*++
  1239. Routine Description:
  1240. Initialize the supplied buffer with a string representation
  1241. of the current user's SID.
  1242. Arguments:
  1243. CurrentUserKeyPath - Returns a string that represents the current
  1244. user's root key in the Registry. Caller must call
  1245. RtlFreeUnicodeString to free the buffer when done with it.
  1246. Return Value:
  1247. NTSTATUS - Returns STATUS_SUCCESS if the user string was
  1248. succesfully initialized.
  1249. --*/
  1250. {
  1251. HANDLE TokenHandle;
  1252. UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
  1253. ULONG ReturnLength;
  1254. ULONG SidStringLength ;
  1255. UNICODE_STRING SidString ;
  1256. NTSTATUS Status;
  1257. Status = ZwOpenThreadTokenEx( NtCurrentThread(),
  1258. TOKEN_READ,
  1259. TRUE,
  1260. OBJ_KERNEL_HANDLE,
  1261. &TokenHandle
  1262. );
  1263. if ( !NT_SUCCESS( Status ) && ( Status != STATUS_NO_TOKEN ) ) {
  1264. return Status;
  1265. }
  1266. if ( !NT_SUCCESS( Status ) ) {
  1267. Status = ZwOpenProcessTokenEx( NtCurrentProcess(),
  1268. TOKEN_READ,
  1269. OBJ_KERNEL_HANDLE,
  1270. &TokenHandle
  1271. );
  1272. if ( !NT_SUCCESS( Status )) {
  1273. return Status;
  1274. }
  1275. }
  1276. Status = ZwQueryInformationToken( TokenHandle,
  1277. TokenUser,
  1278. TokenInformation,
  1279. sizeof( TokenInformation ),
  1280. &ReturnLength
  1281. );
  1282. ZwClose( TokenHandle );
  1283. if ( !NT_SUCCESS( Status )) {
  1284. return Status;
  1285. }
  1286. Status = RtlLengthSidAsUnicodeString(
  1287. ((PTOKEN_USER)TokenInformation)->User.Sid,
  1288. &SidStringLength
  1289. );
  1290. if ( !NT_SUCCESS( Status ) ) {
  1291. return Status ;
  1292. }
  1293. CurrentUserKeyPath->Length = 0;
  1294. CurrentUserKeyPath->MaximumLength = (USHORT)(SidStringLength +
  1295. sizeof( L"\\REGISTRY\\USER\\" ) +
  1296. sizeof( UNICODE_NULL ));
  1297. CurrentUserKeyPath->Buffer = (RtlAllocateStringRoutine)( CurrentUserKeyPath->MaximumLength );
  1298. if (CurrentUserKeyPath->Buffer == NULL) {
  1299. return STATUS_NO_MEMORY;
  1300. }
  1301. //
  1302. // Copy "\REGISTRY\USER" to the current user string.
  1303. //
  1304. RtlAppendUnicodeToString( CurrentUserKeyPath, L"\\REGISTRY\\USER\\" );
  1305. SidString.MaximumLength = (USHORT)SidStringLength ;
  1306. SidString.Length = 0 ;
  1307. SidString.Buffer = CurrentUserKeyPath->Buffer +
  1308. (CurrentUserKeyPath->Length / sizeof(WCHAR) );
  1309. Status = RtlConvertSidToUnicodeString( &SidString,
  1310. ((PTOKEN_USER)TokenInformation)->User.Sid,
  1311. FALSE
  1312. );
  1313. if ( !NT_SUCCESS( Status )) {
  1314. RtlFreeUnicodeString( CurrentUserKeyPath );
  1315. } else {
  1316. CurrentUserKeyPath->Length += SidString.Length ;
  1317. }
  1318. return Status;
  1319. }
  1320. NTSTATUS
  1321. RtlOpenCurrentUser(
  1322. IN ULONG DesiredAccess,
  1323. OUT PHANDLE CurrentUserKey
  1324. )
  1325. /*++
  1326. Routine Description:
  1327. Attempts to open the the HKEY_CURRENT_USER predefined handle.
  1328. Arguments:
  1329. DesiredAccess - Specifies the access to open the key for.
  1330. CurrentUserKey - Returns a handle to the key \REGISTRY\USER\*.
  1331. Return Value:
  1332. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  1333. --*/
  1334. {
  1335. UNICODE_STRING CurrentUserKeyPath;
  1336. OBJECT_ATTRIBUTES Obja;
  1337. NTSTATUS Status;
  1338. RTL_PAGED_CODE();
  1339. //
  1340. // Format the registry path for the current user.
  1341. //
  1342. Status = RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath );
  1343. if ( NT_SUCCESS(Status) ) {
  1344. InitializeObjectAttributes( &Obja,
  1345. &CurrentUserKeyPath,
  1346. OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE|OBJ_FORCE_ACCESS_CHECK,
  1347. NULL,
  1348. NULL
  1349. );
  1350. Status = ZwOpenKey( CurrentUserKey,
  1351. DesiredAccess,
  1352. &Obja
  1353. );
  1354. RtlFreeUnicodeString( &CurrentUserKeyPath );
  1355. }
  1356. if ( !NT_SUCCESS(Status) ) {
  1357. //
  1358. // Opening \REGISTRY\USER\<SID> failed, try \REGISTRY\USER\.DEFAULT
  1359. //
  1360. RtlInitUnicodeString( &CurrentUserKeyPath, RtlpRegistryPaths[ RTL_REGISTRY_USER ] );
  1361. InitializeObjectAttributes( &Obja,
  1362. &CurrentUserKeyPath,
  1363. OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE|OBJ_FORCE_ACCESS_CHECK,
  1364. NULL,
  1365. NULL
  1366. );
  1367. Status = ZwOpenKey( CurrentUserKey,
  1368. DesiredAccess,
  1369. &Obja
  1370. );
  1371. }
  1372. return Status;
  1373. }
  1374. NTSTATUS
  1375. RtlpGetTimeZoneInfoHandle(
  1376. IN BOOLEAN WriteAccess,
  1377. OUT PHANDLE Key
  1378. )
  1379. {
  1380. return RtlpGetRegistryHandle( RTL_REGISTRY_CONTROL, L"TimeZoneInformation", WriteAccess, Key );
  1381. }
  1382. extern const WCHAR szBias[];
  1383. extern const WCHAR szStandardName[];
  1384. extern const WCHAR szStandardBias[];
  1385. extern const WCHAR szStandardStart[];
  1386. extern const WCHAR szDaylightName[];
  1387. extern const WCHAR szDaylightBias[];
  1388. extern const WCHAR szDaylightStart[];
  1389. NTSTATUS
  1390. RtlQueryTimeZoneInformation(
  1391. OUT PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
  1392. )
  1393. {
  1394. NTSTATUS Status;
  1395. HANDLE Key;
  1396. UNICODE_STRING StandardName, DaylightName;
  1397. RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 8 ];
  1398. RTL_PAGED_CODE();
  1399. Status = RtlpGetTimeZoneInfoHandle( FALSE, &Key );
  1400. if (!NT_SUCCESS( Status )) {
  1401. return Status;
  1402. }
  1403. RtlZeroMemory( TimeZoneInformation, sizeof( *TimeZoneInformation ) );
  1404. RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
  1405. RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1406. RegistryConfigurationTable[ 0 ].Name = (PWSTR)szBias;
  1407. RegistryConfigurationTable[ 0 ].EntryContext = &TimeZoneInformation->Bias;
  1408. StandardName.Buffer = TimeZoneInformation->StandardName;
  1409. StandardName.Length = 0;
  1410. StandardName.MaximumLength = sizeof( TimeZoneInformation->StandardName );
  1411. RegistryConfigurationTable[ 1 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1412. RegistryConfigurationTable[ 1 ].Name = (PWSTR)szStandardName;
  1413. RegistryConfigurationTable[ 1 ].EntryContext = &StandardName;
  1414. RegistryConfigurationTable[ 2 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1415. RegistryConfigurationTable[ 2 ].Name = (PWSTR)szStandardBias;
  1416. RegistryConfigurationTable[ 2 ].EntryContext = &TimeZoneInformation->StandardBias;
  1417. RegistryConfigurationTable[ 3 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1418. RegistryConfigurationTable[ 3 ].Name = (PWSTR)szStandardStart;
  1419. RegistryConfigurationTable[ 3 ].EntryContext = &TimeZoneInformation->StandardStart;
  1420. *(PLONG)(RegistryConfigurationTable[ 3 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
  1421. DaylightName.Buffer = TimeZoneInformation->DaylightName;
  1422. DaylightName.Length = 0;
  1423. DaylightName.MaximumLength = sizeof( TimeZoneInformation->DaylightName );
  1424. RegistryConfigurationTable[ 4 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1425. RegistryConfigurationTable[ 4 ].Name = (PWSTR)szDaylightName;
  1426. RegistryConfigurationTable[ 4 ].EntryContext = &DaylightName;
  1427. RegistryConfigurationTable[ 5 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1428. RegistryConfigurationTable[ 5 ].Name = (PWSTR)szDaylightBias;
  1429. RegistryConfigurationTable[ 5 ].EntryContext = &TimeZoneInformation->DaylightBias;
  1430. RegistryConfigurationTable[ 6 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1431. RegistryConfigurationTable[ 6 ].Name = (PWSTR)szDaylightStart;
  1432. RegistryConfigurationTable[ 6 ].EntryContext = &TimeZoneInformation->DaylightStart;
  1433. *(PLONG)(RegistryConfigurationTable[ 6 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
  1434. Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
  1435. (PWSTR)Key,
  1436. RegistryConfigurationTable,
  1437. NULL,
  1438. NULL
  1439. );
  1440. ZwClose( Key );
  1441. return Status;
  1442. }
  1443. NTSTATUS
  1444. RtlSetTimeZoneInformation(
  1445. IN PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
  1446. )
  1447. {
  1448. NTSTATUS Status;
  1449. HANDLE Key;
  1450. RTL_PAGED_CODE();
  1451. Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
  1452. if (!NT_SUCCESS( Status )) {
  1453. return Status;
  1454. }
  1455. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1456. (PWSTR)Key,
  1457. szBias,
  1458. REG_DWORD,
  1459. &TimeZoneInformation->Bias,
  1460. sizeof( TimeZoneInformation->Bias )
  1461. );
  1462. if (NT_SUCCESS( Status )) {
  1463. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1464. (PWSTR)Key,
  1465. szStandardName,
  1466. REG_SZ,
  1467. TimeZoneInformation->StandardName,
  1468. (wcslen( TimeZoneInformation->StandardName ) + 1) * sizeof( WCHAR )
  1469. );
  1470. }
  1471. if (NT_SUCCESS( Status )) {
  1472. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1473. (PWSTR)Key,
  1474. szStandardBias,
  1475. REG_DWORD,
  1476. &TimeZoneInformation->StandardBias,
  1477. sizeof( TimeZoneInformation->StandardBias )
  1478. );
  1479. }
  1480. if (NT_SUCCESS( Status )) {
  1481. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1482. (PWSTR)Key,
  1483. szStandardStart,
  1484. REG_BINARY,
  1485. &TimeZoneInformation->StandardStart,
  1486. sizeof( TimeZoneInformation->StandardStart )
  1487. );
  1488. }
  1489. if (NT_SUCCESS( Status )) {
  1490. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1491. (PWSTR)Key,
  1492. szDaylightName,
  1493. REG_SZ,
  1494. TimeZoneInformation->DaylightName,
  1495. (wcslen( TimeZoneInformation->DaylightName ) + 1) * sizeof( WCHAR )
  1496. );
  1497. }
  1498. if (NT_SUCCESS( Status )) {
  1499. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1500. (PWSTR)Key,
  1501. szDaylightBias,
  1502. REG_DWORD,
  1503. &TimeZoneInformation->DaylightBias,
  1504. sizeof( TimeZoneInformation->DaylightBias )
  1505. );
  1506. }
  1507. if (NT_SUCCESS( Status )) {
  1508. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1509. (PWSTR)Key,
  1510. szDaylightStart,
  1511. REG_BINARY,
  1512. &TimeZoneInformation->DaylightStart,
  1513. sizeof( TimeZoneInformation->DaylightStart )
  1514. );
  1515. }
  1516. ZwClose( Key );
  1517. return Status;
  1518. }
  1519. NTSTATUS
  1520. RtlSetActiveTimeBias(
  1521. IN LONG ActiveBias
  1522. )
  1523. {
  1524. NTSTATUS Status;
  1525. HANDLE Key;
  1526. RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 2 ];
  1527. LONG CurrentActiveBias;
  1528. RTL_PAGED_CODE();
  1529. Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
  1530. if (!NT_SUCCESS( Status )) {
  1531. return Status;
  1532. }
  1533. RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
  1534. RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
  1535. RegistryConfigurationTable[ 0 ].Name = L"ActiveTimeBias";
  1536. RegistryConfigurationTable[ 0 ].EntryContext = &CurrentActiveBias;
  1537. Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
  1538. (PWSTR)Key,
  1539. RegistryConfigurationTable,
  1540. NULL,
  1541. NULL
  1542. );
  1543. if ( !NT_SUCCESS(Status) || CurrentActiveBias != ActiveBias ) {
  1544. Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
  1545. (PWSTR)Key,
  1546. L"ActiveTimeBias",
  1547. REG_DWORD,
  1548. &ActiveBias,
  1549. sizeof( ActiveBias )
  1550. );
  1551. }
  1552. ZwClose( Key );
  1553. return Status;
  1554. }