xref: /reactos/ntoskrnl/se/access.c (revision bbabe248)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Security access state functions support
5  * COPYRIGHT:       Copyright Alex Ionescu <alex@relsoft.net>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <debug.h>
13 
14 /* GLOBALS ********************************************************************/
15 
16 ERESOURCE SepSubjectContextLock;
17 
18 /* PRIVATE FUNCTIONS **********************************************************/
19 
20 /**
21  * @brief
22  * Checks if a SID is present in a token.
23  *
24  * @param[in] _Token
25  * A valid token object.
26  *
27  * @param[in] PrincipalSelfSid
28  * A principal self SID.
29  *
30  * @param[in] _Sid
31  * A regular SID.
32  *
33  * @param[in] Deny
34  * If set to TRUE, the caller expected that a SID in a token
35  * must be a deny-only SID, that is, access checks are performed
36  * only for deny-only ACEs of the said SID.
37  *
38  * @param[in] Restricted
39  * If set to TRUE, the caller expects that a SID in a token is
40  * restricted.
41  *
42  * @return
43  * Returns TRUE if the specified SID in the call is present in the token,
44  * FALSE otherwise.
45  */
46 BOOLEAN
47 NTAPI
48 SepSidInTokenEx(
49     _In_ PACCESS_TOKEN _Token,
50     _In_ PSID PrincipalSelfSid,
51     _In_ PSID _Sid,
52     _In_ BOOLEAN Deny,
53     _In_ BOOLEAN Restricted)
54 {
55     ULONG i;
56     PTOKEN Token = (PTOKEN)_Token;
57     PISID TokenSid, Sid = (PISID)_Sid;
58     PSID_AND_ATTRIBUTES SidAndAttributes;
59     ULONG SidCount, SidLength;
60     USHORT SidMetadata;
61     PAGED_CODE();
62 
63     /* Not yet supported */
64     ASSERT(PrincipalSelfSid == NULL);
65     ASSERT(Restricted == FALSE);
66 
67     /* Check if a principal SID was given, and this is our current SID already */
68     if ((PrincipalSelfSid) && (RtlEqualSid(SePrincipalSelfSid, Sid)))
69     {
70         /* Just use the principal SID in this case */
71         Sid = PrincipalSelfSid;
72     }
73 
74     /* Check if this is a restricted token or not */
75     if (Restricted)
76     {
77         /* Use the restricted SIDs and count */
78         SidAndAttributes = Token->RestrictedSids;
79         SidCount = Token->RestrictedSidCount;
80     }
81     else
82     {
83         /* Use the normal SIDs and count */
84         SidAndAttributes = Token->UserAndGroups;
85         SidCount = Token->UserAndGroupCount;
86     }
87 
88     /* Do checks here by hand instead of the usual 4 function calls */
89     SidLength = FIELD_OFFSET(SID,
90                              SubAuthority[Sid->SubAuthorityCount]);
91     SidMetadata = *(PUSHORT)&Sid->Revision;
92 
93     /* Loop every SID */
94     for (i = 0; i < SidCount; i++)
95     {
96         TokenSid = (PISID)SidAndAttributes->Sid;
97 #if SE_SID_DEBUG
98         UNICODE_STRING sidString;
99         RtlConvertSidToUnicodeString(&sidString, TokenSid, TRUE);
100         DPRINT1("SID in Token: %wZ\n", &sidString);
101         RtlFreeUnicodeString(&sidString);
102 #endif
103         /* Check if the SID metadata matches */
104         if (*(PUSHORT)&TokenSid->Revision == SidMetadata)
105         {
106             /* Check if the SID data matches */
107             if (RtlEqualMemory(Sid, TokenSid, SidLength))
108             {
109                 /* Check if the group is enabled, or used for deny only */
110                 if ((!(i) && !(SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)) ||
111                     (SidAndAttributes->Attributes & SE_GROUP_ENABLED) ||
112                     ((Deny) && (SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)))
113                 {
114                     /* SID is present */
115                     return TRUE;
116                 }
117                 else
118                 {
119                     /* SID is not present */
120                     return FALSE;
121                 }
122             }
123         }
124 
125         /* Move to the next SID */
126         SidAndAttributes++;
127     }
128 
129     /* SID is not present */
130     return FALSE;
131 }
132 
133 /**
134  * @brief
135  * Checks if a SID is present in a token.
136  *
137  * @param[in] _Token
138  * A valid token object.
139  *
140  * @param[in] _Sid
141  * A regular SID.
142  *
143  * @return
144  * Returns TRUE if the specified SID in the call is present in the token,
145  * FALSE otherwise.
146  */
147 BOOLEAN
148 NTAPI
149 SepSidInToken(
150     _In_ PACCESS_TOKEN _Token,
151     _In_ PSID Sid)
152 {
153     /* Call extended API */
154     return SepSidInTokenEx(_Token, NULL, Sid, FALSE, FALSE);
155 }
156 
157 /**
158  * @brief
159  * Checks if a token belongs to the main user, being the owner.
160  *
161  * @param[in] _Token
162  * A valid token object.
163  *
164  * @param[in] SecurityDescriptor
165  * A security descriptor where the owner is to be found.
166  *
167  * @param[in] TokenLocked
168  * If set to TRUE, the token has been already locked and there's
169  * no need to lock it again. Otherwise the function will acquire
170  * the lock.
171  *
172  * @return
173  * Returns TRUE if the token belongs to a owner, FALSE otherwise.
174  */
175 BOOLEAN
176 NTAPI
177 SepTokenIsOwner(
178     _In_ PACCESS_TOKEN _Token,
179     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
180     _In_ BOOLEAN TokenLocked)
181 {
182     PSID Sid;
183     BOOLEAN Result;
184     PTOKEN Token = _Token;
185 
186     /* Get the owner SID */
187     Sid = SepGetOwnerFromDescriptor(SecurityDescriptor);
188     ASSERT(Sid != NULL);
189 
190     /* Lock the token if needed */
191     if (!TokenLocked) SepAcquireTokenLockShared(Token);
192 
193     /* Check if the owner SID is found, handling restricted case as well */
194     Result = SepSidInToken(Token, Sid);
195     if ((Result) && (Token->TokenFlags & TOKEN_IS_RESTRICTED))
196     {
197         Result = SepSidInTokenEx(Token, NULL, Sid, FALSE, TRUE);
198     }
199 
200     /* Release the lock if we had acquired it */
201     if (!TokenLocked) SepReleaseTokenLock(Token);
202 
203     /* Return the result */
204     return Result;
205 }
206 
207 /**
208  * @brief
209  * Retrieves token control information.
210  *
211  * @param[in] _Token
212  * A valid token object.
213  *
214  * @param[out] SecurityDescriptor
215  * The returned token control information.
216  *
217  * @return
218  * Nothing.
219  */
220 VOID
221 NTAPI
222 SeGetTokenControlInformation(
223     _In_ PACCESS_TOKEN _Token,
224     _Out_ PTOKEN_CONTROL TokenControl)
225 {
226     PTOKEN Token = _Token;
227     PAGED_CODE();
228 
229     /* Capture the main fields */
230     TokenControl->AuthenticationId = Token->AuthenticationId;
231     TokenControl->TokenId = Token->TokenId;
232     TokenControl->TokenSource = Token->TokenSource;
233 
234     /* Lock the token */
235     SepAcquireTokenLockShared(Token);
236 
237     /* Capture the modified ID */
238     TokenControl->ModifiedId = Token->ModifiedId;
239 
240     /* Unlock it */
241     SepReleaseTokenLock(Token);
242 }
243 
244 /**
245  * @brief
246  * Creates a client security context based upon an access token.
247  *
248  * @param[in] Token
249  * A valid token object.
250  *
251  * @param[in] ClientSecurityQos
252  * The Quality of Service (QoS) of a client security context.
253  *
254  * @param[in] ServerIsRemote
255  * If the client is a remote server (TRUE), the function will retrieve the
256  * control information of an access token, that is, we're doing delegation
257  * and that the server isn't local.
258  *
259  * @param[in] TokenType
260  * Type of token.
261  *
262  * @param[in] ThreadEffectiveOnly
263  * If set to TRUE, the client wants that the current thread wants to modify
264  * (enable or disable) privileges and groups.
265  *
266  * @param[in] ImpersonationLevel
267  * Security impersonation level filled in the QoS context.
268  *
269  * @param[out] ClientContext
270  * The returned security client context.
271  *
272  * @return
273  * Returns STATUS_SUCCESS if client security creation has completed successfully.
274  * STATUS_INVALID_PARAMETER is returned if one or more of the parameters are bogus.
275  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the current impersonation level
276  * within QoS context doesn't meet with the conditions required. A failure
277  * NTSTATUS code is returned otherwise.
278  */
279 NTSTATUS
280 NTAPI
281 SepCreateClientSecurity(
282     _In_ PACCESS_TOKEN Token,
283     _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
284     _In_ BOOLEAN ServerIsRemote,
285     _In_ TOKEN_TYPE TokenType,
286     _In_ BOOLEAN ThreadEffectiveOnly,
287     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
288     _Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
289 {
290     NTSTATUS Status;
291     PACCESS_TOKEN NewToken;
292     PAGED_CODE();
293 
294     /* Check for bogus impersonation level */
295     if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel))
296     {
297         /* Fail the call */
298         return STATUS_INVALID_PARAMETER;
299     }
300 
301     /* Check what kind of token this is */
302     if (TokenType != TokenImpersonation)
303     {
304         /* On a primary token, if we do direct access, copy the flag from the QOS */
305         ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly;
306     }
307     else
308     {
309         /* This is an impersonation token, is the level ok? */
310         if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel)
311         {
312             /* Nope, fail */
313             return STATUS_BAD_IMPERSONATION_LEVEL;
314         }
315 
316         /* Is the level too low, or are we doing something other than delegation remotely */
317         if ((ImpersonationLevel == SecurityAnonymous) ||
318             (ImpersonationLevel == SecurityIdentification) ||
319             ((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation)))
320         {
321             /* Fail the call */
322             return STATUS_BAD_IMPERSONATION_LEVEL;
323         }
324 
325         /* Pick either the thread setting or the QOS setting */
326         ClientContext->DirectAccessEffectiveOnly =
327             ((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE;
328     }
329 
330     /* Is this static tracking */
331     if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)
332     {
333         /* Do not use direct access and make a copy */
334         ClientContext->DirectlyAccessClientToken = FALSE;
335         Status = SeCopyClientToken(Token,
336                                    ClientSecurityQos->ImpersonationLevel,
337                                    KernelMode,
338                                    &NewToken);
339         if (!NT_SUCCESS(Status))
340             return Status;
341     }
342     else
343     {
344         /* Use direct access and check if this is local */
345         ClientContext->DirectlyAccessClientToken = TRUE;
346         if (ServerIsRemote)
347         {
348             /* We are doing delegation, so make a copy of the control data */
349             SeGetTokenControlInformation(Token,
350                                          &ClientContext->ClientTokenControl);
351         }
352 
353         /* Keep the same token */
354         NewToken = Token;
355     }
356 
357     /* Fill out the context and return success */
358     ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
359     ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel;
360     ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode;
361     ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly;
362     ClientContext->ServerIsRemote = ServerIsRemote;
363     ClientContext->ClientToken = NewToken;
364     return STATUS_SUCCESS;
365 }
366 
367 /* PUBLIC FUNCTIONS ***********************************************************/
368 
369 /**
370  * @brief
371  * An extended function that captures the security subject context based upon
372  * the specified thread and process.
373  *
374  * @param[in] Thread
375  * A thread where the calling thread's token is to be referenced for
376  * the security context.
377  *
378  * @param[in] Process
379  * A process where the main process' token is to be referenced for
380  * the security context.
381  *
382  * @param[out] SubjectContext
383  * The returned security subject context.
384  *
385  * @return
386  * Nothing.
387  */
388 VOID
389 NTAPI
390 SeCaptureSubjectContextEx(
391     _In_ PETHREAD Thread,
392     _In_ PEPROCESS Process,
393     _Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
394 {
395     BOOLEAN CopyOnOpen, EffectiveOnly;
396 
397     PAGED_CODE();
398 
399     /* Save the unique ID */
400     SubjectContext->ProcessAuditId = Process->UniqueProcessId;
401 
402     /* Check if we have a thread */
403     if (!Thread)
404     {
405         /* We don't, so no token */
406         SubjectContext->ClientToken = NULL;
407     }
408     else
409     {
410         /* Get the impersonation token */
411         SubjectContext->ClientToken = PsReferenceImpersonationToken(Thread,
412                                                                     &CopyOnOpen,
413                                                                     &EffectiveOnly,
414                                                                     &SubjectContext->ImpersonationLevel);
415     }
416 
417     /* Get the primary token */
418     SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process);
419 }
420 
421 /**
422  * @brief
423  * Captures the security subject context of the calling thread and calling
424  * process.
425  *
426  * @param[out] SubjectContext
427  * The returned security subject context.
428  *
429  * @return
430  * Nothing.
431  */
432 VOID
433 NTAPI
434 SeCaptureSubjectContext(
435     _Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
436 {
437     /* Call the extended API */
438     SeCaptureSubjectContextEx(PsGetCurrentThread(),
439                               PsGetCurrentProcess(),
440                               SubjectContext);
441 }
442 
443 /**
444  * @brief
445  * Locks both the referenced primary and client access tokens of a
446  * security subject context.
447  *
448  * @param[in] SubjectContext
449  * A valid security context with both referenced tokens.
450  *
451  * @return
452  * Nothing.
453  */
454 VOID
455 NTAPI
456 SeLockSubjectContext(
457     _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
458 {
459     PTOKEN PrimaryToken, ClientToken;
460     PAGED_CODE();
461 
462     /* Read both tokens */
463     PrimaryToken = SubjectContext->PrimaryToken;
464     ClientToken = SubjectContext->ClientToken;
465 
466     /* Always lock the primary */
467     SepAcquireTokenLockShared(PrimaryToken);
468 
469     /* Lock the impersonation one if it's there */
470     if (!ClientToken) return;
471     SepAcquireTokenLockShared(ClientToken);
472 }
473 
474 /**
475  * @brief
476  * Unlocks both the referenced primary and client access tokens of a
477  * security subject context.
478  *
479  * @param[in] SubjectContext
480  * A valid security context with both referenced tokens.
481  *
482  * @return
483  * Nothing.
484  */
485 VOID
486 NTAPI
487 SeUnlockSubjectContext(
488     _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
489 {
490     PTOKEN PrimaryToken, ClientToken;
491     PAGED_CODE();
492 
493     /* Read both tokens */
494     PrimaryToken = SubjectContext->PrimaryToken;
495     ClientToken = SubjectContext->ClientToken;
496 
497     /* Unlock the impersonation one if it's there */
498     if (ClientToken)
499     {
500         SepReleaseTokenLock(ClientToken);
501     }
502 
503     /* Always unlock the primary one */
504     SepReleaseTokenLock(PrimaryToken);
505 }
506 
507 /**
508  * @brief
509  * Releases both the primary and client tokens of a security
510  * subject context.
511  *
512  * @param[in] SubjectContext
513  * The captured security context.
514  *
515  * @return
516  * Nothing.
517  */
518 VOID
519 NTAPI
520 SeReleaseSubjectContext(
521     _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
522 {
523     PAGED_CODE();
524 
525     /* Drop reference on the primary */
526     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken);
527     SubjectContext->PrimaryToken = NULL;
528 
529     /* Drop reference on the impersonation, if there was one */
530     PsDereferenceImpersonationToken(SubjectContext->ClientToken);
531     SubjectContext->ClientToken = NULL;
532 }
533 
534 /**
535  * @brief
536  * An extended function that creates an access state.
537  *
538  * @param[in] Thread
539  * Valid thread object where subject context is to be captured.
540  *
541  * @param[in] Process
542  * Valid process object where subject context is to be captured.
543  *
544  * @param[in,out] AccessState
545  * An initialized returned parameter to an access state.
546  *
547  * @param[in] AuxData
548  * Auxiliary security data for access state.
549  *
550  * @param[in] Access
551  * Type of access mask to assign.
552  *
553  * @param[in] GenericMapping
554  * Generic mapping for the access state to assign.
555  *
556  * @return
557  * Returns STATUS_SUCCESS.
558  */
559 NTSTATUS
560 NTAPI
561 SeCreateAccessStateEx(
562     _In_ PETHREAD Thread,
563     _In_ PEPROCESS Process,
564     _Inout_ PACCESS_STATE AccessState,
565     _In_ PAUX_ACCESS_DATA AuxData,
566     _In_ ACCESS_MASK Access,
567     _In_ PGENERIC_MAPPING GenericMapping)
568 {
569     ACCESS_MASK AccessMask = Access;
570     PTOKEN Token;
571     PAGED_CODE();
572 
573     /* Map the Generic Acess to Specific Access if we have a Mapping */
574     if ((Access & GENERIC_ACCESS) && (GenericMapping))
575     {
576         RtlMapGenericMask(&AccessMask, GenericMapping);
577     }
578 
579     /* Initialize the Access State */
580     RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
581     ASSERT(AccessState->SecurityDescriptor == NULL);
582     ASSERT(AccessState->PrivilegesAllocated == FALSE);
583 
584     /* Initialize and save aux data */
585     RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
586     AccessState->AuxData = AuxData;
587 
588     /* Capture the Subject Context */
589     SeCaptureSubjectContextEx(Thread,
590                               Process,
591                               &AccessState->SubjectSecurityContext);
592 
593     /* Set Access State Data */
594     AccessState->RemainingDesiredAccess = AccessMask;
595     AccessState->OriginalDesiredAccess = AccessMask;
596     ExAllocateLocallyUniqueId(&AccessState->OperationID);
597 
598     /* Get the Token to use */
599     Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
600 
601     /* Check for Travers Privilege */
602     if (Token->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE)
603     {
604         /* Preserve the Traverse Privilege */
605         AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
606     }
607 
608     /* Set the Auxiliary Data */
609     AuxData->PrivilegeSet = (PPRIVILEGE_SET)((ULONG_PTR)AccessState +
610                                              FIELD_OFFSET(ACCESS_STATE,
611                                                           Privileges));
612     if (GenericMapping) AuxData->GenericMapping = *GenericMapping;
613 
614     /* Return Sucess */
615     return STATUS_SUCCESS;
616 }
617 
618 /**
619  * @brief
620  * Creates an access state.
621  *
622  * @param[in,out] AccessState
623  * An initialized returned parameter to an access state.
624  *
625  * @param[in] AuxData
626  * Auxiliary security data for access state.
627  *
628  * @param[in] Access
629  * Type of access mask to assign.
630  *
631  * @param[in] GenericMapping
632  * Generic mapping for the access state to assign.
633  *
634  * @return
635  * See SeCreateAccessStateEx.
636  */
637 NTSTATUS
638 NTAPI
639 SeCreateAccessState(
640     _Inout_ PACCESS_STATE AccessState,
641     _In_ PAUX_ACCESS_DATA AuxData,
642     _In_ ACCESS_MASK Access,
643     _In_ PGENERIC_MAPPING GenericMapping)
644 {
645     PAGED_CODE();
646 
647     /* Call the extended API */
648     return SeCreateAccessStateEx(PsGetCurrentThread(),
649                                  PsGetCurrentProcess(),
650                                  AccessState,
651                                  AuxData,
652                                  Access,
653                                  GenericMapping);
654 }
655 
656 /**
657  * @brief
658  * Deletes an allocated access state from the memory.
659  *
660  * @param[in] AccessState
661  * A valid access state.
662  *
663  * @return
664  * Nothing.
665  */
666 VOID
667 NTAPI
668 SeDeleteAccessState(
669     _In_ PACCESS_STATE AccessState)
670 {
671     PAUX_ACCESS_DATA AuxData;
672     PAGED_CODE();
673 
674     /* Get the Auxiliary Data */
675     AuxData = AccessState->AuxData;
676 
677     /* Deallocate Privileges */
678     if (AccessState->PrivilegesAllocated)
679         ExFreePoolWithTag(AuxData->PrivilegeSet, TAG_PRIVILEGE_SET);
680 
681     /* Deallocate Name and Type Name */
682     if (AccessState->ObjectName.Buffer)
683     {
684         ExFreePool(AccessState->ObjectName.Buffer);
685     }
686 
687     if (AccessState->ObjectTypeName.Buffer)
688     {
689         ExFreePool(AccessState->ObjectTypeName.Buffer);
690     }
691 
692     /* Release the Subject Context */
693     SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
694 }
695 
696 /**
697  * @brief
698  * Sets a new generic mapping for an allocated access state.
699  *
700  * @param[in] AccessState
701  * A valid access state.
702  *
703  * @param[in] GenericMapping
704  * New generic mapping to assign.
705  *
706  * @return
707  * Nothing.
708  */
709 VOID
710 NTAPI
711 SeSetAccessStateGenericMapping(
712     _In_ PACCESS_STATE AccessState,
713     _In_ PGENERIC_MAPPING GenericMapping)
714 {
715     PAGED_CODE();
716 
717     /* Set the Generic Mapping */
718     ((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping;
719 }
720 
721 /**
722  * @brief
723  * Creates a client security context.
724  *
725  * @param[in] Thread
726  * Thread object of the client where impersonation has to begin.
727  *
728  * @param[in] Qos
729  * Quality of service to specify what kind of impersonation to be done.
730  *
731  * @param[in] RemoteClient
732  * If set to TRUE, the client that we're going to impersonate is remote.
733  *
734  * @param[out] ClientContext
735  * The returned security client context.
736  *
737  * @return
738  * See SepCreateClientSecurity.
739  */
740 NTSTATUS
741 NTAPI
742 SeCreateClientSecurity(
743     _In_ PETHREAD Thread,
744     _In_ PSECURITY_QUALITY_OF_SERVICE Qos,
745     _In_ BOOLEAN RemoteClient,
746     _Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
747 {
748     TOKEN_TYPE TokenType;
749     BOOLEAN ThreadEffectiveOnly;
750     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
751     PACCESS_TOKEN Token;
752     NTSTATUS Status;
753     PAGED_CODE();
754 
755     /* Reference the correct token */
756     Token = PsReferenceEffectiveToken(Thread,
757                                       &TokenType,
758                                       &ThreadEffectiveOnly,
759                                       &ImpersonationLevel);
760 
761     /* Create client security from it */
762     Status = SepCreateClientSecurity(Token,
763                                      Qos,
764                                      RemoteClient,
765                                      TokenType,
766                                      ThreadEffectiveOnly,
767                                      ImpersonationLevel,
768                                      ClientContext);
769 
770     /* Check if we failed or static tracking was used */
771     if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
772     {
773         /* Dereference our copy since it's not being used */
774         ObDereferenceObject(Token);
775     }
776 
777     /* Return status */
778     return Status;
779 }
780 
781 /**
782  * @brief
783  * Creates a client security context based upon the captured security
784  * subject context.
785  *
786  * @param[in] SubjectContext
787  * The captured subject context where client security is to be created
788  * from.
789  *
790  * @param[in] ClientSecurityQos
791  * Quality of service to specify what kind of impersonation to be done.
792  *
793  * @param[in] ServerIsRemote
794  * If set to TRUE, the client that we're going to impersonate is remote.
795  *
796  * @param[out] ClientContext
797  * The returned security client context.
798  *
799  * @return
800  * See SepCreateClientSecurity.
801  */
802 NTSTATUS
803 NTAPI
804 SeCreateClientSecurityFromSubjectContext(
805     _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
806     _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
807     _In_ BOOLEAN ServerIsRemote,
808     _Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
809 {
810     PACCESS_TOKEN Token;
811     NTSTATUS Status;
812     PAGED_CODE();
813 
814     /* Get the right token and reference it */
815     Token = SeQuerySubjectContextToken(SubjectContext);
816     ObReferenceObject(Token);
817 
818     /* Create the context */
819     Status = SepCreateClientSecurity(Token,
820                                      ClientSecurityQos,
821                                      ServerIsRemote,
822                                      SubjectContext->ClientToken ?
823                                      TokenImpersonation : TokenPrimary,
824                                      FALSE,
825                                      SubjectContext->ImpersonationLevel,
826                                      ClientContext);
827 
828     /* Check if we failed or static tracking was used */
829     if (!(NT_SUCCESS(Status)) ||
830         (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
831     {
832         /* Dereference our copy since it's not being used */
833         ObDereferenceObject(Token);
834     }
835 
836     /* Return status */
837     return Status;
838 }
839 
840 /**
841  * @brief
842  * Extended function that impersonates a client.
843  *
844  * @param[in] ClientContext
845  * A valid client context.
846  *
847  * @param[in] ServerThread
848  * The thread where impersonation is to be done.
849  *
850  * @return
851  * STATUS_SUCCESS is returned if the calling thread successfully impersonates
852  * the client. A failure NTSTATUS code is returned otherwise.
853  */
854 NTSTATUS
855 NTAPI
856 SeImpersonateClientEx(
857     _In_ PSECURITY_CLIENT_CONTEXT ClientContext,
858     _In_opt_ PETHREAD ServerThread)
859 {
860     BOOLEAN EffectiveOnly;
861     PAGED_CODE();
862 
863     /* Check if direct access is requested */
864     if (!ClientContext->DirectlyAccessClientToken)
865     {
866         /* No, so get the flag from QOS */
867         EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
868     }
869     else
870     {
871         /* Yes, so see if direct access should be effective only */
872         EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
873     }
874 
875     /* Use the current thread if one was not passed */
876     if (!ServerThread) ServerThread = PsGetCurrentThread();
877 
878     /* Call the lower layer routine */
879     return PsImpersonateClient(ServerThread,
880                                ClientContext->ClientToken,
881                                TRUE,
882                                EffectiveOnly,
883                                ClientContext->SecurityQos.ImpersonationLevel);
884 }
885 
886 /**
887  * @brief
888  * Impersonates a client user.
889  *
890  * @param[in] ClientContext
891  * A valid client context.
892  *
893  * @param[in] ServerThread
894  * The thread where impersonation is to be done.
895  * *
896  * @return
897  * Nothing.
898  */
899 VOID
900 NTAPI
901 SeImpersonateClient(
902     _In_ PSECURITY_CLIENT_CONTEXT ClientContext,
903     _In_opt_ PETHREAD ServerThread)
904 {
905     PAGED_CODE();
906 
907     /* Call the new API */
908     SeImpersonateClientEx(ClientContext, ServerThread);
909 }
910 
911 /* EOF */
912