Author: cfinck Date: Wed Apr 15 02:31:36 2009 New Revision: 40513 URL: http://svn.reactos.org/svn/reactos?rev=40513&view=rev Log: - 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. Modified: trunk/reactos/base/services/umpnpmgr/umpnpmgr.c trunk/reactos/base/services/umpnpmgr/umpnpmgr.rbuild trunk/reactos/dll/win32/newdev/newdev.c Modified: trunk/reactos/base/services/umpnpmgr/umpnpmgr.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/services/umpnpmgr/umpnpmgr.c?rev=40513&r1=40512&r2=40513&view=diff ============================================================================== --- trunk/reactos/base/services/umpnpmgr/umpnpmgr.c [iso-8859-1] (original) +++ trunk/reactos/base/services/umpnpmgr/umpnpmgr.c [iso-8859-1] Wed Apr 15 02:31:36 2009 @@ -23,19 +23,23 @@ * PURPOSE: User-mode Plug and Play manager * PROGRAMMER: Eric Kohl * Hervé Poussineau (hpous...@reactos.org) + * Colin Finck (co...@reactos.org) */ /* INCLUDES *****************************************************************/ //#define HAVE_SLIST_ENTRY_IMPLEMENTED #define WIN32_NO_STATUS #include <windows.h> +#include <stdio.h> #include <cmtypes.h> #include <cmfuncs.h> #include <rtlfuncs.h> +#include <setypes.h> #include <umpnpmgr/sysguid.h> #include <wdmguid.h> #include <cfgmgr32.h> #include <regstr.h> +#include <userenv.h> #include <rpc.h> #include <rpcdce.h> @@ -222,6 +226,7 @@ BOOL Admin, DWORD ProcessId) { + DWORD ReturnValue = CR_FAILURE; HANDLE hProcess; UNREFERENCED_PARAMETER(hBinding); @@ -233,28 +238,37 @@ SetEvent(hInstallEvent); /* Get the users token */ - hProcess = OpenProcess(PROCESS_ALL_ACCESS, - TRUE, - ProcessId); - if (hProcess != NULL) - { - if (hUserToken != NULL) - { - CloseHandle(hUserToken); - hUserToken = NULL; - } - - OpenProcessToken(hProcess, - TOKEN_ALL_ACCESS, - &hUserToken); - CloseHandle(hProcess); + hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, ProcessId); + + if(!hProcess) + { + DPRINT1("OpenProcess failed with error %u\n", GetLastError()); + goto cleanup; + } + + 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,18 +1914,29 @@ } -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); @@ -1934,34 +1959,109 @@ return TRUE; } - /* Install device */ - SetEnvironmentVariableW(L"USERPROFILE", L"."); /* FIXME: why is it needed? */ - - hNewDev = LoadLibraryW(L"newdev.dll"); - if (!hNewDev) - { - DPRINT1("Unable to load newdev.dll\n"); + /* 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]); + + /* 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("CreateNamedPipeW failed with error %u\n", GetLastError()); goto cleanup; } - DevInstallW = (PDEV_INSTALL_W)GetProcAddress(hNewDev, (LPCSTR)"DevInstallW"); - if (!DevInstallW) - { - DPRINT1("'DevInstallW' not found in newdev.dll\n"); + /* 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("ConnectNamedPipe failed with error %u\n", GetLastError()); goto cleanup; } - if (!DevInstallW(NULL, NULL, DeviceInstance, ShowWizard ? SW_SHOWNOACTIVATE : SW_HIDE)) - { - DPRINT1("DevInstallW('%S') failed\n", DeviceInstance); + /* 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("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,12 +2355,16 @@ int wmain(int argc, WCHAR *argv[]) { + BOOLEAN OldValue; DWORD dwError; UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(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) Modified: trunk/reactos/base/services/umpnpmgr/umpnpmgr.rbuild URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/services/umpnpmgr/umpnpmgr.rbuild?rev=40513&r1=40512&r2=40513&view=diff ============================================================================== --- trunk/reactos/base/services/umpnpmgr/umpnpmgr.rbuild [iso-8859-1] (original) +++ trunk/reactos/base/services/umpnpmgr/umpnpmgr.rbuild [iso-8859-1] Wed Apr 15 02:31:36 2009 @@ -11,6 +11,7 @@ <library>rpcrt4</library> <library>pseh</library> <library>wdmguid</library> + <library>userenv</library> <file>umpnpmgr.c</file> <file>umpnpmgr.rc</file> </module> Modified: trunk/reactos/dll/win32/newdev/newdev.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/newdev/newdev.c?rev=40513&r1=40512&r2=40513&view=diff ============================================================================== --- trunk/reactos/dll/win32/newdev/newdev.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/newdev/newdev.c [iso-8859-1] Wed Apr 15 02:31:36 2009 @@ -3,6 +3,7 @@ * * Copyright 2005-2006 Hervé Poussineau (hpous...@reactos.org) * 2005 Christoph von Wittich (christ...@activevb.de) + * 2009 Colin Finck (co...@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 @@ } /* -* @unimplemented +* @implemented */ BOOL WINAPI ClientSideInstallW( @@ -811,11 +812,75 @@ 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