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