andre@1118: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik andre@1118: * Software engineering by Intevation GmbH andre@1118: * andre@1118: * This file is Free Software under the GNU GPL (v>=2) andre@1118: * and comes with ABSOLUTELY NO WARRANTY! andre@1118: * See LICENSE.txt for details. andre@1118: */ andre@1118: andre@1118: /** andre@1118: * @file tasksched_run_as.cpp andre@1118: * @brief Run a command with the Task Scheduler API andre@1118: * andre@1118: * This effectively starts a program as the user regardles of the current andre@1118: * process token. andre@1118: */ andre@1118: #include andre@1118: #include "logging.h" andre@1118: #include "strhelp.h" andre@1118: andre@1118: #ifndef INITGUID andre@1118: #define INITGUID andre@1118: #endif andre@1118: andre@1118: #include andre@1118: #include andre@1118: #include andre@1118: #include andre@1118: #include andre@1118: #include andre@1118: #include andre@1118: #include andre@1118: andre@1118: /* Some declarations missing in mingw-w64 3.1.0 taken from msdn */ andre@1118: andre@1118: __CRT_UUID_DECL(IShellWindows, 0x85CB6900, 0x4D95, 0x11CF, andre@1118: 0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85); andre@1118: andre@1118: DEFINE_GUID(IID_IShellWindows, andre@1118: 0x85CB6900, 0x4D95, 0x11CF, andre@1118: 0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85); andre@1118: DEFINE_GUID(CLSID_ShellWindows, andre@1118: 0x9BA05972, 0xF6A8, 0x11CF, andre@1118: 0xA4, 0x42, 0x00, 0xA0, 0xC9, 0x0A, 0x8F, 0x39); andre@1118: andre@1118: andre@1118: __CRT_UUID_DECL(IShellDispatch2, 0xA4C6892C, 0x3BA9, 0x11d2, andre@1118: 0x9D, 0xEA, 0x00, 0xC0, 0x4F, 0xB1, 0x61, 0x62); andre@1118: __CRT_UUID_DECL(IShellFolderViewDual, 0xe7a1af80, 0x4d96, andre@1118: 0x11cf, 0x96, 0x0c, 0x00, 0x80, 0xc7, 0xf4, 0xee, 0x85); andre@1118: andre@1118: #ifndef SWC_DESKTOP /* Will probably be addedd in future mingw */ andre@1118: #define SWC_DESKTOP 0x00000008 andre@1118: /* from http://msdn.microsoft.com/en-us/library/windows/desktop/cc836581%28v=vs.85%29.aspx */ andre@1118: #endif andre@1118: andre@1118: #undef INITGUID andre@1118: andre@1118: #ifdef DO_RELEASE_BUILD andre@1118: bool g_debug = false; andre@1118: #else andre@1118: bool g_debug = true; andre@1118: #endif andre@1118: andre@1118: typedef struct _stack_t andre@1118: { andre@1118: struct _stack_t *next; andre@1118: char text[1]; /* This should be the length of string_size. */ andre@1118: } stack_t; andre@1118: andre@1118: static HINSTANCE g_hInstance; /* Our Instance. */ andre@1118: andre@1118: #define UNUSED(x) (void)(x) andre@1118: andre@1118: /** @brief the actual execuation call on the shell dispatcher andre@1118: * andre@1118: * @param[in] disp The shell dispatcher to use for shell execute. andre@1118: * @param[in] fName The file that should be exectued. andre@1122: * @param[in] param Optinal parameters to add. andre@1118: * andre@1118: * @returns true on success. andre@1118: */ andre@1118: static bool andre@1122: shellexecute(IShellDispatch2 *disp, wchar_t *fName, wchar_t *param) andre@1118: { andre@1118: BSTR bName = NULL, andre@1118: bParam = NULL, andre@1118: bDir = NULL, andre@1118: bOp = NULL; andre@1118: VARIANT vParams[4]; andre@1118: HRESULT hr; andre@1118: andre@1118: if (!fName || !disp) andre@1118: { andre@1118: ERRORPRINTF ("Invalid call to shellexecute."); andre@1118: return false; andre@1118: } andre@1118: andre@1118: bName = SysAllocString(fName); andre@1122: bParam = SysAllocString(param ? param : L""); andre@1118: bDir = SysAllocString(L""); andre@1118: bOp = SysAllocString(L""); andre@1118: andre@1118: if (!bName || !bParam || !bDir || !bOp) andre@1118: { andre@1118: /* Out of memory */ andre@1118: ERRORPRINTF ("Failed to allocate bstr values "); andre@1118: return false; andre@1118: } andre@1118: andre@1118: vParams[0].vt = VT_BSTR; andre@1118: vParams[0].bstrVal = bParam; andre@1118: vParams[1].vt = VT_BSTR; andre@1118: vParams[1].bstrVal = bDir; andre@1118: vParams[2].vt = VT_BSTR; andre@1118: vParams[2].bstrVal = bOp; andre@1118: vParams[3].vt = VT_INT; andre@1118: vParams[3].intVal = SW_SHOWNORMAL; andre@1118: andre@1118: hr = disp->ShellExecute(bName, vParams[0], vParams[1], vParams[2], vParams[3]); andre@1118: andre@1118: SysFreeString(bName); andre@1118: SysFreeString(bParam); andre@1118: SysFreeString(bOp); andre@1118: SysFreeString(bDir); andre@1118: andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to execute."); andre@1118: return false; andre@1118: } andre@1118: return true; andre@1118: } andre@1118: andre@1118: #ifdef __cplusplus andre@1118: extern "C" { andre@1118: #endif andre@1118: andre@1118: /* Standard entry point for DLLs. */ andre@1118: int WINAPI andre@1118: DllMain (HANDLE hinst, DWORD reason, LPVOID reserved) andre@1118: { andre@1118: UNUSED(reserved); andre@1118: if (reason == DLL_PROCESS_ATTACH) andre@1118: g_hInstance = (HINSTANCE)hinst; andre@1118: return TRUE; andre@1118: } andre@1118: andre@1118: /** @brief Execute a command with the current running shell. andre@1118: * andre@1118: * This function is intended to be called when you want to andre@1118: * make sure that your application is not executed with higher andre@1118: * privileges then the normal desktop session. andre@1118: * andre@1118: * The code is based on the idea: andre@1118: * http://blogs.msdn.com/b/oldnewthing/archive/2013/11/18/10468726.aspx andre@1118: * andre@1118: * The function signature is explained by NSIS. andre@1118: */ andre@1118: void __declspec(dllexport) __cdecl Exec(HWND hwndParent, andre@1118: int string_size, andre@1118: char *variables, andre@1118: stack_t **stacktop) andre@1118: { andre@1118: UNUSED(hwndParent); andre@1118: UNUSED(string_size); andre@1118: HRESULT hr; andre@1122: wchar_t *wbuf = NULL, andre@1122: *params = NULL; andre@1118: IShellWindows *shellWindows = NULL; andre@1118: IShellBrowser *shellBrowser = NULL; andre@1118: IShellView *shellView = NULL; andre@1118: IShellFolderViewDual *folderView = NULL; andre@1118: IShellDispatch2 *shellDispatch = NULL; andre@1118: IServiceProvider *serviceProv = NULL; andre@1118: HWND hwnd; andre@1118: IDispatch *disp = NULL, andre@1118: *bgDisp = NULL, andre@1118: *sDisp = NULL; andre@1118: VARIANT vEmpty = {}; andre@1118: andre@1118: andre@1118: if (!stacktop || !*stacktop || !(*stacktop)->text) andre@1118: { andre@1118: ERRORPRINTF ("Invalid call to exec :"); andre@1118: return; andre@1118: } andre@1118: andre@1118: /* Initialize com ctx */ andre@1118: hr = CoInitialize(NULL); andre@1118: if(FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("CoInitializeEx failed. error = 0x%lx.", hr); andre@1118: return; andre@1118: } andre@1118: andre@1118: /* Get the shell interface */ andre@1118: hr = CoCreateInstance(CLSID_ShellWindows, andre@1118: NULL, CLSCTX_LOCAL_SERVER, andre@1118: IID_PPV_ARGS(&shellWindows)); andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to get shell interface."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: /* Get the desktop shell window */ andre@1118: hr = shellWindows->FindWindowSW(&vEmpty, andre@1118: &vEmpty, andre@1118: SWC_DESKTOP, andre@1118: (long*)&hwnd, andre@1118: SWFO_NEEDDISPATCH, andre@1118: &disp); andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to find the desktop dispatcher."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: hr = disp->QueryInterface(IID_PPV_ARGS(&serviceProv)); andre@1118: andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to get the service provider."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: /* Get the shell browser */ andre@1118: hr = serviceProv->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shellBrowser)); andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to find the top level browser."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: hr = shellBrowser->QueryActiveShellView(&shellView); andre@1118: andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to find the active view."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: hr = shellView->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&bgDisp)); andre@1118: andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to get the views background."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: hr = bgDisp->QueryInterface(IID_PPV_ARGS(&folderView)); andre@1118: andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to get the folder view."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: andre@1118: hr = folderView->get_Application(&sDisp); andre@1118: andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to get the shell dispatch."); andre@1118: goto done; andre@1118: } andre@1118: andre@1118: hr = sDisp->QueryInterface(IID_PPV_ARGS(&shellDispatch)); andre@1118: andre@1118: if (FAILED(hr)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to get the shell dispatch interface."); andre@1118: goto done; andre@1118: } andre@1118: andre@1158: /* For unicodensis this has to be utf8 to wchar */ andre@1158: andre@1158: wbuf = acp_to_wchar((*stacktop)->text, strlen((*stacktop)->text)); andre@1118: if (!wbuf) andre@1118: { andre@1118: ERRORPRINTF ("Failed to convert argument to wchar. error = 0x%lx.", hr); andre@1118: goto done; andre@1118: } andre@1118: andre@1122: if ((*stacktop)->next && (*stacktop)->next->text) andre@1122: { andre@1158: params = acp_to_wchar((*stacktop)->next->text, strlen((*stacktop)->next->text)); andre@1122: } andre@1122: andre@1122: if (!shellexecute(shellDispatch, wbuf, params)) andre@1118: { andre@1118: ERRORPRINTF ("Failed to execute."); andre@1118: } andre@1118: xfree (wbuf); andre@1122: xfree (params); andre@1118: done: andre@1118: if (folderView) andre@1118: { andre@1118: folderView->Release(); andre@1118: } andre@1118: if (disp) andre@1118: { andre@1118: disp->Release(); andre@1118: } andre@1118: if (shellBrowser) andre@1118: { andre@1118: shellBrowser->Release(); andre@1118: } andre@1118: if (shellWindows) andre@1118: { andre@1118: shellWindows->Release(); andre@1118: } andre@1118: if (shellView) andre@1118: { andre@1118: shellView->Release(); andre@1118: } andre@1118: if (sDisp) andre@1118: { andre@1118: sDisp->Release(); andre@1118: } andre@1118: if (shellDispatch) andre@1118: { andre@1118: shellDispatch->Release(); andre@1118: } andre@1118: if (serviceProv) andre@1118: { andre@1118: serviceProv->Release(); andre@1118: } andre@1118: CoUninitialize(); andre@1118: return; andre@1118: } andre@1118: #ifdef __cplusplus andre@1118: } andre@1118: #endif andre@1118: andre@1118: