From: Colin Finck Date: Tue, 14 Apr 2009 22:31:36 +0000 (+0000) Subject: - Use rundll32.exe and CreateProcessAsUserW to call ClientSideInstallW for installing... X-Git-Tag: ReactOS-0.3.9~57 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=484b6902f1062ba802c284c0bd0ee02687703276 - Use rundll32.exe and CreateProcessAsUserW to call ClientSideInstallW for installing new devices and supply all required information over a named pipe. The named pipe communication was monitored under Windows XP SP2, so that the protocol under ReactOS is compatible (except for one data field, see code) - Implement ClientSideInstallW in newdev.dll - Give umpnpmgr the SE_ASSIGNPRIMARYTOKEN privilege to use CreateProcessAsUserW - Open the token of the userinit process with TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY, we don't get TOKEN_ALL_ACCESS and used to fail here without noticing it - Return CR_FAILURE in case of problems inside PNP_ReportLogOn This stuff by the way fixes the "Browse" button in a "New hardware device" dialog See issue #4363 for more details. svn path=/trunk/; revision=40513 --- diff --git a/reactos/base/services/umpnpmgr/umpnpmgr.c b/reactos/base/services/umpnpmgr/umpnpmgr.c index 19dfdb17e66..af5f98ff480 100644 --- a/reactos/base/services/umpnpmgr/umpnpmgr.c +++ b/reactos/base/services/umpnpmgr/umpnpmgr.c @@ -23,19 +23,23 @@ * PURPOSE: User-mode Plug and Play manager * PROGRAMMER: Eric Kohl * Hervé Poussineau (hpoussin@reactos.org) + * Colin Finck (colin@reactos.org) */ /* INCLUDES *****************************************************************/ //#define HAVE_SLIST_ENTRY_IMPLEMENTED #define WIN32_NO_STATUS #include +#include #include #include #include +#include #include #include #include #include +#include #include #include @@ -222,6 +226,7 @@ DWORD PNP_ReportLogOn( BOOL Admin, DWORD ProcessId) { + DWORD ReturnValue = CR_FAILURE; HANDLE hProcess; UNREFERENCED_PARAMETER(hBinding); @@ -233,28 +238,37 @@ DWORD PNP_ReportLogOn( SetEvent(hInstallEvent); /* Get the users token */ - hProcess = OpenProcess(PROCESS_ALL_ACCESS, - TRUE, - ProcessId); - if (hProcess != NULL) + hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, ProcessId); + + if(!hProcess) { - if (hUserToken != NULL) - { - CloseHandle(hUserToken); - hUserToken = NULL; - } + DPRINT1("OpenProcess failed with error %u\n", GetLastError()); + goto cleanup; + } - OpenProcessToken(hProcess, - TOKEN_ALL_ACCESS, - &hUserToken); - CloseHandle(hProcess); + if (hUserToken) + { + CloseHandle(hUserToken); + hUserToken = NULL; + } + + if(!OpenProcessToken(hProcess, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY, &hUserToken)) + { + DPRINT1("OpenProcessToken failed with error %u\n", GetLastError()); + goto cleanup; } /* Trigger the installer thread */ /*if (hInstallEvent != NULL) SetEvent(hInstallEvent);*/ - return CR_SUCCESS; + ReturnValue = CR_SUCCESS; + +cleanup: + if(hProcess) + CloseHandle(hProcess); + + return ReturnValue; } @@ -1900,19 +1914,30 @@ DWORD PNP_DeleteServiceDevices( } -typedef BOOL (WINAPI *PDEV_INSTALL_W)(HWND, HINSTANCE, LPCWSTR, INT); - static BOOL InstallDevice(PCWSTR DeviceInstance, BOOL ShowWizard) { PLUGPLAY_CONTROL_STATUS_DATA PlugPlayData; - HMODULE hNewDev = NULL; - PDEV_INSTALL_W DevInstallW; NTSTATUS Status; BOOL DeviceInstalled = FALSE; + DWORD BytesWritten; + DWORD Value; + HANDLE hPipe = INVALID_HANDLE_VALUE; + LPVOID Environment = NULL; + PROCESS_INFORMATION ProcessInfo; + STARTUPINFOW StartupInfo; + UUID RandomUuid; + + /* The following lengths are constant (see below), they cannot overflow */ + WCHAR CommandLine[116]; + WCHAR InstallEventName[73]; + WCHAR PipeName[74]; + WCHAR UuidString[39]; DPRINT("InstallDevice(%S, %d)\n", DeviceInstance, ShowWizard); + ZeroMemory(&ProcessInfo, sizeof(ProcessInfo)); + RtlInitUnicodeString(&PlugPlayData.DeviceInstance, DeviceInstance); PlugPlayData.Operation = 0; /* Get status */ @@ -1934,34 +1959,109 @@ InstallDevice(PCWSTR DeviceInstance, BOOL ShowWizard) return TRUE; } - /* Install device */ - SetEnvironmentVariableW(L"USERPROFILE", L"."); /* FIXME: why is it needed? */ + /* Create a random UUID for the named pipe */ + UuidCreate(&RandomUuid); + swprintf(UuidString, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + RandomUuid.Data1, RandomUuid.Data2, RandomUuid.Data3, + RandomUuid.Data4[0], RandomUuid.Data4[1], RandomUuid.Data4[2], + RandomUuid.Data4[3], RandomUuid.Data4[4], RandomUuid.Data4[5], + RandomUuid.Data4[6], RandomUuid.Data4[7]); - hNewDev = LoadLibraryW(L"newdev.dll"); - if (!hNewDev) + /* Create the named pipe */ + wcscpy(PipeName, L"\\\\.\\pipe\\PNP_Device_Install_Pipe_0."); + wcscat(PipeName, UuidString); + hPipe = CreateNamedPipeW(PipeName, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 512, 512, 0, NULL); + + if(hPipe == INVALID_HANDLE_VALUE) { - DPRINT1("Unable to load newdev.dll\n"); + DPRINT1("CreateNamedPipeW failed with error %u\n", GetLastError()); goto cleanup; } - DevInstallW = (PDEV_INSTALL_W)GetProcAddress(hNewDev, (LPCSTR)"DevInstallW"); - if (!DevInstallW) + /* Launch rundll32 to call ClientSideInstallW */ + wcscpy(CommandLine, L"rundll32.exe newdev.dll,ClientSideInstall "); + wcscat(CommandLine, PipeName); + + ZeroMemory(&StartupInfo, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(StartupInfo); + + if(hUserToken) + { + /* newdev has to run under the environment of the current user */ + if(!CreateEnvironmentBlock(&Environment, hUserToken, FALSE)) + { + DPRINT1("CreateEnvironmentBlock failed with error %d\n", GetLastError()); + goto cleanup; + } + + if(!CreateProcessAsUserW(hUserToken, NULL, CommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, Environment, NULL, &StartupInfo, &ProcessInfo)) + { + DPRINT1("CreateProcessAsUserW failed with error %u\n", GetLastError()); + goto cleanup; + } + } + else + { + /* FIXME: This is probably not correct, I guess newdev should never be run with SYSTEM privileges. + + Still, we currently do that in 2nd stage setup and probably Console mode as well, so allow it here. + (ShowWizard is only set to FALSE for these two modes) */ + ASSERT(!ShowWizard); + + if(!CreateProcessW(NULL, CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) + { + DPRINT1("CreateProcessW failed with error %u\n", GetLastError()); + goto cleanup; + } + } + + /* Wait for the function to connect to our pipe */ + if(!ConnectNamedPipe(hPipe, NULL)) { - DPRINT1("'DevInstallW' not found in newdev.dll\n"); + DPRINT1("ConnectNamedPipe failed with error %u\n", GetLastError()); goto cleanup; } - if (!DevInstallW(NULL, NULL, DeviceInstance, ShowWizard ? SW_SHOWNOACTIVATE : SW_HIDE)) + /* Pass the data. The following output is partly compatible to Windows XP SP2 (researched using a modified newdev.dll to log this stuff) */ + wcscpy(InstallEventName, L"Global\\PNP_Device_Install_Event_0."); + wcscat(InstallEventName, UuidString); + + Value = sizeof(InstallEventName); + WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL); + WriteFile(hPipe, InstallEventName, Value, &BytesWritten, NULL); + + /* I couldn't figure out what the following value means under WinXP. It's usually 0 in my tests, but was also 5 once. + Therefore the following line is entirely ReactOS-specific. We use the value here to pass the ShowWizard variable. */ + WriteFile(hPipe, &ShowWizard, sizeof(ShowWizard), &BytesWritten, NULL); + + Value = (wcslen(DeviceInstance) + 1) * sizeof(WCHAR); + WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL); + WriteFile(hPipe, DeviceInstance, Value, &BytesWritten, NULL); + + /* Wait for newdev.dll to finish processing */ + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + + /* The following check for success is probably not compatible to Windows, but should do its job */ + if(!GetExitCodeProcess(ProcessInfo.hProcess, &Value)) { - DPRINT1("DevInstallW('%S') failed\n", DeviceInstance); + DPRINT1("GetExitCodeProcess failed with error %u\n", GetLastError()); goto cleanup; } - DeviceInstalled = TRUE; + DeviceInstalled = Value; cleanup: - if (hNewDev != NULL) - FreeLibrary(hNewDev); + if(hPipe != INVALID_HANDLE_VALUE) + CloseHandle(hPipe); + + if(Environment) + DestroyEnvironmentBlock(Environment); + + if(ProcessInfo.hProcess) + CloseHandle(ProcessInfo.hProcess); + + if(ProcessInfo.hThread) + CloseHandle(ProcessInfo.hThread); return DeviceInstalled; } @@ -2255,6 +2355,7 @@ ServiceMain(DWORD argc, LPTSTR *argv) int wmain(int argc, WCHAR *argv[]) { + BOOLEAN OldValue; DWORD dwError; UNREFERENCED_PARAMETER(argc); @@ -2262,6 +2363,9 @@ wmain(int argc, WCHAR *argv[]) DPRINT("Umpnpmgr: main() started\n"); + /* We need this privilege for using CreateProcessAsUserW */ + RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue); + hInstallEvent = CreateEvent(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL); if (hInstallEvent == NULL) { diff --git a/reactos/base/services/umpnpmgr/umpnpmgr.rbuild b/reactos/base/services/umpnpmgr/umpnpmgr.rbuild index 3893ab29638..6b474c1c14a 100644 --- a/reactos/base/services/umpnpmgr/umpnpmgr.rbuild +++ b/reactos/base/services/umpnpmgr/umpnpmgr.rbuild @@ -11,6 +11,7 @@ rpcrt4 pseh wdmguid + userenv umpnpmgr.c umpnpmgr.rc diff --git a/reactos/dll/win32/newdev/newdev.c b/reactos/dll/win32/newdev/newdev.c index 88e6222386c..253d85538f9 100644 --- a/reactos/dll/win32/newdev/newdev.c +++ b/reactos/dll/win32/newdev/newdev.c @@ -3,6 +3,7 @@ * * Copyright 2005-2006 Hervé Poussineau (hpoussin@reactos.org) * 2005 Christoph von Wittich (Christoph@ActiveVB.de) + * 2009 Colin Finck (colin@reactos.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -803,7 +804,7 @@ cleanup: } /* -* @unimplemented +* @implemented */ BOOL WINAPI ClientSideInstallW( @@ -811,11 +812,75 @@ ClientSideInstallW( IN DWORD dwUnknownFlags, IN LPWSTR lpNamedPipeName) { - /* NOTE: pNamedPipeName is in the format: - * "\\.\pipe\PNP_Device_Install_Pipe_0.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" - */ - FIXME("Stub\n"); - return FALSE; + BOOL ReturnValue = FALSE; + BOOL ShowWizard; + DWORD BytesRead; + DWORD Value; + HANDLE hPipe = INVALID_HANDLE_VALUE; + PWSTR DeviceInstance = NULL; + PWSTR InstallEventName = NULL; + + /* Open the pipe */ + hPipe = CreateFileW(lpNamedPipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if(hPipe == INVALID_HANDLE_VALUE) + { + ERR("CreateFileW failed with error %u\n", GetLastError()); + goto cleanup; + } + + /* Read the data. Some is just included for compatibility with Windows right now and not yet used by ReactOS. + See umpnpmgr for more details. */ + if(!ReadFile(hPipe, &Value, sizeof(Value), &BytesRead, NULL)) + { + ERR("ReadFile failed with error %u\n", GetLastError()); + goto cleanup; + } + + InstallEventName = (PWSTR)HeapAlloc(GetProcessHeap(), 0, Value); + + if(!ReadFile(hPipe, InstallEventName, Value, &BytesRead, NULL)) + { + ERR("ReadFile failed with error %u\n", GetLastError()); + goto cleanup; + } + + /* I couldn't figure out what the following value means under Windows XP. + Therefore I used it in umpnpmgr to pass the ShowWizard variable. */ + if(!ReadFile(hPipe, &ShowWizard, sizeof(ShowWizard), &BytesRead, NULL)) + { + ERR("ReadFile failed with error %u\n", GetLastError()); + goto cleanup; + } + + /* Next one is again size in bytes of the following string */ + if(!ReadFile(hPipe, &Value, sizeof(Value), &BytesRead, NULL)) + { + ERR("ReadFile failed with error %u\n", GetLastError()); + goto cleanup; + } + + DeviceInstance = (PWSTR)HeapAlloc(GetProcessHeap(), 0, Value); + + if(!ReadFile(hPipe, DeviceInstance, Value, &BytesRead, NULL)) + { + ERR("ReadFile failed with error %u\n", GetLastError()); + goto cleanup; + } + + ReturnValue = DevInstallW(NULL, NULL, DeviceInstance, ShowWizard ? SW_SHOWNOACTIVATE : SW_HIDE); + +cleanup: + if(hPipe != INVALID_HANDLE_VALUE) + CloseHandle(hPipe); + + if(InstallEventName) + HeapFree(GetProcessHeap(), 0, InstallEventName); + + if(DeviceInstance) + HeapFree(GetProcessHeap(), 0, DeviceInstance); + + return ReturnValue; } BOOL WINAPI