xref: /reactos/ntoskrnl/ob/devicemap.c (revision 5d896433)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ob/devicemap.c
5  * PURPOSE:         Device map implementation
6  * PROGRAMMERS:     Eric Kohl (eric.kohl@reactos.org)
7  *                  Alex Ionescu (alex.ionescu@reactos.org)
8  *                  Pierre Schweitzer (pierre@reactos.org)
9  */
10 
11 /* INCLUDES ***************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 ULONG ObpLUIDDeviceMapsDisabled;
18 ULONG ObpLUIDDeviceMapsEnabled;
19 
20 /* PRIVATE FUNCTIONS ******************************************************/
21 
22 NTSTATUS
23 NTAPI
ObSetDeviceMap(IN PEPROCESS Process,IN HANDLE DirectoryHandle)24 ObSetDeviceMap(IN PEPROCESS Process,
25                IN HANDLE DirectoryHandle)
26 {
27     POBJECT_DIRECTORY DirectoryObject = NULL;
28     PDEVICE_MAP DeviceMap = NULL, NewDeviceMap = NULL, OldDeviceMap;
29     NTSTATUS Status;
30     PEPROCESS WorkProcess;
31     BOOLEAN MakePermanant = FALSE;
32 
33     Status = ObReferenceObjectByHandle(DirectoryHandle,
34                                        DIRECTORY_TRAVERSE,
35                                        ObpDirectoryObjectType,
36                                        KeGetPreviousMode(),
37                                        (PVOID*)&DirectoryObject,
38                                        NULL);
39     if (!NT_SUCCESS(Status))
40     {
41         DPRINT("ObReferenceObjectByHandle() failed (Status 0x%08lx)\n", Status);
42         return Status;
43     }
44 
45     /* Allocate and initialize a new device map */
46     DeviceMap = ExAllocatePoolWithTag(PagedPool,
47                                       sizeof(*DeviceMap),
48                                       'mDbO');
49     if (DeviceMap == NULL)
50     {
51         ObDereferenceObject(DirectoryObject);
52         return STATUS_INSUFFICIENT_RESOURCES;
53     }
54 
55     /* Initialize the device map */
56     RtlZeroMemory(DeviceMap, sizeof(*DeviceMap));
57     DeviceMap->ReferenceCount = 1;
58     DeviceMap->DosDevicesDirectory = DirectoryObject;
59 
60     /* Acquire the device map lock */
61     KeAcquireGuardedMutex(&ObpDeviceMapLock);
62 
63     /* Attach the device map to the directory object */
64     if (DirectoryObject->DeviceMap == NULL)
65     {
66         DirectoryObject->DeviceMap = DeviceMap;
67     }
68     else
69     {
70         NewDeviceMap = DeviceMap;
71 
72         /* There's already a device map,
73            so reuse it */
74         DeviceMap = DirectoryObject->DeviceMap;
75         ++DeviceMap->ReferenceCount;
76     }
77 
78     /* Caller gave a process, use it */
79     if (Process != NULL)
80     {
81         WorkProcess = Process;
82     }
83     /* If no process given, use current and
84      * set system device map */
85     else
86     {
87         WorkProcess = PsGetCurrentProcess();
88         ObSystemDeviceMap = DeviceMap;
89     }
90 
91     /* If current object isn't system one, save system one in current
92      * device map */
93     if (DirectoryObject != ObSystemDeviceMap->DosDevicesDirectory)
94     {
95         /* We also need to make the object permanant */
96         DeviceMap->GlobalDosDevicesDirectory = ObSystemDeviceMap->DosDevicesDirectory;
97         MakePermanant = TRUE;
98     }
99 
100     /* Save old process device map */
101     OldDeviceMap = WorkProcess->DeviceMap;
102     /* Attach the device map to the process */
103     WorkProcess->DeviceMap = DeviceMap;
104 
105     /* Release the device map lock */
106     KeReleaseGuardedMutex(&ObpDeviceMapLock);
107 
108     /* If we have to make the object permamant, do it now */
109     if (MakePermanant)
110     {
111         POBJECT_HEADER ObjectHeader;
112         POBJECT_HEADER_NAME_INFO HeaderNameInfo;
113 
114         ObjectHeader = OBJECT_TO_OBJECT_HEADER(DirectoryObject);
115         HeaderNameInfo = ObpReferenceNameInfo(ObjectHeader);
116 
117         ObpEnterObjectTypeMutex(ObjectHeader->Type);
118         if (HeaderNameInfo != NULL && HeaderNameInfo->Directory != NULL)
119         {
120             ObjectHeader->Flags |= OB_FLAG_PERMANENT;
121         }
122         ObpLeaveObjectTypeMutex(ObjectHeader->Type);
123 
124         if (HeaderNameInfo != NULL)
125         {
126             ObpDereferenceNameInfo(HeaderNameInfo);
127         }
128     }
129 
130     /* Release useless device map if required */
131     if (NewDeviceMap != NULL)
132     {
133         ObDereferenceObject(DirectoryObject);
134         ExFreePoolWithTag(NewDeviceMap, 'mDbO');
135     }
136 
137     /* And dereference previous process device map */
138     if (OldDeviceMap != NULL)
139     {
140         ObfDereferenceDeviceMap(OldDeviceMap);
141     }
142 
143     return STATUS_SUCCESS;
144 }
145 
146 
147 NTSTATUS
148 NTAPI
ObSetDirectoryDeviceMap(OUT PDEVICE_MAP * DeviceMap,IN HANDLE DirectoryHandle)149 ObSetDirectoryDeviceMap(OUT PDEVICE_MAP * DeviceMap,
150                         IN HANDLE DirectoryHandle)
151 {
152     POBJECT_DIRECTORY DirectoryObject = NULL;
153     PDEVICE_MAP LocalMap = NULL, NewDeviceMap = NULL;
154     NTSTATUS Status;
155     POBJECT_HEADER ObjectHeader;
156     POBJECT_HEADER_NAME_INFO HeaderNameInfo;
157 
158     Status = ObReferenceObjectByHandle(DirectoryHandle,
159                                        DIRECTORY_TRAVERSE,
160                                        ObpDirectoryObjectType,
161                                        KernelMode,
162                                        (PVOID*)&DirectoryObject,
163                                        NULL);
164     if (!NT_SUCCESS(Status))
165     {
166         DPRINT("ObReferenceObjectByHandle() failed (Status 0x%08lx)\n", Status);
167         return Status;
168     }
169 
170     /* Allocate and initialize a new device map */
171     LocalMap = ExAllocatePoolWithTag(PagedPool,
172                                      sizeof(*LocalMap),
173                                      'mDbO');
174     if (LocalMap == NULL)
175     {
176         ObDereferenceObject(DirectoryObject);
177         return STATUS_INSUFFICIENT_RESOURCES;
178     }
179 
180     /* Initialize the device map */
181     RtlZeroMemory(LocalMap, sizeof(*LocalMap));
182     LocalMap->ReferenceCount = 1;
183     LocalMap->DosDevicesDirectory = DirectoryObject;
184 
185     /* Acquire the device map lock */
186     KeAcquireGuardedMutex(&ObpDeviceMapLock);
187 
188     /* Attach the device map to the directory object */
189     if (DirectoryObject->DeviceMap == NULL)
190     {
191         DirectoryObject->DeviceMap = LocalMap;
192     }
193     else
194     {
195         NewDeviceMap = LocalMap;
196 
197         /* There's already a device map,
198            so reuse it */
199         LocalMap = DirectoryObject->DeviceMap;
200         ++LocalMap->ReferenceCount;
201     }
202 
203     /* If current object isn't system one, save system one in current
204      * device map */
205     if (DirectoryObject != ObSystemDeviceMap->DosDevicesDirectory)
206     {
207         /* We also need to make the object permanant */
208         LocalMap->GlobalDosDevicesDirectory = ObSystemDeviceMap->DosDevicesDirectory;
209     }
210 
211     /* Release the device map lock */
212     KeReleaseGuardedMutex(&ObpDeviceMapLock);
213 
214     if (DeviceMap != NULL)
215     {
216         *DeviceMap = LocalMap;
217     }
218 
219     /* Caller expects us to make the object permanant, so do it! */
220     ObjectHeader = OBJECT_TO_OBJECT_HEADER(DirectoryObject);
221     HeaderNameInfo = ObpReferenceNameInfo(ObjectHeader);
222 
223     ObpEnterObjectTypeMutex(ObjectHeader->Type);
224     if (HeaderNameInfo != NULL && HeaderNameInfo->Directory != NULL)
225     {
226         ObjectHeader->Flags |= OB_FLAG_PERMANENT;
227     }
228     ObpLeaveObjectTypeMutex(ObjectHeader->Type);
229 
230     if (HeaderNameInfo != NULL)
231     {
232         ObpDereferenceNameInfo(HeaderNameInfo);
233     }
234 
235     /* Release useless device map if required */
236     if (NewDeviceMap != NULL)
237     {
238         ObDereferenceObject(DirectoryObject);
239         ExFreePoolWithTag(NewDeviceMap, 'mDbO');
240     }
241 
242     return Status;
243 }
244 
245 
246 NTSTATUS
247 NTAPI
ObpSetCurrentProcessDeviceMap(VOID)248 ObpSetCurrentProcessDeviceMap(VOID)
249 {
250     PTOKEN Token;
251     LUID LogonId;
252     NTSTATUS Status;
253     PEPROCESS CurrentProcess;
254     LUID SystemLuid = SYSTEM_LUID;
255     PDEVICE_MAP DeviceMap, OldDeviceMap;
256 
257     /* Get our impersonation token */
258     CurrentProcess = PsGetCurrentProcess();
259     Token = PsReferencePrimaryToken(CurrentProcess);
260     if (Token == NULL)
261     {
262         return STATUS_NO_TOKEN;
263     }
264 
265     /* Query the Logon ID */
266     Status = SeQueryAuthenticationIdToken(Token, &LogonId);
267     if (!NT_SUCCESS(Status))
268     {
269         goto done;
270     }
271 
272     /* If that's system, then use system device map */
273     if (RtlEqualLuid(&LogonId, &SystemLuid))
274     {
275         DeviceMap = ObSystemDeviceMap;
276     }
277     /* Otherwise ask Se for the device map */
278     else
279     {
280         Status = SeGetLogonIdDeviceMap(&LogonId, &DeviceMap);
281         if (!NT_SUCCESS(Status))
282         {
283             /* Normalize failure status */
284             Status = STATUS_OBJECT_PATH_INVALID;
285             goto done;
286         }
287     }
288 
289     /* Fail if no device map */
290     if (DeviceMap == NULL)
291     {
292         Status = STATUS_OBJECT_PATH_INVALID;
293         goto done;
294     }
295 
296     /* Acquire the device map lock */
297     KeAcquireGuardedMutex(&ObpDeviceMapLock);
298 
299     /* Save old device map attached to the process */
300     OldDeviceMap = CurrentProcess->DeviceMap;
301 
302     /* Set new device map & reference it */
303     ++DeviceMap->ReferenceCount;
304     CurrentProcess->DeviceMap = DeviceMap;
305 
306     /* Release the device map lock */
307     KeReleaseGuardedMutex(&ObpDeviceMapLock);
308 
309     /* If we had a device map, dereference it */
310     if (OldDeviceMap != NULL)
311     {
312         ObfDereferenceDeviceMap(OldDeviceMap);
313     }
314 
315 done:
316     /* We're done with the token! */
317     ObDereferenceObject(Token);
318 
319     return Status;
320 }
321 
322 
323 PDEVICE_MAP
324 NTAPI
ObpReferenceDeviceMap(VOID)325 ObpReferenceDeviceMap(VOID)
326 {
327     LUID LogonId;
328     NTSTATUS Status;
329     PTOKEN Token = NULL;
330     PDEVICE_MAP DeviceMap;
331     PETHREAD CurrentThread;
332     BOOLEAN LookingForSystem;
333     LUID SystemLuid = SYSTEM_LUID;
334     BOOLEAN CopyOnOpen, EffectiveOnly;
335     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
336 
337     LookingForSystem = FALSE;
338 
339     /* If LUID mapping is enable, try to get appropriate device map */
340     if (ObpLUIDDeviceMapsEnabled != 0)
341     {
342         /* In case of impersonation, we've got a bit of work to do */
343         CurrentThread = PsGetCurrentThread();
344         if (CurrentThread->ActiveImpersonationInfo)
345         {
346             /* Get impersonation token */
347             Token = PsReferenceImpersonationToken(CurrentThread,
348                                                   &CopyOnOpen,
349                                                   &EffectiveOnly,
350                                                   &ImpersonationLevel);
351             /* Get logon LUID */
352             if (Token != NULL)
353             {
354                 Status = SeQueryAuthenticationIdToken(Token, &LogonId);
355             }
356             else
357             {
358                 /* Force failure */
359                 Status = STATUS_NO_TOKEN;
360             }
361 
362             /* If we got logon LUID */
363             if (NT_SUCCESS(Status))
364             {
365                 /*
366                  * Check it's not system, system is easy to handle,
367                  * we just need to return ObSystemDeviceMap
368                  */
369                 if (!RtlEqualLuid(&LogonId, &SystemLuid))
370                 {
371                     /* Ask Se for the device  map */
372                     Status = SeGetLogonIdDeviceMap(&LogonId, &DeviceMap);
373                     if (NT_SUCCESS(Status))
374                     {
375                         /* Acquire the device map lock */
376                         KeAcquireGuardedMutex(&ObpDeviceMapLock);
377 
378                         /* Reference the device map if any */
379                         if (DeviceMap != NULL)
380                         {
381                             ++DeviceMap->ReferenceCount;
382                         }
383 
384                         /* Release the device map lock */
385                         KeReleaseGuardedMutex(&ObpDeviceMapLock);
386 
387                         /* If we got the device map, we're done! */
388                         if (DeviceMap != NULL)
389                         {
390                             ObDereferenceObject(Token);
391 
392                             return DeviceMap;
393                         }
394                     }
395                 }
396                 else
397                 {
398                     LookingForSystem = TRUE;
399                 }
400             }
401         }
402 
403         /*
404          * Fall back case of the LUID mapping, make sure there's a
405          * a device map attached to the current process
406          */
407         if (PsGetCurrentProcess()->DeviceMap == NULL &&
408             !NT_SUCCESS(ObpSetCurrentProcessDeviceMap()))
409         {
410             /* We may have failed after we got impersonation token */
411             if (Token != NULL)
412             {
413                 ObDereferenceObject(Token);
414             }
415 
416             return NULL;
417         }
418     }
419 
420     /* Acquire the device map lock */
421     KeAcquireGuardedMutex(&ObpDeviceMapLock);
422 
423     /* If we're looking for system map, use it */
424     if (LookingForSystem)
425     {
426         DeviceMap = ObSystemDeviceMap;
427     }
428     /* Otherwise, use current process device map */
429     else
430     {
431         DeviceMap = PsGetCurrentProcess()->DeviceMap;
432     }
433 
434     /* If we got one, reference it */
435     if (DeviceMap != NULL)
436     {
437         ++DeviceMap->ReferenceCount;
438     }
439 
440     /* Release the device map lock */
441     KeReleaseGuardedMutex(&ObpDeviceMapLock);
442 
443     /* We may have impersonation token (if we failed in impersonation branch) */
444     if (Token != NULL)
445     {
446         ObDereferenceObject(Token);
447     }
448 
449     /* Return the potentially found device map */
450     return DeviceMap;
451 }
452 
453 
454 VOID
455 NTAPI
ObDereferenceDeviceMap(IN PEPROCESS Process)456 ObDereferenceDeviceMap(IN PEPROCESS Process)
457 {
458     PDEVICE_MAP DeviceMap;
459 
460     DPRINT("ObDereferenceDeviceMap()\n");
461 
462     /* Get the pointer to this process devicemap and reset it
463        holding the device map lock */
464     KeAcquireGuardedMutex(&ObpDeviceMapLock);
465     DeviceMap = Process->DeviceMap;
466     Process->DeviceMap = NULL;
467     KeReleaseGuardedMutex(&ObpDeviceMapLock);
468 
469     /* Continue only if there is a device map */
470     if (DeviceMap != NULL)
471         ObfDereferenceDeviceMap(DeviceMap);
472 }
473 
474 
475 VOID
476 FASTCALL
ObfDereferenceDeviceMap(IN PDEVICE_MAP DeviceMap)477 ObfDereferenceDeviceMap(IN PDEVICE_MAP DeviceMap)
478 {
479     DPRINT("ObfDereferenceDeviceMap()\n");
480 
481     /* Acquire the device map lock */
482     KeAcquireGuardedMutex(&ObpDeviceMapLock);
483 
484     /* Decrement the reference counter */
485     DeviceMap->ReferenceCount--;
486     DPRINT("ReferenceCount: %lu\n", DeviceMap->ReferenceCount);
487 
488     /* Leave, if there are still references to this device map */
489     if (DeviceMap->ReferenceCount != 0)
490     {
491         /* Release the device map lock and leave */
492         KeReleaseGuardedMutex(&ObpDeviceMapLock);
493         return;
494     }
495 
496     /* Nobody is referencing it anymore, unlink the DOS directory */
497     DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
498 
499     /* Release the devicemap lock */
500     KeReleaseGuardedMutex(&ObpDeviceMapLock);
501 
502     /* Dereference the DOS Devices Directory and free the Device Map */
503     ObMakeTemporaryObject(DeviceMap->DosDevicesDirectory);
504     ObDereferenceObject(DeviceMap->DosDevicesDirectory);
505     ExFreePoolWithTag(DeviceMap, 'mDbO');
506 }
507 
508 
509 VOID
510 NTAPI
ObInheritDeviceMap(IN PEPROCESS Parent,IN PEPROCESS Process)511 ObInheritDeviceMap(IN PEPROCESS Parent,
512                    IN PEPROCESS Process)
513 {
514     PDEVICE_MAP DeviceMap;
515 
516     DPRINT("ObInheritDeviceMap()\n");
517 
518     /* Acquire the device map lock */
519     KeAcquireGuardedMutex(&ObpDeviceMapLock);
520 
521     /* Get the parent process device map or the system device map */
522     DeviceMap = (Parent != NULL) ? Parent->DeviceMap : ObSystemDeviceMap;
523     if (DeviceMap != NULL)
524     {
525         /* Reference the device map and attach it to the new process */
526         DeviceMap->ReferenceCount++;
527         DPRINT("ReferenceCount: %lu\n", DeviceMap->ReferenceCount);
528 
529         Process->DeviceMap = DeviceMap;
530     }
531 
532     /* Release the device map lock */
533     KeReleaseGuardedMutex(&ObpDeviceMapLock);
534 }
535 
536 
537 NTSTATUS
538 NTAPI
ObQueryDeviceMapInformation(_In_opt_ PEPROCESS Process,_Out_ PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo,_In_ ULONG Flags)539 ObQueryDeviceMapInformation(
540     _In_opt_ PEPROCESS Process,
541     _Out_ PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo,
542     _In_ ULONG Flags)
543 {
544     PDEVICE_MAP DeviceMap = NULL, GlobalDeviceMap;
545     BOOLEAN Dereference;
546     PROCESS_DEVICEMAP_INFORMATION MapInfo;
547     ULONG BitMask, i;
548     BOOLEAN ReturnAny;
549     NTSTATUS Status;
550 
551     /* Validate flags */
552     if (Flags & ~PROCESS_LUID_DOSDEVICES_ONLY)
553     {
554         return STATUS_INVALID_PARAMETER;
555     }
556 
557     Dereference = FALSE;
558     /* Do we want to return anything? */
559     ReturnAny = ~Flags & PROCESS_LUID_DOSDEVICES_ONLY;
560 
561     /* If LUID mappings are enabled... */
562     if (ObpLUIDDeviceMapsEnabled != 0)
563     {
564         /* Check for process parameter validness */
565         if (Process != NULL && Process != PsGetCurrentProcess())
566         {
567             return STATUS_INVALID_PARAMETER;
568         }
569 
570         /* And get the device map */
571         DeviceMap = ObpReferenceDeviceMap();
572     }
573 
574     /* Acquire the device map lock */
575     KeAcquireGuardedMutex(&ObpDeviceMapLock);
576 
577     /*
578      * If we had a device map, if because of LUID mappings,
579      * we'll have to dereference it afterwards
580      */
581     if (DeviceMap != NULL)
582     {
583         Dereference = TRUE;
584     }
585     else
586     {
587         /* Get the process device map or the system device map */
588         DeviceMap = (Process != NULL) ? Process->DeviceMap : ObSystemDeviceMap;
589     }
590 
591     /* Fail if no device map */
592     if (DeviceMap == NULL)
593     {
594         KeReleaseGuardedMutex(&ObpDeviceMapLock);
595         return STATUS_END_OF_FILE;
596     }
597 
598     /* At that point, assume success */
599     Status = STATUS_SUCCESS;
600 
601     /* Try to get the global device map if any */
602     GlobalDeviceMap = DeviceMap;
603     if (DeviceMap->GlobalDosDevicesDirectory != NULL)
604     {
605         if (DeviceMap->GlobalDosDevicesDirectory->DeviceMap != NULL)
606         {
607             GlobalDeviceMap = DeviceMap->GlobalDosDevicesDirectory->DeviceMap;
608         }
609     }
610 
611     /* Now, setup our device map info, especially drive types */
612     MapInfo.Query.DriveMap = DeviceMap->DriveMap;
613     /* Browse every device */
614     for (i = 0, BitMask = 1; i < 32; ++i, BitMask *= 2)
615     {
616         /* Set the type given current device map */
617         MapInfo.Query.DriveType[i] = DeviceMap->DriveType[i];
618 
619         /*
620          * If device is not existing and we're asked to return
621          * more than just LUID mapped, get the entry
622          * from global device map if not remote
623          */
624         if (!(MapInfo.Query.DriveMap & BitMask) && ReturnAny)
625         {
626             if (ObpLUIDDeviceMapsEnabled != 0 ||
627                 (GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_REMOTE &&
628                  GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_CALCULATE))
629             {
630                 MapInfo.Query.DriveType[i] = GlobalDeviceMap->DriveType[i];
631                 MapInfo.Query.DriveMap |= BitMask & GlobalDeviceMap->DriveMap;
632             }
633         }
634     }
635 
636     /* Release the device map lock */
637     KeReleaseGuardedMutex(&ObpDeviceMapLock);
638 
639     /* Dereference LUID device map */
640     if (Dereference)
641     {
642         ObfDereferenceDeviceMap(DeviceMap);
643     }
644 
645     /* Copy back data */
646     _SEH2_TRY
647     {
648         RtlCopyMemory(DeviceMapInfo, &MapInfo, sizeof(PROCESS_DEVICEMAP_INFORMATION));
649     }
650     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
651     {
652         Status = _SEH2_GetExceptionCode();
653     }
654     _SEH2_END;
655 
656     return Status;
657 }
658 
659 
660 ULONG
661 NTAPI
ObIsLUIDDeviceMapsEnabled(VOID)662 ObIsLUIDDeviceMapsEnabled(VOID)
663 {
664     return ObpLUIDDeviceMapsEnabled;
665 }
666 
667 
668 #if 0
669 NTSTATUS
670 NTAPI
671 ObIsDosDeviceLocallyMapped(
672     IN ULONG Index,
673     OUT PUCHAR DosDeviceState)
674 {
675     /* Check the index */
676     if (Index < 1 || Index > 26)
677         return STATUS_INVALID_PARAMETER;
678 
679     /* Acquire the device map lock */
680     KeAcquireGuardedMutex(&ObpDeviceMapLock);
681 
682     /* Get drive mapping status */
683     *DosDeviceState = (ObSystemDeviceMap->DriveMap & (1 << Index)) != 0;
684 
685     /* Release the device map lock */
686     KeReleaseGuardedMutex(&ObpDeviceMapLock);
687 
688     return STATUS_SUCCESS;
689 }
690 #endif
691 
692 /* EOF */
693