xref: /reactos/ntoskrnl/ob/devicemap.c (revision 7115d7ba)
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
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
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
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
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
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
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
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
539 ObQueryDeviceMapInformation(IN PEPROCESS Process,
540                             IN PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo,
541                             IN ULONG Flags)
542 {
543     PDEVICE_MAP DeviceMap = NULL, GlobalDeviceMap;
544     BOOLEAN Dereference;
545     PROCESS_DEVICEMAP_INFORMATION MapInfo;
546     ULONG BitMask, i;
547     BOOLEAN ReturnAny;
548     NTSTATUS Status;
549 
550     /* Validate flags */
551     if (Flags & ~PROCESS_LUID_DOSDEVICES_ONLY)
552     {
553         return STATUS_INVALID_PARAMETER;
554     }
555 
556     Dereference = FALSE;
557     /* Do we want to return anything? */
558     ReturnAny = ~Flags & PROCESS_LUID_DOSDEVICES_ONLY;
559 
560     /* If LUID mappings are enabled... */
561     if (ObpLUIDDeviceMapsEnabled != 0)
562     {
563         /* Check for process parameter validness */
564         if (Process != NULL && Process != PsGetCurrentProcess())
565         {
566             return STATUS_INVALID_PARAMETER;
567         }
568 
569         /* And get the device map */
570         DeviceMap = ObpReferenceDeviceMap();
571     }
572 
573     /* Acquire the device map lock */
574     KeAcquireGuardedMutex(&ObpDeviceMapLock);
575 
576     /*
577      * If we had a device map, if because of LUID mappings,
578      * we'll have to dereference it afterwards
579      */
580     if (DeviceMap != NULL)
581     {
582         Dereference = TRUE;
583     }
584     else
585     {
586         /* Get the process device map or the system device map */
587         DeviceMap = (Process != NULL) ? Process->DeviceMap : ObSystemDeviceMap;
588     }
589 
590     /* Fail if no device map */
591     if (DeviceMap == NULL)
592     {
593         KeReleaseGuardedMutex(&ObpDeviceMapLock);
594         return STATUS_END_OF_FILE;
595     }
596 
597     /* At that point, assume success */
598     Status = STATUS_SUCCESS;
599 
600     /* Try to get the global device map if any */
601     GlobalDeviceMap = DeviceMap;
602     if (DeviceMap->GlobalDosDevicesDirectory != NULL)
603     {
604         if (DeviceMap->GlobalDosDevicesDirectory->DeviceMap != NULL)
605         {
606             GlobalDeviceMap = DeviceMap->GlobalDosDevicesDirectory->DeviceMap;
607         }
608     }
609 
610     /* Now, setup our device map info, especially drive types */
611     MapInfo.Query.DriveMap = DeviceMap->DriveMap;
612     /* Browse every device */
613     for (i = 0, BitMask = 1; i < 32; ++i, BitMask *= 2)
614     {
615         /* Set the type given current device map */
616         MapInfo.Query.DriveType[i] = DeviceMap->DriveType[i];
617 
618         /*
619          * If device is not existing and we're asked to return
620          * more than just LUID mapped, get the entry
621          * from global device map if not remote
622          */
623         if (!(MapInfo.Query.DriveMap & BitMask) && ReturnAny)
624         {
625             if (ObpLUIDDeviceMapsEnabled != 0 ||
626                 (GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_REMOTE &&
627                  GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_CALCULATE))
628             {
629                 MapInfo.Query.DriveType[i] = GlobalDeviceMap->DriveType[i];
630                 MapInfo.Query.DriveMap |= BitMask & GlobalDeviceMap->DriveMap;
631             }
632         }
633     }
634 
635     /* Release the device map lock */
636     KeReleaseGuardedMutex(&ObpDeviceMapLock);
637 
638     /* Dereference LUID device map */
639     if (Dereference)
640     {
641         ObfDereferenceDeviceMap(DeviceMap);
642     }
643 
644     /* Copy back data */
645     _SEH2_TRY
646     {
647         RtlCopyMemory(DeviceMapInfo, &MapInfo, sizeof(PROCESS_DEVICEMAP_INFORMATION));
648     }
649     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
650     {
651         Status = _SEH2_GetExceptionCode();
652     }
653     _SEH2_END;
654 
655     return Status;
656 }
657 
658 
659 ULONG
660 NTAPI
661 ObIsLUIDDeviceMapsEnabled(VOID)
662 {
663     return ObpLUIDDeviceMapsEnabled;
664 }
665 
666 
667 #if 0
668 NTSTATUS
669 NTAPI
670 ObIsDosDeviceLocallyMapped(
671     IN ULONG Index,
672     OUT PUCHAR DosDeviceState)
673 {
674     /* Check the index */
675     if (Index < 1 || Index > 26)
676         return STATUS_INVALID_PARAMETER;
677 
678     /* Acquire the device map lock */
679     KeAcquireGuardedMutex(&ObpDeviceMapLock);
680 
681     /* Get drive mapping status */
682     *DosDeviceState = (ObSystemDeviceMap->DriveMap & (1 << Index)) != 0;
683 
684     /* Release the device map lock */
685     KeReleaseGuardedMutex(&ObpDeviceMapLock);
686 
687     return STATUS_SUCCESS;
688 }
689 #endif
690 
691 /* EOF */
692