xref: /reactos/ntoskrnl/se/srm.c (revision 34593d93)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/se/srm.c
5  * PURPOSE:         Security Reference Monitor Server
6  *
7  * PROGRAMMERS:     Timo Kreuzer (timo.kreuzer@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 extern LUID SeSystemAuthenticationId;
18 extern LUID SeAnonymousAuthenticationId;
19 
20 /* PRIVATE DEFINITIONS ********************************************************/
21 
22 #define SEP_LOGON_SESSION_TAG 'sLeS'
23 #define SEP_LOGON_NOTIFICATION_TAG 'nLeS'
24 
25 typedef struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION
26 {
27     struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION *Next;
28     PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine;
29 } SEP_LOGON_SESSION_TERMINATED_NOTIFICATION, *PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION;
30 
31 VOID
32 NTAPI
33 SepRmCommandServerThread(
34     PVOID StartContext);
35 
36 static
37 NTSTATUS
38 SepCleanupLUIDDeviceMapDirectory(
39     _In_ PLUID LogonLuid);
40 
41 static
42 NTSTATUS
43 SepRmCreateLogonSession(
44     PLUID LogonLuid);
45 
46 
47 /* GLOBALS ********************************************************************/
48 
49 HANDLE SeRmCommandPort;
50 HANDLE SeLsaInitEvent;
51 
52 PVOID SepCommandPortViewBase;
53 PVOID SepCommandPortViewRemoteBase;
54 ULONG_PTR SepCommandPortViewBaseOffset;
55 
56 static HANDLE SepRmCommandMessagePort;
57 
58 BOOLEAN SepAdtAuditingEnabled;
59 ULONG SepAdtMinListLength = 0x2000;
60 ULONG SepAdtMaxListLength = 0x3000;
61 
62 #define POLICY_AUDIT_EVENT_TYPE_COUNT 9 // (AuditCategoryAccountLogon - AuditCategorySystem + 1)
63 UCHAR SeAuditingState[POLICY_AUDIT_EVENT_TYPE_COUNT];
64 
65 KGUARDED_MUTEX SepRmDbLock;
66 PSEP_LOGON_SESSION_REFERENCES SepLogonSessions = NULL;
67 PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION SepLogonNotifications = NULL;
68 
69 
70 /* PRIVATE FUNCTIONS **********************************************************/
71 
72 /**
73  * @brief
74  * A private registry helper that returns the desired value
75  * data based on the specifics requested by the caller.
76  *
77  * @param[in] KeyName
78  * Name of the key.
79  *
80  * @param[in] ValueName
81  * Name of the registry value.
82  *
83  * @param[in] ValueType
84  * The type of the registry value.
85  *
86  * @param[in] DataLength
87  * The data length, in bytes, representing the size of the registry value.
88  *
89  * @param[out] ValueData
90  * The requested value data provided by the function.
91  *
92  * @return
93  * Returns STATUS_SUCCESS if the operations have completed successfully,
94  * otherwise a failure NTSTATUS code is returned.
95  */
96 NTSTATUS
97 NTAPI
98 SepRegQueryHelper(
99     _In_ PCWSTR KeyName,
100     _In_ PCWSTR ValueName,
101     _In_ ULONG ValueType,
102     _In_ ULONG DataLength,
103     _Out_ PVOID ValueData)
104 {
105     UNICODE_STRING ValueNameString;
106     UNICODE_STRING KeyNameString;
107     ULONG ResultLength;
108     OBJECT_ATTRIBUTES ObjectAttributes;
109     HANDLE KeyHandle = NULL;
110     struct
111     {
112         KEY_VALUE_PARTIAL_INFORMATION Partial;
113         UCHAR Buffer[64];
114     } KeyValueInformation;
115     NTSTATUS Status, CloseStatus;
116     PAGED_CODE();
117 
118     RtlInitUnicodeString(&KeyNameString, KeyName);
119     InitializeObjectAttributes(&ObjectAttributes,
120                                &KeyNameString,
121                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
122                                NULL,
123                                NULL);
124 
125     Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
126     if (!NT_SUCCESS(Status))
127     {
128         return Status;
129     }
130 
131     RtlInitUnicodeString(&ValueNameString, ValueName);
132     Status = ZwQueryValueKey(KeyHandle,
133                              &ValueNameString,
134                              KeyValuePartialInformation,
135                              &KeyValueInformation.Partial,
136                              sizeof(KeyValueInformation),
137                              &ResultLength);
138     if (!NT_SUCCESS(Status))
139     {
140         goto Cleanup;
141     }
142 
143     if ((KeyValueInformation.Partial.Type != ValueType) ||
144         (KeyValueInformation.Partial.DataLength != DataLength))
145     {
146         Status = STATUS_OBJECT_TYPE_MISMATCH;
147         goto Cleanup;
148     }
149 
150     if (ValueType == REG_BINARY)
151     {
152         RtlCopyMemory(ValueData, KeyValueInformation.Partial.Data, DataLength);
153     }
154     else if (ValueType == REG_DWORD)
155     {
156         *(PULONG)ValueData = *(PULONG)KeyValueInformation.Partial.Data;
157     }
158     else
159     {
160         Status = STATUS_INVALID_PARAMETER;
161     }
162 
163 Cleanup:
164     CloseStatus = ZwClose(KeyHandle);
165     ASSERT(NT_SUCCESS( CloseStatus ));
166 
167     return Status;
168 }
169 
170 
171 BOOLEAN
172 NTAPI
173 SeRmInitPhase0(VOID)
174 {
175     NTSTATUS Status;
176 
177     /* Initialize the database lock */
178     KeInitializeGuardedMutex(&SepRmDbLock);
179 
180     /* Create the system logon session */
181     Status = SepRmCreateLogonSession(&SeSystemAuthenticationId);
182     if (!NT_VERIFY(NT_SUCCESS(Status)))
183     {
184         return FALSE;
185     }
186 
187     /* Create the anonymous logon session */
188     Status = SepRmCreateLogonSession(&SeAnonymousAuthenticationId);
189     if (!NT_VERIFY(NT_SUCCESS(Status)))
190     {
191         return FALSE;
192     }
193 
194     return TRUE;
195 }
196 
197 
198 BOOLEAN
199 NTAPI
200 SeRmInitPhase1(VOID)
201 {
202     UNICODE_STRING Name;
203     OBJECT_ATTRIBUTES ObjectAttributes;
204     HANDLE ThreadHandle;
205     NTSTATUS Status;
206 
207     /* Create the SeRm command port */
208     RtlInitUnicodeString(&Name, L"\\SeRmCommandPort");
209     InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL);
210     Status = ZwCreatePort(&SeRmCommandPort,
211                           &ObjectAttributes,
212                           sizeof(ULONG),
213                           PORT_MAXIMUM_MESSAGE_LENGTH,
214                           2 * PAGE_SIZE);
215     if (!NT_SUCCESS(Status))
216     {
217         DPRINT1("Security: Rm Create Command Port failed 0x%lx\n", Status);
218         return FALSE;
219     }
220 
221     /* Create SeLsaInitEvent */
222     RtlInitUnicodeString(&Name, L"\\SeLsaInitEvent");
223     InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL);
224     Status = ZwCreateEvent(&SeLsaInitEvent,
225                            GENERIC_WRITE,
226                            &ObjectAttributes,
227                            NotificationEvent,
228                            FALSE);
229     if (!NT_VERIFY((NT_SUCCESS(Status))))
230     {
231         DPRINT1("Security: LSA init event creation failed.0x%xl\n", Status);
232         return FALSE;
233     }
234 
235     /* Create the SeRm server thread */
236     Status = PsCreateSystemThread(&ThreadHandle,
237                                   THREAD_ALL_ACCESS,
238                                   NULL,
239                                   NULL,
240                                   NULL,
241                                   SepRmCommandServerThread,
242                                   NULL);
243     if (!NT_SUCCESS(Status))
244     {
245         DPRINT1("Security: Rm Server Thread creation failed 0x%lx\n", Status);
246         return FALSE;
247     }
248 
249     ObCloseHandle(ThreadHandle, KernelMode);
250 
251     return TRUE;
252 }
253 
254 static
255 VOID
256 SepAdtInitializeBounds(VOID)
257 {
258     struct
259     {
260         ULONG MaxLength;
261         ULONG MinLength;
262     } ListBounds;
263     NTSTATUS Status;
264     PAGED_CODE();
265 
266     Status = SepRegQueryHelper(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa",
267                                L"Bounds",
268                                REG_BINARY,
269                                sizeof(ListBounds),
270                                &ListBounds);
271     if (!NT_SUCCESS(Status))
272     {
273         /* No registry values, so keep hardcoded defaults */
274         return;
275     }
276 
277     /* Check if the bounds are valid */
278     if ((ListBounds.MaxLength < ListBounds.MinLength) ||
279         (ListBounds.MinLength < 16) ||
280         (ListBounds.MaxLength - ListBounds.MinLength < 16))
281     {
282         DPRINT1("ListBounds are invalid: %u, %u\n",
283                 ListBounds.MinLength, ListBounds.MaxLength);
284         return;
285     }
286 
287     /* Set the new bounds globally */
288     SepAdtMinListLength = ListBounds.MinLength;
289     SepAdtMaxListLength = ListBounds.MaxLength;
290 }
291 
292 
293 static
294 NTSTATUS
295 SepRmSetAuditEvent(
296     PSEP_RM_API_MESSAGE Message)
297 {
298     ULONG i;
299     PAGED_CODE();
300 
301     /* First re-initialize the bounds from the registry */
302     SepAdtInitializeBounds();
303 
304     /* Make sure we have the right message and clear */
305     ASSERT(Message->ApiNumber == RmAuditSetCommand);
306     Message->ApiNumber = 0;
307 
308     /* Store the enable flag in the global variable */
309     SepAdtAuditingEnabled = Message->u.SetAuditEvent.Enabled;
310 
311     /* Loop all audit event types */
312     for (i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++)
313     {
314         /* Save the provided flags in the global array */
315         SeAuditingState[i] = (UCHAR)Message->u.SetAuditEvent.Flags[i];
316     }
317 
318     return STATUS_SUCCESS;
319 }
320 
321 
322 static
323 NTSTATUS
324 SepRmCreateLogonSession(
325     PLUID LogonLuid)
326 {
327     PSEP_LOGON_SESSION_REFERENCES CurrentSession, NewSession;
328     NTSTATUS Status;
329     PAGED_CODE();
330 
331     DPRINT("SepRmCreateLogonSession(%08lx:%08lx)\n",
332            LogonLuid->HighPart, LogonLuid->LowPart);
333 
334     /* Allocate a new session structure */
335     NewSession = ExAllocatePoolWithTag(PagedPool,
336                                        sizeof(SEP_LOGON_SESSION_REFERENCES),
337                                        SEP_LOGON_SESSION_TAG);
338     if (NewSession == NULL)
339     {
340         return STATUS_INSUFFICIENT_RESOURCES;
341     }
342 
343     /* Initialize it */
344     NewSession->LogonId = *LogonLuid;
345     NewSession->ReferenceCount = 0;
346     NewSession->Flags = 0;
347     NewSession->pDeviceMap = NULL;
348     InitializeListHead(&NewSession->TokenList);
349 
350     /* Acquire the database lock */
351     KeAcquireGuardedMutex(&SepRmDbLock);
352 
353     /* Loop all existing sessions */
354     for (CurrentSession = SepLogonSessions;
355          CurrentSession != NULL;
356          CurrentSession = CurrentSession->Next)
357     {
358         /* Check if the LUID matches the new one */
359         if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid))
360         {
361             Status = STATUS_LOGON_SESSION_EXISTS;
362             goto Leave;
363         }
364     }
365 
366     /* Insert the new session */
367     NewSession->Next = SepLogonSessions;
368     SepLogonSessions = NewSession;
369 
370     Status = STATUS_SUCCESS;
371 
372 Leave:
373     /* Release the database lock */
374     KeReleaseGuardedMutex(&SepRmDbLock);
375 
376     if (!NT_SUCCESS(Status))
377     {
378         ExFreePoolWithTag(NewSession, SEP_LOGON_SESSION_TAG);
379     }
380 
381     return Status;
382 }
383 
384 /**
385  * @brief
386  * Deletes a logon session from the logon sessions database.
387  *
388  * @param[in] LogonLuid
389  * A logon ID represented as a LUID. This LUID is used to point
390  * the exact logon session saved within the database.
391  *
392  * @return
393  * STATUS_SUCCESS is returned if the logon session has been deleted successfully.
394  * STATUS_NO_SUCH_LOGON_SESSION is returned if the logon session with the submitted
395  * LUID doesn't exist. STATUS_BAD_LOGON_SESSION_STATE is returned if the logon session
396  * is still in use and we're not allowed to delete it, or if a system or anonymous session
397  * is submitted and we're not allowed to delete them as they're internal parts of the system.
398  * Otherwise a failure NTSTATUS code is returned.
399  */
400 static
401 NTSTATUS
402 SepRmDeleteLogonSession(
403     _In_ PLUID LogonLuid)
404 {
405     PSEP_LOGON_SESSION_REFERENCES SessionToDelete;
406     NTSTATUS Status;
407     PAGED_CODE();
408 
409     DPRINT("SepRmDeleteLogonSession(%08lx:%08lx)\n",
410            LogonLuid->HighPart, LogonLuid->LowPart);
411 
412     /* Acquire the database lock */
413     KeAcquireGuardedMutex(&SepRmDbLock);
414 
415     /* Loop over the existing logon sessions */
416     for (SessionToDelete = SepLogonSessions;
417          SessionToDelete != NULL;
418          SessionToDelete = SessionToDelete->Next)
419     {
420         /*
421          * Does the actual logon session exist in the
422          * saved logon sessions database with the LUID
423          * provided?
424          */
425         if (RtlEqualLuid(&SessionToDelete->LogonId, LogonLuid))
426         {
427             /* Did the caller supply one of these internal sessions? */
428             if (RtlEqualLuid(&SessionToDelete->LogonId, &SeSystemAuthenticationId) ||
429                 RtlEqualLuid(&SessionToDelete->LogonId, &SeAnonymousAuthenticationId))
430             {
431                 /* These logons are critical stuff, we can't delete them */
432                 DPRINT1("SepRmDeleteLogonSession(): We're not allowed to delete anonymous/system sessions!\n");
433                 Status = STATUS_BAD_LOGON_SESSION_STATE;
434                 goto Leave;
435             }
436             else
437             {
438                 /* We found the logon as exactly as we wanted, break the loop */
439                 break;
440             }
441         }
442     }
443 
444     /*
445      * If we reach this then that means we've exhausted all the logon
446      * sessions and couldn't find one with the desired LUID.
447      */
448     if (SessionToDelete == NULL)
449     {
450         DPRINT1("SepRmDeleteLogonSession(): The logon session with this LUID doesn't exist!\n");
451         Status = STATUS_NO_SUCH_LOGON_SESSION;
452         goto Leave;
453     }
454 
455     /* Is somebody still using this logon session? */
456     if (SessionToDelete->ReferenceCount != 0)
457     {
458         /* The logon session is still in use, we cannot delete it... */
459         DPRINT1("SepRmDeleteLogonSession(): The logon session is still in use!\n");
460         Status = STATUS_BAD_LOGON_SESSION_STATE;
461         goto Leave;
462     }
463 
464     /* If we have a LUID device map, clean it */
465     if (SessionToDelete->pDeviceMap != NULL)
466     {
467         Status = SepCleanupLUIDDeviceMapDirectory(LogonLuid);
468         if (!NT_SUCCESS(Status))
469         {
470             /*
471              * We had one job on cleaning the device map directory
472              * of the logon session but we failed, quit...
473              */
474             DPRINT1("SepRmDeleteLogonSession(): Failed to clean the LUID device map directory of the logon (Status: 0x%lx)\n", Status);
475             goto Leave;
476         }
477 
478         /* And dereference the device map of the logon */
479         ObfDereferenceDeviceMap(SessionToDelete->pDeviceMap);
480     }
481 
482     /* If we're here then we've deleted the logon session successfully */
483     DPRINT("SepRmDeleteLogonSession(): Logon session deleted with success!\n");
484     Status = STATUS_SUCCESS;
485     ExFreePoolWithTag(SessionToDelete, SEP_LOGON_SESSION_TAG);
486 
487 Leave:
488     /* Release the database lock */
489     KeReleaseGuardedMutex(&SepRmDbLock);
490     return Status;
491 }
492 
493 
494 NTSTATUS
495 SepRmReferenceLogonSession(
496     PLUID LogonLuid)
497 {
498     PSEP_LOGON_SESSION_REFERENCES CurrentSession;
499 
500     PAGED_CODE();
501 
502     DPRINT("SepRmReferenceLogonSession(%08lx:%08lx)\n",
503            LogonLuid->HighPart, LogonLuid->LowPart);
504 
505     /* Acquire the database lock */
506     KeAcquireGuardedMutex(&SepRmDbLock);
507 
508     /* Loop all existing sessions */
509     for (CurrentSession = SepLogonSessions;
510          CurrentSession != NULL;
511          CurrentSession = CurrentSession->Next)
512     {
513         /* Check if the LUID matches the new one */
514         if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid))
515         {
516             /* Reference the session */
517             ++CurrentSession->ReferenceCount;
518             DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount);
519 
520             /* Release the database lock */
521             KeReleaseGuardedMutex(&SepRmDbLock);
522 
523             return STATUS_SUCCESS;
524         }
525     }
526 
527     /* Release the database lock */
528     KeReleaseGuardedMutex(&SepRmDbLock);
529 
530     return STATUS_NO_SUCH_LOGON_SESSION;
531 }
532 
533 static
534 NTSTATUS
535 SepCleanupLUIDDeviceMapDirectory(
536     _In_ PLUID LogonLuid)
537 {
538     BOOLEAN UseCurrentProc;
539     KAPC_STATE ApcState;
540     WCHAR Buffer[63];
541     UNICODE_STRING DirectoryName;
542     OBJECT_ATTRIBUTES ObjectAttributes;
543     NTSTATUS Status;
544     HANDLE DirectoryHandle, LinkHandle;
545     PHANDLE LinksBuffer;
546     POBJECT_DIRECTORY_INFORMATION DirectoryInfo;
547     ULONG LinksCount, LinksSize, DirInfoLength, ReturnLength, Context, CurrentLinks, i;
548     BOOLEAN RestartScan;
549 
550     PAGED_CODE();
551 
552     /* We need a logon LUID */
553     if (LogonLuid == NULL)
554     {
555         return STATUS_INVALID_PARAMETER;
556     }
557 
558     /* Use current process */
559     UseCurrentProc = ObReferenceObjectSafe(PsGetCurrentProcess());
560     if (UseCurrentProc)
561     {
562         ObDereferenceObject(PsGetCurrentProcess());
563     }
564     /* Unless it's gone, then use system process */
565     else
566     {
567         KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
568     }
569 
570     /* Initialize our directory name */
571     _snwprintf(Buffer,
572                sizeof(Buffer) / sizeof(WCHAR),
573                L"\\Sessions\\0\\DosDevices\\%08x-%08x",
574                LogonLuid->HighPart,
575                LogonLuid->LowPart);
576     RtlInitUnicodeString(&DirectoryName, Buffer);
577 
578     /* And open it */
579     InitializeObjectAttributes(&ObjectAttributes,
580                                &DirectoryName,
581                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
582                                NULL,
583                                NULL);
584     Status = ZwOpenDirectoryObject(&DirectoryHandle,
585                                    DIRECTORY_QUERY,
586                                    &ObjectAttributes);
587     if (!NT_SUCCESS(Status))
588     {
589         if (!UseCurrentProc)
590         {
591             KeUnstackDetachProcess(&ApcState);
592         }
593 
594         return Status;
595     }
596 
597     /* Some initialization needed for browsing all our links... */
598     Context = 0;
599     DirectoryInfo = NULL;
600     DirInfoLength = 0;
601     /* In our buffer, we'll store at max 100 HANDLE */
602     LinksCount = 100;
603     CurrentLinks = 0;
604     /* Which gives a certain size */
605     LinksSize = LinksCount * sizeof(HANDLE);
606 
607     /*
608      * This label is hit if we need to store more than a hundred
609      * of links. In that case, we jump here after having cleaned
610      * and deleted previous buffer.
611      * All handles have been already closed
612      */
613 AllocateLinksAgain:
614     LinksBuffer = ExAllocatePoolWithTag(PagedPool,
615                                         LinksSize,
616                                         TAG_SE_HANDLES_TAB);
617     if (LinksBuffer == NULL)
618     {
619         /*
620          * Failure path: no need to clear handles:
621          * already closed and the buffer is already gone
622          */
623         ZwClose(DirectoryHandle);
624 
625         /*
626          * On the first round, DirectoryInfo is NULL,
627          * if we grow LinksBuffer, it has been allocated
628          */
629         if (DirectoryInfo != NULL)
630         {
631             ExFreePoolWithTag(DirectoryInfo, TAG_SE_DIR_BUFFER);
632         }
633 
634         if (!UseCurrentProc)
635         {
636             KeUnstackDetachProcess(&ApcState);
637         }
638 
639         return STATUS_NO_MEMORY;
640     }
641 
642     /*
643      * We always restart scan, but on the first loop
644      * if we couldn't fit everything in our buffer,
645      * then, we continue scan.
646      * But we restart if link buffer was too small
647      */
648     for (RestartScan = TRUE; ; RestartScan = FALSE)
649     {
650         /*
651          * Loop until our buffer is big enough to store
652          * one entry
653          */
654         while (TRUE)
655         {
656             Status = ZwQueryDirectoryObject(DirectoryHandle,
657                                             DirectoryInfo,
658                                             DirInfoLength,
659                                             TRUE,
660                                             RestartScan,
661                                             &Context,
662                                             &ReturnLength);
663             /* Only handle buffer growth in that loop */
664             if (Status != STATUS_BUFFER_TOO_SMALL)
665             {
666                 break;
667             }
668 
669             /* Get output length as new length */
670             DirInfoLength = ReturnLength;
671             /* Delete old buffer if any */
672             if (DirectoryInfo != NULL)
673             {
674                 ExFreePoolWithTag(DirectoryInfo, 'bDeS');
675             }
676 
677             /* And reallocate a bigger one */
678             DirectoryInfo = ExAllocatePoolWithTag(PagedPool,
679                                                   DirInfoLength,
680                                                   TAG_SE_DIR_BUFFER);
681             /* Fail if we cannot allocate */
682             if (DirectoryInfo == NULL)
683             {
684                 Status = STATUS_INSUFFICIENT_RESOURCES;
685                 break;
686             }
687         }
688 
689         /* If querying the entry failed, quit */
690         if (!NT_SUCCESS(Status))
691         {
692             break;
693         }
694 
695         /* We only look for symbolic links, the rest, we ignore */
696         if (wcscmp(DirectoryInfo->TypeName.Buffer, L"SymbolicLink"))
697         {
698             continue;
699         }
700 
701         /* If our link buffer is out of space, reallocate */
702         if (CurrentLinks >= LinksCount)
703         {
704             /* First, close the links */
705             for (i = 0; i < CurrentLinks; ++i)
706             {
707                 ZwClose(LinksBuffer[i]);
708             }
709 
710             /* Allow 20 more HANDLEs */
711             LinksCount += 20;
712             CurrentLinks = 0;
713             ExFreePoolWithTag(LinksBuffer, TAG_SE_HANDLES_TAB);
714             LinksSize = LinksCount * sizeof(HANDLE);
715 
716             /* And reloop again */
717             goto AllocateLinksAgain;
718         }
719 
720         /* Open the found link */
721         InitializeObjectAttributes(&ObjectAttributes,
722                                    &DirectoryInfo->Name,
723                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
724                                    DirectoryHandle,
725                                    NULL);
726         if (NT_SUCCESS(ZwOpenSymbolicLinkObject(&LinkHandle,
727                                                 SYMBOLIC_LINK_ALL_ACCESS,
728                                                 &ObjectAttributes)))
729         {
730             /* If we cannot make it temporary, just close the link handle */
731             if (!NT_SUCCESS(ZwMakeTemporaryObject(LinkHandle)))
732             {
733                 ZwClose(LinkHandle);
734             }
735             /* Otherwise, store it to defer deletion */
736             else
737             {
738                 LinksBuffer[CurrentLinks] = LinkHandle;
739                 ++CurrentLinks;
740             }
741         }
742     }
743 
744     /* No more entries means we handled all links, that's not a failure */
745     if (Status == STATUS_NO_MORE_ENTRIES)
746     {
747         Status = STATUS_SUCCESS;
748     }
749 
750     /* Close all the links we stored, this will like cause their deletion */
751     for (i = 0; i < CurrentLinks; ++i)
752     {
753         ZwClose(LinksBuffer[i]);
754     }
755     /* And free our links buffer */
756     ExFreePoolWithTag(LinksBuffer, TAG_SE_HANDLES_TAB);
757 
758     /* Free our directory info buffer - it might be NULL if we failed realloc */
759     if (DirectoryInfo != NULL)
760     {
761         ExFreePoolWithTag(DirectoryInfo, TAG_SE_DIR_BUFFER);
762     }
763 
764     /* Close our session directory */
765     ZwClose(DirectoryHandle);
766 
767     /* And detach from system */
768     if (!UseCurrentProc)
769     {
770         KeUnstackDetachProcess(&ApcState);
771     }
772 
773     return Status;
774 }
775 
776 
777 NTSTATUS
778 SepRmDereferenceLogonSession(
779     PLUID LogonLuid)
780 {
781     ULONG RefCount;
782     PDEVICE_MAP DeviceMap;
783     PSEP_LOGON_SESSION_REFERENCES CurrentSession;
784 
785     DPRINT("SepRmDereferenceLogonSession(%08lx:%08lx)\n",
786            LogonLuid->HighPart, LogonLuid->LowPart);
787 
788     /* Acquire the database lock */
789     KeAcquireGuardedMutex(&SepRmDbLock);
790 
791     /* Loop all existing sessions */
792     for (CurrentSession = SepLogonSessions;
793          CurrentSession != NULL;
794          CurrentSession = CurrentSession->Next)
795     {
796         /* Check if the LUID matches the new one */
797         if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid))
798         {
799             /* Dereference the session */
800             RefCount = --CurrentSession->ReferenceCount;
801             DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount);
802 
803             /* Release the database lock */
804             KeReleaseGuardedMutex(&SepRmDbLock);
805 
806             /* We're done with the session */
807             if (RefCount == 0)
808             {
809                 /* Get rid of the LUID device map */
810                 DeviceMap = CurrentSession->pDeviceMap;
811                 if (DeviceMap != NULL)
812                 {
813                     CurrentSession->pDeviceMap = NULL;
814                     SepCleanupLUIDDeviceMapDirectory(LogonLuid);
815                     ObfDereferenceDeviceMap(DeviceMap);
816                 }
817             }
818 
819             return STATUS_SUCCESS;
820         }
821     }
822 
823     /* Release the database lock */
824     KeReleaseGuardedMutex(&SepRmDbLock);
825 
826     return STATUS_NO_SUCH_LOGON_SESSION;
827 }
828 
829 
830 BOOLEAN
831 NTAPI
832 SepRmCommandServerThreadInit(VOID)
833 {
834     SECURITY_QUALITY_OF_SERVICE SecurityQos;
835     SEP_RM_API_MESSAGE Message;
836     UNICODE_STRING PortName;
837     REMOTE_PORT_VIEW RemotePortView;
838     PORT_VIEW PortView;
839     LARGE_INTEGER SectionSize;
840     HANDLE SectionHandle;
841     HANDLE PortHandle;
842     NTSTATUS Status;
843     BOOLEAN Result;
844 
845     SectionHandle = NULL;
846     PortHandle = NULL;
847 
848     /* Assume success */
849     Result = TRUE;
850 
851     /* Wait until LSASS is ready */
852     Status = ZwWaitForSingleObject(SeLsaInitEvent, FALSE, NULL);
853     if (!NT_SUCCESS(Status))
854     {
855         DPRINT1("Security Rm Init: Waiting for LSA Init Event failed 0x%lx\n", Status);
856         goto Cleanup;
857     }
858 
859     /* We don't need this event anymore */
860     ObCloseHandle(SeLsaInitEvent, KernelMode);
861 
862     /* Initialize the connection message */
863     Message.Header.u1.s1.TotalLength = sizeof(Message);
864     Message.Header.u1.s1.DataLength = 0;
865 
866     /* Only LSASS can connect, so handle the connection right now */
867     Status = ZwListenPort(SeRmCommandPort, &Message.Header);
868     if (!NT_SUCCESS(Status))
869     {
870         DPRINT1("Security Rm Init: Listen to Command Port failed 0x%lx\n", Status);
871         goto Cleanup;
872     }
873 
874     /* Set the Port View structure length */
875     RemotePortView.Length = sizeof(RemotePortView);
876 
877     /* Accept the connection */
878     Status = ZwAcceptConnectPort(&SepRmCommandMessagePort,
879                                  NULL,
880                                  &Message.Header,
881                                  TRUE,
882                                  NULL,
883                                  &RemotePortView);
884     if (!NT_SUCCESS(Status))
885     {
886         DPRINT1("Security Rm Init: Accept Connect to Command Port failed 0x%lx\n", Status);
887         goto Cleanup;
888     }
889 
890     /* Complete the connection */
891     Status = ZwCompleteConnectPort(SepRmCommandMessagePort);
892     if (!NT_SUCCESS(Status))
893     {
894         DPRINT1("Security Rm Init: Complete Connect to Command Port failed 0x%lx\n", Status);
895         goto Cleanup;
896     }
897 
898     /* Create a section for messages */
899     SectionSize.QuadPart = PAGE_SIZE;
900     Status = ZwCreateSection(&SectionHandle,
901                              SECTION_ALL_ACCESS,
902                              NULL,
903                              &SectionSize,
904                              PAGE_READWRITE,
905                              SEC_COMMIT,
906                              NULL);
907     if (!NT_SUCCESS(Status))
908     {
909         DPRINT1("Security Rm Init: Create Memory Section for LSA port failed: %X\n", Status);
910         goto Cleanup;
911     }
912 
913     /* Setup the PORT_VIEW structure */
914     PortView.Length = sizeof(PortView);
915     PortView.SectionHandle = SectionHandle;
916     PortView.SectionOffset = 0;
917     PortView.ViewSize = SectionSize.LowPart;
918     PortView.ViewBase = NULL;
919     PortView.ViewRemoteBase = NULL;
920 
921     /* Setup security QOS */
922     SecurityQos.Length = sizeof(SecurityQos);
923     SecurityQos.ImpersonationLevel = SecurityImpersonation;
924     SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
925     SecurityQos.EffectiveOnly = TRUE;
926 
927     /* Connect to LSASS */
928     RtlInitUnicodeString(&PortName, L"\\SeLsaCommandPort");
929     Status = ZwConnectPort(&PortHandle,
930                            &PortName,
931                            &SecurityQos,
932                            &PortView,
933                            NULL,
934                            0,
935                            0,
936                            0);
937     if (!NT_SUCCESS(Status))
938     {
939         DPRINT1("Security Rm Init: Connect to LSA Port failed 0x%lx\n", Status);
940         goto Cleanup;
941     }
942 
943     /* Remember section base and view offset */
944     SepCommandPortViewBase = PortView.ViewBase;
945     SepCommandPortViewRemoteBase = PortView.ViewRemoteBase;
946     SepCommandPortViewBaseOffset = (ULONG_PTR)SepCommandPortViewRemoteBase -
947                                    (ULONG_PTR)SepCommandPortViewBase;
948 
949     DPRINT("SepRmCommandServerThreadInit: done\n");
950 
951 Cleanup:
952     /* Check for failure */
953     if (!NT_SUCCESS(Status))
954     {
955         if (PortHandle != NULL)
956         {
957             ObCloseHandle(PortHandle, KernelMode);
958         }
959 
960         Result = FALSE;
961     }
962 
963     /* Did we create a section? */
964     if (SectionHandle != NULL)
965     {
966         ObCloseHandle(SectionHandle, KernelMode);
967     }
968 
969     return Result;
970 }
971 
972 VOID
973 NTAPI
974 SepRmCommandServerThread(
975     PVOID StartContext)
976 {
977     SEP_RM_API_MESSAGE Message;
978     PPORT_MESSAGE ReplyMessage;
979     HANDLE DummyPortHandle;
980     NTSTATUS Status;
981 
982     /* Initialize the server thread */
983     if (!SepRmCommandServerThreadInit())
984     {
985         DPRINT1("Security: Terminating Rm Command Server Thread\n");
986         return;
987     }
988 
989     /* No reply yet */
990     ReplyMessage = NULL;
991 
992     /* Start looping */
993     while (TRUE)
994     {
995         /* Wait for a message */
996         Status = ZwReplyWaitReceivePort(SepRmCommandMessagePort,
997                                         NULL,
998                                         ReplyMessage,
999                                         &Message.Header);
1000         if (!NT_SUCCESS(Status))
1001         {
1002             DPRINT1("Failed to get message: 0x%lx", Status);
1003             ReplyMessage = NULL;
1004             continue;
1005         }
1006 
1007         /* Check if this is a connection request */
1008         if (Message.Header.u2.s2.Type == LPC_CONNECTION_REQUEST)
1009         {
1010             /* Reject connection request */
1011             ZwAcceptConnectPort(&DummyPortHandle,
1012                                 NULL,
1013                                 &Message.Header,
1014                                 FALSE,
1015                                 NULL,
1016                                 NULL);
1017 
1018             /* Start over */
1019             ReplyMessage = NULL;
1020             continue;
1021         }
1022 
1023         /* Check if the port died */
1024         if ((Message.Header.u2.s2.Type == LPC_PORT_CLOSED) ||
1025             (Message.Header.u2.s2.Type == LPC_CLIENT_DIED))
1026         {
1027             /* LSASS is dead, so let's quit as well */
1028             break;
1029         }
1030 
1031         /* Check if this is an actual request */
1032         if (Message.Header.u2.s2.Type != LPC_REQUEST)
1033         {
1034             DPRINT1("SepRmCommandServerThread: unexpected message type: 0x%lx\n",
1035                     Message.Header.u2.s2.Type);
1036 
1037             /* Restart without replying */
1038             ReplyMessage = NULL;
1039             continue;
1040         }
1041 
1042         ReplyMessage = &Message.Header;
1043 
1044         switch (Message.ApiNumber)
1045         {
1046             case RmAuditSetCommand:
1047                 Status = SepRmSetAuditEvent(&Message);
1048                 break;
1049 
1050             case RmCreateLogonSession:
1051                 Status = SepRmCreateLogonSession(&Message.u.LogonLuid);
1052                 break;
1053 
1054             case RmDeleteLogonSession:
1055                 Status = SepRmDeleteLogonSession(&Message.u.LogonLuid);
1056                 break;
1057 
1058             default:
1059                 DPRINT1("SepRmDispatchRequest: invalid API number: 0x%lx\n",
1060                         Message.ApiNumber);
1061                 ReplyMessage = NULL;
1062         }
1063 
1064         Message.u.ResultStatus = Status;
1065     }
1066 
1067     /* Close the port handles */
1068     ObCloseHandle(SepRmCommandMessagePort, KernelMode);
1069     ObCloseHandle(SeRmCommandPort, KernelMode);
1070 }
1071 
1072 
1073 /* PUBLIC FUNCTIONS ***********************************************************/
1074 
1075 /*
1076  * @unimplemented
1077  */
1078 NTSTATUS
1079 NTAPI
1080 SeGetLogonIdDeviceMap(
1081     IN PLUID LogonId,
1082     OUT PDEVICE_MAP * DeviceMap
1083     )
1084 {
1085     NTSTATUS Status;
1086     WCHAR Buffer[63];
1087     PDEVICE_MAP LocalMap;
1088     HANDLE DirectoryHandle, LinkHandle;
1089     OBJECT_ATTRIBUTES ObjectAttributes;
1090     PSEP_LOGON_SESSION_REFERENCES CurrentSession;
1091     UNICODE_STRING DirectoryName, LinkName, TargetName;
1092 
1093     PAGED_CODE();
1094 
1095     if  (LogonId == NULL ||
1096          DeviceMap == NULL)
1097     {
1098         return STATUS_INVALID_PARAMETER;
1099     }
1100 
1101     /* Acquire the database lock */
1102     KeAcquireGuardedMutex(&SepRmDbLock);
1103 
1104     /* Loop all existing sessions */
1105     for (CurrentSession = SepLogonSessions;
1106          CurrentSession != NULL;
1107          CurrentSession = CurrentSession->Next)
1108     {
1109         /* Check if the LUID matches the provided one */
1110         if (RtlEqualLuid(&CurrentSession->LogonId, LogonId))
1111         {
1112             break;
1113         }
1114     }
1115 
1116     /* No session found, fail */
1117     if (CurrentSession == NULL)
1118     {
1119         /* Release the database lock */
1120         KeReleaseGuardedMutex(&SepRmDbLock);
1121 
1122         return STATUS_NO_SUCH_LOGON_SESSION;
1123     }
1124 
1125     /* The found session has a device map, return it! */
1126     if (CurrentSession->pDeviceMap != NULL)
1127     {
1128         *DeviceMap = CurrentSession->pDeviceMap;
1129 
1130         /* Release the database lock */
1131         KeReleaseGuardedMutex(&SepRmDbLock);
1132 
1133         return STATUS_SUCCESS;
1134     }
1135 
1136     /* At that point, we'll setup a new device map for the session */
1137     LocalMap = NULL;
1138 
1139     /* Reference the session so that it doesn't go away */
1140     CurrentSession->ReferenceCount += 1;
1141 
1142     /* Release the database lock */
1143     KeReleaseGuardedMutex(&SepRmDbLock);
1144 
1145     /* Create our object directory given the LUID */
1146     _snwprintf(Buffer,
1147                sizeof(Buffer) / sizeof(WCHAR),
1148                L"\\Sessions\\0\\DosDevices\\%08x-%08x",
1149                LogonId->HighPart,
1150                LogonId->LowPart);
1151     RtlInitUnicodeString(&DirectoryName, Buffer);
1152 
1153     InitializeObjectAttributes(&ObjectAttributes,
1154                                &DirectoryName,
1155                                OBJ_KERNEL_HANDLE | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
1156                                NULL,
1157                                NULL);
1158     Status = ZwCreateDirectoryObject(&DirectoryHandle,
1159                                      DIRECTORY_ALL_ACCESS,
1160                                      &ObjectAttributes);
1161     if (NT_SUCCESS(Status))
1162     {
1163         /* Create the associated device map */
1164         Status = ObSetDirectoryDeviceMap(&LocalMap, DirectoryHandle);
1165         if (NT_SUCCESS(Status))
1166         {
1167             /* Make Global point to \Global?? in the directory */
1168             RtlInitUnicodeString(&LinkName, L"Global");
1169             RtlInitUnicodeString(&TargetName, L"\\Global??");
1170 
1171             InitializeObjectAttributes(&ObjectAttributes,
1172                                        &LinkName,
1173                                        OBJ_KERNEL_HANDLE | OBJ_OPENIF | OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
1174                                        DirectoryHandle,
1175                                        NULL);
1176             Status = ZwCreateSymbolicLinkObject(&LinkHandle,
1177                                                 SYMBOLIC_LINK_ALL_ACCESS,
1178                                                 &ObjectAttributes,
1179                                                 &TargetName);
1180             if (!NT_SUCCESS(Status))
1181             {
1182                 ObfDereferenceDeviceMap(LocalMap);
1183             }
1184             else
1185             {
1186                 ZwClose(LinkHandle);
1187             }
1188         }
1189 
1190         ZwClose(DirectoryHandle);
1191     }
1192 
1193     /* Acquire the database lock */
1194     KeAcquireGuardedMutex(&SepRmDbLock);
1195 
1196     /* If we succeed... */
1197     if (NT_SUCCESS(Status))
1198     {
1199         /* The session now has a device map? We raced with someone else */
1200         if (CurrentSession->pDeviceMap != NULL)
1201         {
1202             /* Give up on our new device map */
1203             ObfDereferenceDeviceMap(LocalMap);
1204         }
1205         /* Otherwise use our newly allocated device map */
1206         else
1207         {
1208             CurrentSession->pDeviceMap = LocalMap;
1209         }
1210 
1211         /* Return the device map */
1212         *DeviceMap = CurrentSession->pDeviceMap;
1213     }
1214     /* Zero output */
1215     else
1216     {
1217         *DeviceMap = NULL;
1218     }
1219 
1220     /* Release the database lock */
1221     KeReleaseGuardedMutex(&SepRmDbLock);
1222 
1223     /* We're done with the session */
1224     SepRmDereferenceLogonSession(&CurrentSession->LogonId);
1225 
1226     return Status;
1227 }
1228 
1229 /*
1230  * @unimplemented
1231  */
1232 NTSTATUS
1233 NTAPI
1234 SeMarkLogonSessionForTerminationNotification(
1235     IN PLUID LogonId)
1236 {
1237     UNIMPLEMENTED;
1238     return STATUS_NOT_IMPLEMENTED;
1239 }
1240 
1241 
1242 /*
1243  * @implemented
1244  */
1245 NTSTATUS
1246 NTAPI
1247 SeRegisterLogonSessionTerminatedRoutine(
1248     IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine)
1249 {
1250     PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION Notification;
1251     PAGED_CODE();
1252 
1253     /* Fail, if we don not have a callback routine */
1254     if (CallbackRoutine == NULL)
1255         return STATUS_INVALID_PARAMETER;
1256 
1257     /* Allocate a new notification item */
1258     Notification = ExAllocatePoolWithTag(PagedPool,
1259                                          sizeof(SEP_LOGON_SESSION_TERMINATED_NOTIFICATION),
1260                                          SEP_LOGON_NOTIFICATION_TAG);
1261     if (Notification == NULL)
1262         return STATUS_INSUFFICIENT_RESOURCES;
1263 
1264     /* Acquire the database lock */
1265     KeAcquireGuardedMutex(&SepRmDbLock);
1266 
1267     /* Set the callback routine */
1268     Notification->CallbackRoutine = CallbackRoutine;
1269 
1270     /* Insert the new notification item into the list */
1271     Notification->Next = SepLogonNotifications;
1272     SepLogonNotifications = Notification;
1273 
1274     /* Release the database lock */
1275     KeReleaseGuardedMutex(&SepRmDbLock);
1276 
1277     return STATUS_SUCCESS;
1278 }
1279 
1280 
1281 /*
1282  * @implemented
1283  */
1284 NTSTATUS
1285 NTAPI
1286 SeUnregisterLogonSessionTerminatedRoutine(
1287     IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine)
1288 {
1289     PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION Current, Previous = NULL;
1290     NTSTATUS Status;
1291     PAGED_CODE();
1292 
1293     /* Fail, if we don not have a callback routine */
1294     if (CallbackRoutine == NULL)
1295         return STATUS_INVALID_PARAMETER;
1296 
1297     /* Acquire the database lock */
1298     KeAcquireGuardedMutex(&SepRmDbLock);
1299 
1300     /* Loop all registered notification items */
1301     for (Current = SepLogonNotifications;
1302          Current != NULL;
1303          Current = Current->Next)
1304     {
1305         /* Check if the callback routine matches the provided one */
1306         if (Current->CallbackRoutine == CallbackRoutine)
1307             break;
1308 
1309         Previous = Current;
1310     }
1311 
1312     if (Current == NULL)
1313     {
1314         Status = STATUS_NOT_FOUND;
1315     }
1316     else
1317     {
1318         /* Remove the current notification item from the list */
1319         if (Previous == NULL)
1320             SepLogonNotifications = Current->Next;
1321         else
1322             Previous->Next = Current->Next;
1323 
1324         /* Free the current notification item */
1325         ExFreePoolWithTag(Current,
1326                           SEP_LOGON_NOTIFICATION_TAG);
1327 
1328         Status = STATUS_SUCCESS;
1329     }
1330 
1331     /* Release the database lock */
1332     KeReleaseGuardedMutex(&SepRmDbLock);
1333 
1334     return Status;
1335 }
1336