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.

2396 lines
65 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. RegClass.c
  5. Abstract:
  6. This module contains routines to manipulate class registration
  7. registry keys for the win32 registry apis. These routines are called
  8. from several of the functions for manipulating registry, including
  9. the functions that open, enumerate, create, and delete keys.
  10. Author:
  11. Adam P. Edwards (adamed) 14-Nov-1997
  12. Key Functions:
  13. OpenCombinedClassesRoot
  14. BaseRegGetKeySemantics
  15. BaseRegOpenClassKey
  16. BaseRegOpenClassKeyFromLocation
  17. BaseRegGetUserAndMachineClass
  18. Notes:
  19. ****************************************************
  20. PLEASE READ THIS IF YOU ARE NEW TO THIS CODE!!!!
  21. ****************************************************
  22. Starting with NT5, the HKEY_CLASSES_ROOT key is per-user
  23. instead of per-machine -- previously, HKCR was an alias for
  24. HKLM\Software\Classes.
  25. The per-user HKCR combines machine classes stored it the
  26. traditional HKLM\Software\Classes location with classes
  27. stored in HKCU\Software\Classes.
  28. Certain keys, such as CLSID, will have subkeys that come
  29. from both the machine and user locations. When there is a conflict
  30. in key names, the user oriented key overrides the other one --
  31. only the user key is seen in that case.
  32. Here are the key ideas for this implementation:
  33. 1. The changes for this module only affect keys under
  34. HKEY_CLASSES_ROOT. Only the Local registry
  35. implementation supports HKCR so all the changes are
  36. local only, they do not exist in the
  37. remote rpc registry server.
  38. 2. We parse each key under HKCR as
  39. <prefix>\<intermediate>\<special>[\<classreg>]\[<lastelement>]
  40. where <prefix> is one of the forms
  41. \Registry\Machine\Software\Classes
  42. \Registry\User\<sid>\Software\Classes
  43. \Registry\User\<sid>_Classes
  44. <intermediate> can be a subpath of arbitrary length
  45. <special> is a certain list of keys, shown below in the
  46. gSpecialSubtrees table, e.g. IID, CLSID.
  47. <classreg> is any subkey of <special>. <lastelement> is
  48. the remainder of the path.
  49. 3. In order to quickly distinguish keys in HKCR from keys not in HKCR, we
  50. use tag bits on each registry handle that we return from an open or create
  51. if the key is under HKCR. When the HKCR predefined handle is opened,
  52. we set a tag on its handle index -- any children open or created with a
  53. parent key whose tag is set like this will inherit the tag. There are other
  54. tags, such as those for local and remote regkeys, already in use prior
  55. to the implementation of per user class registration in NT5. Please see
  56. the header file for more information on how to interpret the tags.
  57. 4. The special keys have the following properties which differentiate
  58. them from standard registry keys:
  59. a. The children of a special key come from both HKLM\Software\Classes
  60. and HKCU\Software\Classes. Thus, since CLSID is a special key,
  61. if HKLM\Software\Classes\CLSID\Key1 exists and
  62. HKCU\Software\Classes\CLSID\Key2 exists, one would find the keys
  63. Key1 and Key2 under HKCR\CLSID.
  64. b. If the same key exists in both the user and machine locations, only
  65. the user version of the key is seen under HKCR.
  66. 5. To create the illusion described above, the code for several api's
  67. had to be modified:
  68. a. RegOpenKeyEx -- for HKCR subkeys, this api was modified to look
  69. for the key to open first in the user part of the registry,
  70. then the machine part if the user version did not exist. All
  71. keys opened with HKCR as an ancestor get a bit set in the handle
  72. index.
  73. b. RegCreateKeyEx -- modified in a fashion similar to RegOpenKeyEx.
  74. c. RegDeleteKey -- modified to find key to delete in fashion similar
  75. to RegOpenKeyEx.
  76. d. RegEnumKeyEx -- extensive changes for HKCR. Previously this api was
  77. simply a wrapper to the kernel version. This is insufficient now
  78. because the kernel knows nothing of our hkcr scheme. See regecls.*
  79. for details.
  80. e. RegQueryInfoKey -- changes related to RegEnumKeyEx changes -- see
  81. regecls.*, regqkey.c.
  82. It should be noted that HKCU\Software\Classes is not the true
  83. location of the user-only class data. If it were, all the class
  84. data would be in ntuser.dat, which roams with the user. Since
  85. class data can get very large, installation of a few apps
  86. would cause HKCU (ntuser.dat) to grow from a manageable size
  87. to many megabytes. Since user-specific class data comes from
  88. the directory, it does not need to roam and therefore it was
  89. separated from HKCU (ntuser.dat) and stored in another hive
  90. mounted under HKEY_USERS.
  91. It is still desirable to allow access to this hive through
  92. HKCU\Software\Classes, so we use some trickery (symlinks) to
  93. make it seem as if the user class data exists there.
  94. **************************
  95. IMPORTANT ASSUMPTIONS:
  96. **************************
  97. This code assumes that all special keys exist in both
  98. HKEY_LOCAL_MACHINE\Software\Classes and HKEY_CURRENT_USER\Software\Classes.
  99. The code may break if this is not true.
  100. --*/
  101. #ifdef LOCAL
  102. #include <rpc.h>
  103. #include <string.h>
  104. #include <wchar.h>
  105. #include "regrpc.h"
  106. #include "localreg.h"
  107. #include "regclass.h"
  108. #include "regecls.h"
  109. #include <malloc.h>
  110. NTSTATUS QueryKeyInfo(
  111. HKEY hKey,
  112. PKEY_FULL_INFORMATION* ppKeyFullInfo,
  113. ULONG BufferLength,
  114. BOOL fClass,
  115. USHORT MaxClassLength);
  116. extern HKEY HKEY_ClassesRoot;
  117. BOOL gbCombinedClasses = TRUE;
  118. PKEY_VALUE_PARTIAL_INFORMATION gpNameSpaceKeyInfo = NULL;
  119. #if defined(_REGCLASS_MALLOC_INSTRUMENTED_)
  120. RTL_CRITICAL_SECTION gRegClassHeapCritSect;
  121. DWORD gcbAllocated = 0;
  122. DWORD gcAllocs = 0;
  123. DWORD gcbMaxAllocated = 0;
  124. DWORD gcMaxAllocs = 0;
  125. PVOID gpvAllocs;
  126. #endif // defined(_REGCLASS_MALLOC_INSTRUMENTED_)
  127. UNICODE_STRING gMachineClassesName = {
  128. REG_MACHINE_CLASSES_HIVE_NAMELEN,
  129. REG_MACHINE_CLASSES_HIVE_NAMELEN,
  130. REG_MACHINE_CLASSES_HIVE_NAME};
  131. error_status_t
  132. OpenCombinedClassesRoot(
  133. IN REGSAM samDesired,
  134. OUT HANDLE * phKey
  135. )
  136. /*++
  137. Routine Description:
  138. Attempts to open the the HKEY_CLASSES_ROOT predefined handle.
  139. Arguments:
  140. ServerName - Not used.
  141. samDesired - This access mask describes the desired security access
  142. for the key.
  143. phKey - Returns a handle to the key \REGISTRY\MACHINE\SOFTWARE\CLASSES.
  144. Return Value:
  145. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  146. --*/
  147. {
  148. OBJECT_ATTRIBUTES Obja;
  149. NTSTATUS Status;
  150. UNICODE_STRING UsersHive;
  151. UNICODE_STRING UsersMergedHive;
  152. // first try for a per-user HKCR
  153. RtlFormatCurrentUserKeyPath( &UsersHive );
  154. UsersMergedHive.MaximumLength = UsersHive.MaximumLength +
  155. REG_USER_HIVE_CLASSES_SUFFIXLEN + REG_CHAR_SIZE;
  156. //
  157. // alloca does not return NULL on failure, it throws an exception,
  158. // so return value is not checked.
  159. //
  160. UsersMergedHive.Buffer = alloca(UsersMergedHive.MaximumLength);
  161. RtlCopyUnicodeString(&UsersMergedHive, &UsersHive );
  162. // add the _Merged_Classes suffix
  163. Status = RtlAppendUnicodeToString( &UsersMergedHive, REG_USER_HIVE_CLASSES_SUFFIX);
  164. ASSERT(NT_SUCCESS(Status));
  165. //
  166. // Initialize the OBJECT_ATTRIBUTES structure so that it creates
  167. // (opens) the key "\HKU\<sid>_Merged_Classes" with a Security
  168. // Descriptor that allows everyone complete access.
  169. //
  170. InitializeObjectAttributes(
  171. &Obja,
  172. &UsersMergedHive,
  173. OBJ_CASE_INSENSITIVE,
  174. NULL,
  175. NULL
  176. );
  177. Status = NtOpenKey(
  178. phKey,
  179. samDesired, // MAXIMUM_ALLOWED,
  180. &Obja
  181. );
  182. RtlFreeUnicodeString( &UsersHive );
  183. //
  184. // This key is the ancestor of all keys in HKCR, so
  185. // we must mark its handle so that its origin in HKCR
  186. // is propagated to all children opened with this handle
  187. //
  188. if (NT_SUCCESS(Status)) {
  189. *phKey = REG_CLASS_SET_SPECIAL_KEY(*phKey);
  190. }
  191. return Status;
  192. }
  193. NTSTATUS BaseRegGetKeySemantics(
  194. HKEY hkParent,
  195. PUNICODE_STRING pSubKey,
  196. SKeySemantics* pKeySemantics)
  197. /*++
  198. Routine Description:
  199. This function parses a key in HKEY_CLASSES_ROOT. It is used to determine if a given key
  200. is a class registration unit key, as well as other syntactic / semantic information about
  201. the key. It sets the value of pfIsClsRegKey to TRUE if it is, FALSE if not.
  202. The key in question is defined by the (hkParent, pSubKey) pair.
  203. Definitions for terms such as Prefix, Special Key, and class registration can
  204. be found at the top of this module.
  205. Arguments:
  206. hkParent - parent portion of key
  207. pSubKey - child portion of key
  208. pKeySemantics - pointer to struct containing key semantic information -- the
  209. following members of this structure are affected:
  210. _fUser: TRUE if this key is rooted in HKEY_USERS, FALSE if not
  211. _fMachine: TRUE if this key is rooted in HKLM, FALSE if not
  212. _fCombinedClasses: TRUE if this key is rooted in HKEY_USERS\\<Sid>_Classes
  213. _fClassRegistration: TRUE if this key is a class registration unit
  214. _fClassRegParent: TRUE if this key is the parent of a class registration unit
  215. _ichKeyStart: index to start of a class reg after the prefix -- this is after
  216. the pathsep which follows the prefix
  217. _cbPrefixLen: Length (in bytes) of prefix from start of full path
  218. _cbSpecialKey: Length (in bytes) of the name of the special key -- this is
  219. not from the start of the full path, just that key name only. It
  220. includes an initial pathsep.
  221. _cbClassRegKey: length of class reg key name (not from start of full path).
  222. Includes an initial pathsep.
  223. _cbFullPath: size of buffer structure pointed to by _pFullPath. On return,
  224. this member is set to the number of bytes written to _pFullPath
  225. by the function, or the required number of bytes if the buffer
  226. passed in was too small
  227. _pFullPath: KEY_NAME_INFORMATION structure containing the full pathname
  228. of the registry key defined by (hkParent, pSubKey). This pathname
  229. is null terminated.
  230. Returns:
  231. NT_SUCCESS If the function completed successfully. If the buffer pointed to by
  232. pKeySemantics->_pFullPath is not large enough to hold the name of the key, the
  233. function returns STATUS_BUFFER_TOO_SMALL and the required size in bytes is
  234. written to pKeySemantics->_cbFullPath. The caller may then reallocate the buffer
  235. and call this function again. All other errors return the appropriate NTSTATUS
  236. failure code.
  237. Notes:
  238. After calling this function and getting a successful return status, the pKeySemantics
  239. structure should be freed by calling BaseRegReleaseKeySemantics
  240. --*/
  241. {
  242. NTSTATUS Status;
  243. UNICODE_STRING NameInfo;
  244. PKEY_NAME_INFORMATION pNameInfo;
  245. USHORT ichClassesKeyNameEnd;
  246. USHORT ichSpecialKeyNameEnd;
  247. USHORT cbName;
  248. ULONG cbObjInfo;
  249. WCHAR* szClassRegKeyEnd;
  250. //
  251. // Save in params
  252. //
  253. cbObjInfo = pKeySemantics->_cbFullPath - REG_CHAR_SIZE; // subtract one for trailing \0
  254. pNameInfo = pKeySemantics->_pFullPath;
  255. //
  256. // reset out params
  257. //
  258. memset(&(pKeySemantics->_pFullPath), 0, sizeof(*(pKeySemantics->_pFullPath->Name)));
  259. memset(pKeySemantics, 0, sizeof(*pKeySemantics));
  260. //
  261. // restore in params
  262. //
  263. pKeySemantics->_pFullPath = pNameInfo;
  264. pKeySemantics->_cbFullPath = cbObjInfo;
  265. //
  266. // Get full name of key -- first, we need to find the path
  267. // for the registry key hkParent
  268. //
  269. if (!hkParent) {
  270. //
  271. // If no key name was specified, the full path is simply the subkey name
  272. //
  273. pKeySemantics->_cbFullPath = REG_CHAR_SIZE;
  274. (pKeySemantics->_pFullPath->Name)[0] = L'\0';
  275. pKeySemantics->_pFullPath->NameLength = 0;
  276. pKeySemantics->_cbFullPath = sizeof(*(pKeySemantics->_pFullPath));
  277. } else {
  278. Status = NtQueryKey(
  279. hkParent,
  280. KeyNameInformation,
  281. pKeySemantics->_pFullPath,
  282. cbObjInfo,
  283. &pKeySemantics->_cbFullPath);
  284. if (STATUS_KEY_DELETED == Status) {
  285. Status = STATUS_SUCCESS;
  286. }
  287. //
  288. // Kernel set the _cbFullPath member to the necessary size -- tack
  289. // on the length of the subkey too
  290. //
  291. //
  292. // Kernel set the _cbFullPath member to the necessary size -- tack
  293. // on the length of the subkey too
  294. //
  295. pKeySemantics->_cbFullPath += pSubKey->Length + REG_CHAR_SIZE * 2;
  296. //
  297. // The retrieval of the object's name information may have succeeded,
  298. // but we still need to append the subkey, so verify that enough
  299. // space is left
  300. //
  301. if (NT_SUCCESS(Status) && (cbObjInfo < pKeySemantics->_cbFullPath)) {
  302. //
  303. // we have successfully retrieved the info from the kernel,
  304. // but adding the subkey, we overflow ==> allocate a buffer
  305. // big enough and copy the info from _pFullPath
  306. //
  307. pNameInfo = (PKEY_NAME_INFORMATION) RegClassHeapAlloc(
  308. pKeySemantics->_cbFullPath);
  309. if (!pNameInfo) {
  310. return STATUS_NO_MEMORY;
  311. }
  312. RtlCopyMemory(pNameInfo->Name,
  313. pKeySemantics->_pFullPath->Name,
  314. pKeySemantics->_pFullPath->NameLength);
  315. pNameInfo->NameLength = pKeySemantics->_pFullPath->NameLength;
  316. }
  317. if (!NT_SUCCESS(Status)) {
  318. //
  319. // Retry by allocating a new buffer if the kernel thought the
  320. // supplied buffer was too small. Add extra padding
  321. // because we may need to add a null terminator and pathsep later
  322. //
  323. if (STATUS_BUFFER_OVERFLOW == Status) {
  324. //
  325. // The _cbFullPath member was to the required length in the
  326. // call to NtQueryKey above and includes extra padding
  327. // for appending more characters
  328. //
  329. pNameInfo = (PKEY_NAME_INFORMATION) RegClassHeapAlloc(
  330. pKeySemantics->_cbFullPath);
  331. if (!pNameInfo) {
  332. return STATUS_NO_MEMORY;
  333. }
  334. cbObjInfo = pKeySemantics->_cbFullPath;
  335. //
  336. // Retry -- we should have a large enough buffer now
  337. //
  338. Status = NtQueryKey(
  339. hkParent,
  340. KeyNameInformation,
  341. pNameInfo,
  342. cbObjInfo,
  343. &pKeySemantics->_cbFullPath);
  344. if (STATUS_KEY_DELETED == Status) {
  345. Status = STATUS_SUCCESS;
  346. }
  347. }
  348. if (!NT_SUCCESS(Status)) {
  349. //
  350. // We allocated heap for the second query, but since it failed,
  351. // we need to free the allocated memory.
  352. //
  353. if (pNameInfo != pKeySemantics->_pFullPath) {
  354. RegClassHeapFree(pNameInfo);
  355. }
  356. return Status;
  357. }
  358. }
  359. }
  360. //
  361. // If this isn't set, we know a non-registry key handle was passed in since
  362. // all registry handles have a path associated with them, whereas other types
  363. // of handles may not
  364. //
  365. if (!(pNameInfo->Name)) {
  366. return STATUS_INVALID_HANDLE;
  367. }
  368. //
  369. // We will always return success after this point, so it's
  370. // ok to set the full path member of the structure now. Make
  371. // sure we set the flag indicating that we had to allocate
  372. // memory to store the name if that was indeed the case
  373. //
  374. if (pNameInfo != pKeySemantics->_pFullPath) {
  375. pKeySemantics->_fAllocedNameBuf = TRUE;
  376. }
  377. pKeySemantics->_pFullPath = pNameInfo;
  378. //
  379. // Now that we know the name of the parent key, we can concatenate it
  380. // with the pSubKey parameter
  381. //
  382. //
  383. // First we need to add a trailing pathsep and NULL terminate it
  384. //
  385. pNameInfo->Name[pNameInfo->NameLength / 2] = L'\\';
  386. pNameInfo->Name[pNameInfo->NameLength / 2 + 1] = L'\0';
  387. //
  388. // Get a unicode string so we can perform string operations
  389. //
  390. RtlInitUnicodeString(&NameInfo, pNameInfo->Name);
  391. //
  392. // Adjust the length to reflect the unicode string -- right
  393. // now it inlcudes the length of the Length member of the
  394. // KEY_NAME_INFORMATION structure -- we just want the length
  395. // of the string
  396. //
  397. pNameInfo->NameLength = NameInfo.Length;
  398. //
  399. // Now add space to the string for the subkey and slash
  400. //
  401. NameInfo.MaximumLength += pSubKey->Length + REG_CHAR_SIZE;
  402. //
  403. // append the subkey to the parent key
  404. //
  405. //
  406. // We made sure the buffer was big enough, so the only way this will
  407. // fail is if pSubKey is invalid, which will cause an
  408. // access violation, so no need no test
  409. //
  410. Status = RtlAppendUnicodeStringToString(&NameInfo, pSubKey);
  411. ASSERT(NT_SUCCESS(Status));
  412. //
  413. // if the key name isn't at least as long as the shortest
  414. // classes hive name, leave.
  415. // This assumes that
  416. // HKU\\Sid_Classes is shorter than
  417. // HKU\\Sid\\Software\\Classes
  418. //
  419. if (NameInfo.Length < REG_CLASSES_HIVE_MIN_NAMELEN) {
  420. return STATUS_SUCCESS;
  421. }
  422. //
  423. // remove any terminating pathsep
  424. //
  425. if (NameInfo.Buffer[NameInfo.Length / 2 - 1] == L'\\') {
  426. NameInfo.Length-= sizeof(L'\\');
  427. }
  428. //
  429. // We're done getting the name of the key, save its length
  430. // for the caller
  431. //
  432. pNameInfo->NameLength = NameInfo.Length;
  433. //
  434. // cache the name length
  435. //
  436. cbName = (USHORT) pNameInfo->NameLength;
  437. //
  438. // null terminate the name
  439. //
  440. pNameInfo->Name[cbName / REG_CHAR_SIZE] = L'\0';
  441. if (REG_CLASS_IS_SPECIAL_KEY(hkParent)) {
  442. pKeySemantics->_fCombinedClasses = TRUE;
  443. }
  444. //
  445. // First, see if we're even in the correct hive -- we can check
  446. // certain characters in the path to avoid doing extra string compares
  447. //
  448. switch (pNameInfo->Name[REG_CLASSES_FIRST_DISTINCT_ICH])
  449. {
  450. case L'M':
  451. case L'm':
  452. //
  453. // check if we're in the machine hive
  454. //
  455. NameInfo.Length = REG_MACHINE_CLASSES_HIVE_NAMELEN;
  456. //
  457. // Compare prefix with the name for the machine classes key
  458. // Set machine flag if comparison returns equality.
  459. //
  460. if (RtlEqualUnicodeString(
  461. &NameInfo,
  462. &gMachineClassesName,
  463. TRUE) != 0) {
  464. NameInfo.Length = cbName;
  465. ichClassesKeyNameEnd = REG_MACHINE_CLASSES_HIVE_NAMECCH;
  466. pKeySemantics->_fMachine = TRUE;
  467. break;
  468. }
  469. return STATUS_SUCCESS;
  470. case L'U':
  471. case L'u':
  472. //
  473. // check if we're in the users hive
  474. //
  475. {
  476. //
  477. // This will try to find the user prefix -- it fails
  478. // if we're not in the user hive and returns a zero-length
  479. // prefix. Set the flag if it succeeds.
  480. //
  481. ichClassesKeyNameEnd = BaseRegGetUserPrefixLength(
  482. &NameInfo);
  483. if (!ichClassesKeyNameEnd) {
  484. return STATUS_SUCCESS;
  485. }
  486. pKeySemantics->_fUser = TRUE;
  487. break;
  488. }
  489. //
  490. // this isn't a class registration because it isn't in any of the
  491. // correct trees
  492. //
  493. return STATUS_SUCCESS;
  494. default:
  495. //
  496. // the appropriate characters weren't in the key name, so
  497. // this can't be a class registration
  498. //
  499. return STATUS_SUCCESS;
  500. }
  501. //
  502. // At this point, we've found the prefix. The next part of the key
  503. // is the special key -- we look for that now.
  504. //
  505. pKeySemantics->_cbPrefixLen = ichClassesKeyNameEnd * REG_CHAR_SIZE;
  506. pKeySemantics->_ichKeyStart = ichClassesKeyNameEnd;
  507. //
  508. // the start of the special key
  509. // is the character right after the end of the prefix
  510. //
  511. if (pKeySemantics->_cbPrefixLen < pNameInfo->NameLength) {
  512. pKeySemantics->_ichKeyStart++;
  513. }
  514. //
  515. // search for a special subkey of the classes hive --
  516. // this will return the index in the full path of the end
  517. // of the special key name.
  518. //
  519. ichSpecialKeyNameEnd = BaseRegCchSpecialKeyLen(
  520. &NameInfo,
  521. ichClassesKeyNameEnd,
  522. pKeySemantics);
  523. //
  524. // if we find that the entire key is a special key, we're done --
  525. // there's nothing after it in this case so there's no more to
  526. // parse
  527. //
  528. if (pKeySemantics->_fClassRegParent) {
  529. return STATUS_SUCCESS;
  530. }
  531. //
  532. // at this point, we know the key itself is a class registration
  533. //
  534. pKeySemantics->_fClassRegistration = TRUE;
  535. pKeySemantics->_cbClassRegKey = (USHORT) pNameInfo->NameLength -
  536. (pKeySemantics->_cbPrefixLen + pKeySemantics->_cbSpecialKey + REG_CHAR_SIZE);
  537. return STATUS_SUCCESS;
  538. }
  539. void BaseRegReleaseKeySemantics(SKeySemantics* pKeySemantics)
  540. /*++
  541. Routine Description:
  542. This function frees resources associated with an SKeySemantics object
  543. Arguments:
  544. pKeySemantics - pointer to SKeySemantics object whose resources should
  545. be freed
  546. Return Value:
  547. None
  548. --*/
  549. {
  550. if (pKeySemantics->_fAllocedNameBuf) {
  551. RegClassHeapFree(pKeySemantics->_pFullPath);
  552. }
  553. }
  554. NTSTATUS BaseRegOpenClassKey(
  555. IN HKEY hKey,
  556. IN PUNICODE_STRING lpSubKey,
  557. IN DWORD dwOptions,
  558. IN REGSAM samDesired,
  559. OUT PHKEY phkResult)
  560. /*++
  561. Routine Description:
  562. This function is used to retry opening a class registration key.
  563. Arguments:
  564. hKey - Supplies a handle to an open key. The lpSubKey pathname
  565. parameter is relative to this key handle.
  566. lpSubKey - Supplies the downward key path to the key to open.
  567. lpSubKey is always relative to the key specified by hKey.
  568. dwOptions -- reserved.
  569. samDesired -- This access mask describes the desired security access
  570. for the key.
  571. phkResult -- Returns the handle to the newly opened key.
  572. Return Value:
  573. Returns STATUS_SUCCESS if a key was successfully opened, otherwise it
  574. returns an NTSTATUS error code
  575. Note:
  576. The key must be a class registration key in order to be opened
  577. --*/
  578. {
  579. BYTE rgNameInfoBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE];
  580. SKeySemantics keyinfo;
  581. NTSTATUS Status;
  582. //
  583. // Set up the buffer that will hold the name of the key
  584. //
  585. keyinfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameInfoBuf;
  586. keyinfo._cbFullPath = sizeof(rgNameInfoBuf);
  587. keyinfo._fAllocedNameBuf = FALSE;
  588. //
  589. // get information about this key
  590. //
  591. Status = BaseRegGetKeySemantics(hKey, lpSubKey, &keyinfo);
  592. if (!NT_SUCCESS(Status)) {
  593. return Status;
  594. }
  595. //
  596. // Use the information above to look in both user and machine
  597. // hives for the key to be opened
  598. //
  599. Status = BaseRegOpenClassKeyFromLocation(
  600. &keyinfo,
  601. hKey,
  602. lpSubKey,
  603. samDesired,
  604. LOCATION_BOTH,
  605. phkResult);
  606. BaseRegReleaseKeySemantics(&keyinfo);
  607. return Status;
  608. }
  609. NTSTATUS BaseRegOpenClassKeyFromLocation(
  610. SKeySemantics* pKeyInfo,
  611. HKEY hKey,
  612. PUNICODE_STRING lpSubKey,
  613. REGSAM samDesired,
  614. DWORD dwLocation,
  615. HKEY* phkResult)
  616. /*++
  617. Routine Description:
  618. This function will try to open a class registration key that has no link
  619. in the combined classes hive -- it does this by attempting to open the
  620. class registration in the machine hive. If it succeeds, it also creates
  621. a link to the key in the combined classes hive
  622. Arguments:
  623. pKeyInfo -- structure supplying information about a key
  624. hKey -- Supplies a handle to an open key. The lpSubKey pathname
  625. parameter is relative to this key handle.
  626. lpSubKey -- Supplies the downward key path to the key to open.
  627. lpSubKey is always relative to the key specified by hKey.
  628. samDesired -- This access mask describes the desired security access
  629. for the key.
  630. phkResult -- Returns the handle to the newly opened key. If NULL,
  631. no open key handle is returned.
  632. dwLocation -- set of flags that specify where to look for the key.
  633. If LOCATION_MACHINE is specified, the function looks in machine.
  634. If LOCATION_USER is specified, the function looks in user. Both
  635. flags may be specified simultaneously, so that it will look in both
  636. places, or LOCATION_BOTH may be specified for this purpose. If
  637. the function looks in both places, an existing key in the user hive
  638. takes precedence over one in the machine hive.
  639. Return Value:
  640. Returns STATUS_SUCCESS if a key was successfully opened, otherwise it
  641. returns an NTSTATUS error code
  642. Note:
  643. --*/
  644. {
  645. WCHAR* FullPathBuf;
  646. USHORT NewPathLen;
  647. UNICODE_STRING ClassRegkey;
  648. UNICODE_STRING ClassRegSubkey;
  649. OBJECT_ATTRIBUTES Obja;
  650. NTSTATUS Status;
  651. USHORT PrefixLen;
  652. //
  653. // Init locals
  654. //
  655. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  656. NewPathLen = (USHORT) pKeyInfo->_pFullPath->NameLength + REG_CLASSES_SUBTREE_PADDING;
  657. //
  658. // Allocate space for the remapped key -- note that if alloca
  659. // fails, it throws an exception, so we don't check for NULL return value
  660. //
  661. FullPathBuf = (WCHAR*) RegClassHeapAlloc(NewPathLen);
  662. if (!FullPathBuf) {
  663. return STATUS_NO_MEMORY;
  664. }
  665. //
  666. // Set up a unicode string to use this buffer
  667. //
  668. ClassRegkey.MaximumLength = NewPathLen;
  669. ClassRegkey.Buffer = FullPathBuf;
  670. ASSERT((dwLocation == LOCATION_USER) || (dwLocation == LOCATION_MACHINE) ||
  671. (dwLocation == LOCATION_BOTH));
  672. //
  673. // Opening the entire key is a two step process. First, open
  674. // the class registration portion -- we need to do that from
  675. // either the machine or user location. The second step
  676. // is to open everything after the class registration using the
  677. // key obtained in the first step.
  678. //
  679. //
  680. // Below we try to find a user or machine version of the
  681. // class registration
  682. //
  683. if ( LOCATION_USER & dwLocation ) {
  684. //
  685. // Try the user location -- first, move the key name to
  686. // the user hive's namespace
  687. //
  688. if( pKeyInfo->_fUser ) {
  689. //
  690. // in the user's hive we can try a relative open
  691. //
  692. InitializeObjectAttributes(
  693. &Obja,
  694. lpSubKey,
  695. OBJ_CASE_INSENSITIVE,
  696. hKey, // relative path
  697. NULL);
  698. } else {
  699. //
  700. // we need to do an absolute path open
  701. //
  702. Status = BaseRegTranslateToUserClassKey(
  703. pKeyInfo,
  704. &ClassRegkey,
  705. &PrefixLen);
  706. if (!NT_SUCCESS(Status)) {
  707. goto cleanup;
  708. }
  709. //
  710. // now try opening the key with the new HKCU string
  711. //
  712. InitializeObjectAttributes(
  713. &Obja,
  714. &ClassRegkey,
  715. OBJ_CASE_INSENSITIVE,
  716. NULL, // using absolute path, no hkey
  717. NULL);
  718. }
  719. Status = NtOpenKey(
  720. phkResult,
  721. samDesired,
  722. &Obja);
  723. }
  724. //
  725. // Only try machine if we failed to open user key above
  726. // (or didn't even try to open it)
  727. //
  728. if ((LOCATION_MACHINE & dwLocation) && !NT_SUCCESS(Status)) {
  729. //
  730. // Now try HKLM -- translate the key to the machine
  731. // namespace
  732. //
  733. if( pKeyInfo->_fMachine ) {
  734. //
  735. // in the machine hive we can try a relative open
  736. //
  737. InitializeObjectAttributes(
  738. &Obja,
  739. lpSubKey,
  740. OBJ_CASE_INSENSITIVE,
  741. hKey, // relative path
  742. NULL);
  743. } else {
  744. //
  745. // we need to do an absolute path open
  746. //
  747. Status = BaseRegTranslateToMachineClassKey(
  748. pKeyInfo,
  749. &ClassRegkey,
  750. &PrefixLen);
  751. if (!NT_SUCCESS(Status)) {
  752. goto cleanup;
  753. }
  754. //
  755. // now try opening the key with the new HKLM string
  756. //
  757. InitializeObjectAttributes(
  758. &Obja,
  759. &ClassRegkey,
  760. OBJ_CASE_INSENSITIVE,
  761. NULL, // using absolute path, no hkey
  762. NULL);
  763. }
  764. Status = NtOpenKey(
  765. phkResult,
  766. samDesired,
  767. &Obja);
  768. if (!NT_SUCCESS(Status)) {
  769. goto cleanup;
  770. }
  771. }
  772. //
  773. // mark this key as a class key from HKCR
  774. //
  775. if (NT_SUCCESS(Status)) {
  776. *phkResult = REG_CLASS_SET_SPECIAL_KEY(*phkResult);
  777. }
  778. cleanup:
  779. RegClassHeapFree(FullPathBuf);
  780. return Status;
  781. }
  782. NTSTATUS BaseRegConstructUserClassPrefix(
  783. SKeySemantics* pKeyInfo,
  784. PUNICODE_STRING pUserClassPrefix)
  785. /*++
  786. Routine Description:
  787. This function creates a prefix for a class key that is in the user hive
  788. Arguments:
  789. pKeyInfo - pointer to struct containing key semantic information
  790. pUserClassPrefix - out param for the constructed prefix
  791. Returns: NT_SUCCESS If the function completed successfully.
  792. Notes:
  793. --*/
  794. {
  795. UNICODE_STRING UserKey;
  796. NTSTATUS Status;
  797. //
  798. // The prefix looks like <sid>_Classes
  799. //
  800. //
  801. // First obtain the sid
  802. //
  803. if (pKeyInfo->_fUser) {
  804. UNICODE_STRING SidString;
  805. //
  806. // construct a string that contains the user's sid
  807. //
  808. KeySemanticsGetSid(pKeyInfo, &SidString);
  809. RtlInitUnicodeString(&UserKey, REG_USER_HIVE_NAME);
  810. //
  811. // create a string that starts with the HKU prefix
  812. //
  813. RtlCopyUnicodeString(pUserClassPrefix, &UserKey);
  814. //
  815. // append the sid to the user prefix
  816. //
  817. Status = RtlAppendUnicodeStringToString(pUserClassPrefix, &SidString);
  818. if (!NT_SUCCESS(Status)) {
  819. return Status;
  820. }
  821. } else {
  822. UNICODE_STRING UsersHive;
  823. //
  824. // This will only happen if a special key has been deleted from
  825. // the user hive
  826. //
  827. Status = RtlFormatCurrentUserKeyPath( &UsersHive );
  828. if (!NT_SUCCESS(Status)) {
  829. return Status;
  830. }
  831. RtlCopyUnicodeString(pUserClassPrefix, &UsersHive );
  832. RtlFreeUnicodeString(&UsersHive);
  833. }
  834. //
  835. // Append the suffix to the sid
  836. //
  837. return RtlAppendUnicodeToString(pUserClassPrefix, REG_USER_HIVE_CLASSES_SUFFIX);
  838. }
  839. NTSTATUS BaseRegTranslateToMachineClassKey(
  840. SKeySemantics* pKeyInfo,
  841. PUNICODE_STRING pMachineClassKey,
  842. USHORT* pPrefixLen)
  843. /*++
  844. Routine Description:
  845. This function translates a class key rooted in HKCR to the machine hive
  846. Arguments:
  847. pKeyInfo - pointer to struct containing key semantic information -- the
  848. pMachineClassKey - out param for result of translation
  849. pPrefixLen - out param for length of the prefix of the resulting translation
  850. Returns: NT_SUCCESS If the function completed successfully.
  851. Notes:
  852. --*/
  853. {
  854. UNICODE_STRING MachineKey;
  855. UNICODE_STRING ClassSubkey;
  856. RtlInitUnicodeString(&MachineKey, REG_MACHINE_CLASSES_HIVE_NAME);
  857. //
  858. // get the unique class key portion
  859. //
  860. KeySemanticsRemovePrefix(pKeyInfo, &ClassSubkey, REMOVEPREFIX_KEEP_INITIAL_PATHSEP);
  861. //
  862. // create a string that starts with the HKLM prefix and has the
  863. // desired class registration key as a subkey
  864. //
  865. RtlCopyUnicodeString(pMachineClassKey, &MachineKey);
  866. *pPrefixLen = REG_MACHINE_CLASSES_HIVE_NAMELEN;
  867. return RtlAppendUnicodeStringToString(pMachineClassKey, &ClassSubkey);
  868. }
  869. NTSTATUS BaseRegTranslateToUserClassKey(
  870. SKeySemantics* pKeyInfo,
  871. PUNICODE_STRING pUserClassKey,
  872. USHORT* pPrefixLen)
  873. /*++
  874. Routine Description:
  875. This function translates a class key rooted in HKCR to the user hive
  876. Arguments:
  877. pKeyInfo - pointer to struct containing key semantic information -- the
  878. pUserClassKey - out param for result of translation
  879. pPrefixLen - out param for length of the prefix of the resulting translation
  880. Returns: NT_SUCCESS If the function completed successfully.
  881. Notes:
  882. --*/
  883. {
  884. UNICODE_STRING ClassSubkey;
  885. NTSTATUS Status;
  886. //
  887. // get the unique class key portion
  888. //
  889. KeySemanticsRemovePrefix(pKeyInfo, &ClassSubkey, REMOVEPREFIX_KEEP_INITIAL_PATHSEP);
  890. if (!NT_SUCCESS(Status = BaseRegConstructUserClassPrefix(
  891. pKeyInfo,
  892. pUserClassKey))) {
  893. return Status;
  894. }
  895. *pPrefixLen = pUserClassKey->Length;
  896. //
  897. // finally, append the class key
  898. //
  899. return RtlAppendUnicodeStringToString(pUserClassKey, &ClassSubkey);
  900. }
  901. USHORT BaseRegGetUserPrefixLength(PUNICODE_STRING pFullPath)
  902. /*++
  903. Routine Description:
  904. This function is used to determine the length of the prefix
  905. \\Registry\\User\\<Sid>\\Software\Classes or \\Registry\\User\\\<Sid>_classes
  906. Arguments:
  907. pFullPath - full path of the registry, rooted at \\Registry
  908. Return Value:
  909. Returns the length of the prefix (which must be nonzero), 0 if unsuccessful
  910. --*/
  911. {
  912. UNICODE_STRING UserHive;
  913. UNICODE_STRING FullPath;
  914. USHORT ich;
  915. USHORT ichMax;
  916. FullPath = *pFullPath;
  917. //
  918. // set ourselves up to look for the user hive portion
  919. // of the prefix
  920. //
  921. RtlInitUnicodeString(&UserHive, REG_USER_HIVE_NAME);
  922. if (FullPath.Length <= UserHive.Length) {
  923. return 0;
  924. }
  925. FullPath.Length = UserHive.Length;
  926. //
  927. // check for the user hive prefix, leave if not found
  928. //
  929. if (!RtlEqualUnicodeString(&UserHive, &FullPath, TRUE)) {
  930. return 0;
  931. }
  932. ichMax = pFullPath->Length / REG_CHAR_SIZE;
  933. //
  934. // before looking for the classes subtree, we must skip past
  935. // the user's sid -- the prefix is in the form
  936. // \\Registry\\User\\<sid>\\Software\\Classes or
  937. // \\Registyr\\User\\<sid>_Classes
  938. //
  939. for (ich = REG_USER_HIVE_NAMECCH + 1; ich < ichMax; ich++)
  940. {
  941. //
  942. // if we find a pathsep, we cannot be in the combined
  943. // classes hive or the user classes hive
  944. //
  945. if (pFullPath->Buffer[ich] == L'\\') {
  946. return 0;
  947. }
  948. //
  949. // if we find the underscore character, we are in the combined
  950. // classes hive or the user classes hive -- i.e. the prefix looks like
  951. // \\Registry\\User\\<sid>_Classes
  952. // -- use the underscore to distinguish from other cases
  953. //
  954. if (pFullPath->Buffer[ich] == L'_') {
  955. UNICODE_STRING Suffix;
  956. RtlInitUnicodeString(&Suffix, REG_USER_HIVE_CLASSES_SUFFIX);
  957. FullPath.Length = Suffix.Length;
  958. FullPath.Buffer = &(pFullPath->Buffer[ich]);
  959. // look for the user classes suffix in the user hive
  960. if (RtlEqualUnicodeString(&FullPath, &Suffix, TRUE)) {
  961. return ich + REG_USER_HIVE_CLASSES_SUFFIXCCH;
  962. }
  963. return 0;
  964. }
  965. }
  966. return 0;
  967. }
  968. USHORT BaseRegCchSpecialKeyLen(
  969. PUNICODE_STRING pFullPath,
  970. USHORT ichSpecialKeyStart,
  971. SKeySemantics* pKeySemantics)
  972. /*++
  973. Routine Description:
  974. This function is used to determine the length of a special subkey contained
  975. on the pSpecialKey parameter. If the entire pFullPath is a special key,
  976. a flag in pKeySemantics will be set to TRUE
  977. Arguments:
  978. pFullPath - full path of the registry, rooted at \\Registry
  979. ichSpecialKeyStart - index in the full path of the start of the special key path
  980. pKeySemantics - pointer to structure which stores semantics information about a key
  981. Return Value:
  982. Returns the length of the special key if there is a special key in the pSpecialKey
  983. path, 0 if there is none
  984. Notes:
  985. This function depends on the gSpecialSubtree array being a *sorted* list of special
  986. key names.
  987. --*/
  988. {
  989. WCHAR* wszSpecialKey;
  990. USHORT ichSpecialKeyLen;
  991. ASSERT(pFullPath->Length / REG_CHAR_SIZE >= ichSpecialKeyStart);
  992. //
  993. // For hkcr itself, there is no ancestor -- detect this special
  994. // case and return
  995. //
  996. if (pFullPath->Length / REG_CHAR_SIZE == ichSpecialKeyStart) {
  997. pKeySemantics->_fClassRegParent = TRUE;
  998. return ichSpecialKeyStart;
  999. }
  1000. //
  1001. // The special key is now just the parent of this key -- find
  1002. // the immediate ancestor of this key
  1003. //
  1004. wszSpecialKey = wcsrchr(&(pFullPath->Buffer[ichSpecialKeyStart]), L'\\');
  1005. ASSERT(wszSpecialKey);
  1006. //
  1007. // The length of the special key is the difference
  1008. // between the '\' at the end of the special key and the start
  1009. // of the string
  1010. //
  1011. ichSpecialKeyLen = (USHORT)(wszSpecialKey - pFullPath->Buffer);
  1012. //
  1013. // Store the length of the special key name by itself as well
  1014. //
  1015. pKeySemantics->_cbSpecialKey = ichSpecialKeyLen - ichSpecialKeyStart;
  1016. return ichSpecialKeyLen;
  1017. }
  1018. NTSTATUS BaseRegOpenClassKeyRoot(
  1019. SKeySemantics* pKeyInfo,
  1020. PHKEY phkClassRoot,
  1021. PUNICODE_STRING pClassKeyPath,
  1022. BOOL fMachine)
  1023. /*++
  1024. Routine Description:
  1025. This function will try to open the class root key appropriate to
  1026. a given key being opened from HKEY_CLASSES_ROOT. The key opened is either
  1027. HKEY_USERS\<Sid>_Classes or HKLM\Software\Classes. If the key exists
  1028. in the user portion, then that the user key will be opened. Otherwise,
  1029. the machine key is returned. It also returns the unicode string
  1030. subkey name used to open the key specified in
  1031. pKeyInfo relative to the class root key returned in phkClassRoot.
  1032. Arguments:
  1033. pKeyInfo -- structure supplying information about a key
  1034. phkClassRoot -- out param for the class root key result of the function
  1035. pClassKeyPath -- Supplies the downward key path to the key to open.
  1036. pClassKeyPath is always relative to the key specified by hKey.
  1037. pfMachine -- out param flag that indicates that whether or not
  1038. this key was opened in the machine hive.
  1039. Return Value:
  1040. Returns STATUS_SUCCESS if a key was successfully deleted, otherwise it
  1041. returns an NTSTATUS error code
  1042. Note:
  1043. --*/
  1044. {
  1045. NTSTATUS Status;
  1046. USHORT PrefixLen;
  1047. UNICODE_STRING NewFullPath;
  1048. //
  1049. // Allocate space for a full path -- note that
  1050. // we don't check the return value since alloca throws
  1051. // an exception if it fails
  1052. //
  1053. NewFullPath.Buffer = alloca(pClassKeyPath->MaximumLength);
  1054. NewFullPath.MaximumLength = pClassKeyPath->MaximumLength;
  1055. //
  1056. // Translate to appropriate location
  1057. //
  1058. if (fMachine) {
  1059. Status = BaseRegTranslateToMachineClassKey(
  1060. pKeyInfo,
  1061. &NewFullPath,
  1062. &PrefixLen);
  1063. } else {
  1064. Status = BaseRegTranslateToUserClassKey(
  1065. pKeyInfo,
  1066. &NewFullPath,
  1067. &PrefixLen);
  1068. }
  1069. if (!NT_SUCCESS(Status)) {
  1070. return Status;
  1071. }
  1072. //
  1073. // Open the prefix
  1074. //
  1075. {
  1076. UNICODE_STRING RootKey;
  1077. OBJECT_ATTRIBUTES Obja;
  1078. RootKey.Buffer = NewFullPath.Buffer;
  1079. //
  1080. // Calculate the length of the prefix
  1081. //
  1082. RootKey.Length = PrefixLen;
  1083. //
  1084. // now, get ready to open it
  1085. //
  1086. InitializeObjectAttributes(&Obja,
  1087. &RootKey,
  1088. OBJ_CASE_INSENSITIVE,
  1089. NULL, // full path, no hkey
  1090. NULL);
  1091. Status = NtOpenKey(
  1092. phkClassRoot,
  1093. MAXIMUM_ALLOWED,
  1094. &Obja);
  1095. }
  1096. if (NT_SUCCESS(Status)) {
  1097. //
  1098. // Skip past the prefix
  1099. //
  1100. NewFullPath.Buffer += (PrefixLen / REG_CHAR_SIZE);
  1101. NewFullPath.Length -= PrefixLen;
  1102. if (L'\\' == NewFullPath.Buffer[0]) {
  1103. NewFullPath.Length -= REG_CHAR_SIZE;
  1104. NewFullPath.Buffer ++;
  1105. }
  1106. //
  1107. // Copy everything after the prefix
  1108. //
  1109. RtlCopyUnicodeString(pClassKeyPath, &NewFullPath);
  1110. }
  1111. return Status;
  1112. }
  1113. NTSTATUS
  1114. BaseRegMapClassRegistrationKey(
  1115. HKEY hKey,
  1116. PUNICODE_STRING pSubKey,
  1117. SKeySemantics* pKeyInfo,
  1118. PUNICODE_STRING pDestSubKey,
  1119. BOOL* pfRetryOnAccessDenied,
  1120. PHKEY phkDestResult,
  1121. PUNICODE_STRING* ppSubKeyResult)
  1122. /*++
  1123. Routine Description:
  1124. This function will map a key from HKEY_CLASSES_ROOT into either the
  1125. user hive or machine hive. The remapped key is returned in the
  1126. (*phkDestResult, *ppSubKeyResult) pair.
  1127. Arguments:
  1128. hKey -- root of key to remap
  1129. pSubKey -- Supplies the downward key path to the key to remap.
  1130. pSubKey is always relative to the key specified by hKey.
  1131. pKeyInfo -- structure supplying information about a key
  1132. pDestSubKey -- unicode string in which to store result data
  1133. if the key gets remapped
  1134. pfRetryOnAccessDenied -- out param flag to set indicating whether
  1135. failure to open the remapped key because of access denied should
  1136. be retried
  1137. phkDestResult -- out param for root of remapped key
  1138. ppSubKeyResult -- out param for remainder of path of remapped key
  1139. Return Value:
  1140. Returns STATUS_SUCCESS if a key was successfully deleted, otherwise it
  1141. returns an NTSTATUS error code
  1142. Note:
  1143. --*/
  1144. {
  1145. BOOL fMachine;
  1146. UNICODE_STRING ClassKeyPath;
  1147. NTSTATUS Status;
  1148. HKEY hkUser;
  1149. //
  1150. // by default, use machine
  1151. //
  1152. fMachine = TRUE;
  1153. //
  1154. // Check for existence of the key in the
  1155. // user hive
  1156. //
  1157. Status = BaseRegOpenClassKeyFromLocation(
  1158. pKeyInfo,
  1159. hKey,
  1160. pSubKey,
  1161. MAXIMUM_ALLOWED,
  1162. LOCATION_USER,
  1163. &hkUser);
  1164. if (!NT_SUCCESS(Status)) {
  1165. //
  1166. // a not found error is fine -- this just means that
  1167. // neither key exists already -- in this case we
  1168. // choose to use machine
  1169. //
  1170. if (STATUS_OBJECT_NAME_NOT_FOUND != Status) {
  1171. return Status;
  1172. }
  1173. } else {
  1174. //
  1175. // The user key exists, we choose it over
  1176. // the machine key
  1177. //
  1178. fMachine = FALSE;
  1179. NtClose(hkUser);
  1180. }
  1181. //
  1182. // Get a buffer for the new path
  1183. //
  1184. ClassKeyPath.Buffer = (WCHAR*) RegClassHeapAlloc(
  1185. ClassKeyPath.MaximumLength = ((USHORT) pKeyInfo->_pFullPath->NameLength +
  1186. REG_CLASSES_SUBTREE_PADDING));
  1187. if (!(ClassKeyPath.Buffer)) {
  1188. return STATUS_NO_MEMORY;
  1189. }
  1190. //
  1191. // Remap the key
  1192. //
  1193. Status = BaseRegOpenClassKeyRoot(
  1194. pKeyInfo,
  1195. phkDestResult,
  1196. &ClassKeyPath,
  1197. fMachine);
  1198. if (!NT_SUCCESS(Status)) {
  1199. RegClassHeapFree(ClassKeyPath.Buffer);
  1200. return Status;
  1201. }
  1202. //
  1203. // If the remapped key is in the machine hive, set the flag so that
  1204. // retries are not permitted.
  1205. //
  1206. if (*pfRetryOnAccessDenied && !fMachine) {
  1207. *pfRetryOnAccessDenied = FALSE;
  1208. }
  1209. //
  1210. // phkDestResult, the root portion of the remapped key, was set above.
  1211. // now set the subkey portion and leave
  1212. //
  1213. *pDestSubKey = ClassKeyPath;
  1214. *ppSubKeyResult = pDestSubKey;
  1215. return STATUS_SUCCESS;
  1216. }
  1217. NTSTATUS BaseRegGetUserAndMachineClass(
  1218. SKeySemantics* pKeySemantics,
  1219. HKEY hKey,
  1220. REGSAM samDesired,
  1221. PHKEY phkMachine,
  1222. PHKEY phkUser)
  1223. /*++
  1224. Routine Description:
  1225. This function will return kernel objects corresponding to the user
  1226. and machine components of a given kernel object.
  1227. Arguments:
  1228. pKeySemantics -- supplies information about hKey. This is optional --
  1229. if the caller does not supply it, the function will query for the information.
  1230. This is an optimization for callers that already have this info
  1231. and can save us the time of
  1232. hKey -- key for which to open user and machine versions
  1233. samDesired -- security access mask for one of the returned keys -- see
  1234. note below for important info on this
  1235. phkMachine -- out param for machine version of key
  1236. phkUser -- out param for user version of key
  1237. Return Value:
  1238. Returns STATUS_SUCCESS if a key was successfully deleted, otherwise it
  1239. returns an NTSTATUS error code
  1240. Notes:
  1241. ***VERY IMPORTANT!!!***
  1242. One of the two returned keys will alias hKey -- this way we only open
  1243. one object (one trip to the kernel) instead of two. This means that the caller
  1244. should not blindly call NtClose on the two returned objects -- a == comparison
  1245. between one of the keys and hKey should be made to determine if it that key is
  1246. the alias -- if it is, you should *NOT* call NtClose on it because otherwise the
  1247. owner of hKey will call NtClose on the same handle value after your call to close
  1248. that handle which will cause an exception. You *should* close the handle that does not
  1249. alias hKey -- if you don't you'll get a handle leak.
  1250. Another important note -- only the new key (non-aliased) will have the access mask
  1251. specified in samDesired -- the aliased key is just hKey, so it has the same access
  1252. mask. If you want to ensure the correct access on that key, you'll need to explicitly
  1253. duplicate or open that key with the correct access.
  1254. --*/
  1255. {
  1256. NTSTATUS Status;
  1257. SKeySemantics keyinfo;
  1258. SKeySemantics* pKeyInfo;
  1259. UNICODE_STRING EmptyString = {0, 0, 0};
  1260. BYTE rgNameBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE + sizeof(KEY_NAME_INFORMATION)];
  1261. DWORD dwLocation;
  1262. PHKEY phkNew;
  1263. //
  1264. // Clear out parameters
  1265. //
  1266. *phkMachine = NULL;
  1267. *phkUser = NULL;
  1268. //
  1269. // Try to use caller supplied key information
  1270. //
  1271. if (pKeySemantics) {
  1272. pKeyInfo = pKeySemantics;
  1273. } else {
  1274. //
  1275. // Set buffer to store info about this key
  1276. //
  1277. keyinfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameBuf;
  1278. keyinfo._cbFullPath = sizeof(rgNameBuf);
  1279. keyinfo._fAllocedNameBuf = FALSE;
  1280. //
  1281. // get information about this key
  1282. //
  1283. Status = BaseRegGetKeySemantics(hKey, &EmptyString, &keyinfo);
  1284. if (!NT_SUCCESS(Status)) {
  1285. return Status;
  1286. }
  1287. pKeyInfo = &keyinfo;
  1288. }
  1289. if (pKeyInfo->_fMachine) {
  1290. *phkMachine = hKey;
  1291. dwLocation = LOCATION_USER;
  1292. phkNew = phkUser;
  1293. } else {
  1294. *phkUser = hKey;
  1295. dwLocation = LOCATION_MACHINE;
  1296. phkNew = phkMachine;
  1297. }
  1298. (void) BaseRegOpenClassKeyFromLocation(
  1299. pKeyInfo,
  1300. hKey,
  1301. &EmptyString,
  1302. (samDesired & KEY_WOW64_RES) | MAXIMUM_ALLOWED,
  1303. dwLocation,
  1304. phkNew);
  1305. if (!pKeySemantics) {
  1306. BaseRegReleaseKeySemantics(&keyinfo);
  1307. }
  1308. return STATUS_SUCCESS;
  1309. }
  1310. NTSTATUS GetFixedKeyInfo(
  1311. HKEY hkUser,
  1312. HKEY hkMachine,
  1313. LPDWORD pdwUserValues,
  1314. LPDWORD pdwMachineValues,
  1315. LPDWORD pdwUserMaxDataLen,
  1316. LPDWORD pdwMachineMaxDataLen,
  1317. LPDWORD pdwMaxValueNameLen)
  1318. {
  1319. NTSTATUS Status;
  1320. DWORD cUserValues;
  1321. DWORD cMachineValues;
  1322. KEY_CACHED_INFORMATION KeyInfo;
  1323. DWORD dwRead;
  1324. DWORD cbMaxNameLen;
  1325. DWORD cbUserMaxDataLen;
  1326. DWORD cbMachineMaxDataLen;
  1327. //
  1328. // Init locals
  1329. //
  1330. cUserValues = 0;
  1331. cMachineValues = 0;
  1332. cbMaxNameLen = 0;
  1333. cbUserMaxDataLen = 0;
  1334. cbMachineMaxDataLen = 0;
  1335. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1336. //
  1337. // Init out params
  1338. //
  1339. if (pdwUserValues) {
  1340. *pdwUserValues = 0;
  1341. }
  1342. if (pdwMachineValues) {
  1343. *pdwMachineValues = 0;
  1344. }
  1345. if (pdwMaxValueNameLen) {
  1346. *pdwMaxValueNameLen = 0;
  1347. }
  1348. if (pdwUserMaxDataLen) {
  1349. *pdwUserMaxDataLen = 0;
  1350. }
  1351. if (pdwMachineMaxDataLen) {
  1352. *pdwMachineMaxDataLen = 0;
  1353. }
  1354. //
  1355. // Get user information
  1356. //
  1357. if (hkUser) {
  1358. Status = NtQueryKey(
  1359. hkUser,
  1360. KeyCachedInformation,
  1361. &KeyInfo,
  1362. sizeof(KeyInfo),
  1363. &dwRead);
  1364. //
  1365. // KEY_CACHED_INFORMATION is a fixed struct !!!
  1366. //
  1367. ASSERT( Status != STATUS_BUFFER_OVERFLOW);
  1368. //if (STATUS_BUFFER_OVERFLOW == Status) {
  1369. // Status = STATUS_SUCCESS;
  1370. //}
  1371. if (!NT_SUCCESS(Status)) {
  1372. return Status;
  1373. }
  1374. cUserValues = KeyInfo.Values;
  1375. cbMaxNameLen = KeyInfo.MaxValueNameLen;
  1376. cbUserMaxDataLen = KeyInfo.MaxValueDataLen;
  1377. }
  1378. //
  1379. // Get machine information
  1380. //
  1381. if (hkMachine) {
  1382. Status = NtQueryKey(
  1383. hkMachine,
  1384. KeyCachedInformation,
  1385. &KeyInfo,
  1386. sizeof(KeyInfo),
  1387. &dwRead);
  1388. //
  1389. // KEY_CACHED_INFORMATION is a fixed struct !!!
  1390. //
  1391. ASSERT( Status != STATUS_BUFFER_OVERFLOW);
  1392. //if (STATUS_BUFFER_OVERFLOW == Status) {
  1393. // Status = STATUS_SUCCESS;
  1394. //}
  1395. if (!NT_SUCCESS(Status)) {
  1396. return Status;
  1397. }
  1398. cMachineValues = KeyInfo.Values;
  1399. cbMachineMaxDataLen = KeyInfo.MaxValueDataLen;
  1400. if (KeyInfo.MaxValueNameLen > cbMaxNameLen) {
  1401. cbMaxNameLen = KeyInfo.MaxValueNameLen;
  1402. }
  1403. }
  1404. if (pdwUserValues) {
  1405. *pdwUserValues = cUserValues;
  1406. }
  1407. if (pdwMachineValues) {
  1408. *pdwMachineValues = cMachineValues;
  1409. }
  1410. if (pdwMaxValueNameLen) {
  1411. *pdwMaxValueNameLen = cbMaxNameLen;
  1412. }
  1413. if (pdwUserMaxDataLen) {
  1414. *pdwUserMaxDataLen = cbUserMaxDataLen;
  1415. }
  1416. if (pdwMachineMaxDataLen) {
  1417. *pdwMachineMaxDataLen = cbMachineMaxDataLen;
  1418. }
  1419. return Status;
  1420. }
  1421. //#ifdef CLASSES_RETRY_ON_ACCESS_DENIED
  1422. NTSTATUS
  1423. BaseRegMapClassOnAccessDenied(
  1424. SKeySemantics* pKeySemantics,
  1425. PHKEY phkDest,
  1426. PUNICODE_STRING pDestSubKey,
  1427. BOOL* pfRetryOnAccessDenied)
  1428. /*++
  1429. Routine Description:
  1430. This function will remap a key to the user hive when an access denied
  1431. error is encountered creating it in the machine hive
  1432. Arguments:
  1433. pKeySemantics -- structure supplying information about a key
  1434. phkDest -- out param for root of remapped key
  1435. pDestSubKey -- out param for remainder of path of remapped key
  1436. pfRetryOnAccessDenied -- in / out param. If true, we can
  1437. remap it. On return this value indicates whether or not
  1438. we can do another retry
  1439. Return Value:
  1440. Returns STATUS_SUCCESS if a key was successfully deleted, otherwise it
  1441. returns an NTSTATUS error code
  1442. Note:
  1443. --*/
  1444. {
  1445. NTSTATUS Status;
  1446. UNICODE_STRING NewFullPath;
  1447. Status = STATUS_ACCESS_DENIED;
  1448. NewFullPath.Buffer=NULL;
  1449. if (pKeySemantics->_fCombinedClasses &&
  1450. *pfRetryOnAccessDenied) {
  1451. USHORT PrefixLen;
  1452. //
  1453. // Close the original key -- we don't need it anymore.
  1454. //
  1455. if(*phkDest) {
  1456. NtClose(*phkDest);
  1457. *phkDest = NULL;
  1458. }
  1459. //
  1460. // No more retries permitted for this key
  1461. //
  1462. *pfRetryOnAccessDenied = FALSE;
  1463. //
  1464. // Get space for the new path -- we will free this below.
  1465. // We avoid using alloca because of stack overflows
  1466. //
  1467. NewFullPath.MaximumLength = (USHORT)(pKeySemantics->_pFullPath->NameLength) + REG_CLASSES_SUBTREE_PADDING;
  1468. NewFullPath.Buffer = RegClassHeapAlloc(NewFullPath.MaximumLength);
  1469. if (!(NewFullPath.Buffer)) {
  1470. return STATUS_NO_MEMORY;
  1471. }
  1472. //
  1473. // Translate this key to the user hive
  1474. //
  1475. Status = BaseRegTranslateToUserClassKey(
  1476. pKeySemantics,
  1477. &NewFullPath,
  1478. &PrefixLen);
  1479. if (NT_SUCCESS(Status)) {
  1480. UNICODE_STRING Prefix;
  1481. OBJECT_ATTRIBUTES Obja;
  1482. //
  1483. // Allocate space for the new key name to give back to the caller
  1484. //
  1485. pDestSubKey->MaximumLength = NewFullPath.MaximumLength - PrefixLen + 1;
  1486. pDestSubKey->Buffer = (WCHAR*) RegClassHeapAlloc(pDestSubKey->MaximumLength);
  1487. if (!(pDestSubKey->Buffer)) {
  1488. Status = STATUS_NO_MEMORY;
  1489. goto cleanup;
  1490. }
  1491. //
  1492. // Make a string which strips off every thing after the prefix --
  1493. // we will open up to the prefix
  1494. //
  1495. Prefix.Buffer = NewFullPath.Buffer;
  1496. Prefix.Length = PrefixLen;
  1497. //
  1498. // Move our full path past the prefix
  1499. //
  1500. NewFullPath.Buffer += (PrefixLen + REG_CHAR_SIZE) / REG_CHAR_SIZE;
  1501. NewFullPath.Length -= (PrefixLen + REG_CHAR_SIZE);
  1502. //
  1503. // Copy everything after the prefix to the subkey path
  1504. // that we're returning to the caller
  1505. //
  1506. RtlCopyUnicodeString(pDestSubKey, &NewFullPath);
  1507. //
  1508. //Get the original pointer back so we could free it!
  1509. //
  1510. NewFullPath.Buffer -= (PrefixLen + REG_CHAR_SIZE) / REG_CHAR_SIZE;
  1511. NewFullPath.Length += (PrefixLen + REG_CHAR_SIZE);
  1512. //
  1513. // Now open the root for the caller
  1514. //
  1515. InitializeObjectAttributes(&Obja,
  1516. &Prefix,
  1517. OBJ_CASE_INSENSITIVE,
  1518. NULL, // full path, no hkey
  1519. NULL);
  1520. Status = NtOpenKey(
  1521. phkDest,
  1522. MAXIMUM_ALLOWED,
  1523. &Obja);
  1524. if(!NT_SUCCESS(Status)) {
  1525. RegClassHeapFree(pDestSubKey->Buffer);
  1526. pDestSubKey->Buffer=NULL;
  1527. }
  1528. }
  1529. }
  1530. cleanup:
  1531. //
  1532. // Free the buffer we allocated above
  1533. //
  1534. if(NewFullPath.Buffer) {
  1535. RegClassHeapFree(NewFullPath.Buffer);
  1536. }
  1537. return Status;
  1538. }
  1539. //#endif // CLASSES_RETRY_ON_ACCESS_DENIED
  1540. NTSTATUS
  1541. CreateMultipartUserClassKey(
  1542. IN HKEY hKey,
  1543. OUT PHKEY phkResult)
  1544. /*++
  1545. Routine Description:
  1546. This function creates HKCU\Software\Classes\... subkey
  1547. corresponding to given HKLM\Software\Classes\... subkey
  1548. Arguments:
  1549. IN HKEY hKey - handle of HKLM\Software\Classes\... subkey
  1550. OUT PHKEY phkResult - handle of HKCU\Software\Classes\... subkey
  1551. Return Value:
  1552. Returns STATUS_SUCCESS on success, other NTSTATUS if failed.
  1553. --*/
  1554. {
  1555. LPWSTR KeyBuffer;
  1556. ULONG NumberOfSubKeys;
  1557. LPWSTR p;
  1558. ULONG i;
  1559. LPWSTR Token;
  1560. UNICODE_STRING KeyName;
  1561. HANDLE TempHandle1;
  1562. HANDLE TempHandle2;
  1563. OBJECT_ATTRIBUTES Obja;
  1564. NTSTATUS Status;
  1565. ////////////////////////////////////////////////////////////////////////////
  1566. BYTE rgNameInfoBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE];
  1567. SKeySemantics keyinfo;
  1568. UNICODE_STRING EmptyString= {0, 0, 0};
  1569. HKEY hkDestKey=NULL;
  1570. UNICODE_STRING DestSubkey;
  1571. BOOL fRetryOnAccessDenied=TRUE;
  1572. //
  1573. // Set up the buffer that will hold the name of the key
  1574. //
  1575. keyinfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameInfoBuf;
  1576. keyinfo._cbFullPath = sizeof(rgNameInfoBuf);
  1577. keyinfo._fAllocedNameBuf = FALSE;
  1578. //
  1579. // get information about this key
  1580. //
  1581. Status = BaseRegGetKeySemantics(hKey, &EmptyString, &keyinfo);
  1582. if (!NT_SUCCESS(Status)) {
  1583. return Status;
  1584. }
  1585. DestSubkey.Buffer=NULL;
  1586. //
  1587. //Remap key to the user hive.
  1588. //
  1589. Status = BaseRegMapClassOnAccessDenied(
  1590. &keyinfo,
  1591. &hkDestKey,
  1592. &DestSubkey,
  1593. &fRetryOnAccessDenied);
  1594. BaseRegReleaseKeySemantics(&keyinfo);
  1595. if(!NT_SUCCESS(Status)) {
  1596. return Status;
  1597. }
  1598. //
  1599. //If we've opened new hkDestKey we must close it.
  1600. //
  1601. if( hkDestKey != hKey ) {
  1602. TempHandle1 = hkDestKey;
  1603. } else {
  1604. TempHandle1 = NULL;
  1605. }
  1606. //
  1607. // Initialize the buffer to be tokenized.
  1608. //
  1609. KeyBuffer = DestSubkey.Buffer;
  1610. //
  1611. // Find out the number of subkeys to be created
  1612. //
  1613. NumberOfSubKeys = 1;
  1614. p = KeyBuffer;
  1615. while ( ( p = wcschr( p, ( WCHAR )'\\' ) ) != NULL ) {
  1616. p++;
  1617. NumberOfSubKeys++;
  1618. }
  1619. for( i = 0, Token = KeyBuffer; i < NumberOfSubKeys; i++ ) {
  1620. ASSERT(Token != NULL);
  1621. if( ( *Token == ( WCHAR )'\\' ) &&
  1622. ( i != NumberOfSubKeys - 1 ) ) {
  1623. //
  1624. // If the first character of the key name is '\', and the key
  1625. // is not the last to be created, then ignore this key name.
  1626. // This condition can happen if the key name contains
  1627. // consecutive '\'.
  1628. // This behavior is consistent with the one we had in the past
  1629. // when the API used wcstok() to get the key names.
  1630. // Note that if the key name is an empty string, we return a handle
  1631. // that is different than hKey, even though both point to the same
  1632. // key. This is by design.
  1633. //
  1634. Token++;
  1635. continue;
  1636. }
  1637. //
  1638. // Convert the token to a counted Unicode string.
  1639. //
  1640. KeyName.Buffer = Token;
  1641. if (i == NumberOfSubKeys - 1) {
  1642. KeyName.Length = wcslen(Token)*sizeof(WCHAR);
  1643. } else {
  1644. KeyName.Length = (USHORT)(wcschr(Token, ( WCHAR )'\\') - Token)*sizeof(WCHAR);
  1645. }
  1646. //
  1647. // Remember the intermediate handle (NULL the first time through).
  1648. //
  1649. TempHandle2 = TempHandle1;
  1650. //
  1651. // Initialize the OBJECT_ATTRIBUTES structure, close the
  1652. // intermediate key and create or open the key.
  1653. //
  1654. InitializeObjectAttributes(
  1655. &Obja,
  1656. &KeyName,
  1657. OBJ_CASE_INSENSITIVE,
  1658. hkDestKey,
  1659. NULL
  1660. );
  1661. Status = NtCreateKey(
  1662. &TempHandle1,
  1663. MAXIMUM_ALLOWED,
  1664. &Obja,
  1665. 0,
  1666. NULL,
  1667. REG_OPTION_NON_VOLATILE,
  1668. NULL
  1669. );
  1670. //
  1671. // Initialize the next object directory (i.e. parent key) handle.
  1672. //
  1673. hkDestKey = TempHandle1;
  1674. //
  1675. // Close the intermediate key.
  1676. // This fails the first time through the loop since the
  1677. // handle is NULL.
  1678. //
  1679. if( TempHandle2 != NULL ) {
  1680. NtClose( TempHandle2 );
  1681. }
  1682. //
  1683. // If creating the key failed, return the error.
  1684. //
  1685. if( ! NT_SUCCESS( Status )) {
  1686. break;
  1687. }
  1688. Token = wcschr( Token, ( WCHAR )'\\') + 1;
  1689. }
  1690. if(DestSubkey.Buffer) {
  1691. RtlFreeHeap(RtlProcessHeap(), 0, DestSubkey.Buffer);
  1692. }
  1693. //
  1694. // Only set the return value once we know we've
  1695. // succeeded.
  1696. //
  1697. if( NT_SUCCESS( Status )) {
  1698. *phkResult = hkDestKey;
  1699. }
  1700. return Status;
  1701. }
  1702. #if defined(_REGCLASS_MALLOC_INSTRUMENTED_)
  1703. BOOL InitializeInstrumentedRegClassHeap()
  1704. {
  1705. NTSTATUS Status;
  1706. Status = RtlInitializeCriticalSection(
  1707. &(gRegClassHeapCritSect));
  1708. return NT_SUCCESS(Status);
  1709. }
  1710. BOOL CleanupInstrumentedRegClassHeap()
  1711. {
  1712. NTSTATUS Status;
  1713. Status = RtlDeleteCriticalSection(
  1714. &(gRegClassHeapCritSect));
  1715. DbgPrint("WINREG: Instrumented memory data for process id 0x%x\n", NtCurrentTeb()->ClientId.UniqueProcess);
  1716. DbgPrint("WINREG: Classes Heap Maximum Allocated: 0x%x\n", gcbMaxAllocated);
  1717. DbgPrint("WINREG: Classes Heap Maximum Outstanding Allocs: 0x%x\n", gcMaxAllocs);
  1718. if (gcbAllocated || gcAllocs) {
  1719. DbgPrint("WINREG: Classes Heap ERROR!\n");
  1720. DbgPrint("WINREG: Classes Heap not completely freed!\n");
  1721. DbgPrint("WINREG: Classes Heap Leaked 0x%x bytes\n", gcbAllocated);
  1722. DbgPrint("WINREG: Classes Heap Outstanding Allocs: 0x%x\n", gcAllocs);
  1723. DbgBreakPoint();
  1724. } else {
  1725. DbgPrint("WINREG: Classes Heap is OK.\n");
  1726. }
  1727. return NT_SUCCESS(Status);
  1728. }
  1729. #endif // defined(_REGCLASS_MALLOC_INSTRUMENTED_)
  1730. /*++
  1731. Routine Description:
  1732. Replaces HKLM\Software\Classes\<something> path with HKCR\<something>
  1733. Arguments:
  1734. phKey - The pointer to the handle of the key to map
  1735. lpSubKey - The key that is to be opened/created/deleted
  1736. Return Value:
  1737. If the [phKey] is in an interesting area return TRUE
  1738. If not, or if an error occurs, return FALSE.
  1739. --*/
  1740. BOOL
  1741. ExtractClassKey(
  1742. IN OUT HKEY *phKey,
  1743. IN OUT PUNICODE_STRING lpSubKey)
  1744. {
  1745. #define STR_CLASSES1 L"Classes\\"
  1746. #define STR_CLASSES2 L"Software\\Classes\\"
  1747. #define STR_MACHINE1 L"\\Registry\\Machine\\Software"
  1748. #define STR_MACHINE2 L"\\Registry\\Machine"
  1749. static const WCHAR *szCmpStr1[2]={STR_CLASSES1,STR_CLASSES2};
  1750. static const WCHAR *szCmpStr2[2]={STR_MACHINE1,STR_MACHINE2};
  1751. static const USHORT SizeCmpStr1[2]={sizeof(STR_CLASSES1)/sizeof(WCHAR)-2,
  1752. sizeof(STR_CLASSES2)/sizeof(WCHAR)-2};
  1753. static const USHORT SizeCmpStr2[2]={sizeof(STR_MACHINE1)/sizeof(WCHAR)-1,
  1754. sizeof(STR_MACHINE2)/sizeof(WCHAR)-1};
  1755. static const USHORT LengthCmpStr1[2]={sizeof(STR_CLASSES1)-2*sizeof(WCHAR),
  1756. sizeof(STR_CLASSES2)-2*sizeof(WCHAR)};
  1757. static const USHORT LengthCmpStr2[2]={sizeof(STR_MACHINE1)-sizeof(WCHAR),
  1758. sizeof(STR_MACHINE2)-sizeof(WCHAR)};
  1759. static const USHORT offset[2]={sizeof(STR_CLASSES1)-sizeof(WCHAR),
  1760. sizeof(STR_CLASSES2)-sizeof(WCHAR)};
  1761. static BYTE pBuff[sizeof(STR_MACHINE1)+sizeof(KEY_NAME_INFORMATION)];
  1762. PKEY_NAME_INFORMATION pNameInfo = (PKEY_NAME_INFORMATION)pBuff;
  1763. ULONG ResultLength;
  1764. NTSTATUS Status;
  1765. int index = 0;
  1766. //if length of the subkey name is less than "Classes" this is
  1767. //not the key we are interested in.
  1768. if(lpSubKey->Length < LengthCmpStr1[index]) {
  1769. return FALSE;
  1770. }
  1771. //see if subkey name starts with "Classes"
  1772. //If length of the subkey name is longer than "Classes",
  1773. //see if we have '\\' in the right place
  1774. //(because it could be something like "ClassesSomething").
  1775. if(_wcsnicmp(lpSubKey->Buffer,szCmpStr1[index],SizeCmpStr1[index]) ||
  1776. (lpSubKey->Length > LengthCmpStr1[index] &&
  1777. lpSubKey->Buffer[SizeCmpStr1[index]] != L'\\')) {
  1778. index = 1;
  1779. //if length of the subkey name is less than "Software\\Classes" this is
  1780. //not the key we are interested in.
  1781. if(lpSubKey->Length < LengthCmpStr1[index]) {
  1782. return FALSE;
  1783. }
  1784. //see if subkey name starts with "Software\\Classes"
  1785. //If length of the subkey name is longer than "Software\\Classes",
  1786. //see if we have '\\' in the right place
  1787. //(because it could be something like "Software\\ClassesSomething").
  1788. if(_wcsnicmp(lpSubKey->Buffer,szCmpStr1[index],SizeCmpStr1[index]) ||
  1789. (lpSubKey->Length > LengthCmpStr1[index] &&
  1790. lpSubKey->Buffer[SizeCmpStr1[index]] != L'\\')) {
  1791. return FALSE;
  1792. }
  1793. }
  1794. //Get the parent key name
  1795. Status = NtQueryKey(
  1796. *phKey,
  1797. KeyNameInformation,
  1798. pNameInfo,
  1799. sizeof(pBuff),
  1800. &ResultLength);
  1801. if (!NT_SUCCESS(Status)) {
  1802. return FALSE;
  1803. }
  1804. if(pNameInfo->NameLength != LengthCmpStr2[index] ||
  1805. _wcsnicmp(pNameInfo->Name,szCmpStr2[index],SizeCmpStr2[index])) {
  1806. return FALSE;
  1807. }
  1808. //Get handle to HKCR
  1809. if( !HKEY_ClassesRoot ) {
  1810. if( LocalOpenClassesRoot(NULL, MAXIMUM_ALLOWED, &HKEY_ClassesRoot) != ERROR_SUCCESS ) {
  1811. return FALSE;
  1812. }
  1813. }
  1814. // cut "Classes\\" or "Software\\Classes\\" from lpSubKey
  1815. if(lpSubKey->Length <= offset[index] ) {
  1816. //user tries to access HKLM\Software\Classes[\]"
  1817. //we will just return him handle to HKCR.
  1818. lpSubKey->Length = 0;
  1819. } else {
  1820. //move pointer after "Classes\\" or "Software\\Classes\\"
  1821. //it will be restored by caller
  1822. lpSubKey->Length -= offset[index];
  1823. lpSubKey->MaximumLength -= offset[index];
  1824. lpSubKey->Buffer += offset[index]/sizeof(WCHAR);
  1825. }
  1826. //replace hKey with HKCR
  1827. *phKey = HKEY_ClassesRoot;
  1828. return TRUE;
  1829. }
  1830. #endif // LOCAL