1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Base API Server DLL
4 * FILE: subsystems/win/basesrv/dosdev.c
5 * PURPOSE: DOS Devices Management
6 * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "basesrv.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 typedef struct _BSM_REQUEST
17 {
18 struct _BSM_REQUEST * Next;
19 LUID BroadcastLuid;
20 LONG DriveLetter;
21 LONG RemoveDefinition;
22 } BSM_REQUEST, *PBSM_REQUEST;
23
24 /* GLOBALS ********************************************************************/
25
26 static RTL_CRITICAL_SECTION BaseDefineDosDeviceCritSec;
27 RTL_CRITICAL_SECTION BaseSrvDDDBSMCritSec;
28 PBSM_REQUEST BSM_Request_Queue = NULL, BSM_Request_Queue_End = NULL;
29 ULONG BaseSrvpBSMThreadCount = 0;
30 LONG (WINAPI *PBROADCASTSYSTEMMESSAGEEXW)(DWORD, LPDWORD, UINT, WPARAM, LPARAM, PBSMINFO) = NULL;
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
BaseInitDefineDosDevice(VOID)34 VOID BaseInitDefineDosDevice(VOID)
35 {
36 RtlInitializeCriticalSection(&BaseDefineDosDeviceCritSec);
37 }
38
BaseCleanupDefineDosDevice(VOID)39 VOID BaseCleanupDefineDosDevice(VOID)
40 {
41 RtlDeleteCriticalSection(&BaseDefineDosDeviceCritSec);
42 }
43
44 NTSTATUS
GetCallerLuid(PLUID CallerLuid)45 GetCallerLuid(PLUID CallerLuid)
46 {
47 NTSTATUS Status;
48 HANDLE TokenHandle;
49 ULONG ReturnLength;
50 TOKEN_STATISTICS TokenInformation;
51
52 /* We need an output buffer */
53 if (CallerLuid == NULL)
54 {
55 return STATUS_INVALID_PARAMETER;
56 }
57
58 /* Open thread token */
59 TokenHandle = 0;
60 ReturnLength = 0;
61 Status = NtOpenThreadToken(NtCurrentThread(),
62 READ_CONTROL | TOKEN_QUERY,
63 FALSE, &TokenHandle);
64 /* If we fail, open process token */
65 if (Status == STATUS_NO_TOKEN)
66 {
67 Status = NtOpenProcessToken(NtCurrentProcess(),
68 READ_CONTROL | TOKEN_QUERY,
69 &TokenHandle);
70 }
71
72 /* In case of a success get caller LUID and copy it back */
73 if (NT_SUCCESS(Status))
74 {
75 Status = NtQueryInformationToken(TokenHandle,
76 TokenStatistics,
77 &TokenInformation,
78 sizeof(TokenInformation),
79 &ReturnLength);
80 if (NT_SUCCESS(Status))
81 {
82 RtlCopyLuid(CallerLuid, &TokenInformation.AuthenticationId);
83 }
84 }
85
86 /* Close token handle */
87 if (TokenHandle != 0)
88 {
89 NtClose(TokenHandle);
90 }
91
92 return Status;
93 }
94
95 NTSTATUS
IsGlobalSymbolicLink(HANDLE LinkHandle,PBOOLEAN IsGlobal)96 IsGlobalSymbolicLink(HANDLE LinkHandle,
97 PBOOLEAN IsGlobal)
98 {
99 NTSTATUS Status;
100 DWORD ReturnLength;
101 UNICODE_STRING GlobalString;
102 OBJECT_NAME_INFORMATION NameInfo, *PNameInfo;
103
104 /* We need both parameters */
105 if (LinkHandle == 0 || IsGlobal == NULL)
106 {
107 return STATUS_INVALID_PARAMETER;
108 }
109
110 PNameInfo = NULL;
111 _SEH2_TRY
112 {
113 /* Query handle information */
114 Status = NtQueryObject(LinkHandle,
115 ObjectNameInformation,
116 &NameInfo,
117 0,
118 &ReturnLength);
119 /* Only failure we tolerate is length mismatch */
120 if (NT_SUCCESS(Status) || Status == STATUS_INFO_LENGTH_MISMATCH)
121 {
122 /* Allocate big enough buffer */
123 PNameInfo = RtlAllocateHeap(BaseSrvHeap, 0, ReturnLength);
124 if (PNameInfo == NULL)
125 {
126 Status = STATUS_NO_MEMORY;
127 _SEH2_LEAVE;
128 }
129
130 /* Query again handle information */
131 Status = NtQueryObject(LinkHandle,
132 ObjectNameInformation,
133 PNameInfo,
134 ReturnLength,
135 &ReturnLength);
136
137 /*
138 * If it succeed, check we have Global??
139 * If so, return success
140 */
141 if (NT_SUCCESS(Status))
142 {
143 RtlInitUnicodeString(&GlobalString, L"\\GLOBAL??");
144 *IsGlobal = RtlPrefixUnicodeString(&GlobalString, &PNameInfo->Name, FALSE);
145 Status = STATUS_SUCCESS;
146 }
147 }
148 }
149 _SEH2_FINALLY
150 {
151 if (PNameInfo != NULL)
152 {
153 RtlFreeHeap(BaseSrvHeap, 0, PNameInfo);
154 }
155 }
156 _SEH2_END;
157
158 return Status;
159 }
160
161 BOOLEAN
CheckForGlobalDriveLetter(SHORT DriveLetter)162 CheckForGlobalDriveLetter(SHORT DriveLetter)
163 {
164 WCHAR Path[8];
165 NTSTATUS Status;
166 BOOLEAN IsGlobal;
167 UNICODE_STRING PathU;
168 HANDLE SymbolicLinkHandle;
169 OBJECT_ATTRIBUTES ObjectAttributes;
170
171 /* Setup our drive path */
172 wcsncpy(Path, L"\\??\\X:", (sizeof(L"\\??\\X:") / sizeof(WCHAR)));
173 Path[4] = DriveLetter + L'A';
174 Path[6] = UNICODE_NULL;
175
176 /* Prepare everything to open the link */
177 RtlInitUnicodeString(&PathU, Path);
178 InitializeObjectAttributes(&ObjectAttributes,
179 &PathU,
180 OBJ_CASE_INSENSITIVE,
181 NULL,
182 NULL);
183
184 /* Impersonate the caller */
185 if (!CsrImpersonateClient(NULL))
186 {
187 return FALSE;
188 }
189
190 /* Open our drive letter */
191 Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle,
192 SYMBOLIC_LINK_QUERY,
193 &ObjectAttributes);
194
195 CsrRevertToSelf();
196
197 if (!NT_SUCCESS(Status))
198 {
199 return FALSE;
200 }
201
202 /* Check whether it's global */
203 Status = IsGlobalSymbolicLink(SymbolicLinkHandle, &IsGlobal);
204 NtClose(SymbolicLinkHandle);
205
206 if (!NT_SUCCESS(Status))
207 {
208 return FALSE;
209 }
210
211 return IsGlobal;
212 }
213
214 NTSTATUS
SendWinStationBSM(DWORD Flags,LPDWORD Recipients,UINT Message,WPARAM wParam,LPARAM lParam)215 SendWinStationBSM(DWORD Flags,
216 LPDWORD Recipients,
217 UINT Message,
218 WPARAM wParam,
219 LPARAM lParam)
220 {
221 UNIMPLEMENTED;
222 return STATUS_NOT_IMPLEMENTED;
223 }
224
225 NTSTATUS
BroadcastDriveLetterChange(LONG DriveLetter,BOOLEAN RemoveDefinition,PLUID BroadcastLuid)226 BroadcastDriveLetterChange(LONG DriveLetter,
227 BOOLEAN RemoveDefinition,
228 PLUID BroadcastLuid)
229 {
230 HANDLE hUser32;
231 NTSTATUS Status;
232 UNICODE_STRING User32U;
233 ANSI_STRING ProcedureName;
234 DWORD Recipients, Flags, wParam;
235 LUID SystemLuid = SYSTEM_LUID;
236 BSMINFO Info;
237 DEV_BROADCAST_VOLUME Volume;
238
239 /* We need a broadcast LUID */
240 if (BroadcastLuid == NULL)
241 {
242 return STATUS_INVALID_PARAMETER;
243 }
244
245 /* Get the Csr procedure, and keep it forever */
246 if (PBROADCASTSYSTEMMESSAGEEXW == NULL)
247 {
248 hUser32 = NULL;
249 RtlInitUnicodeString(&User32U, L"user32");
250 Status = LdrGetDllHandle(NULL, NULL, &User32U, &hUser32);
251 if (hUser32 != NULL && NT_SUCCESS(Status))
252 {
253 RtlInitString(&ProcedureName, "CsrBroadcastSystemMessageExW");
254 Status = LdrGetProcedureAddress(hUser32,
255 &ProcedureName,
256 0,
257 (PVOID *)&PBROADCASTSYSTEMMESSAGEEXW);
258 if (!NT_SUCCESS(Status))
259 {
260 PBROADCASTSYSTEMMESSAGEEXW = NULL;
261 }
262 }
263
264 /* If we failed to get broadcast procedure, no more actions left */
265 if (PBROADCASTSYSTEMMESSAGEEXW == NULL)
266 {
267 return Status;
268 }
269 }
270
271 /* Initialize broadcast info */
272 Info.cbSize = sizeof(BSMINFO);
273 Info.hdesk = 0;
274 Info.hwnd = 0;
275 RtlCopyLuid(&Info.luid, BroadcastLuid);
276
277 /* Initialize volume information */
278 Volume.dbcv_size = sizeof(DEV_BROADCAST_VOLUME);
279 Volume.dbcv_devicetype = DBT_DEVTYP_VOLUME;
280 Volume.dbcv_reserved = 0;
281 Volume.dbcv_unitmask = 1 << DriveLetter;
282 Volume.dbcv_flags = DBTF_NET;
283
284 /* Wide broadcast */
285 Recipients = BSM_APPLICATIONS | BSM_ALLDESKTOPS;
286 Flags = BSF_NOHANG | BSF_NOTIMEOUTIFNOTHUNG | BSF_FORCEIFHUNG;
287
288 /*
289 * If we don't broadcast as system, it's not a global drive
290 * notification, then mark it as LUID mapped drive
291 */
292 if (!RtlEqualLuid(&Info.luid, &SystemLuid))
293 {
294 Flags |= BSF_LUID;
295 }
296
297 /* Set event type */
298 wParam = RemoveDefinition ? DBT_DEVICEREMOVECOMPLETE : DBT_DEVICEARRIVAL;
299
300 /* And broadcast! */
301 Status = PBROADCASTSYSTEMMESSAGEEXW(Flags, &Recipients, WM_DEVICECHANGE, wParam, (LPARAM)&Volume, &Info);
302
303 /* If the drive is global, notify Winsta */
304 if (!(Flags & BSF_LUID))
305 {
306 Status = SendWinStationBSM(Flags, &Recipients, WM_DEVICECHANGE, wParam, (LPARAM)&Volume);
307 }
308
309 return Status;
310 }
311
312 ULONG
313 NTAPI
BaseSrvBSMThread(PVOID StartupContext)314 BaseSrvBSMThread(PVOID StartupContext)
315 {
316 ULONG ExitStatus;
317 NTSTATUS Status;
318 PBSM_REQUEST CurrentRequest;
319
320 /* We have a thread */
321 ExitStatus = 0;
322 RtlEnterCriticalSection(&BaseSrvDDDBSMCritSec);
323 ++BaseSrvpBSMThreadCount;
324
325 while (TRUE)
326 {
327 /* If we flushed the queue, job done */
328 if (BSM_Request_Queue == NULL)
329 {
330 break;
331 }
332
333 /* Queue current request, and remove it from the queue */
334 CurrentRequest = BSM_Request_Queue;
335 BSM_Request_Queue = BSM_Request_Queue->Next;
336
337 /* If that was the last request, NULLify queue end */
338 if (BSM_Request_Queue == NULL)
339 {
340 BSM_Request_Queue_End = NULL;
341 }
342
343 RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
344
345 /* Broadcast the message */
346 Status = BroadcastDriveLetterChange(CurrentRequest->DriveLetter,
347 CurrentRequest->RemoveDefinition,
348 &CurrentRequest->BroadcastLuid);
349
350 /* Reflect the last entry status on stop */
351 CurrentRequest->Next = NULL;
352 ExitStatus = Status;
353
354 RtlFreeHeap(BaseSrvHeap, 0, CurrentRequest);
355 RtlEnterCriticalSection(&BaseSrvDDDBSMCritSec);
356 }
357
358 /* Here, we've flushed the queue, quit the user thread */
359 --BaseSrvpBSMThreadCount;
360 RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
361
362 NtCurrentTeb()->FreeStackOnTermination = TRUE;
363 NtTerminateThread(NtCurrentThread(), ExitStatus);
364
365 return ExitStatus;
366 }
367
368 NTSTATUS
CreateBSMThread(VOID)369 CreateBSMThread(VOID)
370 {
371 /* This can only be true for LUID mappings */
372 if (BaseStaticServerData->LUIDDeviceMapsEnabled == 0)
373 {
374 return STATUS_ACCESS_DENIED;
375 }
376
377 /* Create our user thread */
378 return RtlCreateUserThread(NtCurrentProcess(),
379 NULL,
380 FALSE,
381 0,
382 0,
383 0,
384 BaseSrvBSMThread,
385 NULL,
386 NULL,
387 NULL);
388 }
389
390 NTSTATUS
AddBSMRequest(LONG DriveLetter,BOOLEAN RemoveDefinition,PLUID BroadcastLuid)391 AddBSMRequest(LONG DriveLetter,
392 BOOLEAN RemoveDefinition,
393 PLUID BroadcastLuid)
394 {
395 LUID CallerLuid;
396 NTSTATUS Status;
397 LUID SystemLuid = SYSTEM_LUID;
398 PBSM_REQUEST Request;
399
400 /* We need a broadcast LUID */
401 if (BroadcastLuid == NULL)
402 {
403 return STATUS_INVALID_PARAMETER;
404 }
405
406 /*
407 * If LUID mappings are not enabled, this call makes no sense
408 * It should not happen though
409 */
410 if (BaseStaticServerData->LUIDDeviceMapsEnabled == 0)
411 {
412 return STATUS_ACCESS_DENIED;
413 }
414
415 /* Get our caller LUID (not the broadcaster!) */
416 Status = GetCallerLuid(&CallerLuid);
417 if (!NT_SUCCESS(Status))
418 {
419 return Status;
420 }
421
422 /* System cannot create LUID mapped drives - thus broadcast makes no sense */
423 if (!RtlEqualLuid(&CallerLuid, &SystemLuid))
424 {
425 return STATUS_ACCESS_DENIED;
426 }
427
428 /* Allocate our request */
429 Request = RtlAllocateHeap(BaseSrvHeap, 0, sizeof(BSM_REQUEST));
430 if (Request == NULL)
431 {
432 return STATUS_NO_MEMORY;
433 }
434
435 /* Initialize it */
436 Request->DriveLetter = DriveLetter;
437 Request->RemoveDefinition = RemoveDefinition;
438 RtlCopyLuid(&Request->BroadcastLuid, BroadcastLuid);
439 Request->Next = NULL;
440
441 /* And queue it */
442 RtlEnterCriticalSection(&BaseSrvDDDBSMCritSec);
443
444 /* At the end of the queue if not empty */
445 if (BSM_Request_Queue_End != NULL)
446 {
447 BSM_Request_Queue_End->Next = Request;
448 }
449 /* Otherwise, initialize the queue */
450 else
451 {
452 BSM_Request_Queue = Request;
453 }
454
455 /* We're in FIFO mode */
456 BSM_Request_Queue_End = Request;
457
458 /* If we don't have a messaging thread running, then start one */
459 if (BaseSrvpBSMThreadCount >= 1)
460 {
461 RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
462 }
463 else
464 {
465 RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
466 Status = CreateBSMThread();
467 }
468
469 return Status;
470 }
471
472 /* PUBLIC SERVER APIS *********************************************************/
473
CSR_API(BaseSrvDefineDosDevice)474 CSR_API(BaseSrvDefineDosDevice)
475 {
476 NTSTATUS Status;
477 PBASE_DEFINE_DOS_DEVICE DefineDosDeviceRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.DefineDosDeviceRequest;
478 OBJECT_ATTRIBUTES ObjectAttributes;
479 HANDLE LinkHandle;
480 UNICODE_STRING DeviceName = {0};
481 UNICODE_STRING LinkTarget = {0};
482 ULONG Length;
483 SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
484 SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
485 PSID SystemSid;
486 PSID WorldSid;
487 PWSTR lpBuffer;
488 WCHAR Letter;
489 SHORT AbsLetter;
490 BOOLEAN DriveLetter = FALSE;
491 BOOLEAN RemoveDefinition;
492 BOOLEAN HandleTarget;
493 BOOLEAN Broadcast = FALSE;
494 BOOLEAN IsGlobal = FALSE;
495 ULONG CchLengthLeft;
496 ULONG CchLength;
497 ULONG TargetLength;
498 PWSTR TargetBuffer;
499 PWSTR CurrentBuffer;
500 /* We store them on the stack, they are known in advance */
501 union {
502 SECURITY_DESCRIPTOR SecurityDescriptor;
503 UCHAR Buffer[20];
504 } SecurityDescriptor;
505 union {
506 ACL Dacl;
507 UCHAR Buffer[256];
508 } Dacl;
509 ACCESS_MASK AccessMask;
510 LUID CallerLuid;
511 WCHAR * CurrentPtr;
512 WCHAR CurrentChar;
513 PWSTR OrigPtr;
514 PWSTR InterPtr;
515 BOOLEAN RemoveFound;
516
517 if (!CsrValidateMessageBuffer(ApiMessage,
518 (PVOID*)&DefineDosDeviceRequest->DeviceName.Buffer,
519 DefineDosDeviceRequest->DeviceName.Length,
520 sizeof(BYTE)) ||
521 (DefineDosDeviceRequest->DeviceName.Length & 1) != 0 ||
522 !CsrValidateMessageBuffer(ApiMessage,
523 (PVOID*)&DefineDosDeviceRequest->TargetPath.Buffer,
524 DefineDosDeviceRequest->TargetPath.Length +
525 (DefineDosDeviceRequest->TargetPath.Length != 0
526 ? sizeof(UNICODE_NULL) : 0),
527 sizeof(BYTE)) ||
528 (DefineDosDeviceRequest->TargetPath.Length & 1) != 0)
529 {
530 return STATUS_INVALID_PARAMETER;
531 }
532
533 DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ (%d), TargetPath:%wZ (%d)\n",
534 DefineDosDeviceRequest->Flags,
535 &DefineDosDeviceRequest->DeviceName,
536 DefineDosDeviceRequest->DeviceName.Length,
537 &DefineDosDeviceRequest->TargetPath,
538 DefineDosDeviceRequest->TargetPath.Length);
539
540 /*
541 * Allocate a buffer big enough to contain:
542 * - device name
543 * - targets
544 */
545 lpBuffer = RtlAllocateHeap(BaseSrvHeap, 0, 0x2000);
546 if (lpBuffer == NULL)
547 {
548 return STATUS_NO_MEMORY;
549 }
550
551 /* Enter our critical section */
552 Status = RtlEnterCriticalSection(&BaseDefineDosDeviceCritSec);
553 if (!NT_SUCCESS(Status))
554 {
555 DPRINT1("RtlEnterCriticalSection() failed (Status %lx)\n",
556 Status);
557 RtlFreeHeap(BaseSrvHeap, 0, lpBuffer);
558 return Status;
559 }
560
561 LinkHandle = 0;
562 /* Does the caller wants to remove definition? */
563 RemoveDefinition = !!(DefineDosDeviceRequest->Flags & DDD_REMOVE_DEFINITION);
564 _SEH2_TRY
565 {
566 /* First of all, check if that's a drive letter device amongst LUID mappings */
567 if (BaseStaticServerData->LUIDDeviceMapsEnabled && !(DefineDosDeviceRequest->Flags & DDD_NO_BROADCAST_SYSTEM))
568 {
569 if (DefineDosDeviceRequest->DeviceName.Buffer != NULL &&
570 DefineDosDeviceRequest->DeviceName.Length == 2 * sizeof(WCHAR) &&
571 DefineDosDeviceRequest->DeviceName.Buffer[1] == L':')
572 {
573 Letter = DefineDosDeviceRequest->DeviceName.Buffer[0];
574
575 /* Handle both lower cases and upper cases */
576 AbsLetter = Letter - L'a';
577 if (AbsLetter < 26 && AbsLetter >= 0)
578 {
579 Letter = RtlUpcaseUnicodeChar(Letter);
580 }
581
582 AbsLetter = Letter - L'A';
583 if (AbsLetter < 26)
584 {
585 /* That's a letter! */
586 DriveLetter = TRUE;
587 }
588 }
589 }
590
591 /* We can only broadcast drive letters in case of LUID mappings */
592 if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE &&
593 !DriveLetter)
594 {
595 Status = STATUS_INVALID_PARAMETER;
596 _SEH2_LEAVE;
597 }
598
599 /* First usage of our buffer: create device name */
600 CchLength = _snwprintf(lpBuffer, 0x1000, L"\\??\\%wZ", &DefineDosDeviceRequest->DeviceName);
601 CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */
602 CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */
603 RtlInitUnicodeString(&DeviceName, lpBuffer);
604
605 /* And prepare to open it */
606 InitializeObjectAttributes(&ObjectAttributes,
607 &DeviceName,
608 OBJ_CASE_INSENSITIVE,
609 NULL,
610 NULL);
611
612 /* Assume it's OK and has a target to deal with */
613 HandleTarget = TRUE;
614
615 /* Move to the client context if the mapping was local */
616 if (!CsrImpersonateClient(NULL))
617 {
618 Status = STATUS_BAD_IMPERSONATION_LEVEL;
619 _SEH2_LEAVE;
620 }
621
622 /*
623 * While impersonating the caller, also get its LUID.
624 * This is mandatory in case we have a driver letter,
625 * Because we're in the case we've got LUID mapping
626 * enabled and broadcasting enabled. LUID will be required
627 * for the latter
628 */
629 if (DriveLetter)
630 {
631 Status = GetCallerLuid(&CallerLuid);
632 if (NT_SUCCESS(Status))
633 {
634 Broadcast = TRUE;
635 }
636 }
637
638 /* Now, open the device */
639 Status = NtOpenSymbolicLinkObject(&LinkHandle,
640 DELETE | SYMBOLIC_LINK_QUERY,
641 &ObjectAttributes);
642
643 /* And get back to our context */
644 CsrRevertToSelf();
645
646 /* In case of LUID broadcast, do nothing but return to trigger broadcast */
647 if (DefineDosDeviceRequest->Flags & DDD_LUID_BROADCAST_DRIVE)
648 {
649 /* Zero handle in case of a failure */
650 if (!NT_SUCCESS(Status))
651 {
652 LinkHandle = 0;
653 }
654
655 /* If removal was asked, and no object found: the remval was successful */
656 if (RemoveDefinition && Status == STATUS_OBJECT_NAME_NOT_FOUND)
657 {
658 Status = STATUS_SUCCESS;
659 }
660
661 /* We're done here, nothing more to do */
662 _SEH2_LEAVE;
663 }
664
665 /* If device was not found */
666 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
667 {
668 /* No handle */
669 LinkHandle = 0;
670
671 /* If we were asked to remove... */
672 if (RemoveDefinition)
673 {
674 /*
675 * If caller asked to pop first entry, nothing specific,
676 * then, we can consider this as a success
677 */
678 if (DefineDosDeviceRequest->TargetPath.Length == 0)
679 {
680 Status = STATUS_SUCCESS;
681 }
682
683 /* We're done, nothing to change */
684 _SEH2_LEAVE;
685 }
686
687 /* There's no target to handle */
688 HandleTarget = FALSE;
689
690 /*
691 * We'll consider, that's a success
692 * Failing to open the device doesn't prevent
693 * from creating it later on to create
694 * the linking.
695 */
696 Status = STATUS_SUCCESS;
697 }
698 else
699 {
700 /* Unexpected failure, forward to caller */
701 if (!NT_SUCCESS(Status))
702 {
703 _SEH2_LEAVE;
704 }
705
706 /* If LUID mapping enabled */
707 if (BaseStaticServerData->LUIDDeviceMapsEnabled)
708 {
709 /* Check if that's global link */
710 Status = IsGlobalSymbolicLink(LinkHandle, &IsGlobal);
711 if (!NT_SUCCESS(Status))
712 {
713 _SEH2_LEAVE;
714 }
715
716 /* If so, change our device name namespace to GLOBAL?? for link creation */
717 if (IsGlobal)
718 {
719 CchLength = _snwprintf(lpBuffer, 0x1000, L"\\GLOBAL??\\%wZ", &DefineDosDeviceRequest->DeviceName);
720 CchLengthLeft = 0x1000 - 1 - CchLength; /* UNICODE_NULL */
721 CurrentBuffer = lpBuffer + CchLength + 1; /* UNICODE_NULL */
722
723 DeviceName.Length = CchLength * sizeof(WCHAR);
724 DeviceName.MaximumLength = CchLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
725 }
726 }
727 }
728
729 /* If caller provided a target */
730 if (DefineDosDeviceRequest->TargetPath.Length != 0)
731 {
732 /* Make sure it's null terminated */
733 DefineDosDeviceRequest->TargetPath.Buffer[DefineDosDeviceRequest->TargetPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
734
735 /* Compute its size */
736 TargetLength = wcslen(DefineDosDeviceRequest->TargetPath.Buffer);
737
738 /* And make sure it fits our buffer */
739 if (TargetLength + 1 >= CchLengthLeft)
740 {
741 Status = STATUS_INVALID_PARAMETER;
742 _SEH2_LEAVE;
743 }
744
745 /* Copy it to our internal buffer */
746 RtlMoveMemory(CurrentBuffer, DefineDosDeviceRequest->TargetPath.Buffer, TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
747 TargetBuffer = CurrentBuffer;
748
749 /* Update our buffer status */
750 CchLengthLeft -= (TargetLength + 1);
751 CurrentBuffer += (TargetLength + 1);
752 }
753 /* Otherwise, zero everything */
754 else
755 {
756 TargetBuffer = NULL;
757 TargetLength = 0;
758 }
759
760 /* If we opened the device, then, handle its current target */
761 if (HandleTarget)
762 {
763 /* Query it with our internal buffer */
764 LinkTarget.Length = 0;
765 LinkTarget.MaximumLength = CchLengthLeft * sizeof(WCHAR);
766 LinkTarget.Buffer = CurrentBuffer;
767
768 Status = NtQuerySymbolicLinkObject(LinkHandle,
769 &LinkTarget,
770 &Length);
771 /* If we overflow, give up */
772 if (Length == LinkTarget.MaximumLength)
773 {
774 Status = STATUS_BUFFER_OVERFLOW;
775 }
776 /* In case of a failure, bye bye */
777 if (!NT_SUCCESS(Status))
778 {
779 _SEH2_LEAVE;
780 }
781
782 /*
783 * Properly null it for MULTI_SZ if needed
784 * Always update max length with
785 * the need size
786 * This is needed to hand relatively "small"
787 * strings to Ob and avoid killing ourselves
788 * on the next query
789 */
790 CchLength = Length / sizeof(WCHAR);
791 if (CchLength < 2 ||
792 CurrentBuffer[CchLength - 2] != UNICODE_NULL ||
793 CurrentBuffer[CchLength - 1] != UNICODE_NULL)
794 {
795 CurrentBuffer[CchLength] = UNICODE_NULL;
796 LinkTarget.MaximumLength = Length + sizeof(UNICODE_NULL);
797 }
798 else
799 {
800 LinkTarget.MaximumLength = Length;
801 }
802 }
803 /* There's no target, and we're asked to remove, so null target */
804 else if (RemoveDefinition)
805 {
806 RtlInitUnicodeString(&LinkTarget, NULL);
807 }
808 /* There's a target provided - new device, update buffer */
809 else
810 {
811 RtlInitUnicodeString(&LinkTarget, CurrentBuffer - TargetLength - 1);
812 }
813
814 /*
815 * We no longer need old symlink, just drop it, we'll recreate it now
816 * with updated target.
817 * The benefit of it is that if caller asked us to drop last target, then
818 * the device is removed and not dangling
819 */
820 if (LinkHandle != 0)
821 {
822 Status = NtMakeTemporaryObject(LinkHandle);
823 NtClose(LinkHandle);
824 LinkHandle = 0;
825 }
826
827 /* At this point, we must have no failure */
828 if (!NT_SUCCESS(Status))
829 {
830 _SEH2_LEAVE;
831 }
832
833 /*
834 * If we have to remove definition, let's start to browse our
835 * target to actually drop it.
836 */
837 if (RemoveDefinition)
838 {
839 /* We'll browse our multi sz string */
840 RemoveFound = FALSE;
841 CurrentPtr = LinkTarget.Buffer;
842 InterPtr = LinkTarget.Buffer;
843 while (*CurrentPtr != UNICODE_NULL)
844 {
845 CchLength = 0;
846 OrigPtr = CurrentPtr;
847 /* First, find next string */
848 while (TRUE)
849 {
850 CurrentChar = *CurrentPtr;
851 ++CurrentPtr;
852
853 if (CurrentChar == UNICODE_NULL)
854 {
855 break;
856 }
857
858 ++CchLength;
859 }
860
861 /* This check is a bit tricky, but dead useful:
862 * If on the previous loop, we found the caller provided target
863 * in our list, then, we'll move current entry over the found one
864 * So that, it gets deleted.
865 * Also, if we don't find caller entry in our entries, then move
866 * current entry in the string if a previous one got deleted
867 */
868 if (RemoveFound ||
869 ((!(DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) ||
870 TargetLength != CchLength || _wcsicmp(OrigPtr, TargetBuffer) != 0) &&
871 ((DefineDosDeviceRequest->Flags & DDD_EXACT_MATCH_ON_REMOVE) ||
872 (TargetLength != 0 && _wcsnicmp(OrigPtr, TargetBuffer, TargetLength) != 0))))
873 {
874 if (InterPtr != OrigPtr)
875 {
876 RtlMoveMemory(InterPtr, OrigPtr, sizeof(WCHAR) * CchLength + sizeof(UNICODE_NULL));
877 }
878
879 InterPtr += (CchLength + 1);
880 }
881 else
882 {
883 /* Match case! Remember for next loop turn and to delete it */
884 RemoveFound = TRUE;
885 }
886 }
887
888 /*
889 * Drop last entry, as required (pop)
890 * If there was a match previously, everything
891 * is already moved, so we're just nulling
892 * the end of the string
893 * If there was no match, this is the pop
894 */
895 *InterPtr = UNICODE_NULL;
896 ++InterPtr;
897
898 /* Compute new target length */
899 TargetLength = wcslen(LinkTarget.Buffer) * sizeof(WCHAR);
900 /*
901 * If it's empty, quit
902 * Beware, here, we quit with STATUS_SUCCESS, and that's expected!
903 * In case we dropped last target entry, then, it's empty
904 * and there's no need to recreate the device we deleted previously
905 */
906 if (TargetLength == 0)
907 {
908 _SEH2_LEAVE;
909 }
910
911 /* Update our target string */
912 LinkTarget.Length = TargetLength;
913 LinkTarget.MaximumLength = (ULONG_PTR)InterPtr - (ULONG_PTR)LinkTarget.Buffer;
914 }
915 /* If that's not a removal, just update the target to include new target */
916 else if (HandleTarget)
917 {
918 LinkTarget.Buffer = LinkTarget.Buffer - TargetLength - 1;
919 LinkTarget.Length = TargetLength * sizeof(WCHAR);
920 LinkTarget.MaximumLength += (TargetLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
921 TargetLength *= sizeof(WCHAR);
922 }
923 /* No changes */
924 else
925 {
926 TargetLength = LinkTarget.Length;
927 }
928
929 /* Make sure we don't create empty symlink */
930 if (TargetLength == 0)
931 {
932 _SEH2_LEAVE;
933 }
934
935 /* Initialize our SIDs for symlink ACLs */
936 Status = RtlAllocateAndInitializeSid(&WorldAuthority,
937 1,
938 SECURITY_NULL_RID,
939 SECURITY_NULL_RID,
940 SECURITY_NULL_RID,
941 SECURITY_NULL_RID,
942 SECURITY_NULL_RID,
943 SECURITY_NULL_RID,
944 SECURITY_NULL_RID,
945 SECURITY_NULL_RID,
946 &WorldSid);
947 if (!NT_SUCCESS(Status))
948 {
949 _SEH2_LEAVE;
950 }
951
952 Status = RtlAllocateAndInitializeSid(&SystemAuthority,
953 1,
954 SECURITY_RESTRICTED_CODE_RID,
955 SECURITY_NULL_RID,
956 SECURITY_NULL_RID,
957 SECURITY_NULL_RID,
958 SECURITY_NULL_RID,
959 SECURITY_NULL_RID,
960 SECURITY_NULL_RID,
961 SECURITY_NULL_RID,
962 &SystemSid);
963 if (!NT_SUCCESS(Status))
964 {
965 RtlFreeSid(WorldSid);
966 _SEH2_LEAVE;
967 }
968
969 /* Initialize our SD (on stack) */
970 RtlCreateSecurityDescriptor(&SecurityDescriptor,
971 SECURITY_DESCRIPTOR_REVISION);
972
973 /* And our ACL (still on stack) */
974 RtlCreateAcl(&Dacl.Dacl, sizeof(Dacl), ACL_REVISION);
975
976 /*
977 * For access mask, if we have no session ID, or if
978 * protection mode is disabled, make them wide open
979 */
980 if (SessionId == 0 ||
981 (ProtectionMode & 3) == 0)
982 {
983 AccessMask = DELETE | SYMBOLIC_LINK_QUERY;
984 }
985 else
986 {
987 AccessMask = SYMBOLIC_LINK_QUERY;
988 }
989
990 /* Setup the ACL */
991 RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, WorldSid);
992 RtlAddAccessAllowedAce(&Dacl.Dacl, ACL_REVISION2, AccessMask, SystemSid);
993
994 /* Drop SIDs */
995 RtlFreeSid(WorldSid);
996 RtlFreeSid(SystemSid);
997
998 /* Link DACL to the SD */
999 RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, &Dacl.Dacl, TRUE);
1000
1001 /* And set it in the OA used for creation */
1002 ObjectAttributes.SecurityDescriptor = &SecurityDescriptor;
1003
1004 /*
1005 * If LUID and not global, we need to impersonate the caller
1006 * to make it local.
1007 */
1008 if (BaseStaticServerData->LUIDDeviceMapsEnabled)
1009 {
1010 if (!IsGlobal)
1011 {
1012 if (!CsrImpersonateClient(NULL))
1013 {
1014 Status = STATUS_BAD_IMPERSONATION_LEVEL;
1015 _SEH2_LEAVE;
1016 }
1017 }
1018 }
1019 /* The object will be permanent */
1020 else
1021 {
1022 ObjectAttributes.Attributes |= OBJ_PERMANENT;
1023 }
1024
1025 /* (Re)Create the symbolic link/device */
1026 Status = NtCreateSymbolicLinkObject(&LinkHandle,
1027 SYMBOLIC_LINK_ALL_ACCESS,
1028 &ObjectAttributes,
1029 &LinkTarget);
1030
1031 /* Revert to self if required */
1032 if (BaseStaticServerData->LUIDDeviceMapsEnabled && !IsGlobal)
1033 {
1034 CsrRevertToSelf();
1035 }
1036
1037 /* In case of a success, make object permanent for LUID links */
1038 if (NT_SUCCESS(Status))
1039 {
1040 if (BaseStaticServerData->LUIDDeviceMapsEnabled)
1041 {
1042 Status = NtMakePermanentObject(LinkHandle);
1043 }
1044
1045 /* Close the link */
1046 NtClose(LinkHandle);
1047
1048 /*
1049 * Specific failure case here:
1050 * We were asked to remove something
1051 * but we didn't find the something
1052 * (we recreated the symlink hence the fail here!)
1053 * so fail with appropriate status
1054 */
1055 if (RemoveDefinition && !RemoveFound)
1056 {
1057 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1058 }
1059 }
1060
1061 /* We closed link, don't double close */
1062 LinkHandle = 0;
1063 }
1064 _SEH2_FINALLY
1065 {
1066 /* If we need to close the link, do it now */
1067 if (LinkHandle != 0)
1068 {
1069 NtClose(LinkHandle);
1070 }
1071
1072 /* Free our internal buffer */
1073 RtlFreeHeap(BaseSrvHeap, 0, lpBuffer);
1074
1075 /* Broadcast drive letter creation */
1076 if (DriveLetter && Status == STATUS_SUCCESS && Broadcast)
1077 {
1078 LUID SystemLuid = SYSTEM_LUID;
1079
1080 /* If that's a global drive, broadcast as system */
1081 if (IsGlobal)
1082 {
1083 RtlCopyLuid(&CallerLuid, &SystemLuid);
1084 }
1085
1086 /* Broadcast the event */
1087 AddBSMRequest(AbsLetter, RemoveDefinition, &CallerLuid);
1088
1089 /*
1090 * If we removed drive, and the drive was shadowing a global one
1091 * broadcast the arrival of the global drive (as system - global)
1092 */
1093 if (RemoveDefinition && !RtlEqualLuid(&CallerLuid, &SystemLuid))
1094 {
1095 if (CheckForGlobalDriveLetter(AbsLetter))
1096 {
1097 AddBSMRequest(AbsLetter, FALSE, &CallerLuid);
1098 }
1099 }
1100 }
1101
1102 /* Done! */
1103 RtlLeaveCriticalSection(&BaseDefineDosDeviceCritSec);
1104 }
1105 _SEH2_END;
1106
1107 return Status;
1108 }
1109
1110 /* EOF */
1111