1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef mozilla_ShellHeaderOnlyUtils_h
#define mozilla_ShellHeaderOnlyUtils_h
#if defined(LIBXUL) && !defined(UNICODE)
# error \
"UNICODE not set - must be set to prevent compile failure in `comdef.h` due to us deleting `FormatMessage` when absent."
#endif
#include "mozilla/WinHeaderOnlyUtils.h"
#include <objbase.h>
#include <exdisp.h>
#include <shldisp.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <shobjidl.h>
#include <shtypes.h>
// NB: include this after shldisp.h so its macros do not conflict with COM
// interfaces defined by shldisp.h
#include <shellapi.h>
#include <type_traits>
#include <comdef.h>
#include <comutil.h>
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
/**
* Ask the current user's Desktop to ShellExecute on our behalf, thus causing
* the resulting launched process to inherit its security priviliges from
* Explorer instead of our process.
*
* This is useful in two scenarios, in particular:
* * We are running as an elevated user and we want to start something as the
* "normal" user;
* * We are starting a process that is incompatible with our process's
* process mitigation policies. By delegating to Explorer, the child process
* will not be affected by our process mitigations.
*
* Since this communication happens over DCOM, Explorer's COM DACL governs
* whether or not we can execute against it, thus avoiding privilege escalation.
*/
inline LauncherVoidResult ShellExecuteByExplorer(const _bstr_t& aPath,
const _variant_t& aArgs,
const _variant_t& aVerb,
const _variant_t& aWorkingDir,
const _variant_t& aShowCmd) {
// NB: Explorer may be a local server, not an inproc server
RefPtr<IShellWindows> shellWindows;
HRESULT hr = ::CoCreateInstance(
CLSID_ShellWindows, nullptr, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IID_IShellWindows, getter_AddRefs(shellWindows));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
// 1. Find the shell view for the desktop.
_variant_t loc(int(CSIDL_DESKTOP));
_variant_t empty;
long hwnd;
RefPtr<IDispatch> dispDesktop;
hr = shellWindows->FindWindowSW(&loc, &empty, SWC_DESKTOP, &hwnd,
SWFO_NEEDDISPATCH,
getter_AddRefs(dispDesktop));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
if (hr == S_FALSE) {
// The call succeeded but the window was not found.
return LAUNCHER_ERROR_FROM_WIN32(ERROR_NOT_FOUND);
}
RefPtr<IServiceProvider> servProv;
hr = dispDesktop->QueryInterface(IID_IServiceProvider,
getter_AddRefs(servProv));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
RefPtr<IShellBrowser> browser;
hr = servProv->QueryService(SID_STopLevelBrowser, IID_IShellBrowser,
getter_AddRefs(browser));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
RefPtr<IShellView> activeShellView;
hr = browser->QueryActiveShellView(getter_AddRefs(activeShellView));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
// 2. Get the automation object for the desktop.
RefPtr<IDispatch> dispView;
hr = activeShellView->GetItemObject(SVGIO_BACKGROUND, IID_IDispatch,
getter_AddRefs(dispView));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
RefPtr<IShellFolderViewDual> folderView;
hr = dispView->QueryInterface(IID_IShellFolderViewDual,
getter_AddRefs(folderView));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
// 3. Get the interface to IShellDispatch2
RefPtr<IDispatch> dispShell;
hr = folderView->get_Application(getter_AddRefs(dispShell));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
RefPtr<IShellDispatch2> shellDisp;
hr =
dispShell->QueryInterface(IID_IShellDispatch2, getter_AddRefs(shellDisp));
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
// Passing the foreground privilege so that the shell can launch an
// application in the foreground. This fails with E_ACCESSDENIED if the
// current window is shown in the background. We keep a soft assert for
// the other failures to investigate how it happened.
hr = ::CoAllowSetForegroundWindow(shellDisp, nullptr);
MOZ_ASSERT(SUCCEEDED(hr) || hr == E_ACCESSDENIED);
// shellapi.h macros interfere with the correct naming of the method being
// called on IShellDispatch2. Temporarily remove that definition.
#if defined(ShellExecute)
# define MOZ_REDEFINE_SHELLEXECUTE
# undef ShellExecute
#endif // defined(ShellExecute)
// 4. Now call IShellDispatch2::ShellExecute to ask Explorer to execute.
hr = shellDisp->ShellExecute(aPath, aArgs, aWorkingDir, aVerb, aShowCmd);
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
// Restore the macro that was removed prior to IShellDispatch2::ShellExecute
#if defined(MOZ_REDEFINE_SHELLEXECUTE)
# if defined(UNICODE)
# define ShellExecute ShellExecuteW
# else
# define ShellExecute ShellExecuteA
# endif
# undef MOZ_REDEFINE_SHELLEXECUTE
#endif // defined(MOZ_REDEFINE_SHELLEXECUTE)
return Ok();
}
using UniqueAbsolutePidl =
UniquePtr<std::remove_pointer_t<PIDLIST_ABSOLUTE>, CoTaskMemFreeDeleter>;
inline LauncherResult<UniqueAbsolutePidl> ShellParseDisplayName(
const wchar_t* aPath) {
PIDLIST_ABSOLUTE rawAbsPidl = nullptr;
SFGAOF sfgao;
HRESULT hr = ::SHParseDisplayName(aPath, nullptr, &rawAbsPidl, 0, &sfgao);
if (FAILED(hr)) {
return LAUNCHER_ERROR_FROM_HRESULT(hr);
}
return UniqueAbsolutePidl(rawAbsPidl);
}
} // namespace mozilla
#endif // mozilla_ShellHeaderOnlyUtils_h
|