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.

1714 lines
50 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1996 - 1999
  6. //
  7. // File: rshx32.cpp
  8. //
  9. // Remote administration shell extension.
  10. //
  11. //--------------------------------------------------------------------------
  12. ///////////////////////////////////////////////////////////////////////////////
  13. // //
  14. // Include files //
  15. // //
  16. ///////////////////////////////////////////////////////////////////////////////
  17. #include "rshx32.h"
  18. #include <winnetwk.h> // WNetGetConnection
  19. #include <lm.h>
  20. #include <lmdfs.h> // NetDfsGetClientInfo
  21. #include <atlconv.h>
  22. #include <initguid.h>
  23. DEFINE_GUID(CLSID_NTFSSecurityExt, 0x1f2e5c40, 0x9550, 0x11ce, 0x99, 0xd2, 0x00, 0xaa, 0x00, 0x6e, 0x08, 0x6c);
  24. DEFINE_GUID(CLSID_PrintSecurityExt, 0xf37c5810, 0x4d3f, 0x11d0, 0xb4, 0xbf, 0x00, 0xaa, 0x00, 0xbb, 0xb7, 0x23);
  25. #define IID_PPV_ARG(IType, ppType) IID_##IType, reinterpret_cast<void**>(static_cast<IType**>(ppType))
  26. #define RSX_SECURITY_CHECKED 0x00000001L
  27. #define RSX_HAVE_SECURITY 0x00000002L
  28. #define DOBJ_RES_CONT 0x00000001L
  29. #define DOBJ_RES_ROOT 0x00000002L
  30. #define DOBJ_VOL_NTACLS 0x00000004L // NTFS or OFS
  31. class CRShellExtCF : public IClassFactory
  32. {
  33. protected:
  34. ULONG m_cRef;
  35. SE_OBJECT_TYPE m_seType;
  36. public:
  37. CRShellExtCF(SE_OBJECT_TYPE seType);
  38. ~CRShellExtCF();
  39. // IUnknown methods
  40. STDMETHODIMP QueryInterface(REFIID, void **);
  41. STDMETHODIMP_(ULONG) AddRef();
  42. STDMETHODIMP_(ULONG) Release();
  43. // IClassFactory methods
  44. STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, void **);
  45. STDMETHODIMP LockServer(BOOL);
  46. };
  47. class CRShellExt : public IShellExtInit, IShellPropSheetExt, IContextMenu
  48. {
  49. protected:
  50. ULONG m_cRef;
  51. SE_OBJECT_TYPE m_seType;
  52. IDataObject *m_lpdobj; // interface passed in by shell
  53. HRESULT m_hrSecurityCheck;
  54. DWORD m_dwSIFlags;
  55. LPTSTR m_pszServer;
  56. LPTSTR m_pszObject;
  57. HDPA m_hItemList;
  58. BOOL m_bShowLossInheritedAclWarning;
  59. public:
  60. CRShellExt(SE_OBJECT_TYPE seType);
  61. ~CRShellExt();
  62. // IUnknown methods
  63. STDMETHODIMP QueryInterface(REFIID, void **);
  64. STDMETHODIMP_(ULONG) AddRef();
  65. STDMETHODIMP_(ULONG) Release();
  66. // IShellExtInit method
  67. STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY);
  68. // IShellPropSheetExt methods
  69. STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE, LPARAM);
  70. STDMETHODIMP ReplacePage(UINT, LPFNADDPROPSHEETPAGE, LPARAM);
  71. //IContextMenu methods
  72. STDMETHODIMP QueryContextMenu(HMENU hMenu,
  73. UINT indexMenu,
  74. UINT idCmdFirst,
  75. UINT idCmdLast,
  76. UINT uFlags);
  77. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
  78. STDMETHODIMP GetCommandString(UINT_PTR idCmd,
  79. UINT uFlags,
  80. UINT *reserved,
  81. LPSTR pszName,
  82. UINT cchMax);
  83. private:
  84. STDMETHODIMP DoSecurityCheck(LPIDA pIDA);
  85. STDMETHODIMP CheckForSecurity(LPIDA pIDA);
  86. STDMETHODIMP CreateSI(LPSECURITYINFO *ppsi);
  87. STDMETHODIMP AddSecurityPage(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam);
  88. BOOL IsAddPrinterWizard() const;
  89. STDMETHODIMP AddMountedVolumePage(LPFNADDPROPSHEETPAGE lpfnAddPage,
  90. LPARAM lParam);
  91. };
  92. typedef CRShellExt* PRSHELLEXT;
  93. ///////////////////////////////////////////////////////////////////////////////
  94. // //
  95. // Global variables //
  96. // //
  97. ///////////////////////////////////////////////////////////////////////////////
  98. HINSTANCE g_hInstance = NULL;
  99. LONG g_cRefThisDll = 0;
  100. CLIPFORMAT g_cfShellIDList = 0;
  101. CLIPFORMAT g_cfPrinterGroup = 0;
  102. CLIPFORMAT g_cfMountedVolume = 0;
  103. HMODULE g_hAclui = NULL;
  104. ///////////////////////////////////////////////////////////////////////////////
  105. // //
  106. // Private prototypes //
  107. // //
  108. ///////////////////////////////////////////////////////////////////////////////
  109. void GetFileInfo(LPCTSTR pszPath,
  110. LPDWORD pdwFileType,
  111. LPTSTR pszServer,
  112. ULONG cchServer,
  113. BOOL *pbShowLossInheritedAclWarning = NULL);
  114. ///////////////////////////////////////////////////////////////////////////////
  115. // //
  116. // General routines //
  117. // //
  118. ///////////////////////////////////////////////////////////////////////////////
  119. /*++
  120. Routine Description:
  121. Dll's entry point.
  122. In order to service requests for file selection information from
  123. any of the file manager extensions to be included in this library,
  124. we must first register a window class to accept these requests.
  125. The Microsoft_Network provider transfers information via a private
  126. clipboard format called "Net Resource" which we must register.
  127. Arguments:
  128. Same as DllEntryPoint.
  129. Return Values:
  130. Same as DllEntryPoint.
  131. --*/
  132. STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void * /*lpReserved*/)
  133. {
  134. switch (dwReason)
  135. {
  136. case DLL_PROCESS_ATTACH:
  137. g_hInstance = hInstance;
  138. g_cfShellIDList = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST);
  139. g_cfPrinterGroup = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PRINTERGROUP);
  140. g_cfMountedVolume = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_MOUNTEDVOLUME);
  141. DebugProcessAttach();
  142. TraceSetMaskFromCLSID(CLSID_NTFSSecurityExt);
  143. #ifndef DEBUG
  144. DisableThreadLibraryCalls(hInstance);
  145. #endif
  146. break;
  147. case DLL_PROCESS_DETACH:
  148. if (g_hAclui)
  149. FreeLibrary(g_hAclui);
  150. DebugProcessDetach();
  151. break;
  152. case DLL_THREAD_DETACH:
  153. DebugThreadDetach();
  154. break;
  155. }
  156. return TRUE;
  157. }
  158. /*++
  159. Routine Description:
  160. Called by shell to create a class factory object.
  161. Arguments:
  162. rclsid - reference to class id specifier.
  163. riid - reference to interface id specifier.
  164. ppv - pointer to location to receive interface pointer.
  165. Return Values:
  166. Returns HRESULT signifying success or failure.
  167. --*/
  168. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
  169. {
  170. HRESULT hr;
  171. SE_OBJECT_TYPE seType;
  172. *ppv = NULL;
  173. if (IsEqualCLSID(rclsid, CLSID_NTFSSecurityExt))
  174. seType = SE_FILE_OBJECT;
  175. else if (IsEqualCLSID(rclsid, CLSID_PrintSecurityExt))
  176. seType = SE_PRINTER;
  177. else
  178. return CLASS_E_CLASSNOTAVAILABLE;
  179. CRShellExtCF *pShellExtCF = new CRShellExtCF(seType); // ref == 1
  180. if (!pShellExtCF)
  181. return E_OUTOFMEMORY;
  182. hr = pShellExtCF->QueryInterface(riid, ppv);
  183. pShellExtCF->Release(); // release initial ref
  184. return hr;
  185. }
  186. /*++
  187. Routine Description:
  188. Called by shell to find out if dll can be unloaded.
  189. Arguments:
  190. None.
  191. Return Values:
  192. Returns S_OK if dll can be unloaded, S_FALSE if not.
  193. --*/
  194. STDAPI DllCanUnloadNow()
  195. {
  196. return (g_cRefThisDll == 0 ? S_OK : S_FALSE);
  197. }
  198. STDAPI DllRegisterServer(void)
  199. {
  200. return CallRegInstall(g_hInstance, "DefaultInstall");
  201. }
  202. STDAPI DllUnregisterServer(void)
  203. {
  204. return CallRegInstall(g_hInstance, "DefaultUninstall");
  205. }
  206. ///////////////////////////////////////////////////////////////////////////////
  207. // //
  208. // Class factory object implementation //
  209. // //
  210. ///////////////////////////////////////////////////////////////////////////////
  211. CRShellExtCF::CRShellExtCF(SE_OBJECT_TYPE seType) : m_cRef(1), m_seType(seType)
  212. {
  213. InterlockedIncrement(&g_cRefThisDll);
  214. }
  215. CRShellExtCF::~CRShellExtCF()
  216. {
  217. ASSERT( 0 != g_cRefThisDll );
  218. InterlockedDecrement(&g_cRefThisDll);
  219. }
  220. ///////////////////////////////////////////////////////////////////////////////
  221. // //
  222. // Class factory object implementation (IUnknown) //
  223. // //
  224. ///////////////////////////////////////////////////////////////////////////////
  225. STDMETHODIMP_(ULONG) CRShellExtCF::AddRef()
  226. {
  227. return ++m_cRef;
  228. }
  229. STDMETHODIMP_(ULONG) CRShellExtCF::Release()
  230. {
  231. if (--m_cRef == 0)
  232. {
  233. delete this;
  234. return 0;
  235. }
  236. return m_cRef;
  237. }
  238. STDMETHODIMP CRShellExtCF::QueryInterface(REFIID riid, void ** ppv)
  239. {
  240. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
  241. {
  242. *ppv = (IClassFactory *)this;
  243. m_cRef++;
  244. return S_OK;
  245. }
  246. else
  247. {
  248. *ppv = NULL;
  249. return E_NOINTERFACE;
  250. }
  251. }
  252. ///////////////////////////////////////////////////////////////////////////////
  253. // //
  254. // Class factory object implementation (IClassFactory) //
  255. // //
  256. ///////////////////////////////////////////////////////////////////////////////
  257. /*++
  258. Routine Description:
  259. Support for IClassFactory::CreateInstance.
  260. Arguments:
  261. pUnkOuter - pointer to controlling unknown.
  262. riid - reference to interface id specifier.
  263. ppvObj - pointer to location to receive interface pointer.
  264. Return Values:
  265. Returns HRESULT signifying success or failure.
  266. --*/
  267. STDMETHODIMP CRShellExtCF::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void ** ppvObj)
  268. {
  269. *ppvObj = NULL;
  270. if (pUnkOuter)
  271. return CLASS_E_NOAGGREGATION;
  272. CRShellExt *pShellExt = new CRShellExt(m_seType);// ref count == 1
  273. if (!pShellExt)
  274. return E_OUTOFMEMORY;
  275. HRESULT hr = pShellExt->QueryInterface(riid, ppvObj);
  276. pShellExt->Release(); // release initial ref
  277. return hr;
  278. }
  279. /*++
  280. Routine Description:
  281. Support for IClassFactory::LockServer (not implemented).
  282. Arguments:
  283. fLock - true if lock count to be incremented.
  284. Return Values:
  285. Returns E_NOTIMPL.
  286. --*/
  287. STDMETHODIMP CRShellExtCF::LockServer(BOOL /*fLock*/)
  288. {
  289. return E_NOTIMPL;
  290. }
  291. ///////////////////////////////////////////////////////////////////////////////
  292. // //
  293. // Shell extension object implementation //
  294. // //
  295. ///////////////////////////////////////////////////////////////////////////////
  296. CRShellExt::CRShellExt(SE_OBJECT_TYPE seType) : m_cRef(1), m_seType(seType),
  297. m_dwSIFlags(SI_EDIT_ALL | SI_ADVANCED | SI_EDIT_EFFECTIVE), m_hrSecurityCheck((HRESULT)-1),
  298. m_hItemList(NULL),
  299. m_bShowLossInheritedAclWarning(FALSE)
  300. {
  301. InterlockedIncrement(&g_cRefThisDll);
  302. }
  303. CRShellExt::~CRShellExt()
  304. {
  305. DoRelease(m_lpdobj);
  306. LocalFreeString(&m_pszServer);
  307. LocalFreeString(&m_pszObject);
  308. LocalFreeDPA(m_hItemList);
  309. ASSERT( 0 != g_cRefThisDll );
  310. InterlockedDecrement(&g_cRefThisDll);
  311. }
  312. ///////////////////////////////////////////////////////////////////////////////
  313. // //
  314. // Shell extension object implementation (IUnknown) //
  315. // //
  316. ///////////////////////////////////////////////////////////////////////////////
  317. STDMETHODIMP_(ULONG)
  318. CRShellExt::AddRef()
  319. {
  320. return ++m_cRef;
  321. }
  322. STDMETHODIMP_(ULONG)
  323. CRShellExt::Release()
  324. {
  325. if (--m_cRef == 0)
  326. {
  327. delete this;
  328. return 0;
  329. }
  330. return m_cRef;
  331. }
  332. STDMETHODIMP CRShellExt::QueryInterface(REFIID riid, void ** ppv)
  333. {
  334. if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
  335. {
  336. *ppv = (LPSHELLEXTINIT)this;
  337. m_cRef++;
  338. return S_OK;
  339. }
  340. else if (IsEqualIID(riid, IID_IContextMenu))
  341. {
  342. *ppv = (LPCONTEXTMENU)this;
  343. m_cRef++;
  344. return S_OK;
  345. }
  346. else if (IsEqualIID(riid, IID_IShellPropSheetExt))
  347. {
  348. *ppv = (LPSHELLPROPSHEETEXT)this;
  349. m_cRef++;
  350. return S_OK;
  351. }
  352. else
  353. {
  354. *ppv = NULL;
  355. return E_NOINTERFACE;
  356. }
  357. }
  358. ///////////////////////////////////////////////////////////////////////////////
  359. // //
  360. // Shell extension object implementation (IShellExtInit) //
  361. // //
  362. ///////////////////////////////////////////////////////////////////////////////
  363. /*++
  364. Routine Description:
  365. Support for IShellExtInit::Initialize.
  366. Arguments:
  367. pidlFolder - pointer to id list identifying parent folder.
  368. lpdobj - pointer to IDataObject interface for selected object(s).
  369. hKeyProgId - registry key handle.
  370. Return Values:
  371. Returns HRESULT signifying success or failure.
  372. --*/
  373. STDMETHODIMP CRShellExt::Initialize(LPCITEMIDLIST /*pidlFolder*/, IDataObject *lpdobj, HKEY /*hKeyProgID*/)
  374. {
  375. DoRelease(m_lpdobj);
  376. m_lpdobj = lpdobj; // processed in AddPages
  377. if (m_lpdobj)
  378. m_lpdobj->AddRef();
  379. return S_OK;
  380. }
  381. ///////////////////////////////////////////////////////////////////////////////
  382. // //
  383. // Shell extension object implementation (IShellPropSheetExt) //
  384. // //
  385. ///////////////////////////////////////////////////////////////////////////////
  386. /*++
  387. Routine Description:
  388. Support for IShellPropSheetExt::AddPages.
  389. Arguments:
  390. lpfnAddPage - pointer to function called to add a page.
  391. lParam - lParam parameter to be passed to lpfnAddPage.
  392. Return Values:
  393. Returns HRESULT signifying success or failure.
  394. --*/
  395. STDMETHODIMP
  396. CRShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage,
  397. LPARAM lParam)
  398. {
  399. HRESULT hr;
  400. STGMEDIUM medium = {0};
  401. FORMATETC fe = { g_cfShellIDList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  402. LPIDA pIDA = NULL;
  403. TraceEnter(TRACE_RSHX32, "CRShellExt::AddPages");
  404. if (IsSimpleUI())
  405. ExitGracefully(hr, E_FAIL, "No Security page in simple mode");
  406. //
  407. //Check if Security Tab is hidden by privacy policy
  408. //NTRAID#NTBUG9-223899-2001/03/06-hiteshr
  409. //
  410. if(IsUIHiddenByPrivacyPolicy())
  411. ExitGracefully(hr, E_FAIL, "Security Page is hidden by Privacy Policy");
  412. // Get the ID List data
  413. hr = m_lpdobj->GetData(&fe, &medium);
  414. if (FAILED(hr) && m_seType == SE_FILE_OBJECT)
  415. TraceLeaveResult(AddMountedVolumePage(lpfnAddPage, lParam));
  416. FailGracefully(hr, "Can't get ID List format from data object");
  417. pIDA = (LPIDA)GlobalLock(medium.hGlobal);
  418. TraceAssert(pIDA != NULL);
  419. // Only support single selection for printers
  420. if (m_seType == SE_PRINTER && pIDA->cidl != 1)
  421. ExitGracefully(hr, E_FAIL, "Printer multiple selection not supported");
  422. hr = DoSecurityCheck(pIDA);
  423. if (S_OK == hr)
  424. hr = AddSecurityPage(lpfnAddPage, lParam);
  425. exit_gracefully:
  426. if (pIDA)
  427. GlobalUnlock(medium.hGlobal);
  428. ReleaseStgMedium(&medium);
  429. TraceLeaveResult(hr);
  430. }
  431. /*++
  432. Routine Description:
  433. Support for IShellPropSheetExt::ReplacePages (not supported).
  434. Arguments:
  435. uPageID - page to replace.
  436. lpfnReplaceWith - pointer to function called to replace a page.
  437. lParam - lParam parameter to be passed to lpfnReplaceWith.
  438. Return Values:
  439. Returns E_FAIL.
  440. --*/
  441. STDMETHODIMP
  442. CRShellExt::ReplacePage(UINT /* uPageID */,
  443. LPFNADDPROPSHEETPAGE /* lpfnReplaceWith */,
  444. LPARAM /* lParam */)
  445. {
  446. return E_NOTIMPL;
  447. }
  448. ///////////////////////////////////////////////////////////////////////////////
  449. // //
  450. // Shell extension object implementation (IContextMenu) //
  451. // //
  452. ///////////////////////////////////////////////////////////////////////////////
  453. //
  454. // FUNCTION: IContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
  455. //
  456. // PURPOSE: Called by the shell just before the context menu is displayed.
  457. // This is where you add your specific menu items.
  458. //
  459. // PARAMETERS:
  460. // hMenu - Handle to the context menu
  461. // indexMenu - Index of where to begin inserting menu items
  462. // idCmdFirst - Lowest value for new menu ID's
  463. // idCmtLast - Highest value for new menu ID's
  464. // uFlags - Specifies the context of the menu event
  465. //
  466. // RETURN VALUE:
  467. // HRESULT signifying success or failure.
  468. //
  469. // COMMENTS:
  470. //
  471. STDMETHODIMP
  472. CRShellExt::QueryContextMenu(HMENU hMenu,
  473. UINT indexMenu,
  474. UINT idCmdFirst,
  475. UINT /*idCmdLast*/,
  476. UINT uFlags)
  477. {
  478. HRESULT hr = ResultFromShort(0);
  479. STGMEDIUM medium = {0};
  480. FORMATETC fe = { g_cfShellIDList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  481. if (uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY))
  482. return hr;
  483. TraceEnter(TRACE_RSHX32, "CRShellExt::QueryContextMenu");
  484. // Get the ID List data
  485. hr = m_lpdobj->GetData(&fe, &medium);
  486. if (SUCCEEDED(hr))
  487. {
  488. LPIDA pIDA = (LPIDA)GlobalLock(medium.hGlobal);
  489. TraceAssert(pIDA != NULL);
  490. // Only support single selection
  491. if (pIDA->cidl == 1)
  492. {
  493. if (S_OK == DoSecurityCheck(pIDA))
  494. {
  495. TCHAR szSecurity[32];
  496. if (LoadString(g_hInstance, IDS_SECURITY_MENU, szSecurity, ARRAYSIZE(szSecurity)))
  497. {
  498. MENUITEMINFO mii;
  499. mii.cbSize = sizeof(mii);
  500. mii.fMask = MIIM_TYPE | MIIM_ID;
  501. mii.fType = MFT_STRING;
  502. mii.wID = idCmdFirst;
  503. mii.dwTypeData = szSecurity;
  504. mii.cch = lstrlen(szSecurity);
  505. InsertMenuItem(hMenu, indexMenu, TRUE /*fByPosition*/, &mii);
  506. hr = ResultFromShort(1); // Return number of items we added
  507. }
  508. }
  509. }
  510. GlobalUnlock(medium.hGlobal);
  511. ReleaseStgMedium(&medium);
  512. }
  513. TraceLeaveResult(hr);
  514. }
  515. //
  516. // FUNCTION: IContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
  517. //
  518. // PURPOSE: Called by the shell after the user has selected on of the
  519. // menu items that was added in QueryContextMenu().
  520. //
  521. // PARAMETERS:
  522. // lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
  523. //
  524. // RETURN VALUE:
  525. // HRESULT signifying success or failure.
  526. //
  527. // COMMENTS:
  528. //
  529. STDMETHODIMP
  530. CRShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
  531. {
  532. HRESULT hr = S_OK;
  533. STGMEDIUM medium;
  534. FORMATETC fe = { g_cfShellIDList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  535. // Don't support named verbs
  536. if (HIWORD(lpcmi->lpVerb))
  537. return E_NOTIMPL;
  538. TraceEnter(TRACE_RSHX32, "CRShellExt::InvokeCommand");
  539. // We only have one command, so we should get zero here
  540. TraceAssert(LOWORD(lpcmi->lpVerb) == 0);
  541. // This must be true for us to have added the command to the menu
  542. TraceAssert(S_OK == m_hrSecurityCheck);
  543. //
  544. // Call ShellExecuteEx to execute the "Properties" verb on this object, and
  545. // tell it to select the security property page.
  546. //
  547. // Get the ID List data
  548. hr = m_lpdobj->GetData(&fe, &medium);
  549. if (SUCCEEDED(hr))
  550. {
  551. LPIDA pIDA = (LPIDA)GlobalLock(medium.hGlobal);
  552. LPITEMIDLIST pidl;
  553. // We only support single selection for context menus
  554. TraceAssert(pIDA && pIDA->cidl == 1);
  555. // Build a fully qualified ID List for this object
  556. pidl = ILCombine((LPCITEMIDLIST)ByteOffset(pIDA, pIDA->aoffset[0]),
  557. (LPCITEMIDLIST)ByteOffset(pIDA, pIDA->aoffset[1]));
  558. if (pidl != NULL)
  559. {
  560. TCHAR szTitle[64];
  561. SHELLEXECUTEINFO sei =
  562. {
  563. sizeof(SHELLEXECUTEINFO),
  564. (lpcmi->fMask & (SEE_MASK_HOTKEY | SEE_MASK_ICON)) | SEE_MASK_INVOKEIDLIST,
  565. lpcmi->hwnd,
  566. c_szProperties, // lpVerb ("Properties")
  567. NULL, // lpFile
  568. szTitle, // lpParameters ("Security")
  569. NULL, // lpDirectory,
  570. lpcmi->nShow, // nShow
  571. NULL, // hInstApp
  572. (LPVOID)pidl, // lpIDList
  573. NULL, // lpClass
  574. NULL, // hkeyClass
  575. lpcmi->dwHotKey, // dwHotKey
  576. lpcmi->hIcon, // hIcon
  577. NULL // hProcess
  578. };
  579. LoadString(g_hInstance, IDS_PROPPAGE_TITLE, szTitle, ARRAYSIZE(szTitle));
  580. // Put up the properties dialog
  581. if (!ShellExecuteEx(&sei))
  582. {
  583. DWORD dwErr = GetLastError();
  584. hr = HRESULT_FROM_WIN32(dwErr);
  585. }
  586. ILFree(pidl);
  587. }
  588. GlobalUnlock(medium.hGlobal);
  589. ReleaseStgMedium(&medium);
  590. }
  591. #if 0
  592. //
  593. // SHObjectProperties builds a pidl to the object and then calls
  594. // ShellExecuteEx. Similar to above, but it does more work to obtain the
  595. // ID lists (which we already have).
  596. //
  597. SHObjectProperties(lpcmi->hwnd,
  598. m_seType == SE_PRINTER ? SHOP_PRINTERNAME : SHOP_FILEPATH,
  599. m_pszObject,
  600. TEXT("Security"));
  601. #endif
  602. TraceLeaveResult(hr);
  603. }
  604. //
  605. // FUNCTION: IContextMenu::GetCommandString(UINT, UINT, UINT, LPSTR, UINT)
  606. //
  607. // PURPOSE: Called by the shell after the user has selected on of the
  608. // menu items that was added in QueryContextMenu().
  609. //
  610. // PARAMETERS:
  611. // lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
  612. //
  613. // RETURN VALUE:
  614. // HRESULT signifying success or failure.
  615. //
  616. // COMMENTS:
  617. //
  618. STDMETHODIMP
  619. CRShellExt::GetCommandString(UINT_PTR /*idCmd*/,
  620. UINT uFlags,
  621. LPUINT /*reserved*/,
  622. LPSTR pszName,
  623. UINT cchMax)
  624. {
  625. if (uFlags == GCS_HELPTEXT)
  626. {
  627. LoadString(g_hInstance, IDS_SECURITY_HELPSTRING, (LPTSTR)pszName, cchMax);
  628. return S_OK;
  629. }
  630. // Must be some other flag that we don't handle
  631. return E_NOTIMPL;
  632. }
  633. //
  634. // FUNCTION: CRShellExt::DoSecurityCheck(LPIDA)
  635. //
  636. // PURPOSE: Helper function called by the Property Sheet and Context Menu
  637. // extension code. Used to determine whether to add the menu item
  638. // or property sheet.
  639. //
  640. // PARAMETERS:
  641. // pIDA - pointer to ID List Array specifying selected objects
  642. //
  643. // RETURN VALUE: none
  644. //
  645. // COMMENTS:
  646. // The results are stored in m_hrSecurityCheck, m_dwSIFlags, m_pszServer, and m_pszObject
  647. //
  648. STDMETHODIMP CRShellExt::DoSecurityCheck(LPIDA pIDA)
  649. {
  650. if (((HRESULT)-1) == m_hrSecurityCheck)
  651. {
  652. if (m_seType == SE_PRINTER && IsAddPrinterWizard())
  653. m_hrSecurityCheck = HRESULT_FROM_WIN32(ERROR_NO_SECURITY_ON_OBJECT);
  654. else
  655. m_hrSecurityCheck = CheckForSecurity(pIDA);
  656. }
  657. return m_hrSecurityCheck;
  658. }
  659. //
  660. // PURPOSE: Helper function called by CRShellExt::DoSecurityCheck
  661. //
  662. // PARAMETERS: pIDA - pointer to ID List array
  663. //
  664. // RETURN VALUE: HRESULT - S_OK if ACL editing can proceed
  665. //
  666. // COMMENTS:
  667. // The results are stored in m_dwSIFlags, m_pszServer, and m_pszObject
  668. //
  669. STDMETHODIMP CRShellExt::CheckForSecurity(LPIDA pIDA)
  670. {
  671. HRESULT hr;
  672. TCHAR szServer[MAX_PATH];
  673. LPTSTR pszItem = NULL;
  674. // LPTSTR pszAlternate = NULL;
  675. DWORD dwFlags = 0;
  676. UINT cItems;
  677. IShellFolder2 * psf = NULL;
  678. LPCITEMIDLIST pidl;
  679. DWORD dwAttr;
  680. DWORD dwPrivs[] = { SE_SECURITY_PRIVILEGE, SE_TAKE_OWNERSHIP_PRIVILEGE };
  681. HANDLE hToken = INVALID_HANDLE_VALUE;
  682. ACCESS_MASK dwAccess = 0;
  683. UINT i;
  684. TraceEnter(TRACE_RSHX32, "CRShellExt::CheckForSecurity");
  685. TraceAssert(m_pszServer == NULL); // Shouldn't get called twice
  686. TraceAssert(pIDA != NULL);
  687. szServer[0] = TEXT('\0');
  688. cItems = pIDA->cidl;
  689. TraceAssert(cItems >= 1);
  690. //We don't show effective perm page for multiple selection
  691. if (cItems > 1)
  692. m_dwSIFlags &= ~SI_EDIT_EFFECTIVE;
  693. IShellFolder2 *psfRoot = NULL;
  694. LPCITEMIDLIST pidlFolder = (LPCITEMIDLIST)ByteOffset(pIDA, pIDA->aoffset[0]);
  695. hr = BindToObjectEx(NULL, pidlFolder, NULL, IID_PPV_ARG(IShellFolder2, &psfRoot));
  696. FailGracefully(hr, "Unable to bind to folder");
  697. TraceAssert(psfRoot);
  698. // Create list for item paths
  699. TraceAssert(NULL == m_hItemList);
  700. m_hItemList = DPA_Create(4);
  701. if (NULL == m_hItemList)
  702. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create DPA");
  703. //
  704. // Get the first item and see if it supports security
  705. //
  706. LPCITEMIDLIST pidlItem = (LPCITEMIDLIST)ByteOffset(pIDA, pIDA->aoffset[1]);
  707. hr = BindToFolderIDListParent(psfRoot, pidlItem, IID_PPV_ARG(IShellFolder2, &psf), &pidl);
  708. FailGracefully(hr, "Unable to get item name");
  709. hr = IDA_GetItemName(psf, pidl, &pszItem);
  710. FailGracefully(hr, "Unable to get item name");
  711. dwAttr = SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSTEM;
  712. hr = psf->GetAttributesOf(1, &pidl, &dwAttr);
  713. FailGracefully(hr, "Unable to get item attributes");
  714. DoRelease(psf);
  715. //
  716. //If ACLUI is invoked for filesystem and object is not of filesystem
  717. //return E_FAIL
  718. //
  719. if ((m_seType == SE_FILE_OBJECT) && !(dwAttr & SFGAO_FILESYSTEM))
  720. ExitGracefully(hr, E_FAIL, "Not a filesystem object");
  721. // in the case that an item is both folder and stream, assume its a stream (.zip, .cab file)
  722. // and not a container
  723. if ((dwAttr & (SFGAO_FOLDER | SFGAO_STREAM)) == SFGAO_FOLDER)
  724. dwFlags |= DOBJ_RES_CONT;
  725. //
  726. // Check access on the first item only. If we can write the DACL
  727. // on the first one, we will try (later) to write to all items
  728. // in the selection and report any errors at that time.
  729. //
  730. hToken = EnablePrivileges(dwPrivs, ARRAYSIZE(dwPrivs));
  731. switch (m_seType)
  732. {
  733. case SE_FILE_OBJECT:
  734. GetFileInfo(pszItem, &dwFlags, szServer, ARRAYSIZE(szServer),&m_bShowLossInheritedAclWarning);
  735. if (dwFlags & DOBJ_VOL_NTACLS)
  736. hr = CheckFileAccess(pszItem, &dwAccess);
  737. else
  738. hr = HRESULT_FROM_WIN32(ERROR_NO_SECURITY_ON_OBJECT);
  739. break;
  740. case SE_PRINTER:
  741. // Printers are containers (they contain documents)
  742. // and they don't have a parent (for acl editing purposes)
  743. dwFlags = DOBJ_RES_CONT | DOBJ_RES_ROOT;
  744. hr = CheckPrinterAccess(pszItem, &dwAccess, szServer, ARRAYSIZE(szServer));
  745. break;
  746. default:
  747. hr = E_UNEXPECTED;
  748. }
  749. FailGracefully(hr, "No access");
  750. // If we can't do anything security related, and only one item
  751. // was selected bail out.
  752. // Continue in case of multiple selection. We will show the
  753. // error with the name of problem file when security page
  754. // is brought up.
  755. if (!(dwAccess & ALL_SECURITY_ACCESS) && (cItems == 1))
  756. ExitGracefully(hr, E_ACCESSDENIED, "No access");
  757. // Remember the server name
  758. if (TEXT('\0') != szServer[0])
  759. {
  760. hr = LocalAllocString(&m_pszServer, szServer);
  761. FailGracefully(hr, "LocalAlloc failed");
  762. }
  763. // Remember the item path
  764. DPA_AppendPtr(m_hItemList, pszItem);
  765. pszItem = NULL;
  766. if (!(dwAccess & WRITE_DAC))
  767. m_dwSIFlags |= SI_READONLY;
  768. if (!(dwAccess & WRITE_OWNER))
  769. {
  770. if (!(dwAccess & READ_CONTROL))
  771. m_dwSIFlags &= ~SI_EDIT_OWNER;
  772. else
  773. m_dwSIFlags |= SI_OWNER_READONLY;
  774. }
  775. if (!(dwAccess & ACCESS_SYSTEM_SECURITY))
  776. m_dwSIFlags &= ~SI_EDIT_AUDITS;
  777. //
  778. // Check the rest of the selection. If any part of a multiple
  779. // selection doesn't support ACLs or the selection isn't homogenous,
  780. // then we can't create the security page.
  781. //
  782. for (i = 2; i <= cItems; i++)
  783. {
  784. DWORD dw = 0;
  785. // We only do multiple selections for files
  786. TraceAssert(SE_FILE_OBJECT == m_seType);
  787. LPCITEMIDLIST pidlItem1 = (LPCITEMIDLIST)ByteOffset(pIDA, pIDA->aoffset[i]);
  788. hr = BindToFolderIDListParent(psfRoot, pidlItem1, IID_PPV_ARG(IShellFolder2, &psf), &pidl);
  789. FailGracefully(hr, "Unable to get item name");
  790. hr = IDA_GetItemName(psf, pidl, &pszItem);
  791. FailGracefully(hr, "Unable to get item name");
  792. dwAttr = SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSTEM;
  793. hr = psf->GetAttributesOf(1, &pidl, &dwAttr);
  794. FailGracefully(hr, "Unable to get item attributes");
  795. DoRelease(psf);
  796. //
  797. //If ACLUI is invoked for filesystem and object is not of filesystem
  798. //return E_FAIL
  799. //
  800. if ((m_seType == SE_FILE_OBJECT) && !(dwAttr & SFGAO_FILESYSTEM))
  801. ExitGracefully(hr, E_FAIL, "Not a filesystem object");
  802. if ((dwAttr & (SFGAO_FOLDER | SFGAO_STREAM)) == SFGAO_FOLDER)
  803. dw |= DOBJ_RES_CONT;
  804. if ((dw & DOBJ_RES_CONT) != (dwFlags & DOBJ_RES_CONT))
  805. ExitGracefully(hr, E_FAIL, "Incompatible multiple selection");
  806. GetFileInfo(pszItem, &dw, szServer, ARRAYSIZE(szServer));
  807. // Compare against first item. All flags and the server name
  808. // must match, otherwise we can't edit the ACLs.
  809. if (dw == dwFlags &&
  810. ((NULL == m_pszServer && TEXT('\0') == szServer[0]) ||
  811. (NULL != m_pszServer && 0 == lstrcmpi(m_pszServer, szServer))))
  812. {
  813. // Remember the item path
  814. DPA_AppendPtr(m_hItemList, pszItem);
  815. pszItem = NULL;
  816. }
  817. else
  818. ExitGracefully(hr, E_FAIL, "Incompatible multiple selection");
  819. }
  820. //
  821. // If everything has succeeded up to this point, save some flags
  822. // and the server and object name strings
  823. //
  824. if (dwFlags & DOBJ_RES_CONT)
  825. m_dwSIFlags |= SI_CONTAINER;
  826. //
  827. // For Root objects (e.g. "D:\") hide the ACL Protection checkbox,
  828. // since these objects don't appear to have parents.
  829. //
  830. if (dwFlags & DOBJ_RES_ROOT)
  831. m_dwSIFlags |= SI_NO_ACL_PROTECT;
  832. // Get the "Normal" display name to use as the object name
  833. hr = IDA_GetItemName(psfRoot, (LPCITEMIDLIST)ByteOffset(pIDA, pIDA->aoffset[1]),
  834. szServer, ARRAYSIZE(szServer), SHGDN_NORMAL);
  835. FailGracefully(hr, "Unable to get item name");
  836. if (cItems > 1)
  837. {
  838. int nLength = lstrlen(szServer);
  839. LoadString(g_hInstance, IDS_MULTISEL_ELLIPSIS, szServer + nLength, ARRAYSIZE(szServer) - nLength);
  840. }
  841. hr = LocalAllocString(&m_pszObject, szServer);
  842. exit_gracefully:
  843. ReleasePrivileges(hToken);
  844. DoRelease(psf);
  845. DoRelease(psfRoot);
  846. LocalFreeString(&pszItem);
  847. TraceLeaveResult(hr);
  848. }
  849. //
  850. // FUNCTION: CRShellExt::CreateSI(LPSECURITYINFO *)
  851. //
  852. // PURPOSE: Create a SecurityInformation object of the correct type
  853. //
  854. // PARAMETERS: ppsi - Location to store ISecurityInformation pointer
  855. //
  856. // RETURN VALUE: HRESULT signifying success or failure
  857. //
  858. // COMMENTS:
  859. //
  860. STDMETHODIMP
  861. CRShellExt::CreateSI(LPSECURITYINFO *ppsi)
  862. {
  863. HRESULT hr;
  864. CSecurityInformation *psi;
  865. TraceEnter(TRACE_RSHX32, "CRShellExt::CreateSI");
  866. TraceAssert(ppsi != NULL);
  867. *ppsi = NULL;
  868. switch (m_seType)
  869. {
  870. case SE_FILE_OBJECT:
  871. psi = new CNTFSSecurity(m_seType,m_bShowLossInheritedAclWarning); // ref == 1
  872. break;
  873. case SE_PRINTER:
  874. psi = new CPrintSecurity(m_seType); // ref == 1
  875. break;
  876. default:
  877. TraceLeaveResult(E_UNEXPECTED);
  878. }
  879. if (psi == NULL)
  880. TraceLeaveResult(E_OUTOFMEMORY);
  881. hr = psi->Initialize(m_hItemList,
  882. m_dwSIFlags,
  883. m_pszServer,
  884. m_pszObject);
  885. if (SUCCEEDED(hr))
  886. {
  887. *ppsi = psi;
  888. // The SecurityInfo object takes responsibility for these
  889. m_hItemList = NULL;
  890. m_pszServer = NULL;
  891. m_pszObject = NULL;
  892. m_hrSecurityCheck = (HRESULT)-1;
  893. }
  894. else
  895. psi->Release();
  896. TraceLeaveResult(hr);
  897. }
  898. typedef HPROPSHEETPAGE (WINAPI *PFN_CREATESECPAGE)(LPSECURITYINFO);
  899. HPROPSHEETPAGE _CreateSecurityPage(LPSECURITYINFO psi)
  900. {
  901. HPROPSHEETPAGE hPage = NULL;
  902. const TCHAR szAclui[] = TEXT("aclui.dll");
  903. const char szCreateSecPage[] = "CreateSecurityPage";
  904. if (!g_hAclui)
  905. g_hAclui = LoadLibrary(szAclui);
  906. if (g_hAclui)
  907. {
  908. static PFN_CREATESECPAGE s_pfnCreateSecPage = NULL;
  909. if (!s_pfnCreateSecPage)
  910. s_pfnCreateSecPage = (PFN_CREATESECPAGE)GetProcAddress(g_hAclui, szCreateSecPage);
  911. if (s_pfnCreateSecPage)
  912. hPage = (*s_pfnCreateSecPage)(psi);
  913. }
  914. return hPage;
  915. }
  916. STDMETHODIMP
  917. CRShellExt::AddSecurityPage(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
  918. {
  919. HRESULT hr;
  920. LPSECURITYINFO psi;
  921. hr = CreateSI(&psi); // ref == 1
  922. if (SUCCEEDED(hr))
  923. {
  924. HPROPSHEETPAGE hPermPage = _CreateSecurityPage(psi);
  925. if (hPermPage)
  926. {
  927. if (!lpfnAddPage(hPermPage, lParam))
  928. DestroyPropertySheetPage(hPermPage);
  929. }
  930. else
  931. {
  932. DWORD dwErr = GetLastError();
  933. hr = HRESULT_FROM_WIN32(dwErr);
  934. }
  935. psi->Release(); // release initial ref
  936. }
  937. return hr;
  938. }
  939. //
  940. // PURPOSE: Check for the Add Printer wizard
  941. //
  942. // PARAMETERS: none
  943. //
  944. // RETURN VALUE: TRUE if the selected object is the Add Printer wizard,
  945. // FALSE otherwise
  946. //
  947. // COMMENTS:
  948. //
  949. BOOL CRShellExt::IsAddPrinterWizard() const
  950. {
  951. BOOL bRetval = FALSE;
  952. STGMEDIUM medium;
  953. FORMATETC fe = { g_cfPrinterGroup, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  954. TCHAR szFile[MAX_PATH];
  955. TraceEnter(TRACE_RSHX32, "CRShellExt::IsAddPrinterWizard");
  956. TraceAssert(m_seType == SE_PRINTER);
  957. //
  958. // Fail the call if m_lpdobj is NULL.
  959. //
  960. if ( m_lpdobj && SUCCEEDED( m_lpdobj->GetData( &fe, &medium ) ) )
  961. {
  962. //
  963. // Get the selected item name.
  964. //
  965. if ( DragQueryFile( (HDROP)medium.hGlobal, 0, szFile, ARRAYSIZE( szFile ) ) )
  966. {
  967. //
  968. // Check if this is the magic Add Printer Wizard shell object.
  969. // The check is not case sensitive and the string is not localized.
  970. //
  971. if ( 0 == lstrcmpi( szFile, TEXT("WinUtils_NewObject") ) )
  972. {
  973. TraceMsg("Found Add Printer wizard");
  974. bRetval = TRUE;
  975. }
  976. }
  977. //
  978. // Release the storage medium.
  979. //
  980. ReleaseStgMedium( &medium );
  981. }
  982. TraceLeaveValue(bRetval);
  983. }
  984. //
  985. // FUNCTION: CRShellExt::AddMountedVolumePage()
  986. //
  987. // PURPOSE: Create Security page for mounted volume properties
  988. //
  989. // PARAMETERS: lpfnAddPage - pointer to function called to add a page.
  990. // lParam - lParam parameter to be passed to lpfnAddPage.
  991. //
  992. // RETURN VALUE: HRESULT signifying success or failure
  993. //
  994. // COMMENTS:
  995. //
  996. STDMETHODIMP
  997. CRShellExt::AddMountedVolumePage(LPFNADDPROPSHEETPAGE lpfnAddPage,
  998. LPARAM lParam)
  999. {
  1000. HRESULT hr = S_OK;
  1001. STGMEDIUM medium = {0};
  1002. FORMATETC fe = { g_cfMountedVolume, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  1003. TCHAR szMountPoint[MAX_PATH];
  1004. TCHAR szVolumeID[MAX_PATH];
  1005. TCHAR szLabel[64];
  1006. LPTSTR pszVolID = NULL;
  1007. DWORD dwVolFlags = 0;
  1008. DWORD dwPrivs[] = { SE_SECURITY_PRIVILEGE, SE_TAKE_OWNERSHIP_PRIVILEGE };
  1009. HANDLE hToken = INVALID_HANDLE_VALUE;
  1010. ACCESS_MASK dwAccess = 0;
  1011. BOOL bHasSecurity = FALSE;
  1012. TraceEnter(TRACE_RSHX32, "CRShellExt::AddMountedVolumePage");
  1013. TraceAssert(m_seType == SE_FILE_OBJECT);
  1014. TraceAssert(m_lpdobj);
  1015. // Try to get the mounted volume host folder path
  1016. hr = m_lpdobj->GetData(&fe, &medium);
  1017. FailGracefully(hr, "Not a mounted volume");
  1018. // Get the host folder path
  1019. if (!DragQueryFile((HDROP)medium.hGlobal, 0, szMountPoint, ARRAYSIZE(szMountPoint)))
  1020. ExitGracefully(hr, E_FAIL, "Can't get mount point from storage medium");
  1021. PathAddBackslash(szMountPoint);
  1022. // Get the volume ID, which looks like
  1023. // "\\?\Volume{9e2df3f5-c7f1-11d1-84d5-000000000000}\"
  1024. if (!GetVolumeNameForVolumeMountPoint(szMountPoint, szVolumeID, ARRAYSIZE(szVolumeID)))
  1025. ExitGracefully(hr, E_FAIL, "GetVolumeNameForVolumeMountPoint failed");
  1026. if (GetVolumeInformation(szMountPoint, //szVolumeID,
  1027. szLabel,
  1028. ARRAYSIZE(szLabel),
  1029. NULL,
  1030. NULL,
  1031. &dwVolFlags,
  1032. NULL,
  1033. 0))
  1034. {
  1035. if (dwVolFlags & FS_PERSISTENT_ACLS)
  1036. {
  1037. bHasSecurity = TRUE;
  1038. }
  1039. }
  1040. else if (GetLastError() == ERROR_ACCESS_DENIED)
  1041. {
  1042. // If we can't get the volume information because we don't have
  1043. // access, then there must be security!
  1044. bHasSecurity = TRUE;
  1045. }
  1046. if (!bHasSecurity)
  1047. ExitGracefully(hr, E_FAIL, "Volume inaccessible or not NTFS");
  1048. hToken = EnablePrivileges(dwPrivs, ARRAYSIZE(dwPrivs));
  1049. hr = CheckFileAccess(szVolumeID, &dwAccess);
  1050. FailGracefully(hr, "Volume inaccessible");
  1051. // If we can't do anything security related, don't continue.
  1052. if (!(dwAccess & ALL_SECURITY_ACCESS))
  1053. ExitGracefully(hr, E_ACCESSDENIED, "No security access");
  1054. if (!(dwAccess & WRITE_DAC))
  1055. m_dwSIFlags |= SI_READONLY;
  1056. if (!(dwAccess & WRITE_OWNER))
  1057. {
  1058. if (!(dwAccess & READ_CONTROL))
  1059. m_dwSIFlags &= ~SI_EDIT_OWNER;
  1060. else
  1061. m_dwSIFlags |= SI_OWNER_READONLY;
  1062. }
  1063. if (!(dwAccess & ACCESS_SYSTEM_SECURITY))
  1064. m_dwSIFlags &= ~SI_EDIT_AUDITS;
  1065. m_dwSIFlags |= SI_CONTAINER | SI_NO_ACL_PROTECT;
  1066. if (!FormatStringID(&m_pszObject,
  1067. g_hInstance,
  1068. IDS_FMT_VOLUME_DISPLAY,
  1069. szLabel,
  1070. szMountPoint))
  1071. {
  1072. LocalAllocString(&m_pszObject, szLabel);
  1073. }
  1074. if (!m_pszObject)
  1075. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to build volume display string");
  1076. m_hItemList = DPA_Create(1);
  1077. if (!m_hItemList)
  1078. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create item list");
  1079. hr = LocalAllocString(&pszVolID, szVolumeID);
  1080. FailGracefully(hr, "Unable to copy volume ID string");
  1081. DPA_AppendPtr(m_hItemList, pszVolID);
  1082. pszVolID = NULL;
  1083. hr = AddSecurityPage(lpfnAddPage, lParam);
  1084. exit_gracefully:
  1085. ReleasePrivileges(hToken);
  1086. LocalFreeString(&pszVolID);
  1087. ReleaseStgMedium(&medium);
  1088. TraceLeaveResult(hr);
  1089. }
  1090. ///////////////////////////////////////////////////////////////////////////////
  1091. // //
  1092. // Miscellaneous helper functions //
  1093. // //
  1094. ///////////////////////////////////////////////////////////////////////////////
  1095. BOOL
  1096. IsDfsPath(LPTSTR pszPath, // in
  1097. LPTSTR pszServer, // out
  1098. UINT cchServer) // out
  1099. {
  1100. BOOL bIsDfs = FALSE;
  1101. WCHAR szPath[MAX_PATH];
  1102. PDFS_INFO_3 pDI3 = NULL;
  1103. WCHAR szServer[MAX_PATH];
  1104. USES_CONVERSION;
  1105. if (!PathIsUNC(pszPath))
  1106. return FALSE; // local machine
  1107. lstrcpynW(szPath, T2CW(pszPath), ARRAYSIZE(szPath));
  1108. // Check for DFS
  1109. for (;;)
  1110. {
  1111. DWORD dwErr;
  1112. __try
  1113. {
  1114. // This is delay-loaded by the linker, so
  1115. // must wrap with an exception handler.
  1116. dwErr = NetDfsGetClientInfo(szPath,
  1117. NULL,
  1118. NULL,
  1119. 3,
  1120. (LPBYTE*)&pDI3);
  1121. }
  1122. __except(EXCEPTION_EXECUTE_HANDLER)
  1123. {
  1124. return FALSE;
  1125. }
  1126. if (NERR_Success == dwErr)
  1127. {
  1128. for (ULONG i = 0; i < pDI3->NumberOfStorages; i++)
  1129. {
  1130. if (DFS_STORAGE_STATE_ONLINE & pDI3->Storage[i].State)
  1131. {
  1132. bIsDfs = TRUE;
  1133. szServer[0] = L'\\';
  1134. szServer[1] = L'\\';
  1135. lstrcpynW(&szServer[2], pDI3->Storage[i].ServerName, ARRAYSIZE(szServer)-2);
  1136. // If this server is active, quit looking
  1137. if (DFS_STORAGE_STATE_ACTIVE & pDI3->Storage[i].State)
  1138. break;
  1139. }
  1140. }
  1141. break;
  1142. }
  1143. else if (NERR_DfsNoSuchVolume == dwErr)
  1144. {
  1145. // If we're at the root, then we can't go any farther.
  1146. if (PathIsRoot(szPath))
  1147. break;
  1148. // Remove the last path element and try again, if nothing is
  1149. //removed, break, don't go in infinite loop
  1150. if (!PathRemoveFileSpec(szPath))
  1151. break;
  1152. }
  1153. else
  1154. {
  1155. // Some other error, bail
  1156. break;
  1157. }
  1158. }
  1159. if (bIsDfs)
  1160. {
  1161. lstrcpyn(pszServer, W2T(szServer), cchServer);
  1162. }
  1163. if (NULL != pDI3)
  1164. NetApiBufferFree(pDI3);
  1165. return bIsDfs;
  1166. }
  1167. void
  1168. GetVolumeInfo(LPCTSTR pszPath,
  1169. BOOL bIsFolder,
  1170. LPDWORD pdwFlags,
  1171. LPTSTR pszVolume,
  1172. ULONG cchVolume)
  1173. {
  1174. TCHAR szVolume[MAX_PATH];
  1175. TCHAR szVolumeID[MAX_PATH];
  1176. //
  1177. // The path can be DFS or contain volume mount points, so start
  1178. // with the full path and try GetVolumeInformation on successively
  1179. // shorter paths until it succeeds or we run out of path.
  1180. //
  1181. // However, if it's a volume mount point, we're interested in the
  1182. // the host folder's volume so back up one level to start. The
  1183. // child volume is handled separately (see AddMountedVolumePage).
  1184. //
  1185. lstrcpyn(szVolume, pszPath, ARRAYSIZE(szVolume));
  1186. if (!bIsFolder
  1187. || GetVolumeNameForVolumeMountPoint(szVolume, szVolumeID, ARRAYSIZE(szVolumeID)))
  1188. {
  1189. PathRemoveFileSpec(szVolume);
  1190. }
  1191. for (;;)
  1192. {
  1193. PathAddBackslash(szVolume); // GetVolumeInformation likes a trailing '\'
  1194. if (GetVolumeInformation(szVolume,
  1195. NULL,
  1196. NULL,
  1197. NULL,
  1198. NULL,
  1199. pdwFlags,
  1200. NULL,
  1201. 0))
  1202. {
  1203. break;
  1204. }
  1205. // Access denied implies that we've reached the deepest volume
  1206. // in the path; we just can't get the flags. It also implies
  1207. // security, so assume persistent acls.
  1208. if (ERROR_ACCESS_DENIED == GetLastError())
  1209. {
  1210. *pdwFlags = FS_PERSISTENT_ACLS;
  1211. break;
  1212. }
  1213. // If we're at the root, then we can't go any farther.
  1214. if (PathIsRoot(szVolume))
  1215. break;
  1216. // Remove the last path element and try again
  1217. PathRemoveBackslash(szVolume);
  1218. //if nothing is removed break instead of going in infinite loop
  1219. if (!PathRemoveFileSpec(szVolume))
  1220. break;
  1221. }
  1222. if (pszVolume)
  1223. {
  1224. PathRemoveBackslash(szVolume);
  1225. lstrcpyn(pszVolume, szVolume, cchVolume);
  1226. }
  1227. }
  1228. /*
  1229. This function checks if pszPath is a root share. pszPath is in the format
  1230. \\server\share where "share" is shared out directory on "server". Function
  1231. attempts to get the local path of "share" on "server" directory. If local
  1232. path is root, pszPath is a root share. In all other cases (including failures)
  1233. its not. Only members of the Administrators or Account Operators local group or
  1234. those with Communication, Print, or Server operator group membership can
  1235. successfully execute the NetShareGetInfo function at level 2, so its likely
  1236. we will encounter failures and in those cases we will simply treat this as
  1237. non-root share.
  1238. */
  1239. BOOL IsShareRoot(LPCTSTR pszPath)
  1240. {
  1241. if(!pszPath)
  1242. {
  1243. return FALSE;
  1244. }
  1245. DWORD dwReturn = FALSE;
  1246. do
  1247. {
  1248. //Check if pszPath is in format \\server\share
  1249. if(!PathIsUNCServerShare(pszPath))
  1250. break;
  1251. //pszShare will point to "share"
  1252. LPWSTR pszShare = PathFindFileName(pszPath);
  1253. if(!pszShare)
  1254. break;
  1255. WCHAR szServer[MAX_PATH];
  1256. if(FAILED(StringCchCopy(szServer,ARRAYSIZE(szServer),pszPath)))
  1257. break;;
  1258. //Remove the "share" portion, szServer will contain \\server
  1259. if(!PathRemoveFileSpec(szServer))
  1260. break;
  1261. //Get the local path of the share on the server
  1262. SHARE_INFO_2 *pbuf = NULL;
  1263. NET_API_STATUS status = NetShareGetInfo(szServer,
  1264. pszShare,
  1265. 2,
  1266. (LPBYTE *)&pbuf);
  1267. if(status != NERR_Success)
  1268. break;
  1269. if(pbuf && pbuf->shi2_path && PathIsRoot(pbuf->shi2_path))
  1270. {
  1271. dwReturn = TRUE;
  1272. }
  1273. if(pbuf)
  1274. {
  1275. NetApiBufferFree(pbuf);
  1276. pbuf = NULL;
  1277. }
  1278. }while(0);
  1279. return dwReturn;
  1280. }
  1281. /*
  1282. Setting permissions can result, in some special cases, in loss of inherited aces.
  1283. pbShowLossInheritedAclWarning is set to TRUE if that's the case and we show
  1284. a warning.
  1285. */
  1286. void
  1287. GetFileInfo(LPCTSTR pszPath,
  1288. LPDWORD pdwFileType,
  1289. LPTSTR pszServer,
  1290. ULONG cchServer,
  1291. BOOL *pbShowLossInheritedAclWarning)
  1292. {
  1293. DWORD dwVolumeFlags = 0;
  1294. TCHAR szVolume[MAX_PATH];
  1295. LPTSTR pszUNC = NULL;
  1296. TraceEnter(TRACE_RSHX32, "GetFileInfo");
  1297. TraceAssert(NULL != pszServer);
  1298. pszServer[0] = TEXT('\0');
  1299. if(pbShowLossInheritedAclWarning)
  1300. *pbShowLossInheritedAclWarning = FALSE;
  1301. if (!PathIsUNC(pszPath) && S_OK == GetRemotePath(pszPath, &pszUNC))
  1302. pszPath = pszUNC;
  1303. //If path is in format "\\server\share", special case this case
  1304. //to determine if it's a root.
  1305. //NTRAID#NTBUG9-501402-2002/05/06-hiteshr
  1306. if(PathIsUNCServerShare(pszPath))
  1307. {
  1308. //check if "share" is root on the "\\server"
  1309. if(IsShareRoot(pszPath))
  1310. {
  1311. *pdwFileType |= DOBJ_RES_ROOT;
  1312. }
  1313. else if(pbShowLossInheritedAclWarning)
  1314. {
  1315. //It's a UNC share which is not root. Setting acl will result in loss of
  1316. //inherited aces
  1317. *pbShowLossInheritedAclWarning = TRUE;
  1318. }
  1319. }
  1320. else if (PathIsRoot(pszPath))
  1321. {
  1322. *pdwFileType |= DOBJ_RES_ROOT;
  1323. if(pbShowLossInheritedAclWarning)
  1324. {
  1325. if(GetDriveType(pszPath) == DRIVE_REMOTE)
  1326. {
  1327. //This is a remote drive and we have been unable to determine
  1328. //if its root of drive. By default we assume, it's not root of
  1329. //drive and show warning.
  1330. *pbShowLossInheritedAclWarning = TRUE;
  1331. }
  1332. }
  1333. }
  1334. GetVolumeInfo(pszPath,
  1335. *pdwFileType & DOBJ_RES_CONT,
  1336. &dwVolumeFlags,
  1337. szVolume,
  1338. ARRAYSIZE(szVolume));
  1339. if (dwVolumeFlags & FS_PERSISTENT_ACLS)
  1340. {
  1341. *pdwFileType |= DOBJ_VOL_NTACLS;
  1342. if (IsDfsPath(szVolume, pszServer, cchServer))
  1343. {
  1344. }
  1345. else if (PathIsUNC(szVolume))
  1346. {
  1347. LPTSTR pSlash = StrChr(&szVolume[2], TEXT('\\'));
  1348. if (pSlash)
  1349. cchServer = min(cchServer, (ULONG)(pSlash - szVolume) + 1);
  1350. lstrcpyn(pszServer, szVolume, cchServer);
  1351. }
  1352. }
  1353. LocalFreeString(&pszUNC);
  1354. TraceLeaveVoid();
  1355. }