Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1513 lines
43 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Regkey.c
  5. Abstract:
  6. This module contains the server side implementation for the Win32
  7. Registry APIs to open, create, flush and close keys. That is:
  8. - BaseRegCloseKey
  9. - BaseRegCreateKey
  10. - BaseRegFlushKey
  11. - BaseRegOpenKey
  12. Author:
  13. David J. Gilman (davegi) 15-Nov-1991
  14. Notes:
  15. These notes apply to the Win32 Registry API implementation as a whole
  16. and not just to this module.
  17. On the client side, modules contain RPC wrappers for both the new
  18. Win32 and compatible Win 3.1 APIs. The Win 3.1 wrappers generally
  19. supply default parameters before calling the Win32 wrappers. In some
  20. cases they may need to call multiple Win32 wrappers in order to
  21. function correctly (e.g. RegSetValue sometimes needs to call
  22. RegCreateKeyEx). The Win32 wrappers are quite thin and usually do
  23. nothing more than map a predefined handle to a real handle and perform
  24. ANSI<->Unicode translations. In some cases (e.g. RegCreateKeyEx) the
  25. wrapper also converts some argument (e.g. SECURITY_ATTRIBUTES) to an
  26. RPCable representation. In both the Win 3.1 and Win32 cases ANSI and
  27. Unicode implementations are provided.
  28. On the server side, there is one entry point for each of the Win32
  29. APIs. Each contains an identical interface with the client side
  30. wrapper with the exception that all string / count arguments are
  31. passed as a single counted Unicode string. Pictorially, for an API
  32. named "F":
  33. RegWin31FA() RegWin31FW() (client side)
  34. | |
  35. | |
  36. | |
  37. | |
  38. V V
  39. RegWin32FExA() RegWin32FExW()
  40. | |
  41. ^ ^
  42. v v (RPC)
  43. | |
  44. | |
  45. +----> BaseRegF() <---+ (server side)
  46. This yields smaller code (as the string conversion is done only once
  47. per API) at the cost of slightly higher maintenance (i.e. Win 3.1
  48. default parameter replacement and Win32 string conversions must be
  49. manually kept in synch).
  50. Another option would be to have a calling sequence that looks like,
  51. RegWin31FA() RegWin31FW()
  52. | |
  53. | |
  54. | |
  55. V V
  56. RegWin32FExA() -----> RegWin32FExW()
  57. and have the RegWin32FExW() API perform all of the actual work. This
  58. method is generally less efficient. It requires the RegWin32FExA()
  59. API to convert its ANSI string arguments to counted Unicode strings,
  60. extract the buffers to call the RegWin32FExW() API only to have it
  61. rebuild a counted Unicode string. However in some cases (e.g.
  62. RegConnectRegistry) where a counted Unicode string was not needed in
  63. the Unicode API this method is used.
  64. Details of an API's functionality, arguments and return value can be
  65. found in the base implementations (e.g. BaseRegF()). All other
  66. function headers contain only minimal routine descriptions and no
  67. descriptions of their arguments or return value.
  68. The comment string "Win3.1ism" indicates special code for Win 3.1
  69. compatability.
  70. Throughout the implementation the following variable names are used
  71. and always refer to the same thing:
  72. Obja - An OBJECT_ATTRIBUTES structure.
  73. Status - A NTSTATUS value.
  74. Error - A Win32 Registry error code (n.b. one of the error
  75. values is ERROR_SUCCESS).
  76. --*/
  77. #include <rpc.h>
  78. #include <string.h>
  79. #include <wchar.h>
  80. #include "regrpc.h"
  81. #include "localreg.h"
  82. #include "regclass.h"
  83. #include "regecls.h"
  84. #include "regsec.h"
  85. #include <malloc.h>
  86. #ifdef LOCAL
  87. #include "tsappcmp.h"
  88. #ifdef LEAK_TRACK
  89. #include "regleak.h"
  90. #endif // LEAK_TRACK
  91. #endif
  92. NTSTATUS
  93. BaseRegCreateMultipartKey(
  94. IN HKEY hkDestKey,
  95. IN PUNICODE_STRING pDestSubKey,
  96. IN PUNICODE_STRING lpClass OPTIONAL,
  97. IN DWORD dwOptions,
  98. IN REGSAM samDesired,
  99. IN PRPC_SECURITY_ATTRIBUTES pRpcSecurityAttributes OPTIONAL,
  100. OUT PHKEY phkResult,
  101. OUT LPDWORD lpdwDisposition OPTIONAL,
  102. ULONG Attributes);
  103. #if DBG
  104. extern HANDLE RestrictedMachineHandle;
  105. #endif
  106. NTSTATUS
  107. OpenMachineKey(PHANDLE phKey);
  108. BOOL
  109. InitializeRegCreateKey(
  110. )
  111. /*++
  112. Routine Description:
  113. This function was used to initialize a critical section that no longer
  114. exists. This critical section was used when a key name '\', and multiple
  115. multiple keys were to be created. The API used the wcstok defined in the
  116. kernel, which was not multi-threaded safe.
  117. This function now will always return TRUE. It will not be removed from the code
  118. to avoid change in the rpc interface.
  119. Arguments:
  120. None.
  121. Return Value:
  122. Returns TRUE always.
  123. --*/
  124. {
  125. return( TRUE );
  126. }
  127. BOOL
  128. CleanupRegCreateKey(
  129. )
  130. /*++
  131. Routine Description:
  132. This function was used to clean up a critical section that no longer
  133. exists. This critical section was used when a key name '\', and multiple
  134. multiple keys were to be created. The API used the wcstok defined in the
  135. kernel, which was not multi-threaded safe.
  136. This function now will always return TRUE. It will not be removed from the code
  137. to avoid change in the rpc interface.
  138. Arguments:
  139. None.
  140. Return Value:
  141. Returns TRUE if the cleanup succeeds.
  142. --*/
  143. {
  144. return( TRUE );
  145. }
  146. error_status_t
  147. BaseRegCloseKeyInternal(
  148. IN OUT PHKEY phKey
  149. )
  150. /*++
  151. Routine Description:
  152. Closes a key handle.
  153. Arguments:
  154. phKey - Supplies a handle to an open key to be closed.
  155. Return Value:
  156. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  157. --*/
  158. {
  159. NTSTATUS Status;
  160. #if defined(LEAK_TRACK)
  161. BOOL fTrack;
  162. #endif // defined(LEAK_TRACK)
  163. //
  164. // Call out to Perflib if the HKEY is HKEY_PERFOMANCE_DATA.
  165. //
  166. if(( *phKey == HKEY_PERFORMANCE_DATA ) ||
  167. ( *phKey == HKEY_PERFORMANCE_TEXT ) ||
  168. ( *phKey == HKEY_PERFORMANCE_NLSTEXT )) {
  169. Status = PerfRegCloseKey( phKey );
  170. return (error_status_t)Status;
  171. }
  172. ASSERT( IsPredefinedRegistryHandle( *phKey ) == FALSE );
  173. #ifndef LOCAL
  174. //
  175. // Quick check for a "restricted" handle; then this turns to noop as the global restricted handle will
  176. // be cleaned up when the service is terminated
  177. //
  178. if ( REGSEC_CHECK_HANDLE( *phKey ) )
  179. {
  180. *phKey = REGSEC_CLEAR_HANDLE( *phKey );
  181. ASSERT( RestrictedMachineHandle == *phKey );
  182. *phKey = NULL;
  183. return ERROR_SUCCESS;
  184. }
  185. #endif //LOCAL
  186. #ifdef LOCAL
  187. //
  188. // now we need to remove any state for registry key enumeration associated
  189. // with this key if it's a class registration parent
  190. //
  191. if (REG_CLASS_IS_SPECIAL_KEY(*phKey)) {
  192. // this may not succeed since someone could have already removed this key
  193. (void) EnumTableRemoveKey(
  194. &gClassesEnumTable,
  195. *phKey,
  196. ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD);
  197. }
  198. #if defined(LEAK_TRACK)
  199. if (g_RegLeakTraceInfo.bEnableLeakTrack) {
  200. fTrack = RegLeakTableIsTrackedObject(&gLeakTable, *phKey);
  201. }
  202. #endif // defined(LEAK_TRACK)
  203. #endif // LOCAL
  204. Status = NtClose( *phKey );
  205. if( NT_SUCCESS( Status )) {
  206. #ifdef LOCAL
  207. #if defined(LEAK_TRACK)
  208. if (g_RegLeakTraceInfo.bEnableLeakTrack) {
  209. if (fTrack) {
  210. (void) UnTrackObject(*phKey);
  211. }
  212. }
  213. #endif // defined(LEAK_TRACK)
  214. #endif // LOCAL
  215. //
  216. // Set the handle to NULL so that RPC knows that it has been closed.
  217. //
  218. *phKey = NULL;
  219. return ERROR_SUCCESS;
  220. } else {
  221. return (error_status_t)RtlNtStatusToDosError( Status );
  222. }
  223. }
  224. error_status_t
  225. BaseRegCloseKey(
  226. IN OUT PHKEY phKey
  227. )
  228. /*++
  229. Routine Description:
  230. Closes a key handle.
  231. Arguments:
  232. phKey - Supplies a handle to an open key to be closed.
  233. Return Value:
  234. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  235. --*/
  236. {
  237. error_status_t Error;
  238. #ifndef LOCAL
  239. RPC_STATUS _rpcstatus = RpcImpersonateClient( NULL );
  240. #if DBG
  241. if( _rpcstatus != ERROR_SUCCESS ) {
  242. DbgPrint("WINREG: BaseRegCloseKey: Failed to impersonate in process %p, thread %p, for handle %p \n",NtCurrentProcess(),NtCurrentThread(),*phKey);
  243. }
  244. #endif
  245. #endif //LOCAL
  246. Error = BaseRegCloseKeyInternal(phKey);
  247. #ifndef LOCAL
  248. #if DBG
  249. if( Error != ERROR_SUCCESS ) {
  250. DbgPrint("WINREG: BaseRegCloseKeyInternal without impersonation returned %lx\n",Error);
  251. }
  252. #endif
  253. if (_rpcstatus == ERROR_SUCCESS) {
  254. _rpcstatus = RpcRevertToSelf();
  255. }
  256. #endif
  257. return Error;
  258. }
  259. error_status_t
  260. BaseRegCreateKey(
  261. IN HKEY hKey,
  262. IN PUNICODE_STRING lpSubKey,
  263. IN PUNICODE_STRING lpClass OPTIONAL,
  264. IN DWORD dwOptions,
  265. IN REGSAM samDesired,
  266. IN PRPC_SECURITY_ATTRIBUTES pRpcSecurityAttributes OPTIONAL,
  267. OUT PHKEY phkResult,
  268. OUT LPDWORD lpdwDisposition OPTIONAL
  269. )
  270. /*++
  271. Routine Description:
  272. Create a new key, with the specified name, or open an already existing
  273. key. RegCreateKeyExW is atomic, meaning that one can use it to create
  274. a key as a lock. If a second caller creates the same key, the call
  275. will return a value that says whether the key already existed or not,
  276. and thus whether the caller "owns" the "lock" or not. RegCreateKeyExW
  277. does NOT truncate an existing entry, so the lock entry may contain
  278. data.
  279. Arguments:
  280. hKey - Supplies a handle to an open key. The lpSubKey key path
  281. parameter is relative to this key handle. Any of the predefined
  282. reserved handle values or a previously opened key handle may be used
  283. for hKey.
  284. lpSubKey - Supplies the downward key path to the key to create.
  285. lpSubKey is always relative to the key specified by hKey.
  286. This parameter may not be NULL.
  287. lpClass - Supplies the class (object type) of this key. Ignored if
  288. the key already exists. No class is associated with this key if
  289. this parameter is NULL.
  290. dwOptions - Supplies special options. Only one is currently defined:
  291. REG_VOLATILE - Specifies that this key should not be preserved
  292. across reboot. The default is not volatile. This is ignored
  293. if the key already exists.
  294. WARNING: All descendent keys of a volatile key are also volatile.
  295. samDesired - Supplies the requested security access mask. This
  296. access mask describes the desired security access to the newly
  297. created key.
  298. lpSecurityAttributes - Supplies a pointer to a SECURITY_ATTRIBUTES
  299. structure for the newly created key. This parameter is ignored
  300. if NULL or not supported by the OS.
  301. phkResult - Returns an open handle to the newly created key.
  302. lpdwDisposition - Returns the disposition state, which can be one of:
  303. REG_CREATED_NEW_KEY - the key did not exist and was created.
  304. REG_OPENED_EXISTING_KEY - the key already existed, and was simply
  305. opened without being changed.
  306. This parameter is ignored if NULL.
  307. Return Value:
  308. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  309. If successful, RegCreateKeyEx creates the new key (or opens the key if
  310. it already exists), and returns an open handle to the newly created
  311. key in phkResult. Newly created keys have no value; RegSetValue, or
  312. RegSetValueEx must be called to set values. hKey must have been
  313. opened for KEY_CREATE_SUB_KEY access.
  314. --*/
  315. {
  316. OBJECT_ATTRIBUTES Obja;
  317. ULONG Attributes;
  318. NTSTATUS Status;
  319. #if DBG
  320. HANDLE DebugKey = hKey;
  321. #endif
  322. HKEY hkDestKey;
  323. UNICODE_STRING DestClassSubkey;
  324. PUNICODE_STRING pDestSubkey;
  325. DWORD dwDisposition;
  326. BOOL fRetryOnAccessDenied;
  327. BOOL fRetried;
  328. BOOL fTrySingleCreate;
  329. #if LOCAL
  330. SKeySemantics keyinfo;
  331. BYTE rgNameInfoBuf[REG_MAX_CLASSKEY_LEN];
  332. REGSAM OriginalSam = samDesired;
  333. UNICODE_STRING TmpStr = *lpSubKey; //used to keep original SubKey string
  334. memset(&keyinfo, 0, sizeof(keyinfo));
  335. #endif
  336. #ifndef LOCAL
  337. BOOL UseFakeMachineKey = FALSE;
  338. #endif LOCAL
  339. ASSERT( IsPredefinedRegistryHandle( hKey ) == FALSE );
  340. ASSERT( lpSubKey->Length > 0 );
  341. DestClassSubkey.Buffer = NULL;
  342. //
  343. // For class registrations, retry on access denied in machine hive --
  344. // if we do retry, this will be set to FALSE so we only retry once
  345. //
  346. fRetryOnAccessDenied = TRUE;
  347. fRetried = FALSE;
  348. //
  349. // First attempt should do create with a single ntcreatekey call
  350. // If that doesn't work, this gets set to false so we remember if we
  351. // have to retry for access denied in the machine hive
  352. //
  353. fTrySingleCreate = TRUE;
  354. hkDestKey = NULL;
  355. pDestSubkey = NULL;
  356. //
  357. // Check for malformed arguments from malicious clients
  358. //
  359. if( (lpSubKey == NULL) ||
  360. (lpSubKey->Length < sizeof(UNICODE_NULL)) ||
  361. (lpSubKey->Buffer == NULL) ||
  362. ((lpSubKey->Length % sizeof(WCHAR)) != 0) ||
  363. (lpSubKey->Buffer[lpSubKey->Length / sizeof(WCHAR) - 1] != L'\0') ||
  364. (phkResult == NULL) ||
  365. (lpClass == NULL) ||
  366. (lpClass->Length & 1) ) {
  367. return(ERROR_INVALID_PARAMETER);
  368. }
  369. //
  370. // Quick check for a "restricted" handle
  371. //
  372. if ( REGSEC_CHECK_HANDLE( hKey ) )
  373. {
  374. if ( ! REGSEC_CHECK_PATH( hKey, lpSubKey ) )
  375. {
  376. return( ERROR_ACCESS_DENIED );
  377. }
  378. hKey = REGSEC_CLEAR_HANDLE( hKey );
  379. ASSERT( RestrictedMachineHandle == hKey );
  380. #ifndef LOCAL
  381. UseFakeMachineKey = TRUE;
  382. #endif LOCAL
  383. }
  384. //
  385. // Impersonate the client.
  386. //
  387. RPC_IMPERSONATE_CLIENT( NULL );
  388. //
  389. // Initialize the variable that will contain the handle to NULL
  390. // to ensure that in case of error the API will not return a
  391. // bogus handle. This is required otherwise RPC will get confused.
  392. // Note that RPC should have already initialized it to 0.
  393. //
  394. *phkResult = NULL;
  395. //
  396. // Subtract the NULLs from the Length of the provided strings.
  397. // These were added on the client side so that the NULLs were
  398. // transmitted by RPC.
  399. //
  400. lpSubKey->Length -= sizeof( UNICODE_NULL );
  401. if( lpSubKey->Buffer[0] == ( WCHAR )'\\' ) {
  402. //
  403. // Do not accept a key name that starts with '\', even though
  404. // the code below would handle it. This is to ensure that
  405. // RegCreateKeyEx and RegOpenKeyEx will behave in the same way
  406. // when they get a key name that starts with '\'.
  407. //
  408. Status = STATUS_OBJECT_PATH_INVALID;
  409. goto cleanup;
  410. }
  411. if ( lpClass->Length > 0 ) {
  412. lpClass->Length -= sizeof( UNICODE_NULL );
  413. }
  414. //
  415. // Determine the correct set of attributes.
  416. //
  417. Attributes = OBJ_CASE_INSENSITIVE;
  418. if( ARGUMENT_PRESENT( pRpcSecurityAttributes )) {
  419. if( pRpcSecurityAttributes->bInheritHandle ) {
  420. Attributes |= OBJ_INHERIT;
  421. }
  422. }
  423. if (dwOptions & REG_OPTION_OPEN_LINK) {
  424. Attributes |= OBJ_OPENLINK;
  425. }
  426. #ifdef LOCAL
  427. if (REG_CLASS_IS_SPECIAL_KEY(hKey) ||
  428. ( (gdwRegistryExtensionFlags & TERMSRV_ENABLE_PER_USER_CLASSES_REDIRECTION )
  429. && ExtractClassKey(&hKey,lpSubKey) ) ) {
  430. //
  431. // Find more information
  432. // about this key -- the most important piece of information
  433. // is whether it's a class registration key
  434. //
  435. keyinfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameInfoBuf;
  436. keyinfo._cbFullPath = sizeof(rgNameInfoBuf);
  437. keyinfo._fAllocedNameBuf = FALSE;
  438. //
  439. // see if this is a class registration
  440. //
  441. Status = BaseRegGetKeySemantics(hKey, lpSubKey, &keyinfo);
  442. // if we can't determine what type of key this is, leave
  443. if (!NT_SUCCESS(Status)) {
  444. goto cleanup;
  445. }
  446. Status = BaseRegMapClassRegistrationKey(
  447. hKey,
  448. lpSubKey,
  449. &keyinfo,
  450. &DestClassSubkey,
  451. &fRetryOnAccessDenied,
  452. &hkDestKey,
  453. &pDestSubkey);
  454. if (!NT_SUCCESS(Status)) {
  455. goto cleanup;
  456. }
  457. } else
  458. #endif // LOCAL
  459. {
  460. #ifdef LOCAL
  461. memset(&keyinfo, 0, sizeof(keyinfo));
  462. #endif // LOCAL
  463. hkDestKey = hKey;
  464. pDestSubkey = lpSubKey;
  465. }
  466. #ifndef LOCAL
  467. //
  468. // open a new key in the caller's context
  469. //
  470. if( UseFakeMachineKey ) {
  471. Status = OpenMachineKey(&hkDestKey);
  472. if( !NT_SUCCESS(Status) ) {
  473. goto cleanup;
  474. }
  475. //
  476. // hkDestKey opened here will be used throught the function to perform the appropiate
  477. // open and it'll be closed on cleanup path (see commented below at the end of the function)
  478. //
  479. //if (hkDestKey && (hkDestKey != hKey)) {
  480. // NtClose(hkDestKey);
  481. //}
  482. }
  483. #endif LOCAL
  484. for (;;) {
  485. #ifdef LOCAL
  486. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  487. if (fTrySingleCreate)
  488. {
  489. #endif
  490. //
  491. // Validate the security descriptor.
  492. //
  493. if( ARGUMENT_PRESENT( pRpcSecurityAttributes ) &&
  494. pRpcSecurityAttributes->RpcSecurityDescriptor.lpSecurityDescriptor)
  495. {
  496. if( !RtlValidRelativeSecurityDescriptor((PSECURITY_DESCRIPTOR)(pRpcSecurityAttributes->RpcSecurityDescriptor.lpSecurityDescriptor),
  497. pRpcSecurityAttributes->RpcSecurityDescriptor.cbInSecurityDescriptor,
  498. 0 )) {
  499. //
  500. // We were passed a bogus security descriptor to set. Bail out
  501. //
  502. Status = STATUS_INVALID_PARAMETER;
  503. goto cleanup;
  504. }
  505. }
  506. //
  507. // Try to create the specified key. This will work if there is only
  508. // one key being created or if the key already exists. If more than
  509. // one key needs to be created, this will fail and we will have to
  510. // do all the complicated stuff to create each intermediate key.
  511. //
  512. InitializeObjectAttributes(&Obja,
  513. pDestSubkey,
  514. Attributes,
  515. hkDestKey,
  516. ARGUMENT_PRESENT( pRpcSecurityAttributes )
  517. ? pRpcSecurityAttributes
  518. ->RpcSecurityDescriptor.lpSecurityDescriptor
  519. : NULL);
  520. Status = NtCreateKey(phkResult,
  521. samDesired,
  522. &Obja,
  523. 0,
  524. lpClass,
  525. dwOptions,
  526. &dwDisposition);
  527. #ifdef LOCAL
  528. if (gpfnTermsrvCreateRegEntry && NT_SUCCESS(Status) && (dwDisposition == REG_CREATED_NEW_KEY)) {
  529. //
  530. // Terminal Server application compatiblity
  531. // Store the newly created key in the Terminal Server registry tracking database
  532. //
  533. gpfnTermsrvCreateRegEntry(*phkResult,
  534. &Obja,
  535. 0,
  536. lpClass,
  537. dwOptions);
  538. }
  539. }
  540. #ifdef CLASSES_RETRY_ON_ACCESS_DENIED
  541. if (fTrySingleCreate && (STATUS_ACCESS_DENIED == Status) && keyinfo._fCombinedClasses &&
  542. fRetryOnAccessDenied ) {
  543. Status = BaseRegMapClassOnAccessDenied(
  544. &keyinfo,
  545. &hkDestKey,
  546. pDestSubkey,
  547. &fRetryOnAccessDenied);
  548. if (NT_SUCCESS(Status)) {
  549. fRetried = TRUE;
  550. continue;
  551. }
  552. // we failed for some reason -- exit
  553. break;
  554. }
  555. #else
  556. //if (it's terminal server; we're trying to create single key;
  557. //we've got ASSESS_DENIED trying to create key
  558. //a key we want to create is HKCR subkey(keyinfo._fCombinedClasses!=0);
  559. //Registry flag is set to allow per user classes redirection.
  560. //(fRetryOnAccessDenied !=0 - means that parent key is not in the user hive))
  561. //then try to create the key in the user hive.
  562. if ( (gdwRegistryExtensionFlags & TERMSRV_ENABLE_PER_USER_CLASSES_REDIRECTION)
  563. && fTrySingleCreate && (STATUS_ACCESS_DENIED == Status)
  564. && keyinfo._fCombinedClasses && fRetryOnAccessDenied
  565. ) {
  566. if (DestClassSubkey.Buffer) {
  567. RegClassHeapFree(DestClassSubkey.Buffer);
  568. DestClassSubkey.Buffer=NULL;
  569. }
  570. Status = BaseRegMapClassOnAccessDenied(
  571. &keyinfo,
  572. &hkDestKey,
  573. pDestSubkey,
  574. &fRetryOnAccessDenied);
  575. if (NT_SUCCESS(Status)) {
  576. fRetried = TRUE;
  577. continue;
  578. }
  579. // we failed for some reason -- exit
  580. break;
  581. }
  582. #endif // CLASSES_RETRY_ON_ACCESS_DENIED
  583. #endif // LOCAL
  584. fTrySingleCreate = FALSE;
  585. if (NT_SUCCESS(Status)) {
  586. if (lpdwDisposition) {
  587. *lpdwDisposition = dwDisposition;
  588. }
  589. } else {
  590. Status = BaseRegCreateMultipartKey(
  591. hkDestKey,
  592. pDestSubkey,
  593. lpClass,
  594. dwOptions,
  595. samDesired,
  596. pRpcSecurityAttributes,
  597. phkResult,
  598. lpdwDisposition,
  599. Attributes);
  600. }
  601. #ifdef LOCAL
  602. #ifdef CLASSES_RETRY_ON_ACCESS_DENIED
  603. if ((STATUS_ACCESS_DENIED == Status) && keyinfo._fCombinedClasses &&
  604. fRetryOnAccessDenied ) {
  605. Status = BaseRegMapClassOnAccessDenied(
  606. &keyinfo,
  607. &hkDestKey,
  608. pDestSubkey,
  609. &fRetryOnAccessDenied);
  610. if (NT_SUCCESS(Status)) {
  611. fRetried = TRUE;
  612. continue;
  613. }
  614. break;
  615. }
  616. #else
  617. //We've tried to create single key and failed (Status !=STATUS_ACCESS_DENIED)
  618. //then we tried to create multipart key and got access denied
  619. //thus we've got here
  620. if ( (gdwRegistryExtensionFlags & TERMSRV_ENABLE_PER_USER_CLASSES_REDIRECTION)
  621. && (STATUS_ACCESS_DENIED == Status)
  622. && keyinfo._fCombinedClasses && fRetryOnAccessDenied
  623. ) {
  624. if (DestClassSubkey.Buffer) {
  625. RegClassHeapFree(DestClassSubkey.Buffer);
  626. DestClassSubkey.Buffer=NULL;
  627. }
  628. Status = BaseRegMapClassOnAccessDenied(
  629. &keyinfo,
  630. &hkDestKey,
  631. pDestSubkey,
  632. &fRetryOnAccessDenied);
  633. if (NT_SUCCESS(Status)) {
  634. fRetried = TRUE;
  635. continue;
  636. }
  637. break;
  638. }
  639. #endif // CLASSES_RETRY_ON_ACCESS_DENIED
  640. if (NT_SUCCESS(Status)) {
  641. if (keyinfo._fCombinedClasses) {
  642. // mark this key as part of hkcr
  643. *phkResult = REG_CLASS_SET_SPECIAL_KEY(*phkResult);
  644. }
  645. }
  646. #endif // LOCAL
  647. break;
  648. }
  649. cleanup:
  650. #ifdef CLASSES_RETRY_ON_ACCESS_DENIED
  651. //
  652. // Memory was allocated if we retried, so free it
  653. //
  654. if (fRetried && pDestSubkey->Buffer) {
  655. RtlFreeHeap(RtlProcessHeap(), 0, pDestSubkey->Buffer);
  656. pDestSubkey->Buffer = NULL;
  657. }
  658. #endif // CLASSES_RETRY_ON_ACCESS_DENIED
  659. if (hkDestKey && (hkDestKey != hKey)) {
  660. NtClose(hkDestKey);
  661. }
  662. #ifdef LOCAL
  663. if (DestClassSubkey.Buffer) {
  664. RegClassHeapFree(DestClassSubkey.Buffer);
  665. }
  666. BaseRegReleaseKeySemantics(&keyinfo);
  667. *lpSubKey = TmpStr; //restore original SubKey string
  668. #endif // LOCAL
  669. if (NT_SUCCESS(Status)) {
  670. #ifdef LOCAL
  671. #if defined(LEAK_TRACK)
  672. if (g_RegLeakTraceInfo.bEnableLeakTrack) {
  673. (void) TrackObject(*phkResult);
  674. }
  675. #endif // defined(LEAK_TRACK)
  676. #endif LOCAL
  677. // disabled, for the case where we specifically close a predefined key inside
  678. // RegOpenKeyExA and RegOpenKeyExW
  679. //ASSERT( *phkResult != DebugKey );
  680. }
  681. RPC_REVERT_TO_SELF();
  682. return (error_status_t)RtlNtStatusToDosError( Status );
  683. }
  684. NTSTATUS
  685. BaseRegCreateMultipartKey(
  686. IN HKEY hkDestKey,
  687. IN PUNICODE_STRING pDestSubKey,
  688. IN PUNICODE_STRING lpClass OPTIONAL,
  689. IN DWORD dwOptions,
  690. IN REGSAM samDesired,
  691. IN PRPC_SECURITY_ATTRIBUTES pRpcSecurityAttributes OPTIONAL,
  692. OUT PHKEY phkResult,
  693. OUT LPDWORD lpdwDisposition OPTIONAL,
  694. ULONG Attributes)
  695. /*++
  696. Routine Description:
  697. This function creates registry keys for which multiple path components
  698. are nonexistent. It parses the key path and creates each intermediate
  699. subkey.
  700. Arguments:
  701. See BaseRegCreateKey.
  702. Return Value:
  703. Returns STATUS_SUCCESS on success, other NTSTATUS if failed.
  704. --*/
  705. {
  706. LPWSTR KeyBuffer;
  707. ULONG NumberOfSubKeys;
  708. LPWSTR p;
  709. ULONG i;
  710. LPWSTR Token;
  711. UNICODE_STRING KeyName;
  712. HANDLE TempHandle1;
  713. HANDLE TempHandle2;
  714. OBJECT_ATTRIBUTES Obja;
  715. NTSTATUS Status;
  716. DWORD dwDisposition;
  717. #ifdef LOCAL
  718. REGSAM OriginalSam = samDesired;
  719. #endif // LOCAL
  720. dwDisposition = REG_OPENED_EXISTING_KEY;
  721. TempHandle1 = NULL;
  722. //
  723. // Win3.1ism - Loop through each '\' separated component in the
  724. // supplied sub key and create a key for each component. This is
  725. // guaranteed to work at least once because lpSubKey was validated
  726. // on the client side.
  727. //
  728. //
  729. // Initialize the buffer to be tokenized.
  730. //
  731. KeyBuffer = pDestSubKey->Buffer;
  732. //
  733. // Find out the number of subkeys to be created
  734. //
  735. NumberOfSubKeys = 1;
  736. p = KeyBuffer;
  737. while ( ( p = wcschr( p, ( WCHAR )'\\' ) ) != NULL ) {
  738. p++;
  739. NumberOfSubKeys++;
  740. }
  741. for( i = 0, Token = KeyBuffer; i < NumberOfSubKeys; i++ ) {
  742. ASSERT(Token != NULL);
  743. if( ( *Token == ( WCHAR )'\\' ) &&
  744. ( i != NumberOfSubKeys - 1 ) ) {
  745. //
  746. // If the first character of the key name is '\', and the key
  747. // is not the last to be created, then ignore this key name.
  748. // This condition can happen if the key name contains
  749. // consecutive '\'.
  750. // This behavior is consistent with the one we had in the past
  751. // when the API used wcstok() to get the key names.
  752. // Note that if the key name is an empty string, we return a handle
  753. // that is different than hKey, even though both point to the same
  754. // key. This is by design.
  755. //
  756. Token++;
  757. continue;
  758. }
  759. //
  760. // Convert the token to a counted Unicode string.
  761. //
  762. KeyName.Buffer = Token;
  763. if (i == NumberOfSubKeys - 1) {
  764. KeyName.Length = wcslen(Token)*sizeof(WCHAR);
  765. } else {
  766. KeyName.Length = (USHORT)(wcschr(Token, ( WCHAR )'\\') - Token)*sizeof(WCHAR);
  767. }
  768. //
  769. // Remember the intermediate handle (NULL the first time through).
  770. //
  771. TempHandle2 = TempHandle1;
  772. {
  773. //
  774. // Initialize the OBJECT_ATTRIBUTES structure, close the
  775. // intermediate key and create or open the key.
  776. //
  777. InitializeObjectAttributes(
  778. &Obja,
  779. &KeyName,
  780. Attributes,
  781. hkDestKey,
  782. ARGUMENT_PRESENT( pRpcSecurityAttributes )
  783. ? pRpcSecurityAttributes
  784. ->RpcSecurityDescriptor.lpSecurityDescriptor
  785. : NULL
  786. );
  787. Status = NtCreateKey(
  788. &TempHandle1,
  789. ( i == NumberOfSubKeys - 1 )? samDesired :
  790. (samDesired & KEY_WOW64_RES) | MAXIMUM_ALLOWED,
  791. &Obja,
  792. 0,
  793. lpClass,
  794. dwOptions,
  795. &dwDisposition
  796. );
  797. if (NT_SUCCESS(Status) && lpdwDisposition) {
  798. *lpdwDisposition = dwDisposition;
  799. }
  800. #ifdef LOCAL
  801. // This code is in Hydra 4. We have disabled this for NT 5
  802. // for now till we are sure that its needed to get some imporatant
  803. // app to work on Hydra 5. Otherwise this should be removed
  804. if ( gdwRegistryExtensionFlags & TERMSRV_ENABLE_ACCESS_FLAG_MODIFICATION ) {
  805. // For Terminal Server only.
  806. // Some apps try to create/open the key with all of the access bits
  807. // turned on. We'll mask off the ones they don't have access to by
  808. // default, (at least under HKEY_LOCAL_MACHINE\Software) and try to
  809. // open the key again.
  810. if (Status == STATUS_ACCESS_DENIED) {
  811. //MAXIMUM_ALLOWED does not include ACCESS_SYSTEM_SECURITY
  812. //so if user asks for this permission, we need to add it.
  813. //It could result in ACCESS_DENIED error but for
  814. //TS App. Compat. it is not important.
  815. Status = NtCreateKey(
  816. &TempHandle1,
  817. (samDesired & (KEY_WOW64_RES | ACCESS_SYSTEM_SECURITY) ) | MAXIMUM_ALLOWED,
  818. &Obja,
  819. 0,
  820. lpClass,
  821. dwOptions,
  822. &dwDisposition);
  823. // Give app back the original error
  824. if (!NT_SUCCESS(Status)) {
  825. Status = STATUS_ACCESS_DENIED;
  826. }
  827. if (lpdwDisposition) {
  828. *lpdwDisposition = dwDisposition;
  829. }
  830. }
  831. }
  832. if (gpfnTermsrvCreateRegEntry && NT_SUCCESS(Status) && (dwDisposition == REG_CREATED_NEW_KEY)) {
  833. //
  834. // Terminal Server application compatiblity
  835. // Store the newly created key in the Terminal Server registry tracking database
  836. //
  837. gpfnTermsrvCreateRegEntry(TempHandle1,
  838. &Obja,
  839. 0,
  840. lpClass,
  841. dwOptions);
  842. }
  843. #endif
  844. }
  845. //
  846. // Initialize the next object directory (i.e. parent key) handle.
  847. //
  848. hkDestKey = TempHandle1;
  849. //
  850. // Close the intermediate key.
  851. // This fails the first time through the loop since the
  852. // handle is NULL.
  853. //
  854. if( TempHandle2 != NULL ) {
  855. NtClose( TempHandle2 );
  856. }
  857. //
  858. // If creating the key failed, map and return the error.
  859. //
  860. if( ! NT_SUCCESS( Status )) {
  861. return Status;
  862. }
  863. Token = wcschr( Token, ( WCHAR )'\\') + 1;
  864. }
  865. //
  866. // Only set the return value once we know we've
  867. // succeeded.
  868. //
  869. *phkResult = hkDestKey;
  870. return STATUS_SUCCESS;
  871. }
  872. error_status_t
  873. BaseRegFlushKey(
  874. IN HKEY hKey
  875. )
  876. /*++
  877. Routine Description:
  878. Flush changes to backing store. Flush will not return until the data
  879. has been written to backing store. It will flush all the attributes
  880. of a single key. Closing a key without flushing it will NOT abort
  881. changes.
  882. Arguments:
  883. hKey - Supplies a handle to the open key.
  884. Return Value:
  885. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  886. If successful, RegFlushKey will flush to backing store any changes
  887. made to the key.
  888. Notes:
  889. RegFlushKey may also flush other data in the Registry, and therefore
  890. can be expensive, it should not be called gratuitously.
  891. --*/
  892. {
  893. if ((hKey == HKEY_PERFORMANCE_DATA) ||
  894. (hKey == HKEY_PERFORMANCE_TEXT) ||
  895. (hKey == HKEY_PERFORMANCE_NLSTEXT)) {
  896. return(ERROR_SUCCESS);
  897. }
  898. ASSERT( IsPredefinedRegistryHandle( hKey ) == FALSE );
  899. //
  900. // Call the Nt Api to flush the key, map the NTSTATUS code to a
  901. // Win32 Registry error code and return.
  902. //
  903. return (error_status_t)RtlNtStatusToDosError( NtFlushKey( hKey ));
  904. }
  905. error_status_t
  906. BaseRegOpenKey(
  907. IN HKEY hKey,
  908. IN PUNICODE_STRING lpSubKey,
  909. IN DWORD dwOptions,
  910. IN REGSAM samDesired,
  911. OUT PHKEY phkResult
  912. )
  913. /*++
  914. Routine Description:
  915. Open a key for access, returning a handle to the key. If the key is
  916. not present, it is not created (see RegCreateKeyExW).
  917. Arguments:
  918. hKey - Supplies a handle to an open key. The lpSubKey pathname
  919. parameter is relative to this key handle. Any of the predefined
  920. reserved handle values or a previously opened key handle may be used
  921. for hKey. NULL is not permitted.
  922. lpSubKey - Supplies the downward key path to the key to open.
  923. lpSubKey is always relative to the key specified by hKey.
  924. dwOptions -- reserved.
  925. samDesired -- This access mask describes the desired security access
  926. for the key.
  927. phkResult -- Returns the handle to the newly opened key.
  928. Return Value:
  929. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  930. If successful, RegOpenKeyEx will return the handle to the newly opened
  931. key in phkResult.
  932. --*/
  933. {
  934. OBJECT_ATTRIBUTES Obja;
  935. NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
  936. error_status_t ret = ERROR_SUCCESS;
  937. #ifdef LOCAL
  938. UNICODE_STRING TmpStr = *lpSubKey; //used to keep original SubKey string
  939. #endif
  940. #ifndef LOCAL
  941. BOOL UseFakeMachineKey = FALSE;
  942. #endif
  943. UNREFERENCED_PARAMETER( dwOptions );
  944. ASSERT( IsPredefinedRegistryHandle( hKey ) == FALSE );
  945. //
  946. // Check for malformed arguments from malicious clients
  947. //
  948. if( (lpSubKey == NULL) ||
  949. (lpSubKey->Length < sizeof(UNICODE_NULL)) ||
  950. ((lpSubKey->Length % sizeof(WCHAR)) != 0) ||
  951. (phkResult == NULL) ) {
  952. return(ERROR_INVALID_PARAMETER);
  953. }
  954. //
  955. // Need to NULL this out param for compat with NT4, even though SDK
  956. // does not define this out param on api failure -- bad apps were written
  957. // which rely on this. Used to get NULLed by call to NtOpenKey, but since
  958. // we don't always call that now, we need to do this here in user mode. Also
  959. // need an exception wrapper since NtOpenKey would simply return an error if
  960. // the pointer were invalid, whereas in user mode we access violate if we simply
  961. // assign -- yet another fix needed for app compatibility as some apps on NT 4
  962. // were actually passing in a bad pointer and ignoring the error returned
  963. // by the api as part of their normal operation.
  964. //
  965. __try {
  966. *phkResult = NULL;
  967. } __except ( EXCEPTION_EXECUTE_HANDLER ) {
  968. Status = GetExceptionCode();
  969. #if DBG
  970. DbgPrint( "WINREG Error: Exception %x in BaseRegOpenKey\n",
  971. Status );
  972. #endif
  973. ret = RtlNtStatusToDosError( Status );
  974. }
  975. //
  976. // This will only be true if there was an exception above --
  977. // return the exception code as an error
  978. //
  979. if (ERROR_SUCCESS != ret) {
  980. return ret;
  981. }
  982. //
  983. // Quick check for a "restricted" handle
  984. //
  985. if ( REGSEC_CHECK_HANDLE( hKey ) )
  986. {
  987. if ( ! REGSEC_CHECK_PATH( hKey, lpSubKey ) )
  988. {
  989. return( ERROR_ACCESS_DENIED );
  990. }
  991. hKey = REGSEC_CLEAR_HANDLE( hKey );
  992. ASSERT( RestrictedMachineHandle == hKey );
  993. #ifndef LOCAL
  994. UseFakeMachineKey = TRUE;
  995. #endif LOCAL
  996. }
  997. //
  998. // Impersonate the client.
  999. //
  1000. RPC_IMPERSONATE_CLIENT( NULL );
  1001. #ifndef LOCAL
  1002. //
  1003. // open a new key in the caller's context
  1004. //
  1005. if( UseFakeMachineKey ) {
  1006. Status = OpenMachineKey(&hKey);
  1007. if( !NT_SUCCESS(Status) ) {
  1008. ret = RtlNtStatusToDosError( Status );
  1009. RPC_REVERT_TO_SELF();
  1010. return ret;
  1011. }
  1012. }
  1013. #endif LOCAL
  1014. //
  1015. // Subtract the NULLs from the Length of the provided string.
  1016. // This was added on the client side so that the NULL was
  1017. // transmited by RPC.
  1018. //
  1019. lpSubKey->Length -= sizeof( UNICODE_NULL );
  1020. //
  1021. // Initialize the OBJECT_ATTRIBUTES structure and open the key.
  1022. //
  1023. InitializeObjectAttributes(
  1024. &Obja,
  1025. lpSubKey,
  1026. dwOptions & REG_OPTION_OPEN_LINK ? (OBJ_OPENLINK | OBJ_CASE_INSENSITIVE)
  1027. : OBJ_CASE_INSENSITIVE,
  1028. hKey,
  1029. NULL
  1030. );
  1031. #ifdef LOCAL
  1032. if ( REG_CLASS_IS_SPECIAL_KEY(hKey) ||
  1033. ( (gdwRegistryExtensionFlags & TERMSRV_ENABLE_PER_USER_CLASSES_REDIRECTION)
  1034. && ExtractClassKey(&hKey,lpSubKey) ) ) {
  1035. Status = BaseRegOpenClassKey(
  1036. hKey,
  1037. lpSubKey,
  1038. dwOptions,
  1039. samDesired,
  1040. phkResult);
  1041. } else
  1042. #endif // LOCAL
  1043. {
  1044. //
  1045. // Obja was initialized above
  1046. //
  1047. Status = NtOpenKey(
  1048. phkResult,
  1049. samDesired,
  1050. &Obja);
  1051. }
  1052. #ifndef LOCAL
  1053. //
  1054. // close the fake machine key
  1055. //
  1056. if( UseFakeMachineKey ) {
  1057. NtClose(hKey);
  1058. }
  1059. #endif LOCAL
  1060. RPC_REVERT_TO_SELF();
  1061. ret = (error_status_t)RtlNtStatusToDosError( Status );
  1062. #ifdef LOCAL
  1063. if (STATUS_ACCESS_DENIED == Status)
  1064. {
  1065. //If key could not be opened with SamDesired access
  1066. //open it with MAXIMUM_ALLOWED.
  1067. //do it only if it's terminal server and proper
  1068. //flag is set in the registry.
  1069. if ( gdwRegistryExtensionFlags & TERMSRV_ENABLE_ACCESS_FLAG_MODIFICATION )
  1070. {
  1071. {
  1072. //MAXIMUM_ALLOWED does not include ACCESS_SYSTEM_SECURITY
  1073. //so if user asks for this permission, we need to add it.
  1074. //It could result in ACCESS_DENIED error but for
  1075. //TS App. Compat. it is not important.
  1076. if(REG_CLASS_IS_SPECIAL_KEY(hKey))
  1077. {
  1078. Status = BaseRegOpenClassKey(
  1079. hKey,
  1080. lpSubKey,
  1081. dwOptions,
  1082. (samDesired & (KEY_WOW64_RES | ACCESS_SYSTEM_SECURITY)) | MAXIMUM_ALLOWED,
  1083. phkResult);
  1084. }
  1085. else
  1086. {
  1087. Status = NtOpenKey(
  1088. phkResult,
  1089. (samDesired & (KEY_WOW64_RES | ACCESS_SYSTEM_SECURITY)) | MAXIMUM_ALLOWED,
  1090. &Obja);
  1091. }
  1092. // Give app back the original error
  1093. if (!NT_SUCCESS(Status)) {
  1094. Status = STATUS_ACCESS_DENIED;
  1095. }
  1096. ret = (error_status_t)RtlNtStatusToDosError( Status );
  1097. }
  1098. }
  1099. }
  1100. if ((!REG_CLASS_IS_SPECIAL_KEY(hKey)) && !NT_SUCCESS(Status) && gpfnTermsrvOpenRegEntry) {
  1101. //
  1102. // Obja was initialized above
  1103. //
  1104. if (gpfnTermsrvOpenRegEntry(phkResult,
  1105. samDesired,
  1106. &Obja)) {
  1107. Status = STATUS_SUCCESS;
  1108. ret = (error_status_t)RtlNtStatusToDosError( Status );
  1109. }
  1110. }
  1111. #if defined(LEAK_TRACK)
  1112. if (g_RegLeakTraceInfo.bEnableLeakTrack) {
  1113. if (ERROR_SUCCESS == ret) {
  1114. (void) TrackObject(*phkResult);
  1115. }
  1116. }
  1117. #endif (LEAK_TRACK)
  1118. *lpSubKey = TmpStr; //Restore original SubKey string
  1119. #endif // LOCAL
  1120. return ret;
  1121. }
  1122. //
  1123. // BaseRegGetVersion - new for Chicago to determine what version a registry
  1124. // key is connected to.
  1125. //
  1126. error_status_t
  1127. BaseRegGetVersion(
  1128. IN HKEY hKey,
  1129. OUT LPDWORD lpdwVersion
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. New for Win95, allows a caller to determine what version a registry
  1134. key is connected to.
  1135. Arguments:
  1136. hKey - Supplies a handle to an open key.
  1137. lpdwVersion - Returns the registry version.
  1138. Return Value:
  1139. Returns ERROR_SUCCESS (0) for success;
  1140. If successful, BaseRegGetVersion returns the registry version in lpdwVersion
  1141. --*/
  1142. {
  1143. if (lpdwVersion != NULL) {
  1144. *lpdwVersion = REMOTE_REGISTRY_VERSION;
  1145. return(ERROR_SUCCESS);
  1146. }
  1147. //
  1148. // ERROR_NOACCESS is kind of a weird thing to return,
  1149. // but we want to return something different in the
  1150. // NULL case because that is how we tell whether we
  1151. // are talking to a Win95 machine. Win95's implementation
  1152. // of BaseRegGetVersion does not actually fill in the
  1153. // version. It just returns ERROR_SUCCESS or
  1154. // ERROR_INVALID_PARAMETER.
  1155. //
  1156. return(ERROR_NOACCESS);
  1157. }