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