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

Reply via email to