[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / ntoskrnl / se / priv.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Security privileges support
5 * COPYRIGHT: Copyright Alex Ionescu <alex@relsoft.net>
6 * Copyright Timo Kreuzer <timo.kreuzer@reactos.org>
7 * Copyright Eric Kohl
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
18 #define CONST_LUID(x1, x2) {x1, x2}
19 const LUID SeCreateTokenPrivilege = CONST_LUID(SE_CREATE_TOKEN_PRIVILEGE, 0);
20 const LUID SeAssignPrimaryTokenPrivilege = CONST_LUID(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, 0);
21 const LUID SeLockMemoryPrivilege = CONST_LUID(SE_LOCK_MEMORY_PRIVILEGE, 0);
22 const LUID SeIncreaseQuotaPrivilege = CONST_LUID(SE_INCREASE_QUOTA_PRIVILEGE, 0);
23 const LUID SeUnsolicitedInputPrivilege = CONST_LUID(6, 0);
24 const LUID SeTcbPrivilege = CONST_LUID(SE_TCB_PRIVILEGE, 0);
25 const LUID SeSecurityPrivilege = CONST_LUID(SE_SECURITY_PRIVILEGE, 0);
26 const LUID SeTakeOwnershipPrivilege = CONST_LUID(SE_TAKE_OWNERSHIP_PRIVILEGE, 0);
27 const LUID SeLoadDriverPrivilege = CONST_LUID(SE_LOAD_DRIVER_PRIVILEGE, 0);
28 const LUID SeSystemProfilePrivilege = CONST_LUID(SE_SYSTEM_PROFILE_PRIVILEGE, 0);
29 const LUID SeSystemtimePrivilege = CONST_LUID(SE_SYSTEMTIME_PRIVILEGE, 0);
30 const LUID SeProfileSingleProcessPrivilege = CONST_LUID(SE_PROF_SINGLE_PROCESS_PRIVILEGE, 0);
31 const LUID SeIncreaseBasePriorityPrivilege = CONST_LUID(SE_INC_BASE_PRIORITY_PRIVILEGE, 0);
32 const LUID SeCreatePagefilePrivilege = CONST_LUID(SE_CREATE_PAGEFILE_PRIVILEGE, 0);
33 const LUID SeCreatePermanentPrivilege = CONST_LUID(SE_CREATE_PERMANENT_PRIVILEGE, 0);
34 const LUID SeBackupPrivilege = CONST_LUID(SE_BACKUP_PRIVILEGE, 0);
35 const LUID SeRestorePrivilege = CONST_LUID(SE_RESTORE_PRIVILEGE, 0);
36 const LUID SeShutdownPrivilege = CONST_LUID(SE_SHUTDOWN_PRIVILEGE, 0);
37 const LUID SeDebugPrivilege = CONST_LUID(SE_DEBUG_PRIVILEGE, 0);
38 const LUID SeAuditPrivilege = CONST_LUID(SE_AUDIT_PRIVILEGE, 0);
39 const LUID SeSystemEnvironmentPrivilege = CONST_LUID(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, 0);
40 const LUID SeChangeNotifyPrivilege = CONST_LUID(SE_CHANGE_NOTIFY_PRIVILEGE, 0);
41 const LUID SeRemoteShutdownPrivilege = CONST_LUID(SE_REMOTE_SHUTDOWN_PRIVILEGE, 0);
42 const LUID SeUndockPrivilege = CONST_LUID(SE_UNDOCK_PRIVILEGE, 0);
43 const LUID SeSyncAgentPrivilege = CONST_LUID(SE_SYNC_AGENT_PRIVILEGE, 0);
44 const LUID SeEnableDelegationPrivilege = CONST_LUID(SE_ENABLE_DELEGATION_PRIVILEGE, 0);
45 const LUID SeManageVolumePrivilege = CONST_LUID(SE_MANAGE_VOLUME_PRIVILEGE, 0);
46 const LUID SeImpersonatePrivilege = CONST_LUID(SE_IMPERSONATE_PRIVILEGE, 0);
47 const LUID SeCreateGlobalPrivilege = CONST_LUID(SE_CREATE_GLOBAL_PRIVILEGE, 0);
48 const LUID SeTrustedCredmanPrivilege = CONST_LUID(SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE, 0);
49 const LUID SeRelabelPrivilege = CONST_LUID(SE_RELABEL_PRIVILEGE, 0);
50 const LUID SeIncreaseWorkingSetPrivilege = CONST_LUID(SE_INC_WORKING_SET_PRIVILEGE, 0);
51 const LUID SeTimeZonePrivilege = CONST_LUID(SE_TIME_ZONE_PRIVILEGE, 0);
52 const LUID SeCreateSymbolicLinkPrivilege = CONST_LUID(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE, 0);
53
54
55 /* PRIVATE FUNCTIONS **********************************************************/
56
57 /**
58 * @brief
59 * Initializes the privileges during the startup phase of the security
60 * manager module. This function serves as a placeholder as it currently
61 * does nothing.
62 *
63 * @return
64 * Nothing.
65 */
66 CODE_SEG("INIT")
67 VOID
68 NTAPI
69 SepInitPrivileges(VOID)
70 {
71
72 }
73
74 /**
75 * @brief
76 * Checks the privileges pointed by Privileges array argument if they exist and
77 * match with the privileges from an access token.
78 *
79 * @param[in] Token
80 * An access token where privileges are to be checked.
81 *
82 * @param[in] Privileges
83 * An array of privileges with attributes used as checking indicator for
84 * the function.
85 *
86 * @param[in] PrivilegeCount
87 * The total number count of privileges in the array.
88 *
89 * @param[in] PrivilegeControl
90 * Privilege control bit mask to determine if we should check all the
91 * privileges based on the number count of privileges or not.
92 *
93 * @param[in] PreviousMode
94 * Processor level access mode.
95 *
96 * @return
97 * Returns TRUE if the required privileges exist and that they do match.
98 * Otherwise the functions returns FALSE.
99 */
100 BOOLEAN
101 NTAPI
102 SepPrivilegeCheck(
103 _In_ PTOKEN Token,
104 _In_ PLUID_AND_ATTRIBUTES Privileges,
105 _In_ ULONG PrivilegeCount,
106 _In_ ULONG PrivilegeControl,
107 _In_ KPROCESSOR_MODE PreviousMode)
108 {
109 ULONG i;
110 ULONG j;
111 ULONG Required;
112
113 DPRINT("SepPrivilegeCheck() called\n");
114
115 PAGED_CODE();
116
117 if (PreviousMode == KernelMode)
118 return TRUE;
119
120 /* Get the number of privileges that are required to match */
121 Required = (PrivilegeControl & PRIVILEGE_SET_ALL_NECESSARY) ? PrivilegeCount : 1;
122
123 /* Acquire a shared token lock */
124 SepAcquireTokenLockShared(Token);
125
126 /* Loop all requested privileges until we found the required ones */
127 for (i = 0; i < PrivilegeCount; i++)
128 {
129 /* Loop the privileges of the token */
130 for (j = 0; j < Token->PrivilegeCount; j++)
131 {
132 /* Check if the LUIDs match */
133 if (RtlEqualLuid(&Token->Privileges[j].Luid, &Privileges[i].Luid))
134 {
135 DPRINT("Found privilege. Attributes: %lx\n",
136 Token->Privileges[j].Attributes);
137
138 /* Check if the privilege is enabled */
139 if (Token->Privileges[j].Attributes & SE_PRIVILEGE_ENABLED)
140 {
141 Privileges[i].Attributes |= SE_PRIVILEGE_USED_FOR_ACCESS;
142 Required--;
143
144 /* Check if we have found all privileges */
145 if (Required == 0)
146 {
147 /* We're done! */
148 SepReleaseTokenLock(Token);
149 return TRUE;
150 }
151 }
152
153 /* Leave the inner loop */
154 break;
155 }
156 }
157 }
158
159 /* Release the token lock */
160 SepReleaseTokenLock(Token);
161
162 /* When we reached this point, we did not find all privileges */
163 ASSERT(Required > 0);
164 return FALSE;
165 }
166
167 /**
168 * @brief
169 * Checks only single privilege based upon the privilege pointed by a LUID and
170 * if it matches with the one from an access token.
171 *
172 * @param[in] PrivilegeValue
173 * The privilege to be checked.
174 *
175 * @param[in] Token
176 * An access token where its privilege is to be checked against the one
177 * provided by the caller.
178 *
179 * @param[in] PreviousMode
180 * Processor level access mode.
181 *
182 * @return
183 * Returns TRUE if the required privilege exists and that it matches
184 * with the one from the access token, FALSE otherwise.
185 */
186 BOOLEAN
187 NTAPI
188 SepSinglePrivilegeCheck(
189 _In_ LUID PrivilegeValue,
190 _In_ PTOKEN Token,
191 _In_ KPROCESSOR_MODE PreviousMode)
192 {
193 LUID_AND_ATTRIBUTES Privilege;
194 PAGED_CODE();
195 ASSERT(!RtlEqualLuid(&PrivilegeValue, &SeTcbPrivilege));
196
197 Privilege.Luid = PrivilegeValue;
198 Privilege.Attributes = SE_PRIVILEGE_ENABLED;
199 return SepPrivilegeCheck(Token,
200 &Privilege,
201 1,
202 PRIVILEGE_SET_ALL_NECESSARY,
203 PreviousMode);
204 }
205
206 /**
207 * @brief
208 * Checks the security policy and returns a set of privileges
209 * based upon the said security policy context.
210 *
211 * @param[in,out] DesiredAccess
212 * The desired access right mask.
213 *
214 * @param[in,out] GrantedAccess
215 * The granted access rights masks. The rights are granted depending
216 * on the desired access rights requested by the calling thread.
217 *
218 * @param[in] SubjectContext
219 * Security subject context. If the caller supplies one, the access token
220 * supplied by the caller will be assigned to one of client or primary tokens
221 * of the subject context in question.
222 *
223 * @param[in] Token
224 * An access token.
225 *
226 * @param[out] OutPrivilegeSet
227 * An array set of privileges to be reported to the caller, if the actual
228 * calling thread wants such set of privileges in the first place.
229 *
230 * @param[in] PreviousMode
231 * Processor level access mode.
232 *
233 * @return
234 * Returns STATUS_PRIVILEGE_NOT_HELD if the respective operations have succeeded
235 * without problems. STATUS_PRIVILEGE_NOT_HELD is returned if the access token
236 * doesn't have SeSecurityPrivilege privilege to warrant ACCESS_SYSTEM_SECURITY
237 * access right. STATUS_INSUFFICIENT_RESOURCES is returned if we failed
238 * to allocate block of memory pool for the array set of privileges.
239 */
240 NTSTATUS
241 NTAPI
242 SePrivilegePolicyCheck(
243 _Inout_ PACCESS_MASK DesiredAccess,
244 _Inout_ PACCESS_MASK GrantedAccess,
245 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
246 _In_ PTOKEN Token,
247 _Out_opt_ PPRIVILEGE_SET *OutPrivilegeSet,
248 _In_ KPROCESSOR_MODE PreviousMode)
249 {
250 SIZE_T PrivilegeSize;
251 PPRIVILEGE_SET PrivilegeSet;
252 ULONG PrivilegeCount = 0, Index = 0;
253 ACCESS_MASK AccessMask = 0;
254 PAGED_CODE();
255
256 /* Check if we have a security subject context */
257 if (SubjectContext != NULL)
258 {
259 /* Check if there is a client impersonation token */
260 if (SubjectContext->ClientToken != NULL)
261 Token = SubjectContext->ClientToken;
262 else
263 Token = SubjectContext->PrimaryToken;
264 }
265
266 /* Check if the caller wants ACCESS_SYSTEM_SECURITY access */
267 if (*DesiredAccess & ACCESS_SYSTEM_SECURITY)
268 {
269 /* Do the privilege check */
270 if (SepSinglePrivilegeCheck(SeSecurityPrivilege, Token, PreviousMode))
271 {
272 /* Remember this access flag */
273 AccessMask |= ACCESS_SYSTEM_SECURITY;
274 PrivilegeCount++;
275 }
276 else
277 {
278 return STATUS_PRIVILEGE_NOT_HELD;
279 }
280 }
281
282 /* Check if the caller wants WRITE_OWNER access */
283 if (*DesiredAccess & WRITE_OWNER)
284 {
285 /* Do the privilege check */
286 if (SepSinglePrivilegeCheck(SeTakeOwnershipPrivilege, Token, PreviousMode))
287 {
288 /* Remember this access flag */
289 AccessMask |= WRITE_OWNER;
290 PrivilegeCount++;
291 }
292 }
293
294 /* Update the access masks */
295 *GrantedAccess |= AccessMask;
296 *DesiredAccess &= ~AccessMask;
297
298 /* Does the caller want a privilege set? */
299 if (OutPrivilegeSet != NULL)
300 {
301 /* Do we have any privileges to report? */
302 if (PrivilegeCount > 0)
303 {
304 /* Calculate size and allocate the structure */
305 PrivilegeSize = FIELD_OFFSET(PRIVILEGE_SET, Privilege[PrivilegeCount]);
306 PrivilegeSet = ExAllocatePoolWithTag(PagedPool, PrivilegeSize, TAG_PRIVILEGE_SET);
307 *OutPrivilegeSet = PrivilegeSet;
308 if (PrivilegeSet == NULL)
309 {
310 return STATUS_INSUFFICIENT_RESOURCES;
311 }
312
313 PrivilegeSet->PrivilegeCount = PrivilegeCount;
314 PrivilegeSet->Control = 0;
315
316 if (AccessMask & WRITE_OWNER)
317 {
318 PrivilegeSet->Privilege[Index].Luid = SeTakeOwnershipPrivilege;
319 PrivilegeSet->Privilege[Index].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
320 Index++;
321 }
322
323 if (AccessMask & ACCESS_SYSTEM_SECURITY)
324 {
325 PrivilegeSet->Privilege[Index].Luid = SeSecurityPrivilege;
326 PrivilegeSet->Privilege[Index].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
327 }
328 }
329 else
330 {
331 /* No privileges, no structure */
332 *OutPrivilegeSet = NULL;
333 }
334 }
335
336 return STATUS_SUCCESS;
337 }
338
339 /**
340 * @brief
341 * Checks a single privilege and performs an audit
342 * against a privileged service based on a security subject
343 * context.
344 *
345 * @param[in] DesiredAccess
346 * Security subject context used for privileged service
347 * auditing.
348 *
349 * @param[in] PreviousMode
350 * Processor level access mode.
351 *
352 * @return
353 * Returns TRUE if service auditing and privilege checking
354 * tests have succeeded, FALSE otherwise.
355 */
356 BOOLEAN
357 NTAPI
358 SeCheckAuditPrivilege(
359 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
360 _In_ KPROCESSOR_MODE PreviousMode)
361 {
362 PRIVILEGE_SET PrivilegeSet;
363 BOOLEAN Result;
364 PAGED_CODE();
365
366 /* Initialize the privilege set with the single privilege */
367 PrivilegeSet.PrivilegeCount = 1;
368 PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
369 PrivilegeSet.Privilege[0].Luid = SeAuditPrivilege;
370 PrivilegeSet.Privilege[0].Attributes = 0;
371
372 /* Check against the primary token! */
373 Result = SepPrivilegeCheck(SubjectContext->PrimaryToken,
374 &PrivilegeSet.Privilege[0],
375 1,
376 PRIVILEGE_SET_ALL_NECESSARY,
377 PreviousMode);
378
379 if (PreviousMode != KernelMode)
380 {
381 SePrivilegedServiceAuditAlarm(NULL,
382 SubjectContext,
383 &PrivilegeSet,
384 Result);
385 }
386
387 return Result;
388 }
389
390 /**
391 * @brief
392 * Captures a LUID with attributes structure. This function is mainly
393 * tied in the context of privileges.
394 *
395 * @param[in] Src
396 * Source of a valid LUID with attributes structure.
397 *
398 * @param[in] PrivilegeCount
399 * Count number of privileges to be captured.
400 *
401 * @param[in] PreviousMode
402 * Processor level access mode.
403 *
404 * @param[in] AllocatedMem
405 * If specified, the function will use this allocated block memory
406 * buffer for the captured LUID and attributes structure. Otherwise
407 * the function will automatically allocate some buffer for it.
408 *
409 * @param[in] AllocatedLength
410 * The length of the buffer, pointed by AllocatedMem.
411 *
412 * @param[in] PoolType
413 * Pool type of the memory allocation.
414 *
415 * @param[in] CaptureIfKernel
416 * If set to TRUE, the capturing is done in the kernel itself.
417 * FALSE if the capturing is done in a kernel mode driver instead.
418 *
419 * @param[out] Dest
420 * The captured LUID with attributes buffer.
421 *
422 * @param[in,out] Length
423 * The length of the captured privileges count.
424 *
425 * @return
426 * Returns STATUS_SUCCESS if the LUID and attributes array
427 * has been captured successfully. STATUS_INSUFFICIENT_RESOURCES is returned
428 * if memory pool allocation for the captured buffer has failed.
429 * STATUS_BUFFER_TOO_SMALL is returned if the buffer size is less than the
430 * required size.
431 */
432 NTSTATUS
433 NTAPI
434 SeCaptureLuidAndAttributesArray(
435 _In_ PLUID_AND_ATTRIBUTES Src,
436 _In_ ULONG PrivilegeCount,
437 _In_ KPROCESSOR_MODE PreviousMode,
438 _In_opt_ PLUID_AND_ATTRIBUTES AllocatedMem,
439 _In_opt_ ULONG AllocatedLength,
440 _In_ POOL_TYPE PoolType,
441 _In_ BOOLEAN CaptureIfKernel,
442 _Out_ PLUID_AND_ATTRIBUTES *Dest,
443 _Inout_ PULONG Length)
444 {
445 ULONG BufferSize;
446 NTSTATUS Status = STATUS_SUCCESS;
447
448 PAGED_CODE();
449
450 if (PrivilegeCount == 0)
451 {
452 *Dest = 0;
453 *Length = 0;
454 return STATUS_SUCCESS;
455 }
456
457 if (PreviousMode == KernelMode && !CaptureIfKernel)
458 {
459 *Dest = Src;
460 return STATUS_SUCCESS;
461 }
462
463 /* FIXME - check PrivilegeCount for a valid number so we don't
464 cause an integer overflow or exhaust system resources! */
465
466 BufferSize = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
467 *Length = ROUND_UP(BufferSize, 4); /* round up to a 4 byte alignment */
468
469 /* probe the buffer */
470 if (PreviousMode != KernelMode)
471 {
472 _SEH2_TRY
473 {
474 ProbeForRead(Src,
475 BufferSize,
476 sizeof(ULONG));
477 }
478 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
479 {
480 /* Return the exception code */
481 _SEH2_YIELD(return _SEH2_GetExceptionCode());
482 }
483 _SEH2_END;
484 }
485
486 /* allocate enough memory or check if the provided buffer is
487 large enough to hold the array */
488 if (AllocatedMem != NULL)
489 {
490 if (AllocatedLength < BufferSize)
491 {
492 return STATUS_BUFFER_TOO_SMALL;
493 }
494
495 *Dest = AllocatedMem;
496 }
497 else
498 {
499 *Dest = ExAllocatePoolWithTag(PoolType,
500 BufferSize,
501 TAG_LUID);
502 if (*Dest == NULL)
503 {
504 return STATUS_INSUFFICIENT_RESOURCES;
505 }
506 }
507
508 /* copy the array to the buffer */
509 _SEH2_TRY
510 {
511 RtlCopyMemory(*Dest,
512 Src,
513 BufferSize);
514 }
515 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
516 {
517 Status = _SEH2_GetExceptionCode();
518 }
519 _SEH2_END;
520
521 if (!NT_SUCCESS(Status) && AllocatedMem == NULL)
522 {
523 ExFreePoolWithTag(*Dest, TAG_LUID);
524 }
525
526 return Status;
527 }
528
529 /**
530 * @brief
531 * Releases a LUID with attributes structure.
532 *
533 * @param[in] Privilege
534 * Array of a LUID and attributes that represents a privilege.
535 *
536 * @param[in] PreviousMode
537 * Processor level access mode.
538 *
539 * @param[in] CaptureIfKernel
540 * If set to TRUE, the releasing is done in the kernel itself.
541 * FALSE if the releasing is done in a kernel mode driver instead.
542 *
543 * @return
544 * Nothing.
545 */
546 VOID
547 NTAPI
548 SeReleaseLuidAndAttributesArray(
549 _In_ PLUID_AND_ATTRIBUTES Privilege,
550 _In_ KPROCESSOR_MODE PreviousMode,
551 _In_ BOOLEAN CaptureIfKernel)
552 {
553 PAGED_CODE();
554
555 if (Privilege != NULL &&
556 (PreviousMode != KernelMode || CaptureIfKernel))
557 {
558 ExFreePoolWithTag(Privilege, TAG_LUID);
559 }
560 }
561
562 /* PUBLIC FUNCTIONS ***********************************************************/
563
564 /**
565 * @brief
566 * Appends additional privileges.
567 *
568 * @param[in] AccessState
569 * Access request to append.
570 *
571 * @param[in] Privileges
572 * Set of new privileges to append.
573 *
574 * @return
575 * Returns STATUS_SUCCESS if the privileges have been successfully
576 * appended. Otherwise STATUS_INSUFFICIENT_RESOURCES is returned,
577 * indicating that pool allocation has failed for the buffer to hold
578 * the new set of privileges.
579 */
580 NTSTATUS
581 NTAPI
582 SeAppendPrivileges(
583 _Inout_ PACCESS_STATE AccessState,
584 _In_ PPRIVILEGE_SET Privileges)
585 {
586 PAUX_ACCESS_DATA AuxData;
587 ULONG OldPrivilegeSetSize;
588 ULONG NewPrivilegeSetSize;
589 PPRIVILEGE_SET PrivilegeSet;
590
591 PAGED_CODE();
592
593 /* Get the Auxiliary Data */
594 AuxData = AccessState->AuxData;
595
596 /* Calculate the size of the old privilege set */
597 OldPrivilegeSetSize = sizeof(PRIVILEGE_SET) +
598 (AuxData->PrivilegeSet->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES);
599
600 if (AuxData->PrivilegeSet->PrivilegeCount +
601 Privileges->PrivilegeCount > INITIAL_PRIVILEGE_COUNT)
602 {
603 /* Calculate the size of the new privilege set */
604 NewPrivilegeSetSize = OldPrivilegeSetSize +
605 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
606
607 /* Allocate a new privilege set */
608 PrivilegeSet = ExAllocatePoolWithTag(PagedPool,
609 NewPrivilegeSetSize,
610 TAG_PRIVILEGE_SET);
611 if (PrivilegeSet == NULL)
612 return STATUS_INSUFFICIENT_RESOURCES;
613
614 /* Copy original privileges from the acess state */
615 RtlCopyMemory(PrivilegeSet,
616 AuxData->PrivilegeSet,
617 OldPrivilegeSetSize);
618
619 /* Append privileges from the privilege set*/
620 RtlCopyMemory((PVOID)((ULONG_PTR)PrivilegeSet + OldPrivilegeSetSize),
621 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)),
622 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
623
624 /* Adjust the number of privileges in the new privilege set */
625 PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount;
626
627 /* Free the old privilege set if it was allocated */
628 if (AccessState->PrivilegesAllocated != FALSE)
629 ExFreePoolWithTag(AuxData->PrivilegeSet, TAG_PRIVILEGE_SET);
630
631 /* Now we are using an allocated privilege set */
632 AccessState->PrivilegesAllocated = TRUE;
633
634 /* Assign the new privileges to the access state */
635 AuxData->PrivilegeSet = PrivilegeSet;
636 }
637 else
638 {
639 /* Append privileges */
640 RtlCopyMemory((PVOID)((ULONG_PTR)AuxData->PrivilegeSet + OldPrivilegeSetSize),
641 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)),
642 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
643
644 /* Adjust the number of privileges in the target privilege set */
645 AuxData->PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount;
646 }
647
648 return STATUS_SUCCESS;
649 }
650
651 /**
652 * @brief
653 * Frees a set of privileges.
654 *
655 * @param[in] Privileges
656 * Set of privileges array to be freed.
657 *
658 * @return
659 * Nothing.
660 */
661 VOID
662 NTAPI
663 SeFreePrivileges(
664 _In_ PPRIVILEGE_SET Privileges)
665 {
666 PAGED_CODE();
667 ExFreePoolWithTag(Privileges, TAG_PRIVILEGE_SET);
668 }
669
670 /**
671 * @brief
672 * Checks if a set of privileges exist and match within a
673 * security subject context.
674 *
675 * @param[in] Privileges
676 * A set of privileges where the check must be performed
677 * against the subject context.
678 *
679 * @param[in] SubjectContext
680 * A subject security context.
681 *
682 * @param[in] PreviousMode
683 * Processor level access mode.
684 *
685 * @return
686 * Returns TRUE if all the privileges do exist and match
687 * with the ones specified by the caller and subject
688 * context, FALSE otherwise.
689 */
690 BOOLEAN
691 NTAPI
692 SePrivilegeCheck(
693 _In_ PPRIVILEGE_SET Privileges,
694 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
695 _In_ KPROCESSOR_MODE PreviousMode)
696 {
697 PACCESS_TOKEN Token = NULL;
698
699 PAGED_CODE();
700
701 if (SubjectContext->ClientToken == NULL)
702 {
703 Token = SubjectContext->PrimaryToken;
704 }
705 else
706 {
707 Token = SubjectContext->ClientToken;
708 if (SubjectContext->ImpersonationLevel < 2)
709 {
710 return FALSE;
711 }
712 }
713
714 return SepPrivilegeCheck(Token,
715 Privileges->Privilege,
716 Privileges->PrivilegeCount,
717 Privileges->Control,
718 PreviousMode);
719 }
720
721 /**
722 * @brief
723 * Checks if a single privilege is present in the context
724 * of the calling thread.
725 *
726 * @param[in] PrivilegeValue
727 * The specific privilege to be checked.
728 *
729 * @param[in] PreviousMode
730 * Processor level access mode.
731 *
732 * @return
733 * Returns TRUE if the privilege is present, FALSE
734 * otherwise.
735 */
736 BOOLEAN
737 NTAPI
738 SeSinglePrivilegeCheck(
739 _In_ LUID PrivilegeValue,
740 _In_ KPROCESSOR_MODE PreviousMode)
741 {
742 SECURITY_SUBJECT_CONTEXT SubjectContext;
743 PRIVILEGE_SET Priv;
744 BOOLEAN Result;
745
746 PAGED_CODE();
747
748 SeCaptureSubjectContext(&SubjectContext);
749
750 Priv.PrivilegeCount = 1;
751 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
752 Priv.Privilege[0].Luid = PrivilegeValue;
753 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
754
755 Result = SePrivilegeCheck(&Priv,
756 &SubjectContext,
757 PreviousMode);
758
759 if (PreviousMode != KernelMode)
760 {
761 SePrivilegedServiceAuditAlarm(NULL,
762 &SubjectContext,
763 &Priv,
764 Result);
765
766 }
767
768 SeReleaseSubjectContext(&SubjectContext);
769
770 return Result;
771 }
772
773 /**
774 * @brief
775 * Checks a privileged object if such object has
776 * the specific privilege submitted by the caller.
777 *
778 * @param[in] PrivilegeValue
779 * A privilege to be checked against the one from
780 * the object.
781 *
782 * @param[in] ObjectHandle
783 * A handle to any kind of object.
784 *
785 * @param[in] DesiredAccess
786 * Desired access right mask requested by the caller.
787 *
788 * @param[in] PreviousMode
789 * Processor level access mode.
790 *
791 * @return
792 * Returns TRUE if the privilege is present, FALSE
793 * otherwise.
794 */
795 BOOLEAN
796 NTAPI
797 SeCheckPrivilegedObject(
798 _In_ LUID PrivilegeValue,
799 _In_ HANDLE ObjectHandle,
800 _In_ ACCESS_MASK DesiredAccess,
801 _In_ KPROCESSOR_MODE PreviousMode)
802 {
803 SECURITY_SUBJECT_CONTEXT SubjectContext;
804 PRIVILEGE_SET Priv;
805 BOOLEAN Result;
806
807 PAGED_CODE();
808
809 SeCaptureSubjectContext(&SubjectContext);
810
811 Priv.PrivilegeCount = 1;
812 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
813 Priv.Privilege[0].Luid = PrivilegeValue;
814 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
815
816 Result = SePrivilegeCheck(&Priv, &SubjectContext, PreviousMode);
817 if (PreviousMode != KernelMode)
818 {
819 #if 0
820 SePrivilegeObjectAuditAlarm(ObjectHandle,
821 &SubjectContext,
822 DesiredAccess,
823 &PrivilegeValue,
824 Result,
825 PreviousMode);
826 #endif
827 }
828
829 SeReleaseSubjectContext(&SubjectContext);
830
831 return Result;
832 }
833
834 /* SYSTEM CALLS ***************************************************************/
835
836 /**
837 * @brief
838 * Checks a client access token if it has the required set of
839 * privileges.
840 *
841 * @param[in] ClientToken
842 * A handle to an access client token.
843 *
844 * @param[in] RequiredPrivileges
845 * A set of required privileges to be checked against the privileges
846 * of the access token.
847 *
848 * @param[out] Result
849 * The result, as a boolean value. If TRUE, the token has all the required
850 * privileges, FALSE otherwise.
851 *
852 * @return
853 * Returns STATUS_SUCCESS if the function has completed successfully.
854 * STATUS_INVALID_PARAMETER is returned if the set array of required
855 * privileges has a bogus number of privileges, that is, the array
856 * has a count of privileges that exceeds the maximum threshold
857 * (or in other words, an integer overflow). A failure NTSTATUS code
858 * is returned otherwise.
859 */
860 NTSTATUS
861 NTAPI
862 NtPrivilegeCheck(
863 _In_ HANDLE ClientToken,
864 _In_ PPRIVILEGE_SET RequiredPrivileges,
865 _Out_ PBOOLEAN Result)
866 {
867 PLUID_AND_ATTRIBUTES Privileges;
868 PTOKEN Token;
869 ULONG PrivilegeCount = 0;
870 ULONG PrivilegeControl = 0;
871 ULONG Length;
872 BOOLEAN CheckResult;
873 KPROCESSOR_MODE PreviousMode;
874 NTSTATUS Status;
875
876 PAGED_CODE();
877
878 PreviousMode = KeGetPreviousMode();
879
880 /* probe the buffers */
881 if (PreviousMode != KernelMode)
882 {
883 _SEH2_TRY
884 {
885 ProbeForWrite(RequiredPrivileges,
886 FIELD_OFFSET(PRIVILEGE_SET,
887 Privilege),
888 sizeof(ULONG));
889
890 PrivilegeCount = RequiredPrivileges->PrivilegeCount;
891 PrivilegeControl = RequiredPrivileges->Control;
892
893 /* Check PrivilegeCount to avoid an integer overflow! */
894 if (FIELD_OFFSET(PRIVILEGE_SET,
895 Privilege[PrivilegeCount]) /
896 sizeof(RequiredPrivileges->Privilege[0]) != PrivilegeCount)
897 {
898 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
899 }
900
901 /* probe all of the array */
902 ProbeForWrite(RequiredPrivileges,
903 FIELD_OFFSET(PRIVILEGE_SET,
904 Privilege[PrivilegeCount]),
905 sizeof(ULONG));
906
907 ProbeForWriteBoolean(Result);
908 }
909 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
910 {
911 /* Return the exception code */
912 _SEH2_YIELD(return _SEH2_GetExceptionCode());
913 }
914 _SEH2_END;
915 }
916 else
917 {
918 PrivilegeCount = RequiredPrivileges->PrivilegeCount;
919 PrivilegeControl = RequiredPrivileges->Control;
920 }
921
922 /* reference the token and make sure we're
923 not doing an anonymous impersonation */
924 Status = ObReferenceObjectByHandle(ClientToken,
925 TOKEN_QUERY,
926 SeTokenObjectType,
927 PreviousMode,
928 (PVOID*)&Token,
929 NULL);
930 if (!NT_SUCCESS(Status))
931 {
932 return Status;
933 }
934
935 if (Token->TokenType == TokenImpersonation &&
936 Token->ImpersonationLevel < SecurityIdentification)
937 {
938 ObDereferenceObject(Token);
939 return STATUS_BAD_IMPERSONATION_LEVEL;
940 }
941
942 /* capture the privileges */
943 Status = SeCaptureLuidAndAttributesArray(RequiredPrivileges->Privilege,
944 PrivilegeCount,
945 PreviousMode,
946 NULL,
947 0,
948 PagedPool,
949 TRUE,
950 &Privileges,
951 &Length);
952 if (!NT_SUCCESS(Status))
953 {
954 ObDereferenceObject (Token);
955 return Status;
956 }
957
958 CheckResult = SepPrivilegeCheck(Token,
959 Privileges,
960 PrivilegeCount,
961 PrivilegeControl,
962 PreviousMode);
963
964 ObDereferenceObject(Token);
965
966 /* return the array */
967 _SEH2_TRY
968 {
969 RtlCopyMemory(RequiredPrivileges->Privilege,
970 Privileges,
971 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
972 *Result = CheckResult;
973 Status = STATUS_SUCCESS;
974 }
975 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
976 {
977 Status = _SEH2_GetExceptionCode();
978 }
979 _SEH2_END;
980
981 SeReleaseLuidAndAttributesArray(Privileges,
982 PreviousMode,
983 TRUE);
984
985 return Status;
986 }
987
988 /* EOF */