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.

1007 lines
33 KiB

  1. //*************************************************************
  2. //
  3. // HKCR management routines
  4. //
  5. // hkcr.c
  6. //
  7. // Microsoft Confidential
  8. // Copyright (c) Microsoft Corporation 1997
  9. // All rights reserved
  10. //
  11. //*************************************************************
  12. /*++
  13. Abstract:
  14. This module contains the code executed at logon for
  15. creating a user classes hive and mapping it into the standard
  16. user hive. The user classes hive and its machine classes
  17. counterpart make up the registry subtree known as
  18. HKEY_CLASSES_ROOT.
  19. Author:
  20. Zeke Odins-Lucas (zekel) 18-Oct-2000
  21. changed from hkcr.c to hive.cpp
  22. Adam P. Edwards (adamed) 10-Oct-1997
  23. Gregory Jensenworth (gregjen) 1-Jul-1997
  24. Key Functions:
  25. LoadUserHives
  26. UnloadUserHives
  27. Notes:
  28. Starting with NT5, the HKEY_CLASSES_ROOT key is per-user
  29. instead of per-machine -- previously, HKCR was an alias for
  30. HKLM\Software\Classes.
  31. The per-user HKCR combines machine classes stored it the
  32. traditional HKLM\Software\Classes location with classes
  33. stored in HKCU\Software\Classes.
  34. Certain keys, such as CLSID, will have subkeys that come
  35. from both the machine and user locations. When there is a conflict
  36. in key names, the user oriented key overrides the other one --
  37. only the user key is seen in that case.
  38. Originally, the code in this module was responsible for
  39. creating this combined view. That responsibility has moved
  40. to the win32 registry api's, so the main responsibility of
  41. this module is the mapping of the user-specific classes into
  42. the registry.
  43. It should be noted that HKCU\Software\Classes is not the true
  44. location of the user-only class data. If it were, all the class
  45. data would be in ntuser.dat, which roams with the user. Since
  46. class data can get very large, installation of a few apps
  47. would cause HKCU (ntuser.dat) to grow from a few hundred thousand K
  48. to several megabytes. Since user-only class data comes from
  49. the directory, it does not need to roam and therefore it was
  50. separated from HKCU (ntuser.dat) and stored in another hive
  51. mounted under HKEY_USERS.
  52. It is still desirable to allow access to this hive through
  53. HKCU\Software\Classes, so we use some trickery (symlinks) to
  54. make it seem as if the user class data exists there.
  55. --*/
  56. #include "uenv.h"
  57. #include <malloc.h>
  58. #define USER_CLASSES_HIVE_NAME TEXT("\\UsrClass.dat")
  59. #define CLASSES_SUBTREE TEXT("Software\\Classes\\")
  60. #define TEMPHIVE_FILENAME TEXT("TempClassesHive.dat")
  61. #define CLASSES_CLSID_SUBTREE TEXT("Software\\Classes\\Clsid\\")
  62. #define EXPLORER_CLASSES_SUBTREE TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Clsid\\")
  63. #define LENGTH(x) (sizeof(x) - sizeof(WCHAR))
  64. #define INIT_SPECIALKEY(x) x
  65. typedef WCHAR* SpecialKey;
  66. SpecialKey SpecialSubtrees[]= {
  67. INIT_SPECIALKEY(L"*"),
  68. INIT_SPECIALKEY(L"*\\shellex"),
  69. INIT_SPECIALKEY(L"*\\shellex\\ContextMenuHandlers"),
  70. INIT_SPECIALKEY(L"*\\shellex\\PropertyShellHandlers"),
  71. INIT_SPECIALKEY(L"AppID"),
  72. INIT_SPECIALKEY(L"ClsID"),
  73. INIT_SPECIALKEY(L"Component Categories"),
  74. INIT_SPECIALKEY(L"Drive"),
  75. INIT_SPECIALKEY(L"Drive\\shellex"),
  76. INIT_SPECIALKEY(L"Drive\\shellex\\ContextMenuHandlers"),
  77. INIT_SPECIALKEY(L"Drive\\shellex\\PropertyShellHandlers"),
  78. INIT_SPECIALKEY(L"FileType"),
  79. INIT_SPECIALKEY(L"Folder"),
  80. INIT_SPECIALKEY(L"Folder\\shellex"),
  81. INIT_SPECIALKEY(L"Folder\\shellex\\ColumnHandler"),
  82. INIT_SPECIALKEY(L"Folder\\shellex\\ContextMenuHandlers"),
  83. INIT_SPECIALKEY(L"Folder\\shellex\\ExtShellFolderViews"),
  84. INIT_SPECIALKEY(L"Folder\\shellex\\PropertySheetHandlers"),
  85. INIT_SPECIALKEY(L"Installer\\Components"),
  86. INIT_SPECIALKEY(L"Installer\\Features"),
  87. INIT_SPECIALKEY(L"Installer\\Products"),
  88. INIT_SPECIALKEY(L"Interface"),
  89. INIT_SPECIALKEY(L"Mime"),
  90. INIT_SPECIALKEY(L"Mime\\Database"),
  91. INIT_SPECIALKEY(L"Mime\\Database\\Charset"),
  92. INIT_SPECIALKEY(L"Mime\\Database\\Codepage"),
  93. INIT_SPECIALKEY(L"Mime\\Database\\Content Type"),
  94. INIT_SPECIALKEY(L"Typelib")
  95. };
  96. #define NUM_SPECIAL_SUBTREES (sizeof(SpecialSubtrees)/sizeof(*SpecialSubtrees))
  97. //*************************************************************
  98. //
  99. // MoveUserClassesBeforeMerge
  100. //
  101. // Purpose: move HKCU\Software\Classes before
  102. // MapUserClassesIntoUserHive() deletes it.
  103. //
  104. // Parameters: lpProfile - Profile information
  105. // lpcszLocalHiveDir - Temp Hive location
  106. //
  107. // Return: ERROR_SUCCESS if successful,
  108. // other error if not
  109. //
  110. // Comments:
  111. //
  112. // History: Date Author Comment
  113. // 5/6/99 vtan Created
  114. //
  115. //*************************************************************
  116. LONG MoveUserClassesBeforeMerge(
  117. LPPROFILE lpProfile,
  118. LPCTSTR lpcszLocalHiveDir)
  119. {
  120. LONG res;
  121. HKEY hKeySource;
  122. // Open HKCU\Software\Classes and see if there is a subkey.
  123. // No subkeys would indicate that the move has already been
  124. // done or there is no data to move.
  125. res = RegOpenKeyEx(lpProfile->hKeyCurrentUser, CLASSES_CLSID_SUBTREE, 0, KEY_ALL_ACCESS, &hKeySource);
  126. if (ERROR_SUCCESS == res)
  127. {
  128. DWORD dwSubKeyCount;
  129. if ((ERROR_SUCCESS == RegQueryInfoKey(hKeySource, NULL, NULL, NULL, &dwSubKeyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) &&
  130. (dwSubKeyCount > 0))
  131. {
  132. LPTSTR pszLocalTempHive;
  133. // Allocate enough space for the local hive directory and the temp hive filename.
  134. pszLocalTempHive = (LPTSTR) alloca((lstrlen(lpcszLocalHiveDir) * sizeof(TCHAR)) +
  135. sizeof(TEMPHIVE_FILENAME) +
  136. (sizeof('\0') * sizeof(TCHAR)));
  137. // Get a path to a file to save HKCU\Software\Classes into.
  138. if (pszLocalTempHive != NULL)
  139. {
  140. HANDLE hToken = NULL;
  141. BOOL bAdjustPriv = FALSE;
  142. lstrcpy(pszLocalTempHive, lpcszLocalHiveDir);
  143. lstrcat(pszLocalTempHive, TEMPHIVE_FILENAME);
  144. // RegSaveKey() fails if the file exists so delete it first.
  145. DeleteFile(pszLocalTempHive);
  146. //
  147. // Check to see if we are impersonating.
  148. //
  149. if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken) || hToken == NULL) {
  150. bAdjustPriv = TRUE;
  151. }
  152. else {
  153. CloseHandle(hToken);
  154. }
  155. if(!bAdjustPriv) {
  156. DWORD dwDisposition;
  157. HKEY hKeyTarget;
  158. BOOL fSavedHive;
  159. // Save HKCU\Software\Classes into the temp hive
  160. // and restore the state of SE_BACKUP_NAME privilege
  161. res = RegSaveKey(hKeySource, pszLocalTempHive, NULL);
  162. if (ERROR_SUCCESS == res)
  163. {
  164. res = RegCreateKeyEx(lpProfile->hKeyCurrentUser, EXPLORER_CLASSES_SUBTREE, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyTarget, &dwDisposition);
  165. if (ERROR_SUCCESS == res)
  166. {
  167. // Restore temp hive to a new location at
  168. // HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer
  169. // This performs the upgrade from NT4 to NT5.
  170. res = RegRestoreKey(hKeyTarget, pszLocalTempHive, 0);
  171. if (ERROR_SUCCESS != res)
  172. {
  173. DebugMsg((DM_WARNING, TEXT("RegRestoreKey failed with error %d"), res));
  174. }
  175. RegCloseKey(hKeyTarget);
  176. }
  177. else
  178. {
  179. DebugMsg((DM_WARNING, TEXT("RegCreateKeyEx failed to create key %s with error %d"), EXPLORER_CLASSES_SUBTREE, res));
  180. }
  181. }
  182. else
  183. {
  184. DebugMsg((DM_WARNING, TEXT("RegSaveKey failed with error %d"), res));
  185. }
  186. }
  187. else {
  188. if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
  189. {
  190. DWORD dwReturnTokenPrivilegesSize;
  191. TOKEN_PRIVILEGES oldTokenPrivileges, newTokenPrivileges;
  192. // Enable SE_BACKUP_NAME privilege
  193. if (LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &newTokenPrivileges.Privileges[0].Luid))
  194. {
  195. newTokenPrivileges.PrivilegeCount = 1;
  196. newTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  197. if (AdjustTokenPrivileges(hToken, FALSE, &newTokenPrivileges, sizeof(newTokenPrivileges), &oldTokenPrivileges, &dwReturnTokenPrivilegesSize))
  198. {
  199. BOOL fSavedHive;
  200. // Save HKCU\Software\Classes into the temp hive
  201. // and restore the state of SE_BACKUP_NAME privilege
  202. res = RegSaveKey(hKeySource, pszLocalTempHive, NULL);
  203. if (!AdjustTokenPrivileges(hToken, FALSE, &oldTokenPrivileges, 0, NULL, NULL))
  204. {
  205. DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed to restore old privileges with error %d"), GetLastError()));
  206. }
  207. if (ERROR_SUCCESS == res)
  208. {
  209. // Enable SE_RESTORE_NAME privilege.
  210. if (LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &newTokenPrivileges.Privileges[0].Luid))
  211. {
  212. newTokenPrivileges.PrivilegeCount = 1;
  213. newTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  214. if (AdjustTokenPrivileges(hToken, FALSE, &newTokenPrivileges, sizeof(newTokenPrivileges), &oldTokenPrivileges, &dwReturnTokenPrivilegesSize))
  215. {
  216. DWORD dwDisposition;
  217. HKEY hKeyTarget;
  218. res = RegCreateKeyEx(lpProfile->hKeyCurrentUser, EXPLORER_CLASSES_SUBTREE, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyTarget, &dwDisposition);
  219. if (ERROR_SUCCESS == res)
  220. {
  221. // Restore temp hive to a new location at
  222. // HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer
  223. // This performs the upgrade from NT4 to NT5.
  224. res = RegRestoreKey(hKeyTarget, pszLocalTempHive, 0);
  225. if (ERROR_SUCCESS != res)
  226. {
  227. DebugMsg((DM_WARNING, TEXT("RegRestoreKey failed with error %d"), res));
  228. }
  229. RegCloseKey(hKeyTarget);
  230. }
  231. else
  232. {
  233. DebugMsg((DM_WARNING, TEXT("RegCreateKeyEx failed to create key %s with error %d"), EXPLORER_CLASSES_SUBTREE, res));
  234. }
  235. if (!AdjustTokenPrivileges(hToken, FALSE, &oldTokenPrivileges, 0, NULL, NULL))
  236. {
  237. DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed to restore old privileges with error %d"), GetLastError()));
  238. }
  239. }
  240. else
  241. {
  242. res = GetLastError();
  243. DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed with error %d"), res));
  244. }
  245. }
  246. else
  247. {
  248. res = GetLastError();
  249. DebugMsg((DM_WARNING, TEXT("LookupPrivilegeValue failed with error %d"), res));
  250. }
  251. }
  252. else
  253. {
  254. DebugMsg((DM_WARNING, TEXT("RegSaveKey failed with error %d"), res));
  255. }
  256. }
  257. else
  258. {
  259. res = GetLastError();
  260. DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed with error %d"), res));
  261. }
  262. }
  263. else
  264. {
  265. res = GetLastError();
  266. DebugMsg((DM_WARNING, TEXT("LookupPrivilegeValue failed with error %d"), res));
  267. }
  268. CloseHandle(hToken);
  269. }
  270. else
  271. {
  272. res = GetLastError();
  273. DebugMsg((DM_WARNING, TEXT("OpenProcessToken failed to get token with error %d"), res));
  274. }
  275. } // if(!bAdjustPriv) else
  276. // Delete local temporary hive file.
  277. DeleteFile(pszLocalTempHive);
  278. }
  279. else
  280. {
  281. DebugMsg((DM_WARNING, TEXT("alloca failed to allocate temp hive path buffer")));
  282. }
  283. }
  284. RegCloseKey(hKeySource);
  285. }
  286. else if (ERROR_FILE_NOT_FOUND == res)
  287. {
  288. res = ERROR_SUCCESS;
  289. }
  290. return res;
  291. }
  292. //*************************************************************
  293. //
  294. // CreateRegLink()
  295. //
  296. // Purpose: Create a link from the hkDest + SubKeyName
  297. // pointing to lpSourceRootName
  298. //
  299. // if the key (link) already exists, do nothing
  300. //
  301. // Parameters: hkDest - root of destination
  302. // SubKeyName - subkey of destination
  303. // lpSourceName - target of link
  304. //
  305. // Return: ERROR_SUCCESS if successful
  306. // other NTSTATUS if an error occurs
  307. //
  308. //*************************************************************/
  309. LONG CreateRegLink(HKEY hkDest,
  310. LPCTSTR SubKeyName,
  311. LPCTSTR lpSourceName)
  312. {
  313. NTSTATUS Status;
  314. UNICODE_STRING LinkTarget;
  315. UNICODE_STRING SubKey;
  316. OBJECT_ATTRIBUTES Attributes;
  317. HANDLE hkInternal;
  318. UNICODE_STRING SymbolicLinkValueName;
  319. //
  320. // Initialize special key value used to make symbolic links
  321. //
  322. RtlInitUnicodeString(&SymbolicLinkValueName, L"SymbolicLinkValue");
  323. //
  324. // Initialize unicode string for our in params
  325. //
  326. RtlInitUnicodeString(&LinkTarget, lpSourceName);
  327. RtlInitUnicodeString(&SubKey, SubKeyName);
  328. //
  329. // See if this link already exists -- this is necessary because
  330. // NtCreateKey fails with STATUS_OBJECT_NAME_COLLISION if a link
  331. // already exists and will not return a handle to the existing
  332. // link.
  333. //
  334. InitializeObjectAttributes(&Attributes,
  335. &SubKey,
  336. OBJ_CASE_INSENSITIVE | OBJ_OPENLINK,
  337. hkDest,
  338. NULL);
  339. //
  340. // If this call succeeds, we get a handle to the existing link
  341. //
  342. Status = NtOpenKey( &hkInternal,
  343. MAXIMUM_ALLOWED,
  344. &Attributes );
  345. if (STATUS_OBJECT_NAME_NOT_FOUND == Status) {
  346. //
  347. // There is no existing link, so use NtCreateKey to make a new one
  348. //
  349. Status = NtCreateKey( &hkInternal,
  350. KEY_CREATE_LINK | KEY_SET_VALUE,
  351. &Attributes,
  352. 0,
  353. NULL,
  354. REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
  355. NULL);
  356. }
  357. //
  358. // Whether the link existed already or not, we should still set
  359. // the value which determines the link target
  360. //
  361. if (NT_SUCCESS(Status)) {
  362. Status = NtSetValueKey( hkInternal,
  363. &SymbolicLinkValueName,
  364. 0,
  365. REG_LINK,
  366. LinkTarget.Buffer,
  367. LinkTarget.Length);
  368. NtClose(hkInternal);
  369. }
  370. return RtlNtStatusToDosError(Status);
  371. }
  372. //*************************************************************
  373. //
  374. // DeleteRegLink()
  375. //
  376. // Purpose: Deletes a registry key (or link) without
  377. // using the advapi32 registry apis
  378. //
  379. //
  380. // Parameters: hkRoot - parent key
  381. // lpSubKey - subkey to delete
  382. //
  383. // Return: ERROR_SUCCESS if successful
  384. // other error if not
  385. //
  386. // Comments:
  387. //
  388. // History: Date Author Comment
  389. // 3/6/98 adamed Created
  390. //
  391. //*************************************************************
  392. LONG DeleteRegLink(HKEY hkRoot, LPCTSTR lpSubKey)
  393. {
  394. OBJECT_ATTRIBUTES Attributes;
  395. HANDLE hKey;
  396. NTSTATUS Status;
  397. UNICODE_STRING Subtree;
  398. //
  399. // Initialize string for lpSubKey param
  400. //
  401. RtlInitUnicodeString(&Subtree, lpSubKey);
  402. InitializeObjectAttributes(&Attributes,
  403. &Subtree,
  404. OBJ_CASE_INSENSITIVE | OBJ_OPENLINK,
  405. hkRoot,
  406. NULL);
  407. //
  408. // Open the link
  409. //
  410. Status = NtOpenKey( &hKey,
  411. MAXIMUM_ALLOWED,
  412. &Attributes );
  413. //
  414. // If we succeeded in opening it, delete it
  415. //
  416. if (NT_SUCCESS(Status)) {
  417. Status = NtDeleteKey(hKey);
  418. NtClose(hKey);
  419. }
  420. return RtlNtStatusToDosError(Status);
  421. }
  422. #define HIVEENTRY(h, i, f, l, t) {h, i, f, l, t}
  423. // table of hives to load
  424. typedef struct
  425. {
  426. LPCTSTR pszHive;
  427. int csidl;
  428. LPCTSTR pszFile;
  429. LPCTSTR pszLink;
  430. LPCTSTR pszTargetSubkey;
  431. } USERHIVE;
  432. static USERHIVE s_hives[] =
  433. {
  434. HIVEENTRY(
  435. USER_CLASSES_HIVE_SUFFIX,
  436. CSIDL_LOCAL_APPDATA,
  437. USER_CLASSES_HIVE_NAME,
  438. CLASSES_SUBTREE,
  439. NULL),
  440. HIVEENTRY(
  441. TEXT("_Windows_Local"),
  442. CSIDL_LOCAL_APPDATA,
  443. TEXT("UserWin.dat"),
  444. TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\"),
  445. TEXT("\\ShellNoRoam")),
  446. HIVEENTRY(
  447. TEXT("_Windows"),
  448. CSIDL_APPDATA,
  449. TEXT("UserWin.dat"),
  450. TEXT("Software\\Microsoft\\Windows\\Shell\\"),
  451. TEXT("\\Shell")),
  452. };
  453. #define CCH_TARGETHIVE MAX_PATH - (sizeof(USER_KEY_PREFIX) / sizeof(TCHAR)) + 1
  454. class CUserHives
  455. {
  456. public:
  457. CUserHives() : _pszFile(0) {}
  458. ~CUserHives();
  459. LONG LoadHives(LPPROFILE pProfile, LPTSTR pszSid);
  460. BOOL UnloadHives(LPCTSTR pszSid);
  461. protected: // methods
  462. BOOL _Init(USERPROFILE *pProfile, LPCTSTR pszSid);
  463. LONG _LoadHive(USERHIVE *phive);
  464. BOOL _UnloadHive(LPCTSTR pszHive);
  465. BOOL _CreateHiveFolder(USERHIVE *phive);
  466. BOOL _MakeTargetHive(LPCTSTR pszHive);
  467. LONG _CreateUserHive(USERHIVE *phive);
  468. LONG _PreLoadKey(BOOL fNewHive, LPCTSTR pszSub);
  469. LONG _CreateHiveFile(HKEY hk);
  470. LONG _PostLoadKey(BOOL fNewHive);
  471. LONG _CreateLink(USERHIVE *phive);
  472. LONG _DeleteLink(HKEY hk, LPCTSTR pszLink);
  473. protected: // members
  474. USERPROFILE *_pup;
  475. LPCTSTR _sid;
  476. LPTSTR _pszFile; // MAX_PATH;
  477. LPTSTR _pszTargetObject;
  478. LPTSTR _pszTargetHive; // points inside of _pszTargetObject;
  479. };
  480. BOOL _StrCatSafe(LPTSTR pszDest, LPCTSTR pszSrc, int cchDestBuffSize)
  481. {
  482. LPWSTR psz = pszDest;
  483. // we walk forward till we find the end of pszDest, subtracting
  484. // from cchDestBuffSize as we go.
  485. while (*psz)
  486. {
  487. psz++;
  488. cchDestBuffSize--;
  489. }
  490. if (cchDestBuffSize > 0)
  491. {
  492. lstrcpyn(psz, pszSrc, cchDestBuffSize);
  493. return TRUE;
  494. }
  495. return FALSE;
  496. }
  497. LONG CUserHives::_DeleteLink(HKEY hk, LPCTSTR pszLink)
  498. {
  499. // delete the existing link
  500. LONG Error = DeleteRegLink(hk, pszLink);
  501. //
  502. // It's ok if the deletion fails because the classes key, link or nonlink,
  503. // doesn't exist. It's also ok if it fails because the key exists but is not
  504. // a link and has children -- in this case, the key and its children will
  505. // be eliminated by a subsequent call to RegDelNode.
  506. //
  507. if (ERROR_SUCCESS != Error) {
  508. if ((ERROR_FILE_NOT_FOUND != Error) && (ERROR_ACCESS_DENIED != Error)) {
  509. return Error;
  510. }
  511. }
  512. //
  513. // Just to be safe, destroy any existing HKCU\Software\Classes and children.
  514. // This key may exist from previous unreleased versions of NT5, or from
  515. // someone playing around with hive files and adding bogus keys
  516. //
  517. if (!RegDelnode (hk, (LPTSTR)pszLink)) {
  518. Error = GetLastError();
  519. //
  520. // It's ok if this fails because the key doesn't exist, since
  521. // nonexistence is our goal.
  522. //
  523. if (ERROR_FILE_NOT_FOUND != Error) {
  524. return Error;
  525. }
  526. }
  527. return ERROR_SUCCESS;
  528. }
  529. LONG CUserHives::_CreateLink(USERHIVE *phive)
  530. {
  531. LONG error = _DeleteLink(_pup->hKeyCurrentUser, phive->pszLink);
  532. if (error == ERROR_SUCCESS)
  533. {
  534. //
  535. // At this point, we know that no HKCU\Software\Classes exists, so we should
  536. // be able to make a link there which points to the hive with the user class
  537. // data.
  538. //
  539. if (!phive->pszTargetSubkey || _StrCatSafe(_pszTargetObject, phive->pszTargetSubkey, MAX_PATH))
  540. error = CreateRegLink(_pup->hKeyCurrentUser, phive->pszLink, _pszTargetObject);
  541. else
  542. error = ERROR_NOT_ENOUGH_MEMORY;
  543. }
  544. return error;
  545. }
  546. //*************************************************************
  547. //
  548. // CreateClassesFolder()
  549. //
  550. // Purpose: Create the directory for the classes hives
  551. //
  552. //
  553. // Parameters:
  554. // pProfile - pointer to profile struct
  555. // szLocalHiveDir - out param for location of
  556. // classes hive folder.
  557. //
  558. // Return: ERROR_SUCCESS if successful
  559. // other error if an error occurs
  560. //
  561. //
  562. //*************************************************************
  563. BOOL CUserHives::_CreateHiveFolder(USERHIVE *phive)
  564. {
  565. BOOL fRet = FALSE;
  566. //
  567. // Find out the correct shell location for our subdir --
  568. // this call will create it if it doesn't exist.
  569. // This is a subdir of the user profile which does not
  570. // roam.
  571. //
  572. //
  573. // Need to do this to fix up a localisation prob. in NT4
  574. //
  575. PatchLocalAppData(_pup->hTokenUser);
  576. if (GetFolderPath(phive->csidl, _pup->hTokenUser, _pszFile))
  577. {
  578. // append our appdata sub-dir
  579. _StrCatSafe(_pszFile, TEXT("\\Microsoft\\Windows\\"), MAX_PATH);
  580. //
  581. // We will now create our own subdir, CLASSES_SUBDIRECTORY,
  582. // inside the local appdata subdir we just received above.
  583. //
  584. fRet = CreateNestedDirectory(_pszFile, NULL);
  585. }
  586. else
  587. {
  588. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  589. }
  590. return fRet;
  591. }
  592. LONG CUserHives::_PostLoadKey(BOOL fNewHive)
  593. {
  594. HKEY hk;
  595. LONG err = RegOpenKeyEx(HKEY_USERS, _pszTargetHive, 0,
  596. WRITE_DAC | KEY_ENUMERATE_SUB_KEYS | READ_CONTROL,
  597. &hk);
  598. if (ERROR_SUCCESS == err)
  599. {
  600. if (fNewHive)
  601. {
  602. DebugMsg((DM_VERBOSE, TEXT("CreateClassHive: existing user classes hive not found")));
  603. //
  604. // An existing hive was not found before we created this hive, so we need
  605. // to set security on the new hive
  606. //
  607. if (SetDefaultUserHiveSecurity(_pup, NULL, hk))
  608. {
  609. if (!SetFileAttributes (_pszFile, FILE_ATTRIBUTE_HIDDEN))
  610. {
  611. DebugMsg((DM_WARNING, TEXT("CreateClassHive: unable to set file attributes")
  612. TEXT(" on classes hive %s with error %x"), _pszFile, GetLastError()));
  613. }
  614. }
  615. else
  616. err = GetLastError();
  617. }
  618. RegCloseKey(hk);
  619. }
  620. return err;
  621. }
  622. LONG CUserHives::_CreateHiveFile(HKEY hk)
  623. {
  624. LONG err = ERROR_PRIVILEGE_NOT_HELD;
  625. NTSTATUS status = STATUS_SUCCESS;
  626. BOOLEAN fEnabled;
  627. BOOL fAdjusted = FALSE;
  628. HANDLE hToken;
  629. if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken)
  630. || hToken == NULL)
  631. {
  632. fAdjusted = TRUE;
  633. status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &fEnabled);
  634. }
  635. else
  636. {
  637. CloseHandle(hToken);
  638. }
  639. if (NT_SUCCESS(status))
  640. {
  641. err = RegSaveKeyEx(hk, _pszFile, NULL, REG_LATEST_FORMAT);
  642. if(fAdjusted)
  643. {
  644. RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, fEnabled, FALSE, &fEnabled);
  645. }
  646. }
  647. return err;
  648. }
  649. LONG CUserHives::_PreLoadKey(BOOL fNewHive, LPCTSTR pszSub)
  650. {
  651. LONG err = ERROR_SUCCESS;
  652. if (fNewHive)
  653. {
  654. // we need to create it using REG_LATEST_FORMAT
  655. HKEY hk;
  656. err = RegCreateKeyEx(_pup->hKeyCurrentUser, TEXT("UserTempHive"), 0, NULL,
  657. REG_OPTION_NON_VOLATILE,
  658. MAXIMUM_ALLOWED, NULL,
  659. &hk, NULL);
  660. if (ERROR_SUCCESS == err)
  661. {
  662. if (pszSub)
  663. {
  664. HKEY hkSub;
  665. err = RegCreateKeyEx(hk, pszSub + 1, 0, NULL,
  666. REG_OPTION_NON_VOLATILE,
  667. MAXIMUM_ALLOWED, NULL,
  668. &hkSub, NULL);
  669. if (ERROR_SUCCESS == err)
  670. {
  671. RegCloseKey(hkSub);
  672. }
  673. else
  674. {
  675. DebugMsg((DM_WARNING, TEXT("CUserHives::_PreLoadKey() failed to create %s"), pszSub + 1));
  676. }
  677. }
  678. RegFlushKey(hk);
  679. if (ERROR_SUCCESS == err)
  680. {
  681. err = _CreateHiveFile(hk);
  682. }
  683. RegCloseKey(hk);
  684. RegDelnode(_pup->hKeyCurrentUser, TEXT("UserTempHive"));
  685. }
  686. }
  687. return err;
  688. }
  689. BOOL CUserHives::_MakeTargetHive(LPCTSTR pszHive)
  690. {
  691. // construct the hive name by appending the suffix to the sid
  692. lstrcpyn(_pszTargetHive, _sid, CCH_TARGETHIVE);
  693. return _StrCatSafe(_pszTargetHive, pszHive, CCH_TARGETHIVE);
  694. }
  695. LONG CUserHives::_CreateUserHive(USERHIVE *phive)
  696. {
  697. // need to do something special if it doesnt already exist
  698. BOOL fNewHive = (-1 == GetFileAttributes(_pszFile));
  699. LONG err = _PreLoadKey(fNewHive, phive->pszTargetSubkey);
  700. // mount the hive
  701. if (ERROR_SUCCESS == err)
  702. {
  703. err = MyRegLoadKey(HKEY_USERS, _pszTargetHive, _pszFile);
  704. if (ERROR_SUCCESS == err)
  705. {
  706. err = _PostLoadKey(fNewHive);
  707. if (ERROR_SUCCESS == err)
  708. {
  709. err = _CreateLink(phive);
  710. }
  711. }
  712. else
  713. {
  714. DebugMsg((DM_VERBOSE, TEXT("CreateClassHive: existing user classes hive found")));
  715. }
  716. }
  717. return err;
  718. }
  719. LONG CUserHives::_LoadHive(USERHIVE *phive)
  720. {
  721. // create the directory for the user-specific classes hive
  722. LONG error = ERROR_NOT_ENOUGH_MEMORY;
  723. if (_CreateHiveFolder(phive) && _MakeTargetHive(phive->pszHive))
  724. {
  725. if (0 == lstrcmp(phive->pszHive, USER_CLASSES_HIVE_SUFFIX))
  726. {
  727. // Move HKCU\Software\Classes before merging the two
  728. // branches. Ignore any errors here as this branch is
  729. // about to be deleted by the merge anyway.
  730. // The reason for this move is because NT4 stores customized
  731. // shell icons in HKCU\Software\Classes\CLSID\{CLSID_x} and
  732. // NT5 stores this at HKCU\Software\Microsoft\Windows\
  733. // CurrentVersion\Explorer\CLSID\{CLSID_x} and must be moved
  734. // now before being deleted.
  735. LONG error = MoveUserClassesBeforeMerge(_pup, _pszFile);
  736. if (ERROR_SUCCESS != error)
  737. {
  738. DebugMsg((DM_WARNING, TEXT("MoveUserClassesBeforeMerge: Failed unexpectedly (%d)."),
  739. error));
  740. }
  741. }
  742. // append the file name to load/save to
  743. if (_StrCatSafe(_pszFile, phive->pszFile, MAX_PATH))
  744. {
  745. error = _CreateUserHive(phive);
  746. if (ERROR_SUCCESS != error)
  747. {
  748. DebugMsg((DM_WARNING, TEXT("LoadUserClasses: Failed to create user classes hive (%d)."), error));
  749. }
  750. }
  751. }
  752. else
  753. error = GetLastError();
  754. return error;
  755. }
  756. CUserHives::~CUserHives()
  757. {
  758. if (_pszFile)
  759. LocalFree(_pszFile);
  760. }
  761. BOOL CUserHives::_Init(USERPROFILE *pProfile, LPCTSTR pszSid)
  762. {
  763. // unref'd vars!!!
  764. _pup = pProfile;
  765. _sid = pszSid;
  766. _pszFile = (LPTSTR) LocalAlloc(LPTR, (MAX_PATH * sizeof(TCHAR)) * 2);
  767. if (_pszFile)
  768. {
  769. //
  770. // WARNING - we are being a little clever here
  771. // we only allocate one buffer (_pszFile) that is MAX_PATH * 2
  772. // we then split it up into two buffers of MAX_PATH (_pszFile & _pszTargetObject)
  773. // _pszTargetObject has a constant prefix of USER_KEY_PREFIX ("\Registry\User\")
  774. // we later append the key name that we will load under HKU.
  775. // _pszTargetHive points to that value.
  776. //
  777. // ie _pszTargetObject = "\Registry\User\{SID}_Windows"
  778. // _pszTargetHive = "{SID}_Windows"
  779. //
  780. _pszTargetObject = _pszFile + MAX_PATH;
  781. lstrcpy(_pszTargetObject, USER_KEY_PREFIX);
  782. _pszTargetHive = _pszTargetObject + sizeof(USER_KEY_PREFIX) / sizeof(TCHAR) - 1;
  783. return TRUE;
  784. }
  785. return FALSE;
  786. }
  787. LONG CUserHives::LoadHives(USERPROFILE *pProfile, LPTSTR pszSid)
  788. {
  789. LONG error;
  790. if (_Init(pProfile, pszSid))
  791. {
  792. // load each of the hives in turn
  793. for (int i = 0; i < ARRAYSIZE(s_hives); i++)
  794. {
  795. error = _LoadHive(&s_hives[i]);
  796. if (error != ERROR_SUCCESS)
  797. break;
  798. }
  799. }
  800. else
  801. error = ERROR_NOT_ENOUGH_MEMORY;
  802. return error;
  803. }
  804. LONG LoadUserHives(LPPROFILE pProfile, LPTSTR pszSid)
  805. {
  806. CUserHives hives;
  807. return hives.LoadHives(pProfile, pszSid);
  808. }
  809. BOOL CUserHives::_UnloadHive(LPCTSTR pszHive)
  810. {
  811. if (_MakeTargetHive(pszHive))
  812. {
  813. HKEY hKey;
  814. OBJECT_ATTRIBUTES oa;
  815. UNICODE_STRING usTargetObject;
  816. //
  817. // Initialize string for lpSubKey param
  818. //
  819. RtlInitUnicodeString(&usTargetObject, _pszTargetObject);
  820. InitializeObjectAttributes(&oa,
  821. &usTargetObject,
  822. OBJ_CASE_INSENSITIVE,
  823. NULL,
  824. NULL);
  825. NTSTATUS Status = NtOpenKey((PHANDLE)&hKey,
  826. KEY_READ,
  827. &oa);
  828. if (NT_SUCCESS(Status))
  829. {
  830. //
  831. // Make sure the hive is persisted properly
  832. //
  833. RegFlushKey(hKey);
  834. RegCloseKey(hKey);
  835. //
  836. // Unmount the hive -- this should only fail if
  837. // someone has a subkey of the hive open -- this
  838. // should not normally happen and probably means there's a service
  839. // that is leaking keys.
  840. //
  841. return MyRegUnLoadKey(HKEY_USERS, _pszTargetHive);
  842. }
  843. }
  844. DebugMsg((DM_WARNING, TEXT("UnLoadClassHive: failed to unload user hive %s"), pszHive));
  845. return FALSE;
  846. }
  847. BOOL CUserHives::UnloadHives(LPCTSTR pszSid)
  848. {
  849. if (_Init(NULL, pszSid))
  850. {
  851. // load each of the hives in turn
  852. for (int i = 0; i < ARRAYSIZE(s_hives); i++)
  853. {
  854. if (!_UnloadHive(s_hives[i].pszHive))
  855. return FALSE;
  856. }
  857. }
  858. return TRUE;
  859. }
  860. BOOL UnloadUserHives(LPTSTR pszSid)
  861. {
  862. CUserHives hives;
  863. // remove the implicit reference held by this process
  864. RegCloseKey(HKEY_CLASSES_ROOT);
  865. return hives.UnloadHives(pszSid);
  866. }