1 /* 2 * ReactOS kernel 3 * Copyright (C) 2005 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 /* 20 * COPYRIGHT: See COPYING in the top level directory 21 * PROJECT: ReactOS kernel 22 * FILE: base/services/umpnpmgr/install.c 23 * PURPOSE: Device installer 24 * PROGRAMMER: Eric Kohl (eric.kohl@reactos.org) 25 * Hervé Poussineau (hpoussin@reactos.org) 26 * Colin Finck (colin@reactos.org) 27 */ 28 29 /* INCLUDES *****************************************************************/ 30 31 #include "precomp.h" 32 33 #define NDEBUG 34 #include <debug.h> 35 36 37 /* GLOBALS ******************************************************************/ 38 39 HANDLE hUserToken = NULL; 40 HANDLE hInstallEvent = NULL; 41 HANDLE hNoPendingInstalls = NULL; 42 43 /* Device-install event list */ 44 HANDLE hDeviceInstallListMutex; 45 LIST_ENTRY DeviceInstallListHead; 46 HANDLE hDeviceInstallListNotEmpty; 47 48 DWORD 49 CreatePnpInstallEventSecurity( 50 _Out_ PSECURITY_DESCRIPTOR *EventSd); 51 52 /* FUNCTIONS *****************************************************************/ 53 54 static BOOL 55 InstallDevice(PCWSTR DeviceInstance, BOOL ShowWizard) 56 { 57 BOOL DeviceInstalled = FALSE; 58 DWORD BytesWritten; 59 DWORD Value; 60 DWORD ErrCode; 61 HANDLE hInstallEvent; 62 HANDLE hPipe = INVALID_HANDLE_VALUE; 63 LPVOID Environment = NULL; 64 PROCESS_INFORMATION ProcessInfo; 65 STARTUPINFOW StartupInfo; 66 UUID RandomUuid; 67 HKEY DeviceKey; 68 SECURITY_ATTRIBUTES EventAttrs; 69 PSECURITY_DESCRIPTOR EventSd; 70 71 /* The following lengths are constant (see below), they cannot overflow */ 72 WCHAR CommandLine[116]; 73 WCHAR InstallEventName[73]; 74 WCHAR PipeName[74]; 75 WCHAR UuidString[39]; 76 77 DPRINT("InstallDevice(%S, %d)\n", DeviceInstance, ShowWizard); 78 79 ZeroMemory(&ProcessInfo, sizeof(ProcessInfo)); 80 81 if (RegOpenKeyExW(hEnumKey, 82 DeviceInstance, 83 0, 84 KEY_QUERY_VALUE, 85 &DeviceKey) == ERROR_SUCCESS) 86 { 87 if (RegQueryValueExW(DeviceKey, 88 L"Class", 89 NULL, 90 NULL, 91 NULL, 92 NULL) == ERROR_SUCCESS) 93 { 94 DPRINT("No need to install: %S\n", DeviceInstance); 95 RegCloseKey(DeviceKey); 96 return TRUE; 97 } 98 99 BytesWritten = sizeof(DWORD); 100 if (RegQueryValueExW(DeviceKey, 101 L"ConfigFlags", 102 NULL, 103 NULL, 104 (PBYTE)&Value, 105 &BytesWritten) == ERROR_SUCCESS) 106 { 107 if (Value & CONFIGFLAG_FAILEDINSTALL) 108 { 109 DPRINT("No need to install: %S\n", DeviceInstance); 110 RegCloseKey(DeviceKey); 111 return TRUE; 112 } 113 } 114 115 RegCloseKey(DeviceKey); 116 } 117 118 DPRINT1("Installing: %S\n", DeviceInstance); 119 120 /* Create a random UUID for the named pipe & event*/ 121 UuidCreate(&RandomUuid); 122 swprintf(UuidString, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", 123 RandomUuid.Data1, RandomUuid.Data2, RandomUuid.Data3, 124 RandomUuid.Data4[0], RandomUuid.Data4[1], RandomUuid.Data4[2], 125 RandomUuid.Data4[3], RandomUuid.Data4[4], RandomUuid.Data4[5], 126 RandomUuid.Data4[6], RandomUuid.Data4[7]); 127 128 ErrCode = CreatePnpInstallEventSecurity(&EventSd); 129 if (ErrCode != ERROR_SUCCESS) 130 { 131 DPRINT1("CreatePnpInstallEventSecurity failed with error %u\n", GetLastError()); 132 return FALSE; 133 } 134 135 /* Set up the security attributes for the event */ 136 EventAttrs.nLength = sizeof(SECURITY_ATTRIBUTES); 137 EventAttrs.lpSecurityDescriptor = EventSd; 138 EventAttrs.bInheritHandle = FALSE; 139 140 /* Create the event */ 141 wcscpy(InstallEventName, L"Global\\PNP_Device_Install_Event_0."); 142 wcscat(InstallEventName, UuidString); 143 hInstallEvent = CreateEventW(&EventAttrs, TRUE, FALSE, InstallEventName); 144 HeapFree(GetProcessHeap(), 0, EventSd); 145 if (!hInstallEvent) 146 { 147 DPRINT1("CreateEventW('%ls') failed with error %lu\n", InstallEventName, GetLastError()); 148 goto cleanup; 149 } 150 151 /* Create the named pipe */ 152 wcscpy(PipeName, L"\\\\.\\pipe\\PNP_Device_Install_Pipe_0."); 153 wcscat(PipeName, UuidString); 154 hPipe = CreateNamedPipeW(PipeName, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 512, 512, 0, NULL); 155 if (hPipe == INVALID_HANDLE_VALUE) 156 { 157 DPRINT1("CreateNamedPipeW failed with error %u\n", GetLastError()); 158 goto cleanup; 159 } 160 161 /* Launch rundll32 to call ClientSideInstallW */ 162 wcscpy(CommandLine, L"rundll32.exe newdev.dll,ClientSideInstall "); 163 wcscat(CommandLine, PipeName); 164 165 ZeroMemory(&StartupInfo, sizeof(StartupInfo)); 166 StartupInfo.cb = sizeof(StartupInfo); 167 168 if (hUserToken) 169 { 170 /* newdev has to run under the environment of the current user */ 171 if (!CreateEnvironmentBlock(&Environment, hUserToken, FALSE)) 172 { 173 DPRINT1("CreateEnvironmentBlock failed with error %d\n", GetLastError()); 174 goto cleanup; 175 } 176 177 if (!CreateProcessAsUserW(hUserToken, NULL, CommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, Environment, NULL, &StartupInfo, &ProcessInfo)) 178 { 179 DPRINT1("CreateProcessAsUserW failed with error %u\n", GetLastError()); 180 goto cleanup; 181 } 182 } 183 else 184 { 185 /* FIXME: This is probably not correct, I guess newdev should never be run with SYSTEM privileges. 186 187 Still, we currently do that in 2nd stage setup and probably Console mode as well, so allow it here. 188 (ShowWizard is only set to FALSE for these two modes) */ 189 ASSERT(!ShowWizard); 190 191 if (!CreateProcessW(NULL, CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) 192 { 193 DPRINT1("CreateProcessW failed with error %u\n", GetLastError()); 194 goto cleanup; 195 } 196 } 197 198 /* Wait for the function to connect to our pipe */ 199 if (!ConnectNamedPipe(hPipe, NULL)) 200 { 201 if (GetLastError() != ERROR_PIPE_CONNECTED) 202 { 203 DPRINT1("ConnectNamedPipe failed with error %u\n", GetLastError()); 204 goto cleanup; 205 } 206 } 207 208 /* Pass the data. The following output is partly compatible to Windows XP SP2 (researched using a modified newdev.dll to log this stuff) */ 209 Value = sizeof(InstallEventName); 210 WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL); 211 WriteFile(hPipe, InstallEventName, Value, &BytesWritten, NULL); 212 213 /* I couldn't figure out what the following value means under WinXP. It's usually 0 in my tests, but was also 5 once. 214 Therefore the following line is entirely ReactOS-specific. We use the value here to pass the ShowWizard variable. */ 215 WriteFile(hPipe, &ShowWizard, sizeof(ShowWizard), &BytesWritten, NULL); 216 217 Value = (wcslen(DeviceInstance) + 1) * sizeof(WCHAR); 218 WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL); 219 WriteFile(hPipe, DeviceInstance, Value, &BytesWritten, NULL); 220 221 /* Wait for newdev.dll to finish processing */ 222 WaitForSingleObject(ProcessInfo.hProcess, INFINITE); 223 224 /* If the event got signalled, this is success */ 225 DeviceInstalled = WaitForSingleObject(hInstallEvent, 0) == WAIT_OBJECT_0; 226 227 cleanup: 228 if (hInstallEvent) 229 CloseHandle(hInstallEvent); 230 231 if (hPipe != INVALID_HANDLE_VALUE) 232 CloseHandle(hPipe); 233 234 if (Environment) 235 DestroyEnvironmentBlock(Environment); 236 237 if (ProcessInfo.hProcess) 238 CloseHandle(ProcessInfo.hProcess); 239 240 if (ProcessInfo.hThread) 241 CloseHandle(ProcessInfo.hThread); 242 243 if (!DeviceInstalled) 244 { 245 DPRINT1("InstallDevice failed for DeviceInstance '%ws'\n", DeviceInstance); 246 } 247 248 return DeviceInstalled; 249 } 250 251 252 static LONG 253 ReadRegSzKey( 254 IN HKEY hKey, 255 IN LPCWSTR pszKey, 256 OUT LPWSTR* pValue) 257 { 258 LONG rc; 259 DWORD dwType; 260 DWORD cbData = 0; 261 LPWSTR Value; 262 263 if (!pValue) 264 return ERROR_INVALID_PARAMETER; 265 266 *pValue = NULL; 267 rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData); 268 if (rc != ERROR_SUCCESS) 269 return rc; 270 if (dwType != REG_SZ) 271 return ERROR_FILE_NOT_FOUND; 272 Value = HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR)); 273 if (!Value) 274 return ERROR_NOT_ENOUGH_MEMORY; 275 rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)Value, &cbData); 276 if (rc != ERROR_SUCCESS) 277 { 278 HeapFree(GetProcessHeap(), 0, Value); 279 return rc; 280 } 281 /* NULL-terminate the string */ 282 Value[cbData / sizeof(WCHAR)] = '\0'; 283 284 *pValue = Value; 285 return ERROR_SUCCESS; 286 } 287 288 289 BOOL 290 SetupIsActive(VOID) 291 { 292 HKEY hKey = NULL; 293 DWORD regType, active, size; 294 LONG rc; 295 BOOL ret = FALSE; 296 297 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\Setup", 0, KEY_QUERY_VALUE, &hKey); 298 if (rc != ERROR_SUCCESS) 299 goto cleanup; 300 301 size = sizeof(DWORD); 302 rc = RegQueryValueExW(hKey, L"SystemSetupInProgress", NULL, ®Type, (LPBYTE)&active, &size); 303 if (rc != ERROR_SUCCESS) 304 goto cleanup; 305 if (regType != REG_DWORD || size != sizeof(DWORD)) 306 goto cleanup; 307 308 ret = (active != 0); 309 310 cleanup: 311 if (hKey != NULL) 312 RegCloseKey(hKey); 313 314 DPRINT("System setup in progress? %S\n", ret ? L"YES" : L"NO"); 315 316 return ret; 317 } 318 319 320 /** 321 * @brief 322 * Creates a security descriptor for the PnP event 323 * installation. 324 * 325 * @param[out] EventSd 326 * A pointer to an allocated security descriptor 327 * for the event. 328 * 329 * @return 330 * ERROR_SUCCESS is returned if the function has 331 * successfully created the descriptor, otherwise 332 * a Win32 error code is returned. 333 * 334 * @remarks 335 * Only admins and local system have full power 336 * over this event as privileged users can install 337 * devices on a system. 338 */ 339 DWORD 340 CreatePnpInstallEventSecurity( 341 _Out_ PSECURITY_DESCRIPTOR *EventSd) 342 { 343 DWORD ErrCode; 344 PACL Dacl; 345 ULONG DaclSize; 346 SECURITY_DESCRIPTOR AbsoluteSd; 347 ULONG Size = 0; 348 PSECURITY_DESCRIPTOR RelativeSd = NULL; 349 PSID SystemSid = NULL, AdminsSid = NULL; 350 static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; 351 352 if (!AllocateAndInitializeSid(&NtAuthority, 353 1, 354 SECURITY_LOCAL_SYSTEM_RID, 355 0, 0, 0, 0, 0, 0, 0, 356 &SystemSid)) 357 { 358 return GetLastError(); 359 } 360 361 if (!AllocateAndInitializeSid(&NtAuthority, 362 2, 363 SECURITY_BUILTIN_DOMAIN_RID, 364 DOMAIN_ALIAS_RID_ADMINS, 365 0, 0, 0, 0, 0, 0, 366 &AdminsSid)) 367 { 368 ErrCode = GetLastError(); 369 goto Quit; 370 } 371 372 DaclSize = sizeof(ACL) + 373 sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(SystemSid) + 374 sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(AdminsSid); 375 376 Dacl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DaclSize); 377 if (!Dacl) 378 { 379 ErrCode = ERROR_OUTOFMEMORY; 380 goto Quit; 381 } 382 383 if (!InitializeAcl(Dacl, DaclSize, ACL_REVISION)) 384 { 385 ErrCode = GetLastError(); 386 goto Quit; 387 } 388 389 if (!AddAccessAllowedAce(Dacl, 390 ACL_REVISION, 391 EVENT_ALL_ACCESS, 392 SystemSid)) 393 { 394 ErrCode = GetLastError(); 395 goto Quit; 396 } 397 398 if (!AddAccessAllowedAce(Dacl, 399 ACL_REVISION, 400 EVENT_ALL_ACCESS, 401 AdminsSid)) 402 { 403 ErrCode = GetLastError(); 404 goto Quit; 405 } 406 407 if (!InitializeSecurityDescriptor(&AbsoluteSd, SECURITY_DESCRIPTOR_REVISION)) 408 { 409 ErrCode = GetLastError(); 410 goto Quit; 411 } 412 413 if (!SetSecurityDescriptorDacl(&AbsoluteSd, TRUE, Dacl, FALSE)) 414 { 415 ErrCode = GetLastError(); 416 goto Quit; 417 } 418 419 if (!SetSecurityDescriptorOwner(&AbsoluteSd, SystemSid, FALSE)) 420 { 421 ErrCode = GetLastError(); 422 goto Quit; 423 } 424 425 if (!SetSecurityDescriptorGroup(&AbsoluteSd, AdminsSid, FALSE)) 426 { 427 ErrCode = GetLastError(); 428 goto Quit; 429 } 430 431 if (!MakeSelfRelativeSD(&AbsoluteSd, NULL, &Size) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) 432 { 433 RelativeSd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size); 434 if (RelativeSd == NULL) 435 { 436 ErrCode = ERROR_OUTOFMEMORY; 437 goto Quit; 438 } 439 440 if (!MakeSelfRelativeSD(&AbsoluteSd, RelativeSd, &Size)) 441 { 442 ErrCode = GetLastError(); 443 goto Quit; 444 } 445 } 446 447 *EventSd = RelativeSd; 448 ErrCode = ERROR_SUCCESS; 449 450 Quit: 451 if (SystemSid) 452 { 453 FreeSid(SystemSid); 454 } 455 456 if (AdminsSid) 457 { 458 FreeSid(AdminsSid); 459 } 460 461 if (Dacl) 462 { 463 HeapFree(GetProcessHeap(), 0, Dacl); 464 } 465 466 if (ErrCode != ERROR_SUCCESS) 467 { 468 if (RelativeSd) 469 { 470 HeapFree(GetProcessHeap(), 0, RelativeSd); 471 } 472 } 473 474 return ErrCode; 475 } 476 477 478 static BOOL 479 IsConsoleBoot(VOID) 480 { 481 HKEY ControlKey = NULL; 482 LPWSTR SystemStartOptions = NULL; 483 LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */ 484 BOOL ConsoleBoot = FALSE; 485 LONG rc; 486 487 rc = RegOpenKeyExW( 488 HKEY_LOCAL_MACHINE, 489 L"SYSTEM\\CurrentControlSet\\Control", 490 0, 491 KEY_QUERY_VALUE, 492 &ControlKey); 493 494 rc = ReadRegSzKey(ControlKey, L"SystemStartOptions", &SystemStartOptions); 495 if (rc != ERROR_SUCCESS) 496 goto cleanup; 497 498 /* Check for CONSOLE switch in SystemStartOptions */ 499 CurrentOption = SystemStartOptions; 500 while (CurrentOption) 501 { 502 NextOption = wcschr(CurrentOption, L' '); 503 if (NextOption) 504 *NextOption = L'\0'; 505 if (_wcsicmp(CurrentOption, L"CONSOLE") == 0) 506 { 507 DPRINT("Found %S. Switching to console boot\n", CurrentOption); 508 ConsoleBoot = TRUE; 509 goto cleanup; 510 } 511 CurrentOption = NextOption ? NextOption + 1 : NULL; 512 } 513 514 cleanup: 515 if (ControlKey != NULL) 516 RegCloseKey(ControlKey); 517 HeapFree(GetProcessHeap(), 0, SystemStartOptions); 518 return ConsoleBoot; 519 } 520 521 522 FORCEINLINE 523 BOOL 524 IsUISuppressionAllowed(VOID) 525 { 526 /* Display the newdev.dll wizard UI only if it's allowed */ 527 return (g_IsUISuppressed || GetSuppressNewUIValue()); 528 } 529 530 531 /* Loop to install all queued devices installations */ 532 DWORD 533 WINAPI 534 DeviceInstallThread(LPVOID lpParameter) 535 { 536 PLIST_ENTRY ListEntry; 537 DeviceInstallParams* Params; 538 539 UNREFERENCED_PARAMETER(lpParameter); 540 541 // Step 1: install all drivers which were configured during the boot 542 543 DPRINT("Step 1: Installing devices configured during the boot\n"); 544 545 PWSTR deviceList; 546 547 while (TRUE) 548 { 549 ULONG devListSize; 550 DWORD status = PNP_GetDeviceListSize(NULL, NULL, &devListSize, 0); 551 if (status != CR_SUCCESS) 552 { 553 goto Step2; 554 } 555 556 deviceList = HeapAlloc(GetProcessHeap(), 0, devListSize * sizeof(WCHAR)); 557 if (!deviceList) 558 { 559 goto Step2; 560 } 561 562 status = PNP_GetDeviceList(NULL, NULL, deviceList, &devListSize, 0); 563 if (status == CR_BUFFER_SMALL) 564 { 565 HeapFree(GetProcessHeap(), 0, deviceList); 566 } 567 else if (status != CR_SUCCESS) 568 { 569 DPRINT1("PNP_GetDeviceList failed with error %u\n", status); 570 goto Cleanup; 571 } 572 else // status == CR_SUCCESS 573 { 574 break; 575 } 576 } 577 578 for (PWSTR currentDev = deviceList; 579 currentDev[0] != UNICODE_NULL; 580 currentDev += lstrlenW(currentDev) + 1) 581 { 582 InstallDevice(currentDev, FALSE); 583 } 584 585 Cleanup: 586 HeapFree(GetProcessHeap(), 0, deviceList); 587 588 // Step 2: start the wait-loop for newly added devices 589 Step2: 590 591 DPRINT("Step 2: Starting the wait-loop\n"); 592 593 WaitForSingleObject(hInstallEvent, INFINITE); 594 595 BOOL showWizard = !SetupIsActive() && !IsConsoleBoot(); 596 597 while (TRUE) 598 { 599 /* Dequeue the next oldest device-install event */ 600 WaitForSingleObject(hDeviceInstallListMutex, INFINITE); 601 ListEntry = (IsListEmpty(&DeviceInstallListHead) 602 ? NULL : RemoveHeadList(&DeviceInstallListHead)); 603 ReleaseMutex(hDeviceInstallListMutex); 604 605 if (ListEntry == NULL) 606 { 607 SetEvent(hNoPendingInstalls); 608 WaitForSingleObject(hDeviceInstallListNotEmpty, INFINITE); 609 } 610 else 611 { 612 ResetEvent(hNoPendingInstalls); 613 Params = CONTAINING_RECORD(ListEntry, DeviceInstallParams, ListEntry); 614 InstallDevice(Params->DeviceIds, showWizard && !IsUISuppressionAllowed()); 615 HeapFree(GetProcessHeap(), 0, Params); 616 } 617 } 618 619 return 0; 620 } 621