xref: /reactos/ntoskrnl/se/srm.c (revision 4561998a)
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  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 extern LUID SeSystemAuthenticationId;
17 extern LUID SeAnonymousAuthenticationId;
18 
19 /* PRIVATE DEFINITIONS ********************************************************/
20 
21 #define SEP_LOGON_SESSION_TAG 'sLeS'
22 
23 typedef struct _SEP_LOGON_SESSION_REFERENCES
24 {
25     struct _SEP_LOGON_SESSION_REFERENCES *Next;
26     LUID LogonId;
27     ULONG ReferenceCount;
28     ULONG Flags;
29     PDEVICE_MAP pDeviceMap;
30     LIST_ENTRY TokenList;
31 } SEP_LOGON_SESSION_REFERENCES, *PSEP_LOGON_SESSION_REFERENCES;
32 
33 VOID
34 NTAPI
35 SepRmCommandServerThread(
36     PVOID StartContext);
37 
38 static
39 NTSTATUS
40 SepRmCreateLogonSession(
41     PLUID LogonLuid);
42 
43 
44 /* GLOBALS ********************************************************************/
45 
46 HANDLE SeRmCommandPort;
47 HANDLE SeLsaInitEvent;
48 
49 PVOID SepCommandPortViewBase;
50 PVOID SepCommandPortViewRemoteBase;
51 ULONG_PTR SepCommandPortViewBaseOffset;
52 
53 static HANDLE SepRmCommandMessagePort;
54 
55 BOOLEAN SepAdtAuditingEnabled;
56 ULONG SepAdtMinListLength = 0x2000;
57 ULONG SepAdtMaxListLength = 0x3000;
58 
59 #define POLICY_AUDIT_EVENT_TYPE_COUNT 9 // (AuditCategoryAccountLogon - AuditCategorySystem + 1)
60 UCHAR SeAuditingState[POLICY_AUDIT_EVENT_TYPE_COUNT];
61 
62 KGUARDED_MUTEX SepRmDbLock;
63 PSEP_LOGON_SESSION_REFERENCES SepLogonSessions;
64 
65 
66 /* PRIVATE FUNCTIONS **********************************************************/
67 
68 NTSTATUS
69 NTAPI
70 SepRegQueryHelper(
71     PCWSTR KeyName,
72     PCWSTR ValueName,
73     ULONG ValueType,
74     ULONG DataLength,
75     PVOID ValueData)
76 {
77     UNICODE_STRING ValueNameString;
78     UNICODE_STRING KeyNameString;
79     ULONG ResultLength;
80     OBJECT_ATTRIBUTES ObjectAttributes;
81     HANDLE KeyHandle = NULL;
82     struct
83     {
84         KEY_VALUE_PARTIAL_INFORMATION Partial;
85         UCHAR Buffer[64];
86     } KeyValueInformation;
87     NTSTATUS Status, CloseStatus;
88     PAGED_CODE();
89 
90     RtlInitUnicodeString(&KeyNameString, KeyName);
91     InitializeObjectAttributes(&ObjectAttributes,
92                                &KeyNameString,
93                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
94                                NULL,
95                                NULL);
96 
97     Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
98     if (!NT_SUCCESS(Status))
99     {
100         return Status;
101     }
102 
103     RtlInitUnicodeString(&ValueNameString, ValueName);
104     Status = ZwQueryValueKey(KeyHandle,
105                              &ValueNameString,
106                              KeyValuePartialInformation,
107                              &KeyValueInformation.Partial,
108                              sizeof(KeyValueInformation),
109                              &ResultLength);
110     if (!NT_SUCCESS(Status))
111     {
112         goto Cleanup;
113     }
114 
115     if ((KeyValueInformation.Partial.Type != ValueType) ||
116         (KeyValueInformation.Partial.DataLength != DataLength))
117     {
118         Status = STATUS_OBJECT_TYPE_MISMATCH;
119         goto Cleanup;
120     }
121 
122 
123     if (ValueType == REG_BINARY)
124     {
125         RtlCopyMemory(ValueData, KeyValueInformation.Partial.Data, DataLength);
126     }
127     else if (ValueType == REG_DWORD)
128     {
129         *(PULONG)ValueData = *(PULONG)KeyValueInformation.Partial.Data;
130     }
131     else
132     {
133         Status = STATUS_INVALID_PARAMETER;
134     }
135 
136 Cleanup:
137     CloseStatus = ZwClose(KeyHandle);
138     ASSERT(NT_SUCCESS( CloseStatus ));
139 
140     return Status;
141 }
142 
143 
144 BOOLEAN
145 NTAPI
146 SeRmInitPhase0(VOID)
147 {
148     NTSTATUS Status;
149 
150     /* Initialize the database lock */
151     KeInitializeGuardedMutex(&SepRmDbLock);
152 
153     /* Create the system logon session */
154     Status = SepRmCreateLogonSession(&SeSystemAuthenticationId);
155     if (!NT_VERIFY(NT_SUCCESS(Status)))
156     {
157         return FALSE;
158     }
159 
160     /* Create the anonymous logon session */
161     Status = SepRmCreateLogonSession(&SeAnonymousAuthenticationId);
162     if (!NT_VERIFY(NT_SUCCESS(Status)))
163     {
164         return FALSE;
165     }
166 
167     return TRUE;
168 }
169 
170 
171 BOOLEAN
172 NTAPI
173 SeRmInitPhase1(VOID)
174 {
175     UNICODE_STRING Name;
176     OBJECT_ATTRIBUTES ObjectAttributes;
177     HANDLE ThreadHandle;
178     NTSTATUS Status;
179 
180     /* Create the SeRm command port */
181     RtlInitUnicodeString(&Name, L"\\SeRmCommandPort");
182     InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL);
183     Status = ZwCreatePort(&SeRmCommandPort,
184                           &ObjectAttributes,
185                           sizeof(ULONG),
186                           PORT_MAXIMUM_MESSAGE_LENGTH,
187                           2 * PAGE_SIZE);
188     if (!NT_SUCCESS(Status))
189     {
190         DPRINT1("Security: Rm Create Command Port failed 0x%lx\n", Status);
191         return FALSE;
192     }
193 
194     /* Create SeLsaInitEvent */
195     RtlInitUnicodeString(&Name, L"\\SeLsaInitEvent");
196     InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL);
197     Status = ZwCreateEvent(&SeLsaInitEvent,
198                            GENERIC_WRITE,
199                            &ObjectAttributes,
200                            NotificationEvent,
201                            FALSE);
202     if (!NT_VERIFY((NT_SUCCESS(Status))))
203     {
204         DPRINT1("Security: LSA init event creation failed.0x%xl\n", Status);
205         return FALSE;
206     }
207 
208     /* Create the SeRm server thread */
209     Status = PsCreateSystemThread(&ThreadHandle,
210                                   THREAD_ALL_ACCESS,
211                                   NULL,
212                                   NULL,
213                                   NULL,
214                                   SepRmCommandServerThread,
215                                   NULL);
216     if (!NT_SUCCESS(Status))
217     {
218         DPRINT1("Security: Rm Server Thread creation failed 0x%lx\n", Status);
219         return FALSE;
220     }
221 
222     ObCloseHandle(ThreadHandle, KernelMode);
223 
224     return TRUE;
225 }
226 
227 static
228 VOID
229 SepAdtInitializeBounds(VOID)
230 {
231     struct
232     {
233         ULONG MaxLength;
234         ULONG MinLength;
235     } ListBounds;
236     NTSTATUS Status;
237     PAGED_CODE();
238 
239     Status = SepRegQueryHelper(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa",
240                                L"Bounds",
241                                REG_BINARY,
242                                sizeof(ListBounds),
243                                &ListBounds);
244     if (!NT_SUCCESS(Status))
245     {
246         /* No registry values, so keep hardcoded defaults */
247         return;
248     }
249 
250     /* Check if the bounds are valid */
251     if ((ListBounds.MaxLength < ListBounds.MinLength) ||
252         (ListBounds.MinLength < 16) ||
253         (ListBounds.MaxLength - ListBounds.MinLength < 16))
254     {
255         DPRINT1("ListBounds are invalid: %u, %u\n",
256                 ListBounds.MinLength, ListBounds.MaxLength);
257         return;
258     }
259 
260     /* Set the new bounds globally */
261     SepAdtMinListLength = ListBounds.MinLength;
262     SepAdtMaxListLength = ListBounds.MaxLength;
263 }
264 
265 
266 static
267 NTSTATUS
268 SepRmSetAuditEvent(
269     PSEP_RM_API_MESSAGE Message)
270 {
271     ULONG i;
272     PAGED_CODE();
273 
274     /* First re-initialize the bounds from the registry */
275     SepAdtInitializeBounds();
276 
277     /* Make sure we have the right message and clear */
278     ASSERT(Message->ApiNumber == RmAuditSetCommand);
279     Message->ApiNumber = 0;
280 
281     /* Store the enable flag in the global variable */
282     SepAdtAuditingEnabled = Message->u.SetAuditEvent.Enabled;
283 
284     /* Loop all audit event types */
285     for (i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++)
286     {
287         /* Save the provided flags in the global array */
288         SeAuditingState[i] = (UCHAR)Message->u.SetAuditEvent.Flags[i];
289     }
290 
291     return STATUS_SUCCESS;
292 }
293 
294 
295 static
296 NTSTATUS
297 SepRmCreateLogonSession(
298     PLUID LogonLuid)
299 {
300     PSEP_LOGON_SESSION_REFERENCES CurrentSession, NewSession;
301     NTSTATUS Status;
302     PAGED_CODE();
303 
304     DPRINT("SepRmCreateLogonSession(%08lx:%08lx)\n",
305            LogonLuid->HighPart, LogonLuid->LowPart);
306 
307     /* Allocate a new session structure */
308     NewSession = ExAllocatePoolWithTag(PagedPool,
309                                        sizeof(SEP_LOGON_SESSION_REFERENCES),
310                                        SEP_LOGON_SESSION_TAG);
311     if (NewSession == NULL)
312     {
313         return STATUS_INSUFFICIENT_RESOURCES;
314     }
315 
316     /* Initialize it */
317     NewSession->LogonId = *LogonLuid;
318     NewSession->ReferenceCount = 0;
319     NewSession->Flags = 0;
320     NewSession->pDeviceMap = NULL;
321     InitializeListHead(&NewSession->TokenList);
322 
323     /* Acquire the database lock */
324     KeAcquireGuardedMutex(&SepRmDbLock);
325 
326     /* Loop all existing sessions */
327     for (CurrentSession = SepLogonSessions;
328          CurrentSession != NULL;
329          CurrentSession = CurrentSession->Next)
330     {
331         /* Check if the LUID matches the new one */
332         if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid))
333         {
334             Status = STATUS_LOGON_SESSION_EXISTS;
335             goto Leave;
336         }
337     }
338 
339     /* Insert the new session */
340     NewSession->Next = SepLogonSessions;
341     SepLogonSessions = NewSession;
342 
343     Status = STATUS_SUCCESS;
344 
345 Leave:
346     /* Release the database lock */
347     KeReleaseGuardedMutex(&SepRmDbLock);
348 
349     if (!NT_SUCCESS(Status))
350     {
351         ExFreePoolWithTag(NewSession, SEP_LOGON_SESSION_TAG);
352     }
353 
354     return Status;
355 }
356 
357 static
358 NTSTATUS
359 SepRmDeleteLogonSession(
360     PLUID LogonLuid)
361 {
362     DPRINT("SepRmDeleteLogonSession(%08lx:%08lx)\n",
363            LogonLuid->HighPart, LogonLuid->LowPart);
364 
365     UNIMPLEMENTED;
366     NT_ASSERT(FALSE);
367     return STATUS_NOT_IMPLEMENTED;
368 }
369 
370 
371 NTSTATUS
372 SepRmReferenceLogonSession(
373     PLUID LogonLuid)
374 {
375     PSEP_LOGON_SESSION_REFERENCES CurrentSession;
376 
377     PAGED_CODE();
378 
379     DPRINT("SepRmReferenceLogonSession(%08lx:%08lx)\n",
380            LogonLuid->HighPart, LogonLuid->LowPart);
381 
382     /* Acquire the database lock */
383     KeAcquireGuardedMutex(&SepRmDbLock);
384 
385     /* Loop all existing sessions */
386     for (CurrentSession = SepLogonSessions;
387          CurrentSession != NULL;
388          CurrentSession = CurrentSession->Next)
389     {
390         /* Check if the LUID matches the new one */
391         if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid))
392         {
393             /* Reference the session */
394             CurrentSession->ReferenceCount += 1;
395             DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount);
396 
397             /* Release the database lock */
398             KeReleaseGuardedMutex(&SepRmDbLock);
399 
400             return STATUS_SUCCESS;
401         }
402     }
403 
404     /* Release the database lock */
405     KeReleaseGuardedMutex(&SepRmDbLock);
406 
407     return STATUS_NO_SUCH_LOGON_SESSION;
408 }
409 
410 
411 NTSTATUS
412 SepRmDereferenceLogonSession(
413     PLUID LogonLuid)
414 {
415     PSEP_LOGON_SESSION_REFERENCES CurrentSession;
416 
417     DPRINT("SepRmDereferenceLogonSession(%08lx:%08lx)\n",
418            LogonLuid->HighPart, LogonLuid->LowPart);
419 
420     /* Acquire the database lock */
421     KeAcquireGuardedMutex(&SepRmDbLock);
422 
423     /* Loop all existing sessions */
424     for (CurrentSession = SepLogonSessions;
425          CurrentSession != NULL;
426          CurrentSession = CurrentSession->Next)
427     {
428         /* Check if the LUID matches the new one */
429         if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid))
430         {
431             /* Dereference the session */
432             CurrentSession->ReferenceCount -= 1;
433             DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount);
434 
435             /* Release the database lock */
436             KeReleaseGuardedMutex(&SepRmDbLock);
437 
438             return STATUS_SUCCESS;
439         }
440     }
441 
442     /* Release the database lock */
443     KeReleaseGuardedMutex(&SepRmDbLock);
444 
445     return STATUS_NO_SUCH_LOGON_SESSION;
446 }
447 
448 
449 BOOLEAN
450 NTAPI
451 SepRmCommandServerThreadInit(VOID)
452 {
453     SECURITY_QUALITY_OF_SERVICE SecurityQos;
454     SEP_RM_API_MESSAGE Message;
455     UNICODE_STRING PortName;
456     REMOTE_PORT_VIEW RemotePortView;
457     PORT_VIEW PortView;
458     LARGE_INTEGER SectionSize;
459     HANDLE SectionHandle;
460     HANDLE PortHandle;
461     NTSTATUS Status;
462     BOOLEAN Result;
463 
464     SectionHandle = NULL;
465     PortHandle = NULL;
466 
467     /* Assume success */
468     Result = TRUE;
469 
470     /* Wait until LSASS is ready */
471     Status = ZwWaitForSingleObject(SeLsaInitEvent, FALSE, NULL);
472     if (!NT_SUCCESS(Status))
473     {
474         DPRINT1("Security Rm Init: Waiting for LSA Init Event failed 0x%lx\n", Status);
475         goto Cleanup;
476     }
477 
478     /* We don't need this event anymore */
479     ObCloseHandle(SeLsaInitEvent, KernelMode);
480 
481     /* Initialize the connection message */
482     Message.Header.u1.s1.TotalLength = sizeof(Message);
483     Message.Header.u1.s1.DataLength = 0;
484 
485     /* Only LSASS can connect, so handle the connection right now */
486     Status = ZwListenPort(SeRmCommandPort, &Message.Header);
487     if (!NT_SUCCESS(Status))
488     {
489         DPRINT1("Security Rm Init: Listen to Command Port failed 0x%lx\n", Status);
490         goto Cleanup;
491     }
492 
493     /* Set the Port View structure length */
494     RemotePortView.Length = sizeof(RemotePortView);
495 
496     /* Accept the connection */
497     Status = ZwAcceptConnectPort(&SepRmCommandMessagePort,
498                                  NULL,
499                                  &Message.Header,
500                                  TRUE,
501                                  NULL,
502                                  &RemotePortView);
503     if (!NT_SUCCESS(Status))
504     {
505         DPRINT1("Security Rm Init: Accept Connect to Command Port failed 0x%lx\n", Status);
506         goto Cleanup;
507     }
508 
509     /* Complete the connection */
510     Status = ZwCompleteConnectPort(SepRmCommandMessagePort);
511     if (!NT_SUCCESS(Status))
512     {
513         DPRINT1("Security Rm Init: Complete Connect to Command Port failed 0x%lx\n", Status);
514         goto Cleanup;
515     }
516 
517     /* Create a section for messages */
518     SectionSize.QuadPart = PAGE_SIZE;
519     Status = ZwCreateSection(&SectionHandle,
520                              SECTION_ALL_ACCESS,
521                              NULL,
522                              &SectionSize,
523                              PAGE_READWRITE,
524                              SEC_COMMIT,
525                              NULL);
526     if (!NT_SUCCESS(Status))
527     {
528         DPRINT1("Security Rm Init: Create Memory Section for LSA port failed: %X\n", Status);
529         goto Cleanup;
530     }
531 
532     /* Setup the PORT_VIEW structure */
533     PortView.Length = sizeof(PortView);
534     PortView.SectionHandle = SectionHandle;
535     PortView.SectionOffset = 0;
536     PortView.ViewSize = SectionSize.LowPart;
537     PortView.ViewBase = NULL;
538     PortView.ViewRemoteBase = NULL;
539 
540     /* Setup security QOS */
541     SecurityQos.Length = sizeof(SecurityQos);
542     SecurityQos.ImpersonationLevel = SecurityImpersonation;
543     SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
544     SecurityQos.EffectiveOnly = TRUE;
545 
546     /* Connect to LSASS */
547     RtlInitUnicodeString(&PortName, L"\\SeLsaCommandPort");
548     Status = ZwConnectPort(&PortHandle,
549                            &PortName,
550                            &SecurityQos,
551                            &PortView,
552                            NULL,
553                            0,
554                            0,
555                            0);
556     if (!NT_SUCCESS(Status))
557     {
558         DPRINT1("Security Rm Init: Connect to LSA Port failed 0x%lx\n", Status);
559         goto Cleanup;
560     }
561 
562     /* Remember section base and view offset */
563     SepCommandPortViewBase = PortView.ViewBase;
564     SepCommandPortViewRemoteBase = PortView.ViewRemoteBase;
565     SepCommandPortViewBaseOffset = (ULONG_PTR)SepCommandPortViewRemoteBase -
566                                    (ULONG_PTR)SepCommandPortViewBase;
567 
568     DPRINT("SepRmCommandServerThreadInit: done\n");
569 
570 Cleanup:
571     /* Check for failure */
572     if (!NT_SUCCESS(Status))
573     {
574         if (PortHandle != NULL)
575         {
576             ObCloseHandle(PortHandle, KernelMode);
577         }
578 
579         Result = FALSE;
580     }
581 
582     /* Did we create a section? */
583     if (SectionHandle != NULL)
584     {
585         ObCloseHandle(SectionHandle, KernelMode);
586     }
587 
588     return Result;
589 }
590 
591 VOID
592 NTAPI
593 SepRmCommandServerThread(
594     PVOID StartContext)
595 {
596     SEP_RM_API_MESSAGE Message;
597     PPORT_MESSAGE ReplyMessage;
598     HANDLE DummyPortHandle;
599     NTSTATUS Status;
600 
601     /* Initialize the server thread */
602     if (!SepRmCommandServerThreadInit())
603     {
604         DPRINT1("Security: Terminating Rm Command Server Thread\n");
605         return;
606     }
607 
608     /* No reply yet */
609     ReplyMessage = NULL;
610 
611     /* Start looping */
612     while (TRUE)
613     {
614         /* Wait for a message */
615         Status = ZwReplyWaitReceivePort(SepRmCommandMessagePort,
616                                         NULL,
617                                         ReplyMessage,
618                                         &Message.Header);
619         if (!NT_SUCCESS(Status))
620         {
621             DPRINT1("Failed to get message: 0x%lx", Status);
622             ReplyMessage = NULL;
623             continue;
624         }
625 
626         /* Check if this is a connection request */
627         if (Message.Header.u2.s2.Type == LPC_CONNECTION_REQUEST)
628         {
629             /* Reject connection request */
630             ZwAcceptConnectPort(&DummyPortHandle,
631                                 NULL,
632                                 &Message.Header,
633                                 FALSE,
634                                 NULL,
635                                 NULL);
636 
637             /* Start over */
638             ReplyMessage = NULL;
639             continue;
640         }
641 
642         /* Check if the port died */
643         if ((Message.Header.u2.s2.Type == LPC_PORT_CLOSED) ||
644             (Message.Header.u2.s2.Type == LPC_CLIENT_DIED))
645         {
646             /* LSASS is dead, so let's quit as well */
647             break;
648         }
649 
650         /* Check if this is an actual request */
651         if (Message.Header.u2.s2.Type != LPC_REQUEST)
652         {
653             DPRINT1("SepRmCommandServerThread: unexpected message type: 0x%lx\n",
654                     Message.Header.u2.s2.Type);
655 
656             /* Restart without replying */
657             ReplyMessage = NULL;
658             continue;
659         }
660 
661         ReplyMessage = &Message.Header;
662 
663         switch (Message.ApiNumber)
664         {
665             case RmAuditSetCommand:
666                 Status = SepRmSetAuditEvent(&Message);
667                 break;
668 
669             case RmCreateLogonSession:
670                 Status = SepRmCreateLogonSession(&Message.u.LogonLuid);
671                 break;
672 
673             case RmDeleteLogonSession:
674                 Status = SepRmDeleteLogonSession(&Message.u.LogonLuid);
675                 break;
676 
677             default:
678                 DPRINT1("SepRmDispatchRequest: invalid API number: 0x%lx\n",
679                         Message.ApiNumber);
680                 ReplyMessage = NULL;
681         }
682 
683         Message.u.ResultStatus = Status;
684     }
685 
686     /* Close the port handles */
687     ObCloseHandle(SepRmCommandMessagePort, KernelMode);
688     ObCloseHandle(SeRmCommandPort, KernelMode);
689 }
690 
691 
692 /* PUBLIC FUNCTIONS ***********************************************************/
693 
694 /*
695  * @unimplemented
696  */
697 NTSTATUS
698 NTAPI
699 SeMarkLogonSessionForTerminationNotification(
700     IN PLUID LogonId)
701 {
702     UNIMPLEMENTED;
703     return STATUS_NOT_IMPLEMENTED;
704 }
705 
706 
707 /*
708  * @unimplemented
709  */
710 NTSTATUS
711 NTAPI
712 SeRegisterLogonSessionTerminatedRoutine(
713     IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine)
714 {
715     UNIMPLEMENTED;
716     return STATUS_NOT_IMPLEMENTED;
717 }
718 
719 
720 /*
721  * @unimplemented
722  */
723 NTSTATUS
724 NTAPI
725 SeUnregisterLogonSessionTerminatedRoutine(
726     IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine)
727 {
728     UNIMPLEMENTED;
729     return STATUS_NOT_IMPLEMENTED;
730 }
731