- 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
This commit is contained in:
Colin Finck 2009-04-14 22:31:36 +00:00
parent 2ef63b5190
commit 484b6902f1
3 changed files with 208 additions and 38 deletions

View file

@ -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 <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 @@ 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)
{
if (hUserToken != NULL)
{
CloseHandle(hUserToken);
hUserToken = NULL;
}
hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, ProcessId);
OpenProcessToken(hProcess,
TOKEN_ALL_ACCESS,
&hUserToken);
CloseHandle(hProcess);
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,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)
{
DPRINT1("'DevInstallW' not found in newdev.dll\n");
/* 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))
/* 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)
{

View file

@ -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>

View file

@ -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