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
InstallDevice(PCWSTR DeviceInstance,BOOL ShowWizard)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
ReadRegSzKey(IN HKEY hKey,IN LPCWSTR pszKey,OUT LPWSTR * pValue)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
SetupIsActive(VOID)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
CreatePnpInstallEventSecurity(_Out_ PSECURITY_DESCRIPTOR * EventSd)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
IsConsoleBoot(VOID)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
IsUISuppressionAllowed(VOID)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
DeviceInstallThread(LPVOID lpParameter)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