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

654 lines
19 KiB

  1. /*****************************************************************************
  2. *
  3. * scratch.c
  4. *
  5. * Scratch application.
  6. *
  7. *****************************************************************************/
  8. #include "precomp.h"
  9. #include <shlobj.h>
  10. #include <shlobjp.h>
  11. #include <shlwapi.h>
  12. #include <shlwapip.h>
  13. #include <shellapi.h>
  14. #include <ole2.h>
  15. #include "umrdpdr.h"
  16. #include "drdevlst.h"
  17. #include "umrdpdrv.h"
  18. #include "drdbg.h"
  19. #include <wlnotify.h>
  20. #define ALLOCMEM(size) HeapAlloc(RtlProcessHeap(), 0, size)
  21. #define FREEMEM(pointer) HeapFree(RtlProcessHeap(), 0, \
  22. pointer)
  23. // Global debug flag.
  24. extern DWORD GLOBAL_DEBUG_FLAGS;
  25. extern HINSTANCE g_hInstance;
  26. /******************************************************************************
  27. *
  28. * This is the private version of createsession key
  29. *
  30. ******************************************************************************/
  31. #define REGSTR_PATH_EXPLORER TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer")
  32. HKEY _SHGetExplorerHkey()
  33. {
  34. HKEY hUser;
  35. HKEY hkeyExplorer = NULL;
  36. if (RegOpenCurrentUser(KEY_WRITE, &hUser) == ERROR_SUCCESS) {
  37. RegCreateKey(hUser, REGSTR_PATH_EXPLORER, &hkeyExplorer);
  38. RegCloseKey(hUser);
  39. }
  40. return hkeyExplorer;
  41. }
  42. //
  43. // The "Session key" is a volatile registry key unique to this session.
  44. // A session is a single continuous logon. If Explorer crashes and is
  45. // auto-restarted, the two Explorers share the same session. But if you
  46. // log off and back on, that new Explorer is a new session.
  47. //
  48. // Note that Win9x doesn't support volatile registry keys, so
  49. // we just fake it.
  50. //
  51. //
  52. // The s_SessionKeyName is the name of the session key relative to
  53. // REGSTR_PATH_EXPLORER\SessionInfo. On NT, this is normally the
  54. // Authentication ID, but we pre-initialize it to something safe so
  55. // we don't fault if for some reason we can't get to it. Since
  56. // Win95 supports only one session at a time, it just stays at the
  57. // default value.
  58. //
  59. // Sometimes we want to talk about the full path (SessionInfo\BlahBlah)
  60. // and sometimes just the partial path (BlahBlah) so we wrap it inside
  61. // this goofy structure.
  62. //
  63. union SESSIONKEYNAME {
  64. TCHAR szPath[12+16+1];
  65. struct {
  66. TCHAR szSessionInfo[12]; // strlen("SepssionInfo\\")
  67. TCHAR szName[16+1]; // 16 = two DWORDs converted to hex
  68. };
  69. } s_SessionKeyName = {
  70. { TEXT("SessionInfo\\.Default") }
  71. };
  72. #ifdef WINNT
  73. BOOL g_fHaveSessionKeyName = FALSE;
  74. #endif
  75. //
  76. // samDesired = a registry security access mask, or the special value
  77. // 0xFFFFFFFF to delete the session key.
  78. // phk = receives the session key on success
  79. //
  80. // NOTE! Only Explorer should delete the session key (when the user
  81. // logs off).
  82. //
  83. STDAPI _SHCreateSessionKey(REGSAM samDesired, HKEY *phk)
  84. {
  85. LONG lRes;
  86. HKEY hkExplorer;
  87. *phk = NULL;
  88. #ifdef WINNT
  89. DBGMSG(DBG_TRACE, ("SHLEXT: _SHCreateSessionKey\n"));
  90. if (!g_fHaveSessionKeyName)
  91. {
  92. HANDLE hToken;
  93. //
  94. // Build the name of the session key. We use the authentication ID
  95. // which is guaranteed to be unique forever. We can't use the
  96. // Hydra session ID since that can be recycled.
  97. //
  98. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken) ||
  99. OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
  100. {
  101. TOKEN_STATISTICS stats;
  102. DWORD cbOut;
  103. DBGMSG(DBG_TRACE, ("SHLEXT: thread token: %p\n", hToken));
  104. if (GetTokenInformation(hToken, TokenStatistics, &stats, sizeof(stats), &cbOut))
  105. {
  106. wsprintf(s_SessionKeyName.szName, TEXT("%08x%08x"),
  107. stats.AuthenticationId.HighPart,
  108. stats.AuthenticationId.LowPart);
  109. DBGMSG(DBG_TRACE, ("SHLEXT: Session Key: %S\n", s_SessionKeyName.szName));
  110. g_fHaveSessionKeyName = TRUE;
  111. }
  112. else {
  113. DBGMSG(DBG_TRACE, ("SHLEXT: failed to get token info, error: %d\n",
  114. GetLastError()));
  115. }
  116. CloseHandle(hToken);
  117. }
  118. else {
  119. DBGMSG(DBG_TRACE, ("SHLEXT: failed to open thread token, error: %d\n",
  120. GetLastError()));
  121. }
  122. }
  123. #endif
  124. DBGMSG(DBG_TRACE, ("SHLEXT: SessionKey: %S\n", s_SessionKeyName.szName));
  125. hkExplorer = _SHGetExplorerHkey();
  126. if (hkExplorer)
  127. {
  128. if (samDesired != 0xFFFFFFFF)
  129. {
  130. DWORD dwDisposition;
  131. lRes = RegCreateKeyEx(hkExplorer, s_SessionKeyName.szPath, 0,
  132. NULL,
  133. REG_OPTION_VOLATILE,
  134. samDesired,
  135. NULL,
  136. phk,
  137. &dwDisposition );
  138. }
  139. else
  140. {
  141. lRes = SHDeleteKey(hkExplorer, s_SessionKeyName.szPath);
  142. }
  143. RegCloseKey(hkExplorer);
  144. }
  145. else
  146. {
  147. lRes = ERROR_ACCESS_DENIED;
  148. }
  149. return HRESULT_FROM_WIN32(lRes);
  150. }
  151. //
  152. // Get a key to HKEY_CURRENT_USER\Software\Classes\CLSID
  153. //
  154. HKEY GetHKCUClassesCLSID()
  155. {
  156. HKEY hUser;
  157. HKEY hkClassesCLSID = NULL;
  158. if (RegOpenCurrentUser(KEY_WRITE, &hUser) == ERROR_SUCCESS) {
  159. if (RegCreateKeyW(hUser,
  160. L"Software\\Classes\\CLSID",
  161. &hkClassesCLSID) == ERROR_SUCCESS) {
  162. RegCloseKey(hUser);
  163. return hkClassesCLSID;
  164. } else {
  165. RegCloseKey(hUser);
  166. return NULL;
  167. }
  168. }
  169. else {
  170. return NULL;
  171. }
  172. }
  173. #ifdef _WIN64
  174. //
  175. // Get a key to HKEY_CURRENT_USER\Software\Classes\Wow6432Node\CLSID
  176. //
  177. HKEY GetHKCUWow64ClassesCLSID()
  178. {
  179. HKEY hUser;
  180. HKEY hkClassesCLSID = NULL;
  181. if (RegOpenCurrentUser(KEY_WRITE, &hUser) == ERROR_SUCCESS) {
  182. if (RegCreateKeyW(hUser,
  183. L"Software\\Classes\\Wow6432Node\\CLSID",
  184. &hkClassesCLSID) == ERROR_SUCCESS) {
  185. RegCloseKey(hUser);
  186. return hkClassesCLSID;
  187. } else {
  188. RegCloseKey(hUser);
  189. return NULL;
  190. }
  191. }
  192. else {
  193. return NULL;
  194. }
  195. }
  196. #endif
  197. // Describes a registry key in the form specified in the article
  198. // http://msdn.microsoft.com/library/techart/shellinstobj.htm
  199. //
  200. // {guid}=REG_SZ:"Sample Instance Object"
  201. // value InfoTip=REG_SZ:"Demonstrate sample shell registry folder"
  202. // DefaultIcon=REG_EXPAND_SZ:"%SystemRoot%\system32\shell32.dll,9"
  203. // InProcServer32=REG_EXPAND_SZ:"%SystemRoot%\system32\shdocvw.dll"
  204. // value ThreadingModel=REG_SZ:"Apartment"
  205. // ShellFolder
  206. // value Attributes=REG_DWORD:0x60000000
  207. // value WantsFORPARSING=REG_SZ:""
  208. // Instance
  209. // value CLSID=REG_SZ:"{0AFACED1-E828-11D1-9187-B532F1E9575D}"
  210. // InitPropertyBag
  211. // value Target=REG_SZ:"\\raymondc\public"
  212. typedef struct _REGKEYENTRY {
  213. PWCHAR pszSubkey;
  214. PWCHAR pszValue;
  215. DWORD dwType;
  216. LPVOID pvData;
  217. } REGKEYENTRY;
  218. REGKEYENTRY g_RegEntry[] = {
  219. {
  220. NULL,
  221. NULL,
  222. REG_SZ,
  223. L"tsclient drive", /* folder display name e.g. \\tsclient\c */
  224. },
  225. {
  226. NULL,
  227. L"InfoTip",
  228. REG_SZ,
  229. L"Your local machine's disk storage", // info tip comments
  230. },
  231. {
  232. L"DefaultIcon",
  233. NULL,
  234. REG_EXPAND_SZ,
  235. L"%SystemRoot%\\system32\\shell32.dll,9", // icon resource file
  236. },
  237. {
  238. L"InProcServer32",
  239. NULL,
  240. REG_EXPAND_SZ,
  241. L"%SystemRoot%\\system32\\shdocvw.dll",
  242. },
  243. {
  244. L"InProcServer32",
  245. L"ThreadingModel",
  246. REG_SZ,
  247. L"Apartment",
  248. },
  249. {
  250. L"InProcServer32",
  251. L"LoadWithoutCOM",
  252. REG_SZ,
  253. L"",
  254. },
  255. {
  256. L"ShellFolder",
  257. L"Attributes",
  258. REG_DWORD,
  259. ((VOID *)(ULONG_PTR)0xF0000000),
  260. },
  261. {
  262. L"ShellFolder",
  263. L"WantsFORPARSING",
  264. REG_SZ,
  265. L"",
  266. },
  267. {
  268. L"Instance",
  269. L"CLSID",
  270. REG_SZ,
  271. L"{0AFACED1-E828-11D1-9187-B532F1E9575D}",
  272. },
  273. {
  274. L"Instance",
  275. L"LoadWithoutCOM",
  276. REG_SZ,
  277. L"",
  278. },
  279. {
  280. L"Instance\\InitPropertyBag",
  281. L"Target",
  282. REG_SZ,
  283. L"\\\\tsclient\\c", /* Target name e.g. \\tsclient\c */
  284. },
  285. };
  286. #define NUM_REGKEYENTRY (sizeof(g_RegEntry)/sizeof(g_RegEntry[0]))
  287. #define DISPLAY_INDEX 0
  288. #define INFOTIP_INDEX 1
  289. #define TARGET_INDEX (NUM_REGKEYENTRY - 1)
  290. //
  291. // Create volatile shell folder reg entries
  292. //
  293. BOOL CreateVolatilePerUserCLSID(HKEY hkClassesCLSID, PWCHAR pszGuid)
  294. {
  295. BOOL fSuccess = FALSE;
  296. unsigned i;
  297. HKEY hk;
  298. if (RegCreateKeyEx(hkClassesCLSID, pszGuid, 0, NULL,
  299. REG_OPTION_VOLATILE, KEY_WRITE, NULL,
  300. &hk, NULL) == ERROR_SUCCESS) {
  301. fSuccess = TRUE;
  302. // Okay, now fill the key with the information above
  303. for (i = 0; i < NUM_REGKEYENTRY && fSuccess; i++) {
  304. HKEY hkSub;
  305. HKEY hkClose = NULL;
  306. LONG lRes;
  307. if (g_RegEntry[i].pszSubkey && *g_RegEntry[i].pszSubkey) {
  308. lRes = RegCreateKeyEx(hk, g_RegEntry[i].pszSubkey, 0, NULL,
  309. REG_OPTION_VOLATILE, KEY_WRITE, NULL,
  310. &hkSub, NULL);
  311. hkClose = hkSub;
  312. } else {
  313. hkSub = hk;
  314. lRes = ERROR_SUCCESS;
  315. }
  316. if (lRes == ERROR_SUCCESS) {
  317. LPVOID pvData;
  318. DWORD cbData;
  319. DWORD dwData;
  320. if (g_RegEntry[i].dwType == REG_DWORD) {
  321. cbData = 4;
  322. dwData = PtrToUlong(g_RegEntry[i].pvData);
  323. pvData = (LPVOID)&dwData;
  324. } else {
  325. cbData = (lstrlen((LPCTSTR)g_RegEntry[i].pvData) + 1) * sizeof(TCHAR);
  326. pvData = g_RegEntry[i].pvData;
  327. }
  328. if (RegSetValueEx(hkSub, g_RegEntry[i].pszValue, 0,
  329. g_RegEntry[i].dwType, (LPBYTE)pvData, cbData) != ERROR_SUCCESS) {
  330. fSuccess = FALSE;
  331. }
  332. if (hkClose) RegCloseKey(hkClose);
  333. } else {
  334. fSuccess = FALSE;
  335. }
  336. }
  337. RegCloseKey(hk);
  338. if (!fSuccess) {
  339. SHDeleteKey(hkClassesCLSID, pszGuid);
  340. }
  341. }
  342. return fSuccess;
  343. }
  344. //
  345. // Create shell reg folder for redirected client drive connection
  346. //
  347. BOOL CreateDriveFolder(WCHAR *RemoteName, WCHAR *ClientDisplayName, PDRDEVLSTENTRY deviceEntry)
  348. {
  349. BOOL fSuccess = FALSE;
  350. WCHAR *szGuid = NULL;
  351. WCHAR szBuf[MAX_PATH];
  352. GUID guid;
  353. HRESULT hrInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  354. DBGMSG(DBG_TRACE, ("SHLEXT: CreateDriveFolder\n"));
  355. fSuccess = SUCCEEDED(CoCreateGuid(&guid));
  356. if (fSuccess) {
  357. // Allocate guid string buffer
  358. szGuid = (WCHAR *) ALLOCMEM(GUIDSTR_MAX * sizeof(WCHAR));
  359. if (szGuid != NULL) {
  360. fSuccess = TRUE;
  361. }
  362. else {
  363. fSuccess = FALSE;
  364. }
  365. }
  366. if (fSuccess) {
  367. PVOID pvData;
  368. WCHAR onString[32];
  369. WCHAR infoTip[64];
  370. LPWSTR args[2];
  371. SHStringFromGUID(&guid, szGuid, GUIDSTR_MAX);
  372. onString[0] = L'\0';
  373. infoTip[0] = L'\0';
  374. LoadString(g_hInstance, IDS_ON, onString, sizeof(onString) / sizeof(WCHAR));
  375. LoadString(g_hInstance, IDS_DRIVE_INFO_TIP, infoTip, sizeof(infoTip) / sizeof(WCHAR));
  376. // Set up shell folder display name
  377. pvData = ALLOCMEM(MAX_PATH * sizeof(WCHAR));
  378. if (pvData) {
  379. args[0] = deviceEntry->clientDeviceName;
  380. args[1] = ClientDisplayName;
  381. FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  382. onString, 0, 0, pvData, MAX_PATH, (va_list*)args);
  383. g_RegEntry[DISPLAY_INDEX].pvData = pvData;
  384. }
  385. else {
  386. fSuccess = FALSE;
  387. }
  388. // Setup shell folder target name
  389. if (fSuccess) {
  390. pvData = ALLOCMEM((wcslen(RemoteName) + 1) * sizeof(WCHAR));
  391. if (pvData) {
  392. wcscpy(pvData, RemoteName);
  393. g_RegEntry[TARGET_INDEX].pvData = pvData;
  394. }
  395. else {
  396. fSuccess = FALSE;
  397. FREEMEM(g_RegEntry[DISPLAY_INDEX].pvData);
  398. g_RegEntry[DISPLAY_INDEX].pvData = NULL;
  399. }
  400. // Create the shell instance object as a volatile per-user objects
  401. if (fSuccess) {
  402. pvData = ALLOCMEM((wcslen(infoTip) + 1) * sizeof(WCHAR));
  403. if (pvData) {
  404. wcscpy(pvData, infoTip);
  405. g_RegEntry[INFOTIP_INDEX].pvData = pvData;
  406. }
  407. else {
  408. fSuccess = FALSE;
  409. FREEMEM(g_RegEntry[DISPLAY_INDEX].pvData);
  410. g_RegEntry[DISPLAY_INDEX].pvData = NULL;
  411. FREEMEM(g_RegEntry[TARGET_INDEX].pvData);
  412. g_RegEntry[TARGET_INDEX].pvData = NULL;
  413. }
  414. }
  415. if (fSuccess) {
  416. HKEY hk64ClassesCLSID;
  417. HKEY hkClassesCLSID;
  418. hkClassesCLSID = GetHKCUClassesCLSID();
  419. if (hkClassesCLSID) {
  420. fSuccess = CreateVolatilePerUserCLSID(hkClassesCLSID, szGuid);
  421. RegCloseKey(hkClassesCLSID);
  422. }
  423. else {
  424. fSuccess = FALSE;
  425. }
  426. #ifdef _WIN64
  427. hk64ClassesCLSID = GetHKCUWow64ClassesCLSID();
  428. if (hk64ClassesCLSID) {
  429. fSuccess = CreateVolatilePerUserCLSID(hk64ClassesCLSID, szGuid);
  430. RegCloseKey(hk64ClassesCLSID);
  431. }
  432. else {
  433. fSuccess = FALSE;
  434. }
  435. #endif
  436. FREEMEM(g_RegEntry[DISPLAY_INDEX].pvData);
  437. g_RegEntry[DISPLAY_INDEX].pvData = NULL;
  438. FREEMEM(g_RegEntry[TARGET_INDEX].pvData);
  439. g_RegEntry[TARGET_INDEX].pvData = NULL;
  440. FREEMEM(g_RegEntry[INFOTIP_INDEX].pvData);
  441. g_RegEntry[INFOTIP_INDEX].pvData = NULL;
  442. }
  443. }
  444. }
  445. else {
  446. DBGMSG(DBG_ERROR, ("SHLEXT: Failed to create the GUID\n"));
  447. }
  448. // Register this object under the per-session My Computer namespace
  449. if (fSuccess) {
  450. HKEY hkSession;
  451. HKEY hkOut;
  452. DBGMSG(DBG_TRACE, ("SHLEXT: Created VolatilePerUserCLSID\n"));
  453. fSuccess = SUCCEEDED(_SHCreateSessionKey(KEY_WRITE, &hkSession));
  454. if (fSuccess) {
  455. wnsprintf(szBuf, MAX_PATH, L"MyComputer\\Namespace\\%s", szGuid);
  456. if (RegCreateKeyEx(hkSession, szBuf, 0, NULL,
  457. REG_OPTION_VOLATILE, KEY_WRITE, NULL,
  458. &hkOut, NULL) == ERROR_SUCCESS) {
  459. fSuccess = TRUE;
  460. RegCloseKey(hkOut);
  461. } else {
  462. fSuccess = FALSE;
  463. }
  464. RegCloseKey(hkSession);
  465. }
  466. }
  467. // Now tell the shell that the object was recently created
  468. if (fSuccess) {
  469. DBGMSG(DBG_TRACE, ("SHLEXT: Created per session MyComputer namespace\n"));
  470. wnsprintf(szBuf, MAX_PATH,
  471. TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::%s"),
  472. szGuid);
  473. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szBuf, NULL);
  474. }
  475. else {
  476. DBGMSG(DBG_TRACE, ("SHLEXT: Failed to create per session MyComputer namespace\n"));
  477. }
  478. if (SUCCEEDED(hrInit))
  479. CoUninitialize();
  480. // Save the guid in device entry so we can delete reg entry later
  481. deviceEntry->deviceSpecificData = (PVOID)szGuid;
  482. return fSuccess;
  483. }
  484. //
  485. // Delete shell reg folder for redirected client drive connection
  486. //
  487. BOOL DeleteDriveFolder(IN PDRDEVLSTENTRY deviceEntry)
  488. {
  489. WCHAR szBuf[MAX_PATH];
  490. WCHAR *szGuid;
  491. HKEY hkSession;
  492. HKEY hkClassesCLSID;
  493. HKEY hk64ClassesCLSID;
  494. HRESULT hrInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  495. DBGMSG(DBG_TRACE, ("SHLEXT: DeleteDriveFolder\n"));
  496. ASSERT(deviceEntry != NULL);
  497. szGuid = (WCHAR *)(deviceEntry->deviceSpecificData);
  498. if (szGuid != NULL) {
  499. // Delete it from the namespace
  500. if (SUCCEEDED(_SHCreateSessionKey(KEY_WRITE, &hkSession))) {
  501. wnsprintf(szBuf, MAX_PATH, L"MyComputer\\Namespace\\%s", szGuid);
  502. RegDeleteKey(hkSession, szBuf);
  503. RegCloseKey(hkSession);
  504. DBGMSG(DBG_TRACE, ("SHLEXT: Delete GUID from my computer session namespace\n"));
  505. }
  506. // Delete it from HKCU\...\CLSID
  507. hkClassesCLSID = GetHKCUClassesCLSID();
  508. if (hkClassesCLSID) {
  509. SHDeleteKey(hkClassesCLSID, szGuid);
  510. DBGMSG(DBG_TRACE, ("SHLEXT: Delete GUID from HKCU Classes\n"));
  511. RegCloseKey(hkClassesCLSID);
  512. }
  513. #ifdef _WIN64
  514. hk64ClassesCLSID = GetHKCUWow64ClassesCLSID();
  515. if (hk64ClassesCLSID) {
  516. SHDeleteKey(hk64ClassesCLSID, szGuid);
  517. DBGMSG(DBG_TRACE, ("SHLEXT: Delete GUID from HKCU Classes\n"));
  518. RegCloseKey(hk64ClassesCLSID);
  519. }
  520. #endif
  521. // Tell the shell that it's gone
  522. wnsprintf(szBuf, MAX_PATH,
  523. TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::%s"),
  524. szGuid);
  525. SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, szBuf, NULL);
  526. FREEMEM(szGuid);
  527. deviceEntry->deviceSpecificData = NULL;
  528. }
  529. //
  530. // Need to reset the session key on disconnect/logoff
  531. //
  532. g_fHaveSessionKeyName = FALSE;
  533. if (SUCCEEDED(hrInit))
  534. CoUninitialize();
  535. return TRUE;
  536. }