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