Synchronize with trunk.
[reactos.git] / ntoskrnl / se / priv.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/se/priv.c
5 * PURPOSE: Security manager
6 *
7 * PROGRAMMERS: No programmer listed.
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, SepInitPrivileges)
18 #endif
19
20 /* GLOBALS ********************************************************************/
21
22 LUID SeCreateTokenPrivilege;
23 LUID SeAssignPrimaryTokenPrivilege;
24 LUID SeLockMemoryPrivilege;
25 LUID SeIncreaseQuotaPrivilege;
26 LUID SeUnsolicitedInputPrivilege;
27 LUID SeTcbPrivilege;
28 LUID SeSecurityPrivilege;
29 LUID SeTakeOwnershipPrivilege;
30 LUID SeLoadDriverPrivilege;
31 LUID SeCreatePagefilePrivilege;
32 LUID SeIncreaseBasePriorityPrivilege;
33 LUID SeSystemProfilePrivilege;
34 LUID SeSystemtimePrivilege;
35 LUID SeProfileSingleProcessPrivilege;
36 LUID SeCreatePermanentPrivilege;
37 LUID SeBackupPrivilege;
38 LUID SeRestorePrivilege;
39 LUID SeShutdownPrivilege;
40 LUID SeDebugPrivilege;
41 LUID SeAuditPrivilege;
42 LUID SeSystemEnvironmentPrivilege;
43 LUID SeChangeNotifyPrivilege;
44 LUID SeRemoteShutdownPrivilege;
45 LUID SeUndockPrivilege;
46 LUID SeSyncAgentPrivilege;
47 LUID SeEnableDelegationPrivilege;
48
49 /* PRIVATE FUNCTIONS **********************************************************/
50
51 VOID
52 INIT_FUNCTION
53 NTAPI
54 SepInitPrivileges(VOID)
55 {
56 SeCreateTokenPrivilege.LowPart = SE_CREATE_TOKEN_PRIVILEGE;
57 SeCreateTokenPrivilege.HighPart = 0;
58 SeAssignPrimaryTokenPrivilege.LowPart = SE_ASSIGNPRIMARYTOKEN_PRIVILEGE;
59 SeAssignPrimaryTokenPrivilege.HighPart = 0;
60 SeLockMemoryPrivilege.LowPart = SE_LOCK_MEMORY_PRIVILEGE;
61 SeLockMemoryPrivilege.HighPart = 0;
62 SeIncreaseQuotaPrivilege.LowPart = SE_INCREASE_QUOTA_PRIVILEGE;
63 SeIncreaseQuotaPrivilege.HighPart = 0;
64 SeUnsolicitedInputPrivilege.LowPart = SE_UNSOLICITED_INPUT_PRIVILEGE;
65 SeUnsolicitedInputPrivilege.HighPart = 0;
66 SeTcbPrivilege.LowPart = SE_TCB_PRIVILEGE;
67 SeTcbPrivilege.HighPart = 0;
68 SeSecurityPrivilege.LowPart = SE_SECURITY_PRIVILEGE;
69 SeSecurityPrivilege.HighPart = 0;
70 SeTakeOwnershipPrivilege.LowPart = SE_TAKE_OWNERSHIP_PRIVILEGE;
71 SeTakeOwnershipPrivilege.HighPart = 0;
72 SeLoadDriverPrivilege.LowPart = SE_LOAD_DRIVER_PRIVILEGE;
73 SeLoadDriverPrivilege.HighPart = 0;
74 SeSystemProfilePrivilege.LowPart = SE_SYSTEM_PROFILE_PRIVILEGE;
75 SeSystemProfilePrivilege.HighPart = 0;
76 SeSystemtimePrivilege.LowPart = SE_SYSTEMTIME_PRIVILEGE;
77 SeSystemtimePrivilege.HighPart = 0;
78 SeProfileSingleProcessPrivilege.LowPart = SE_PROF_SINGLE_PROCESS_PRIVILEGE;
79 SeProfileSingleProcessPrivilege.HighPart = 0;
80 SeIncreaseBasePriorityPrivilege.LowPart = SE_INC_BASE_PRIORITY_PRIVILEGE;
81 SeIncreaseBasePriorityPrivilege.HighPart = 0;
82 SeCreatePagefilePrivilege.LowPart = SE_CREATE_PAGEFILE_PRIVILEGE;
83 SeCreatePagefilePrivilege.HighPart = 0;
84 SeCreatePermanentPrivilege.LowPart = SE_CREATE_PERMANENT_PRIVILEGE;
85 SeCreatePermanentPrivilege.HighPart = 0;
86 SeBackupPrivilege.LowPart = SE_BACKUP_PRIVILEGE;
87 SeBackupPrivilege.HighPart = 0;
88 SeRestorePrivilege.LowPart = SE_RESTORE_PRIVILEGE;
89 SeRestorePrivilege.HighPart = 0;
90 SeShutdownPrivilege.LowPart = SE_SHUTDOWN_PRIVILEGE;
91 SeShutdownPrivilege.HighPart = 0;
92 SeDebugPrivilege.LowPart = SE_DEBUG_PRIVILEGE;
93 SeDebugPrivilege.HighPart = 0;
94 SeAuditPrivilege.LowPart = SE_AUDIT_PRIVILEGE;
95 SeAuditPrivilege.HighPart = 0;
96 SeSystemEnvironmentPrivilege.LowPart = SE_SYSTEM_ENVIRONMENT_PRIVILEGE;
97 SeSystemEnvironmentPrivilege.HighPart = 0;
98 SeChangeNotifyPrivilege.LowPart = SE_CHANGE_NOTIFY_PRIVILEGE;
99 SeChangeNotifyPrivilege.HighPart = 0;
100 SeRemoteShutdownPrivilege.LowPart = SE_REMOTE_SHUTDOWN_PRIVILEGE;
101 SeRemoteShutdownPrivilege.HighPart = 0;
102 SeUndockPrivilege.LowPart = SE_UNDOCK_PRIVILEGE;
103 SeUndockPrivilege.HighPart = 0;
104 SeSyncAgentPrivilege.LowPart = SE_SYNC_AGENT_PRIVILEGE;
105 SeSyncAgentPrivilege.HighPart = 0;
106 SeEnableDelegationPrivilege.LowPart = SE_ENABLE_DELEGATION_PRIVILEGE;
107 SeEnableDelegationPrivilege.HighPart = 0;
108 }
109
110
111 BOOLEAN
112 NTAPI
113 SepPrivilegeCheck(PTOKEN Token,
114 PLUID_AND_ATTRIBUTES Privileges,
115 ULONG PrivilegeCount,
116 ULONG PrivilegeControl,
117 KPROCESSOR_MODE PreviousMode)
118 {
119 ULONG i;
120 ULONG j;
121 ULONG Required;
122
123 DPRINT("SepPrivilegeCheck() called\n");
124
125 PAGED_CODE();
126
127 if (PreviousMode == KernelMode)
128 return TRUE;
129
130 /* Get the number of privileges that are required to match */
131 Required = (PrivilegeControl & PRIVILEGE_SET_ALL_NECESSARY) ? PrivilegeCount : 1;
132
133 /* Loop all requested privileges until we found the required ones */
134 for (i = 0; i < PrivilegeCount && Required > 0; i++)
135 {
136 /* Loop the privileges of the token */
137 for (j = 0; j < Token->PrivilegeCount; j++)
138 {
139 /* Check if the LUIDs match */
140 if (Token->Privileges[j].Luid.LowPart == Privileges[i].Luid.LowPart &&
141 Token->Privileges[j].Luid.HighPart == Privileges[i].Luid.HighPart)
142 {
143 DPRINT("Found privilege. Attributes: %lx\n",
144 Token->Privileges[j].Attributes);
145
146 /* Check if the privilege is enabled */
147 if (Token->Privileges[j].Attributes & SE_PRIVILEGE_ENABLED)
148 {
149 Privileges[i].Attributes |= SE_PRIVILEGE_USED_FOR_ACCESS;
150 Required--;
151 }
152
153 /* Leave the inner loop */
154 break;
155 }
156 }
157 }
158
159 /* Return whether we found all required privileges */
160 return (Required == 0);
161 }
162
163 NTSTATUS
164 NTAPI
165 SeCaptureLuidAndAttributesArray(PLUID_AND_ATTRIBUTES Src,
166 ULONG PrivilegeCount,
167 KPROCESSOR_MODE PreviousMode,
168 PLUID_AND_ATTRIBUTES AllocatedMem,
169 ULONG AllocatedLength,
170 POOL_TYPE PoolType,
171 BOOLEAN CaptureIfKernel,
172 PLUID_AND_ATTRIBUTES *Dest,
173 PULONG Length)
174 {
175 ULONG BufferSize;
176 NTSTATUS Status = STATUS_SUCCESS;
177
178 PAGED_CODE();
179
180 if (PrivilegeCount == 0)
181 {
182 *Dest = 0;
183 *Length = 0;
184 return STATUS_SUCCESS;
185 }
186
187 if (PreviousMode == KernelMode && !CaptureIfKernel)
188 {
189 *Dest = Src;
190 return STATUS_SUCCESS;
191 }
192
193 /* FIXME - check PrivilegeCount for a valid number so we don't
194 cause an integer overflow or exhaust system resources! */
195
196 BufferSize = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
197 *Length = ROUND_UP(BufferSize, 4); /* round up to a 4 byte alignment */
198
199 /* probe the buffer */
200 if (PreviousMode != KernelMode)
201 {
202 _SEH2_TRY
203 {
204 ProbeForRead(Src,
205 BufferSize,
206 sizeof(ULONG));
207 }
208 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
209 {
210 /* Return the exception code */
211 _SEH2_YIELD(return _SEH2_GetExceptionCode());
212 }
213 _SEH2_END;
214 }
215
216 /* allocate enough memory or check if the provided buffer is
217 large enough to hold the array */
218 if (AllocatedMem != NULL)
219 {
220 if (AllocatedLength < BufferSize)
221 {
222 return STATUS_BUFFER_TOO_SMALL;
223 }
224
225 *Dest = AllocatedMem;
226 }
227 else
228 {
229 *Dest = ExAllocatePool(PoolType,
230 BufferSize);
231 if (*Dest == NULL)
232 {
233 return STATUS_INSUFFICIENT_RESOURCES;
234 }
235 }
236
237 /* copy the array to the buffer */
238 _SEH2_TRY
239 {
240 RtlCopyMemory(*Dest,
241 Src,
242 BufferSize);
243 }
244 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
245 {
246 Status = _SEH2_GetExceptionCode();
247 }
248 _SEH2_END;
249
250 if (!NT_SUCCESS(Status) && AllocatedMem == NULL)
251 {
252 ExFreePool(*Dest);
253 }
254
255 return Status;
256 }
257
258 VOID
259 NTAPI
260 SeReleaseLuidAndAttributesArray(PLUID_AND_ATTRIBUTES Privilege,
261 KPROCESSOR_MODE PreviousMode,
262 BOOLEAN CaptureIfKernel)
263 {
264 PAGED_CODE();
265
266 if (Privilege != NULL &&
267 (PreviousMode != KernelMode || CaptureIfKernel))
268 {
269 ExFreePool(Privilege);
270 }
271 }
272
273 /* PUBLIC FUNCTIONS ***********************************************************/
274
275 /*
276 * @implemented
277 */
278 NTSTATUS
279 NTAPI
280 SeAppendPrivileges(IN OUT PACCESS_STATE AccessState,
281 IN PPRIVILEGE_SET Privileges)
282 {
283 PAUX_ACCESS_DATA AuxData;
284 ULONG OldPrivilegeSetSize;
285 ULONG NewPrivilegeSetSize;
286 PPRIVILEGE_SET PrivilegeSet;
287
288 PAGED_CODE();
289
290 /* Get the Auxiliary Data */
291 AuxData = AccessState->AuxData;
292
293 /* Calculate the size of the old privilege set */
294 OldPrivilegeSetSize = sizeof(PRIVILEGE_SET) +
295 (AuxData->PrivilegeSet->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES);
296
297 if (AuxData->PrivilegeSet->PrivilegeCount +
298 Privileges->PrivilegeCount > INITIAL_PRIVILEGE_COUNT)
299 {
300 /* Calculate the size of the new privilege set */
301 NewPrivilegeSetSize = OldPrivilegeSetSize +
302 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
303
304 /* Allocate a new privilege set */
305 PrivilegeSet = ExAllocatePool(PagedPool, NewPrivilegeSetSize);
306 if (PrivilegeSet == NULL)
307 return STATUS_INSUFFICIENT_RESOURCES;
308
309 /* Copy original privileges from the acess state */
310 RtlCopyMemory(PrivilegeSet,
311 AuxData->PrivilegeSet,
312 OldPrivilegeSetSize);
313
314 /* Append privileges from the privilege set*/
315 RtlCopyMemory((PVOID)((ULONG_PTR)PrivilegeSet + OldPrivilegeSetSize),
316 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)),
317 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
318
319 /* Adjust the number of privileges in the new privilege set */
320 PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount;
321
322 /* Free the old privilege set if it was allocated */
323 if (AccessState->PrivilegesAllocated == TRUE)
324 ExFreePool(AuxData->PrivilegeSet);
325
326 /* Now we are using an allocated privilege set */
327 AccessState->PrivilegesAllocated = TRUE;
328
329 /* Assign the new privileges to the access state */
330 AuxData->PrivilegeSet = PrivilegeSet;
331 }
332 else
333 {
334 /* Append privileges */
335 RtlCopyMemory((PVOID)((ULONG_PTR)AuxData->PrivilegeSet + OldPrivilegeSetSize),
336 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)),
337 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
338
339 /* Adjust the number of privileges in the target privilege set */
340 AuxData->PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount;
341 }
342
343 return STATUS_SUCCESS;
344 }
345
346 /*
347 * @implemented
348 */
349 VOID
350 NTAPI
351 SeFreePrivileges(IN PPRIVILEGE_SET Privileges)
352 {
353 PAGED_CODE();
354 ExFreePool(Privileges);
355 }
356
357 /*
358 * @implemented
359 */
360 BOOLEAN
361 NTAPI
362 SePrivilegeCheck(PPRIVILEGE_SET Privileges,
363 PSECURITY_SUBJECT_CONTEXT SubjectContext,
364 KPROCESSOR_MODE PreviousMode)
365 {
366 PACCESS_TOKEN Token = NULL;
367
368 PAGED_CODE();
369
370 if (SubjectContext->ClientToken == NULL)
371 {
372 Token = SubjectContext->PrimaryToken;
373 }
374 else
375 {
376 Token = SubjectContext->ClientToken;
377 if (SubjectContext->ImpersonationLevel < 2)
378 {
379 return FALSE;
380 }
381 }
382
383 return SepPrivilegeCheck(Token,
384 Privileges->Privilege,
385 Privileges->PrivilegeCount,
386 Privileges->Control,
387 PreviousMode);
388 }
389
390 /*
391 * @implemented
392 */
393 BOOLEAN
394 NTAPI
395 SeSinglePrivilegeCheck(IN LUID PrivilegeValue,
396 IN KPROCESSOR_MODE PreviousMode)
397 {
398 SECURITY_SUBJECT_CONTEXT SubjectContext;
399 PRIVILEGE_SET Priv;
400 BOOLEAN Result;
401
402 PAGED_CODE();
403
404 SeCaptureSubjectContext(&SubjectContext);
405
406 Priv.PrivilegeCount = 1;
407 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
408 Priv.Privilege[0].Luid = PrivilegeValue;
409 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
410
411 Result = SePrivilegeCheck(&Priv,
412 &SubjectContext,
413 PreviousMode);
414
415 if (PreviousMode != KernelMode)
416 {
417 #if 0
418 SePrivilegedServiceAuditAlarm(0,
419 &SubjectContext,
420 &PrivilegeValue);
421 #endif
422 }
423
424 SeReleaseSubjectContext(&SubjectContext);
425
426 return Result;
427 }
428
429 BOOLEAN
430 NTAPI
431 SeCheckPrivilegedObject(IN LUID PrivilegeValue,
432 IN HANDLE ObjectHandle,
433 IN ACCESS_MASK DesiredAccess,
434 IN KPROCESSOR_MODE PreviousMode)
435 {
436 SECURITY_SUBJECT_CONTEXT SubjectContext;
437 PRIVILEGE_SET Priv;
438 BOOLEAN Result;
439
440 PAGED_CODE();
441
442 SeCaptureSubjectContext(&SubjectContext);
443
444 Priv.PrivilegeCount = 1;
445 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
446 Priv.Privilege[0].Luid = PrivilegeValue;
447 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
448
449 Result = SePrivilegeCheck(&Priv, &SubjectContext, PreviousMode);
450 if (PreviousMode != KernelMode)
451 {
452 #if 0
453 SePrivilegeObjectAuditAlarm(ObjectHandle,
454 &SubjectContext,
455 DesiredAccess,
456 &PrivilegeValue,
457 Result,
458 PreviousMode);
459 #endif
460 }
461
462 SeReleaseSubjectContext(&SubjectContext);
463
464 return Result;
465 }
466
467 /* SYSTEM CALLS ***************************************************************/
468
469 NTSTATUS
470 NTAPI
471 NtPrivilegeCheck(IN HANDLE ClientToken,
472 IN PPRIVILEGE_SET RequiredPrivileges,
473 OUT PBOOLEAN Result)
474 {
475 PLUID_AND_ATTRIBUTES Privileges;
476 PTOKEN Token;
477 ULONG PrivilegeCount = 0;
478 ULONG PrivilegeControl = 0;
479 ULONG Length;
480 BOOLEAN CheckResult;
481 KPROCESSOR_MODE PreviousMode;
482 NTSTATUS Status;
483
484 PAGED_CODE();
485
486 PreviousMode = KeGetPreviousMode();
487
488 /* probe the buffers */
489 if (PreviousMode != KernelMode)
490 {
491 _SEH2_TRY
492 {
493 ProbeForWrite(RequiredPrivileges,
494 FIELD_OFFSET(PRIVILEGE_SET,
495 Privilege),
496 sizeof(ULONG));
497
498 PrivilegeCount = RequiredPrivileges->PrivilegeCount;
499 PrivilegeControl = RequiredPrivileges->Control;
500
501 /* Check PrivilegeCount to avoid an integer overflow! */
502 if (FIELD_OFFSET(PRIVILEGE_SET,
503 Privilege[PrivilegeCount]) /
504 sizeof(RequiredPrivileges->Privilege[0]) != PrivilegeCount)
505 {
506 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
507 }
508
509 /* probe all of the array */
510 ProbeForWrite(RequiredPrivileges,
511 FIELD_OFFSET(PRIVILEGE_SET,
512 Privilege[PrivilegeCount]),
513 sizeof(ULONG));
514
515 ProbeForWriteBoolean(Result);
516 }
517 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
518 {
519 /* Return the exception code */
520 _SEH2_YIELD(return _SEH2_GetExceptionCode());
521 }
522 _SEH2_END;
523 }
524 else
525 {
526 PrivilegeCount = RequiredPrivileges->PrivilegeCount;
527 PrivilegeControl = RequiredPrivileges->Control;
528 }
529
530 /* reference the token and make sure we're
531 not doing an anonymous impersonation */
532 Status = ObReferenceObjectByHandle(ClientToken,
533 TOKEN_QUERY,
534 SeTokenObjectType,
535 PreviousMode,
536 (PVOID*)&Token,
537 NULL);
538 if (!NT_SUCCESS(Status))
539 {
540 return Status;
541 }
542
543 if (Token->TokenType == TokenImpersonation &&
544 Token->ImpersonationLevel < SecurityIdentification)
545 {
546 ObDereferenceObject(Token);
547 return STATUS_BAD_IMPERSONATION_LEVEL;
548 }
549
550 /* capture the privileges */
551 Status = SeCaptureLuidAndAttributesArray(RequiredPrivileges->Privilege,
552 PrivilegeCount,
553 PreviousMode,
554 NULL,
555 0,
556 PagedPool,
557 TRUE,
558 &Privileges,
559 &Length);
560 if (!NT_SUCCESS(Status))
561 {
562 ObDereferenceObject (Token);
563 return Status;
564 }
565
566 CheckResult = SepPrivilegeCheck(Token,
567 Privileges,
568 PrivilegeCount,
569 PrivilegeControl,
570 PreviousMode);
571
572 ObDereferenceObject(Token);
573
574 /* return the array */
575 _SEH2_TRY
576 {
577 RtlCopyMemory(RequiredPrivileges->Privilege,
578 Privileges,
579 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
580 *Result = CheckResult;
581 Status = STATUS_SUCCESS;
582 }
583 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
584 {
585 Status = _SEH2_GetExceptionCode();
586 }
587 _SEH2_END;
588
589 SeReleaseLuidAndAttributesArray(Privileges,
590 PreviousMode,
591 TRUE);
592
593 return Status;
594 }
595
596 /* EOF */