66761d874bfa6816c8c59906ad508977e1d5218f
[reactos.git] / ntoskrnl / se / srm.c
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 }