xref: /reactos/ntoskrnl/se/priv.c (revision 9393fc32)
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 */
989