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