xref: /reactos/ntoskrnl/se/tokenlif.c (revision 5cadc268)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Access token lifetime management implementation (Creation/Duplication/Filtering)
5  * COPYRIGHT:       Copyright David Welch <welch@cwcom.net>
6  *                  Copyright 2021-2022 George Bișoc <george.bisoc@reactos.org>
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* DEFINES ********************************************************************/
16 
17 #define SE_TOKEN_DYNAMIC_SLIM 500
18 
19 /* PRIVATE FUNCTIONS *********************************************************/
20 
21 /**
22  * @brief
23  * Internal function responsible for access token object creation in the kernel.
24  * A fully created token objected is inserted into the token handle, thus the handle
25  * becoming a valid handle to an access token object and ready for use.
26  *
27  * @param[out] TokenHandle
28  * Valid token handle that's ready for use after token creation and object insertion.
29  *
30  * @param[in] PreviousMode
31  * Processor request level mode.
32  *
33  * @param[in] DesiredAccess
34  * Desired access right for the token object to be granted. This kind of access right
35  * impacts how the token can be used and who.
36  *
37  * @param[in] ObjectAttributes
38  * Object attributes for the token to be created.
39  *
40  * @param[in] TokenType
41  * Type of token to assign upon creation.
42  *
43  * @param[in] ImpersonationLevel
44  * Security impersonation level of token to assign upon creation.
45  *
46  * @param[in] AuthenticationId
47  * Authentication ID that represents the authentication information of the token.
48  *
49  * @param[in] ExpirationTime
50  * Expiration time of the token to assign. A value of -1 means that the token never
51  * expires and its life depends upon the amount of references this token object has.
52  *
53  * @param[in] User
54  * User entry to assign to the token.
55  *
56  * @param[in] GroupCount
57  * The total number of groups count for the token.
58  *
59  * @param[in] Groups
60  * The group entries for the token.
61  *
62  * @param[in] GroupsLength
63  * The length size of the groups array, pointed by the Groups parameter.
64  *
65  * @param[in] PrivilegeCount
66  * The total number of priivleges that the newly created token has.
67  *
68  * @param[in] Privileges
69  * The privileges for the token.
70  *
71  * @param[in] Owner
72  * The main user (or also owner) that represents the token that we create.
73  *
74  * @param[in] PrimaryGroup
75  * The main group that represents the token that we create.
76  *
77  * @param[in] DefaultDacl
78  * A discretionary access control list for the token.
79  *
80  * @param[in] TokenSource
81  * Source (or the origin) of the access token that creates it.
82  *
83  * @param[in] SystemToken
84  * If set to TRUE, the newly created token is a system token and only in charge
85  * by the internal system. The function directly returns a pointer to the
86  * created token object for system kernel use. Otherwise if set to FALSE, the
87  * function inserts the object to a handle making it a regular access token.
88  *
89  * @return
90  * Returns STATUS_SUCCESS if token creation has completed successfully.
91  * STATUS_INSUFFICIENT_RESOURCES is returned if the dynamic area of memory of the
92  * token hasn't been allocated because of lack of memory resources. A failure
93  * NTSTATUS code is returned otherwise.
94  */
95 NTSTATUS
96 NTAPI
97 SepCreateToken(
98     _Out_ PHANDLE TokenHandle,
99     _In_ KPROCESSOR_MODE PreviousMode,
100     _In_ ACCESS_MASK DesiredAccess,
101     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
102     _In_ TOKEN_TYPE TokenType,
103     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
104     _In_ PLUID AuthenticationId,
105     _In_ PLARGE_INTEGER ExpirationTime,
106     _In_ PSID_AND_ATTRIBUTES User,
107     _In_ ULONG GroupCount,
108     _In_ PSID_AND_ATTRIBUTES Groups,
109     _In_ ULONG GroupsLength,
110     _In_ ULONG PrivilegeCount,
111     _In_ PLUID_AND_ATTRIBUTES Privileges,
112     _In_opt_ PSID Owner,
113     _In_ PSID PrimaryGroup,
114     _In_opt_ PACL DefaultDacl,
115     _In_ PTOKEN_SOURCE TokenSource,
116     _In_ BOOLEAN SystemToken)
117 {
118     NTSTATUS Status;
119     PTOKEN AccessToken;
120     ULONG TokenFlags = 0;
121     ULONG PrimaryGroupIndex, DefaultOwnerIndex;
122     LUID TokenId;
123     LUID ModifiedId;
124     PVOID EndMem;
125     ULONG PrivilegesLength;
126     ULONG UserGroupsLength;
127     ULONG VariableLength;
128     ULONG DynamicPartSize, TotalSize;
129     ULONG TokenPagedCharges;
130     ULONG i;
131 
132     PAGED_CODE();
133 
134     /* Loop all groups */
135     for (i = 0; i < GroupCount; i++)
136     {
137         /* Check for mandatory groups */
138         if (Groups[i].Attributes & SE_GROUP_MANDATORY)
139         {
140             /* Force them to be enabled */
141             Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
142         }
143 
144         /* Check of the group is an admin group */
145         if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid))
146         {
147             /* Remember this so we can optimize queries later */
148             TokenFlags |= TOKEN_HAS_ADMIN_GROUP;
149         }
150     }
151 
152     /* Allocate unique IDs for the token */
153     ExAllocateLocallyUniqueId(&TokenId);
154     ExAllocateLocallyUniqueId(&ModifiedId);
155 
156     /* Compute how much size we need to allocate for the token */
157 
158     /* Privileges size */
159     PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
160     PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
161 
162     /* User and groups size */
163     UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES);
164     UserGroupsLength += RtlLengthSid(User->Sid);
165     for (i = 0; i < GroupCount; i++)
166     {
167         UserGroupsLength += RtlLengthSid(Groups[i].Sid);
168     }
169     UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID));
170 
171     /* Add the additional groups array length */
172     UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID));
173 
174     VariableLength = PrivilegesLength + UserGroupsLength;
175     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
176 
177     /*
178      * A token is considered slim if it has the default dynamic
179      * contents, or in other words, the primary group and ACL.
180      * We judge if such contents are default by checking their
181      * total size if it's over the range. On Windows this range
182      * is 0x1F4 (aka 500). If the size of the whole dynamic contents
183      * is over that range then the token is considered fat and
184      * the token will be charged the whole of its token body length
185      * plus the dynamic size.
186      */
187     DynamicPartSize = DefaultDacl ? DefaultDacl->AclSize : 0;
188     DynamicPartSize += RtlLengthSid(PrimaryGroup);
189     if (DynamicPartSize > SE_TOKEN_DYNAMIC_SLIM)
190     {
191         TokenPagedCharges = DynamicPartSize + TotalSize;
192     }
193     else
194     {
195         TokenPagedCharges = SE_TOKEN_DYNAMIC_SLIM + TotalSize;
196     }
197 
198     Status = ObCreateObject(PreviousMode,
199                             SeTokenObjectType,
200                             ObjectAttributes,
201                             PreviousMode,
202                             NULL,
203                             TotalSize,
204                             TokenPagedCharges,
205                             0,
206                             (PVOID*)&AccessToken);
207     if (!NT_SUCCESS(Status))
208     {
209         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
210         return Status;
211     }
212 
213     /* Zero out the buffer and initialize the token */
214     RtlZeroMemory(AccessToken, TotalSize);
215 
216     AccessToken->TokenId = TokenId;
217     AccessToken->TokenType = TokenType;
218     AccessToken->ImpersonationLevel = ImpersonationLevel;
219 
220     /* Initialise the lock for the access token */
221     Status = SepCreateTokenLock(AccessToken);
222     if (!NT_SUCCESS(Status))
223         goto Quit;
224 
225     AccessToken->TokenSource.SourceIdentifier = TokenSource->SourceIdentifier;
226     RtlCopyMemory(AccessToken->TokenSource.SourceName,
227                   TokenSource->SourceName,
228                   sizeof(TokenSource->SourceName));
229 
230     AccessToken->ExpirationTime = *ExpirationTime;
231     AccessToken->ModifiedId = ModifiedId;
232     AccessToken->DynamicCharged = TokenPagedCharges - TotalSize;
233 
234     AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
235 
236     /* Copy and reference the logon session */
237     AccessToken->AuthenticationId = *AuthenticationId;
238     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
239     if (!NT_SUCCESS(Status))
240     {
241         /* No logon session could be found, bail out */
242         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
243         /* Set the flag for proper cleanup by the delete procedure */
244         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
245         goto Quit;
246     }
247 
248     /* Insert the referenced logon session into the token */
249     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
250     if (!NT_SUCCESS(Status))
251     {
252         /* Failed to insert the logon session into the token, bail out */
253         DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
254         goto Quit;
255     }
256 
257     /* Fill in token debug information */
258 #if DBG
259     /*
260      * We must determine ourselves that the current
261      * process is not the initial CPU one. The initial
262      * process is not a "real" process, that is, the
263      * Process Manager has not yet been initialized and
264      * as a matter of fact we are creating a token before
265      * any process gets created by Ps. If it turns out
266      * that the current process is the initial CPU process
267      * where token creation execution takes place, don't
268      * do anything.
269      */
270     if (PsGetCurrentProcess() != &KiInitialProcess)
271     {
272         RtlCopyMemory(AccessToken->ImageFileName,
273                       PsGetCurrentProcess()->ImageFileName,
274                       min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
275 
276         AccessToken->ProcessCid = PsGetCurrentProcessId();
277         AccessToken->ThreadCid = PsGetCurrentThreadId();
278     }
279 
280     AccessToken->CreateMethod = TOKEN_CREATE_METHOD;
281 #endif
282 
283     /* Assign the data that reside in the token's variable information area */
284     AccessToken->VariableLength = VariableLength;
285     EndMem = (PVOID)&AccessToken->VariablePart;
286 
287     /* Copy the privileges */
288     AccessToken->PrivilegeCount = PrivilegeCount;
289     AccessToken->Privileges = NULL;
290     if (PrivilegeCount > 0)
291     {
292         AccessToken->Privileges = EndMem;
293         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
294         VariableLength -= PrivilegesLength;
295 
296         if (PreviousMode != KernelMode)
297         {
298             _SEH2_TRY
299             {
300                 RtlCopyMemory(AccessToken->Privileges,
301                               Privileges,
302                               PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
303             }
304             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
305             {
306                 Status = _SEH2_GetExceptionCode();
307             }
308             _SEH2_END;
309         }
310         else
311         {
312             RtlCopyMemory(AccessToken->Privileges,
313                           Privileges,
314                           PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
315         }
316 
317         if (!NT_SUCCESS(Status))
318             goto Quit;
319     }
320 
321     /* Update the privilege flags */
322     SepUpdatePrivilegeFlagsToken(AccessToken);
323 
324     /* Copy the user and groups */
325     AccessToken->UserAndGroupCount = 1 + GroupCount;
326     AccessToken->UserAndGroups = EndMem;
327     EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
328     VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
329 
330     Status = RtlCopySidAndAttributesArray(1,
331                                           User,
332                                           VariableLength,
333                                           &AccessToken->UserAndGroups[0],
334                                           EndMem,
335                                           &EndMem,
336                                           &VariableLength);
337     if (!NT_SUCCESS(Status))
338         goto Quit;
339 
340     Status = RtlCopySidAndAttributesArray(GroupCount,
341                                           Groups,
342                                           VariableLength,
343                                           &AccessToken->UserAndGroups[1],
344                                           EndMem,
345                                           &EndMem,
346                                           &VariableLength);
347     if (!NT_SUCCESS(Status))
348         goto Quit;
349 
350     /* Find the token primary group and default owner */
351     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
352                                                 PrimaryGroup,
353                                                 Owner,
354                                                 &PrimaryGroupIndex,
355                                                 &DefaultOwnerIndex);
356     if (!NT_SUCCESS(Status))
357     {
358         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
359         goto Quit;
360     }
361 
362     /*
363      * Now allocate the token's dynamic information area
364      * and set the data. The dynamic part consists of two
365      * contents, the primary group SID and the default DACL
366      * of the token, in this strict order.
367      */
368     AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
369                                                      DynamicPartSize,
370                                                      TAG_TOKEN_DYNAMIC);
371     if (AccessToken->DynamicPart == NULL)
372     {
373         Status = STATUS_INSUFFICIENT_RESOURCES;
374         goto Quit;
375     }
376 
377     /* Unused memory in the dynamic area */
378     AccessToken->DynamicAvailable = 0;
379 
380     /*
381      * Assign the primary group to the token
382      * and put it in the dynamic part as well.
383      */
384     EndMem = (PVOID)AccessToken->DynamicPart;
385     AccessToken->PrimaryGroup = EndMem;
386     RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
387                             EndMem,
388                             AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
389     AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
390     EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
391 
392     /*
393      * We have assigned a primary group and put it in the
394      * dynamic part, now it's time to copy the provided
395      * default DACL (if it's provided to begin with) into
396      * the DACL field of the token and put it at the end
397      * tail of the dynamic part too.
398      */
399     if (DefaultDacl != NULL)
400     {
401         AccessToken->DefaultDacl = EndMem;
402 
403         RtlCopyMemory(EndMem,
404                       DefaultDacl,
405                       DefaultDacl->AclSize);
406     }
407 
408     /* Insert the token only if it's not the system token, otherwise return it directly */
409     if (!SystemToken)
410     {
411         Status = ObInsertObject(AccessToken,
412                                 NULL,
413                                 DesiredAccess,
414                                 0,
415                                 NULL,
416                                 TokenHandle);
417         if (!NT_SUCCESS(Status))
418         {
419             DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status);
420         }
421     }
422     else
423     {
424         /* Return pointer instead of handle */
425         *TokenHandle = (HANDLE)AccessToken;
426     }
427 
428 Quit:
429     if (!NT_SUCCESS(Status))
430     {
431         /* Dereference the token, the delete procedure will clean it up */
432         ObDereferenceObject(AccessToken);
433     }
434 
435     return Status;
436 }
437 
438 /**
439  * @brief
440  * Duplicates an access token, from an existing valid token.
441  *
442  * @param[in] Token
443  * Access token to duplicate.
444  *
445  * @param[in] ObjectAttributes
446  * Object attributes for the new token.
447  *
448  * @param[in] EffectiveOnly
449  * If set to TRUE, the function removes all the disabled privileges and groups of the token
450  * to duplicate.
451  *
452  * @param[in] TokenType
453  * Type of token.
454  *
455  * @param[in] Level
456  * Security impersonation level of a token.
457  *
458  * @param[in] PreviousMode
459  * The processor request level mode.
460  *
461  * @param[out] NewAccessToken
462  * The duplicated token.
463  *
464  * @return
465  * Returns STATUS_SUCCESS if the token has been duplicated. STATUS_INSUFFICIENT_RESOURCES is returned
466  * if memory pool allocation of the dynamic part of the token for duplication has failed due to the lack
467  * of memory resources. A failure NTSTATUS code is returned otherwise.
468  */
469 NTSTATUS
470 NTAPI
471 SepDuplicateToken(
472     _In_ PTOKEN Token,
473     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
474     _In_ BOOLEAN EffectiveOnly,
475     _In_ TOKEN_TYPE TokenType,
476     _In_ SECURITY_IMPERSONATION_LEVEL Level,
477     _In_ KPROCESSOR_MODE PreviousMode,
478     _Out_ PTOKEN* NewAccessToken)
479 {
480     NTSTATUS Status;
481     PTOKEN AccessToken;
482     PVOID EndMem;
483     ULONG PrimaryGroupIndex;
484     ULONG VariableLength;
485     ULONG DynamicPartSize, TotalSize;
486     ULONG PrivilegesIndex, GroupsIndex;
487 
488     PAGED_CODE();
489 
490     /* Compute how much size we need to allocate for the token */
491     VariableLength = Token->VariableLength;
492     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
493 
494     /*
495      * Compute how much size we need to allocate
496      * the dynamic part of the newly duplicated
497      * token.
498      */
499     DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
500     DynamicPartSize += RtlLengthSid(Token->PrimaryGroup);
501 
502     Status = ObCreateObject(PreviousMode,
503                             SeTokenObjectType,
504                             ObjectAttributes,
505                             PreviousMode,
506                             NULL,
507                             TotalSize,
508                             Token->DynamicCharged,
509                             TotalSize,
510                             (PVOID*)&AccessToken);
511     if (!NT_SUCCESS(Status))
512     {
513         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
514         return Status;
515     }
516 
517     /* Zero out the buffer and initialize the token */
518     RtlZeroMemory(AccessToken, TotalSize);
519 
520     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
521 
522     AccessToken->TokenType = TokenType;
523     AccessToken->ImpersonationLevel = Level;
524 
525     /* Initialise the lock for the access token */
526     Status = SepCreateTokenLock(AccessToken);
527     if (!NT_SUCCESS(Status))
528     {
529         ObDereferenceObject(AccessToken);
530         return Status;
531     }
532 
533     /* Copy the immutable fields */
534     AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier;
535     RtlCopyMemory(AccessToken->TokenSource.SourceName,
536                   Token->TokenSource.SourceName,
537                   sizeof(Token->TokenSource.SourceName));
538 
539     AccessToken->AuthenticationId = Token->AuthenticationId;
540     AccessToken->ParentTokenId = Token->ParentTokenId;
541     AccessToken->ExpirationTime = Token->ExpirationTime;
542     AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
543     AccessToken->DynamicCharged = Token->DynamicCharged;
544 
545     /* Lock the source token and copy the mutable fields */
546     SepAcquireTokenLockShared(Token);
547 
548     AccessToken->SessionId = Token->SessionId;
549     AccessToken->ModifiedId = Token->ModifiedId;
550 
551     AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
552 
553     /* Reference the logon session */
554     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
555     if (!NT_SUCCESS(Status))
556     {
557         /* No logon session could be found, bail out */
558         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
559         /* Set the flag for proper cleanup by the delete procedure */
560         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
561         goto Quit;
562     }
563 
564     /* Insert the referenced logon session into the token */
565     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
566     if (!NT_SUCCESS(Status))
567     {
568         /* Failed to insert the logon session into the token, bail out */
569         DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
570         goto Quit;
571     }
572 
573     /* Fill in token debug information */
574 #if DBG
575     RtlCopyMemory(AccessToken->ImageFileName,
576                   PsGetCurrentProcess()->ImageFileName,
577                   min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
578 
579     AccessToken->ProcessCid = PsGetCurrentProcessId();
580     AccessToken->ThreadCid = PsGetCurrentThreadId();
581     AccessToken->CreateMethod = TOKEN_DUPLICATE_METHOD;
582 #endif
583 
584     /* Assign the data that reside in the token's variable information area */
585     AccessToken->VariableLength = VariableLength;
586     EndMem = (PVOID)&AccessToken->VariablePart;
587 
588     /* Copy the privileges */
589     AccessToken->PrivilegeCount = 0;
590     AccessToken->Privileges = NULL;
591     if (Token->Privileges && (Token->PrivilegeCount > 0))
592     {
593         ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
594         PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
595 
596         ASSERT(VariableLength >= PrivilegesLength);
597 
598         AccessToken->PrivilegeCount = Token->PrivilegeCount;
599         AccessToken->Privileges = EndMem;
600         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
601         VariableLength -= PrivilegesLength;
602 
603         RtlCopyMemory(AccessToken->Privileges,
604                       Token->Privileges,
605                       AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
606     }
607 
608     /* Copy the user and groups */
609     AccessToken->UserAndGroupCount = 0;
610     AccessToken->UserAndGroups = NULL;
611     if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
612     {
613         AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
614         AccessToken->UserAndGroups = EndMem;
615         EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
616         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
617 
618         Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
619                                               Token->UserAndGroups,
620                                               VariableLength,
621                                               AccessToken->UserAndGroups,
622                                               EndMem,
623                                               &EndMem,
624                                               &VariableLength);
625         if (!NT_SUCCESS(Status))
626         {
627             DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status);
628             goto Quit;
629         }
630     }
631 
632     /* Find the token primary group */
633     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
634                                                 Token->PrimaryGroup,
635                                                 NULL,
636                                                 &PrimaryGroupIndex,
637                                                 NULL);
638     if (!NT_SUCCESS(Status))
639     {
640         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
641         goto Quit;
642     }
643 
644     /* Copy the restricted SIDs */
645     AccessToken->RestrictedSidCount = 0;
646     AccessToken->RestrictedSids = NULL;
647     if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
648     {
649         AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
650         AccessToken->RestrictedSids = EndMem;
651         EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
652         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
653 
654         Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
655                                               Token->RestrictedSids,
656                                               VariableLength,
657                                               AccessToken->RestrictedSids,
658                                               EndMem,
659                                               &EndMem,
660                                               &VariableLength);
661         if (!NT_SUCCESS(Status))
662         {
663             DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status);
664             goto Quit;
665         }
666     }
667 
668     /*
669      * Filter the token by removing the disabled privileges
670      * and groups if the caller wants to duplicate an access
671      * token as effective only.
672      */
673     if (EffectiveOnly)
674     {
675         /* Begin querying the groups and search for disabled ones */
676         for (GroupsIndex = 0; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++)
677         {
678             /*
679              * A group or user is considered disabled if its attributes is either
680              * 0 or SE_GROUP_ENABLED is not included in the attributes flags list.
681              * That is because a certain user and/or group can have several attributes
682              * that bear no influence on whether a user/group is enabled or not
683              * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator
684              * that the group has just been enabled by default). A mandatory
685              * group (that is, the group has SE_GROUP_MANDATORY attribute)
686              * by standards it's always enabled and no one can disable it.
687              */
688             if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 ||
689                 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0)
690             {
691                 /*
692                  * If this group is an administrators group
693                  * and the token belongs to such group,
694                  * we've to take away TOKEN_HAS_ADMIN_GROUP
695                  * for the fact that's not enabled and as
696                  * such the token no longer belongs to
697                  * this group.
698                  */
699                 if (RtlEqualSid(SeAliasAdminsSid,
700                                 &AccessToken->UserAndGroups[GroupsIndex].Sid))
701                 {
702                     AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
703                 }
704 
705                 /*
706                  * A group is not enabled, it's time to remove
707                  * from the token and update the groups index
708                  * accordingly and continue with the next group.
709                  */
710                 SepRemoveUserGroupToken(AccessToken, GroupsIndex);
711                 GroupsIndex--;
712             }
713         }
714 
715         /* Begin querying the privileges and search for disabled ones */
716         for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++)
717         {
718             /*
719              * A privilege is considered disabled if its attributes is either
720              * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list.
721              * That is because a certain privilege can have several attributes
722              * that bear no influence on whether a privilege is enabled or not
723              * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator
724              * that the privilege has just been enabled by default).
725              */
726             if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 ||
727                 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0)
728             {
729                 /*
730                  * A privilege is not enabled, therefor it's time
731                  * to strip it from the token and continue with the next
732                  * privilege. Of course we must also want to update the
733                  * privileges index accordingly.
734                  */
735                 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex);
736                 PrivilegesIndex--;
737             }
738         }
739     }
740 
741     /* Now allocate the token's dynamic information area and set the data */
742     AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
743                                                      DynamicPartSize,
744                                                      TAG_TOKEN_DYNAMIC);
745     if (AccessToken->DynamicPart == NULL)
746     {
747         Status = STATUS_INSUFFICIENT_RESOURCES;
748         goto Quit;
749     }
750 
751     /* Unused memory in the dynamic area */
752     AccessToken->DynamicAvailable = 0;
753 
754     /*
755      * Assign the primary group to the token
756      * and put it in the dynamic part as well.
757      */
758     EndMem = (PVOID)AccessToken->DynamicPart;
759     AccessToken->PrimaryGroup = EndMem;
760     RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
761                             EndMem,
762                             AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
763     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
764     EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
765 
766     /*
767      * The existing token has a default DACL only
768      * if it has an allocated dynamic part.
769      */
770     if (Token->DynamicPart && Token->DefaultDacl)
771     {
772         AccessToken->DefaultDacl = EndMem;
773 
774         RtlCopyMemory(EndMem,
775                       Token->DefaultDacl,
776                       Token->DefaultDacl->AclSize);
777     }
778 
779     /* Return the token to the caller */
780     *NewAccessToken = AccessToken;
781     Status = STATUS_SUCCESS;
782 
783 Quit:
784     if (!NT_SUCCESS(Status))
785     {
786         /* Dereference the token, the delete procedure will clean it up */
787         ObDereferenceObject(AccessToken);
788     }
789 
790     /* Unlock the source token */
791     SepReleaseTokenLock(Token);
792 
793     return Status;
794 }
795 
796 /**
797  * @brief
798  * Private helper function responsible for creating a restricted access
799  * token, that is, a filtered token from privileges and groups and with
800  * restricted SIDs added into the token on demand by the caller.
801  *
802  * @param[in] Token
803  * An existing and valid access token.
804  *
805  * @param[in] PrivilegesToBeDeleted
806  * A list of privileges to be deleted within the token that's going
807  * to be filtered. This parameter is ignored if the caller wants to disable
808  * all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags
809  * parameter.
810  *
811  * @param[in] SidsToBeDisabled
812  * A list of group SIDs to be disabled within the token. This parameter
813  * can be NULL.
814  *
815  * @param[in] RestrictedSidsIntoToken
816  * A list of restricted SIDs to be added into the token. This parameter
817  * can be NULL.
818  *
819  * @param[in] PrivilegesCount
820  * The privilege count of the privileges list.
821  *
822  * @param[in] RegularGroupsSidCount
823  * The SIDs count of the group SIDs list.
824  *
825  * @param[in] RestrictedSidsCount
826  * The restricted SIDs count of restricted SIDs list.
827  *
828  * @param[in] PrivilegeFlags
829  * Influences how the privileges should be filtered in an access
830  * token. See NtFilterToken syscall for more information.
831  *
832  * @param[in] PreviousMode
833  * Processor level access mode.
834  *
835  * @param[out] FilteredToken
836  * The filtered token, returned to the caller.
837  *
838  * @return
839  * Returns STATUS_SUCCESS if token token filtering has completed successfully.
840  * STATUS_INVALID_PARAMETER is returned if one or more of the parameters
841  * do not meet the conditions imposed by the function. A failure NTSTATUS
842  * code is returned otherwise.
843  *
844  * @remarks
845  * The final outcome of privileges and/or SIDs filtering is not always
846  * deterministic. That is, any privileges or SIDs that aren't present
847  * in the access token are ignored and the function continues with the
848  * next privilege or SID to find for filtering. For a fully deterministic
849  * outcome the caller is responsible for querying the information details
850  * of privileges and SIDs present in the token and then afterwards use
851  * such obtained information to do any kind of filtering to the token.
852  */
853 static
854 NTSTATUS
855 SepPerformTokenFiltering(
856     _In_ PTOKEN Token,
857     _In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted,
858     _In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled,
859     _In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken,
860     _When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount,
861     _When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount,
862     _When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount,
863     _In_ ULONG PrivilegeFlags,
864     _In_ KPROCESSOR_MODE PreviousMode,
865     _Out_ PTOKEN *FilteredToken)
866 {
867     NTSTATUS Status;
868     PTOKEN AccessToken;
869     PVOID EndMem;
870     ULONG DynamicPartSize;
871     ULONG RestrictedSidsLength;
872     ULONG PrivilegesLength;
873     ULONG PrimaryGroupIndex;
874     ULONG RestrictedSidsInList;
875     ULONG RestrictedSidsInToken;
876     ULONG VariableLength, TotalSize;
877     ULONG PrivsInToken, PrivsInList;
878     ULONG GroupsInToken, GroupsInList;
879     BOOLEAN WantPrivilegesDisabled;
880     BOOLEAN FoundPrivilege;
881     BOOLEAN FoundGroup;
882 
883     PAGED_CODE();
884 
885     /* Ensure that the source token is valid, and lock it */
886     ASSERT(Token);
887     SepAcquireTokenLockShared(Token);
888 
889     /* Assume the caller doesn't want privileges disabled */
890     WantPrivilegesDisabled = FALSE;
891 
892     /* Assume we haven't found anything */
893     FoundPrivilege = FALSE;
894     FoundGroup = FALSE;
895 
896     /*
897      * Take the size that we need for filtered token
898      * allocation based upon the existing access token
899      * we've been given.
900      */
901     VariableLength = Token->VariableLength;
902 
903     if (RestrictedSidsIntoToken != NULL)
904     {
905         /*
906          * If the caller provided a list of restricted SIDs
907          * to be added onto the filtered access token then
908          * we must compute the size which is the total space
909          * of the current token and the length of the restricted
910          * SIDs for the filtered token.
911          */
912         RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES);
913         RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken);
914         RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID));
915 
916         /*
917          * The variable length of the token is not just
918          * the actual space length of the existing token
919          * but also the sum of the restricted SIDs length.
920          */
921         VariableLength += RestrictedSidsLength;
922         TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength;
923     }
924     else
925     {
926         /* Otherwise the size is of the actual current token */
927         TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
928     }
929 
930     /*
931      * Compute how much size we need to allocate
932      * the dynamic part of the newly duplicated
933      * token.
934      */
935     DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
936     DynamicPartSize += RtlLengthSid(Token->PrimaryGroup);
937 
938     /* Set up a filtered token object */
939     Status = ObCreateObject(PreviousMode,
940                             SeTokenObjectType,
941                             NULL,
942                             PreviousMode,
943                             NULL,
944                             TotalSize,
945                             Token->DynamicCharged,
946                             TotalSize,
947                             (PVOID*)&AccessToken);
948     if (!NT_SUCCESS(Status))
949     {
950         DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status);
951 
952         /* Unlock the source token and bail out */
953         SepReleaseTokenLock(Token);
954         return Status;
955     }
956 
957     /* Initialize the token and begin filling stuff to it */
958     RtlZeroMemory(AccessToken, TotalSize);
959 
960     /* Set up a lock for the new token */
961     Status = SepCreateTokenLock(AccessToken);
962     if (!NT_SUCCESS(Status))
963         goto Quit;
964 
965     /* Allocate new IDs for the token */
966     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
967     ExAllocateLocallyUniqueId(&AccessToken->ModifiedId);
968 
969     /* Copy the type and impersonation level from the token */
970     AccessToken->TokenType = Token->TokenType;
971     AccessToken->ImpersonationLevel = Token->ImpersonationLevel;
972 
973     /* Copy the immutable fields */
974     AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier;
975     RtlCopyMemory(AccessToken->TokenSource.SourceName,
976                   Token->TokenSource.SourceName,
977                   sizeof(Token->TokenSource.SourceName));
978 
979     AccessToken->AuthenticationId = Token->AuthenticationId;
980     AccessToken->ParentTokenId = Token->TokenId;
981     AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
982     AccessToken->DynamicCharged = Token->DynamicCharged;
983 
984     AccessToken->ExpirationTime = Token->ExpirationTime;
985 
986     /* Copy the mutable fields */
987     AccessToken->SessionId = Token->SessionId;
988     AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
989 
990     /* Reference the logon session */
991     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
992     if (!NT_SUCCESS(Status))
993     {
994         /* We failed, bail out*/
995         DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status);
996         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
997         goto Quit;
998     }
999 
1000     /* Insert the referenced logon session into the token */
1001     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
1002     if (!NT_SUCCESS(Status))
1003     {
1004         /* Failed to insert the logon session into the token, bail out */
1005         DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status);
1006         goto Quit;
1007     }
1008 
1009     /* Fill in token debug information */
1010 #if DBG
1011     RtlCopyMemory(AccessToken->ImageFileName,
1012                   PsGetCurrentProcess()->ImageFileName,
1013                   min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
1014 
1015     AccessToken->ProcessCid = PsGetCurrentProcessId();
1016     AccessToken->ThreadCid = PsGetCurrentThreadId();
1017     AccessToken->CreateMethod = TOKEN_FILTER_METHOD;
1018 #endif
1019 
1020     /* Assign the data that reside in the token's variable information area */
1021     AccessToken->VariableLength = VariableLength;
1022     EndMem = (PVOID)&AccessToken->VariablePart;
1023 
1024     /* Copy the privileges from the existing token */
1025     AccessToken->PrivilegeCount = 0;
1026     AccessToken->Privileges = NULL;
1027     if (Token->Privileges && (Token->PrivilegeCount > 0))
1028     {
1029         PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
1030         PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
1031 
1032         /*
1033          * Ensure that the token can actually hold all
1034          * the privileges from the existing token.
1035          * Otherwise something's seriously wrong and
1036          * we've to guard ourselves.
1037          */
1038         ASSERT(VariableLength >= PrivilegesLength);
1039 
1040         AccessToken->PrivilegeCount = Token->PrivilegeCount;
1041         AccessToken->Privileges = EndMem;
1042         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1043         VariableLength -= PrivilegesLength;
1044 
1045         RtlCopyMemory(AccessToken->Privileges,
1046                       Token->Privileges,
1047                       AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1048     }
1049 
1050     /* Copy the user and groups */
1051     AccessToken->UserAndGroupCount = 0;
1052     AccessToken->UserAndGroups = NULL;
1053     if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
1054     {
1055         AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
1056         AccessToken->UserAndGroups = EndMem;
1057         EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1058         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1059 
1060         Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
1061                                               Token->UserAndGroups,
1062                                               VariableLength,
1063                                               AccessToken->UserAndGroups,
1064                                               EndMem,
1065                                               &EndMem,
1066                                               &VariableLength);
1067         if (!NT_SUCCESS(Status))
1068         {
1069             DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status);
1070             goto Quit;
1071         }
1072     }
1073 
1074     /* Copy the restricted SIDs */
1075     AccessToken->RestrictedSidCount = 0;
1076     AccessToken->RestrictedSids = NULL;
1077     if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
1078     {
1079         AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
1080         AccessToken->RestrictedSids = EndMem;
1081         EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
1082         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
1083 
1084         Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
1085                                               Token->RestrictedSids,
1086                                               VariableLength,
1087                                               AccessToken->RestrictedSids,
1088                                               EndMem,
1089                                               &EndMem,
1090                                               &VariableLength);
1091         if (!NT_SUCCESS(Status))
1092         {
1093             DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status);
1094             goto Quit;
1095         }
1096     }
1097 
1098     /*
1099      * Insert the restricted SIDs into the token on
1100      * the request by the caller.
1101      */
1102     if (RestrictedSidsIntoToken != NULL)
1103     {
1104         for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++)
1105         {
1106             /* Did the caller assign attributes to the restricted SIDs? */
1107             if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0)
1108             {
1109                 /* There mustn't be any attributes, bail out */
1110                 DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n");
1111                 Status = STATUS_INVALID_PARAMETER;
1112                 goto Quit;
1113             }
1114         }
1115 
1116         /*
1117          * Ensure that the token can hold the restricted SIDs
1118          * (the variable length is calculated at the beginning
1119          * of the routine call).
1120          */
1121         ASSERT(VariableLength >= RestrictedSidsLength);
1122 
1123         /*
1124          * Now let's begin inserting the restricted SIDs into the filtered
1125          * access token from the list the caller gave us.
1126          */
1127         AccessToken->RestrictedSidCount = RestrictedSidsCount;
1128         AccessToken->RestrictedSids = EndMem;
1129         EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength);
1130         VariableLength -= RestrictedSidsLength;
1131 
1132         RtlCopyMemory(AccessToken->RestrictedSids,
1133                       RestrictedSidsIntoToken,
1134                       AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
1135 
1136         /*
1137          * As we've copied the restricted SIDs into
1138          * the token, we must assign them the following
1139          * combination of attributes SE_GROUP_ENABLED,
1140          * SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY.
1141          * With such attributes we estabilish that restricting
1142          * SIDs into the token are enabled for access checks.
1143          */
1144         for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++)
1145         {
1146             AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY);
1147         }
1148 
1149         /*
1150          * As we added restricted SIDs into the token, mark
1151          * it as restricted.
1152          */
1153         AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED;
1154     }
1155 
1156     /* Search for the primary group */
1157     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1158                                                 Token->PrimaryGroup,
1159                                                 NULL,
1160                                                 &PrimaryGroupIndex,
1161                                                 NULL);
1162     if (!NT_SUCCESS(Status))
1163     {
1164         DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status);
1165         goto Quit;
1166     }
1167 
1168     /* Now allocate the token's dynamic information area and set the data */
1169     AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1170                                                      DynamicPartSize,
1171                                                      TAG_TOKEN_DYNAMIC);
1172     if (AccessToken->DynamicPart == NULL)
1173     {
1174         Status = STATUS_INSUFFICIENT_RESOURCES;
1175         goto Quit;
1176     }
1177 
1178     /* Unused memory in the dynamic area */
1179     AccessToken->DynamicAvailable = 0;
1180 
1181     /*
1182      * Assign the primary group to the token
1183      * and put it in the dynamic part as well.
1184      */
1185     EndMem = (PVOID)AccessToken->DynamicPart;
1186     AccessToken->PrimaryGroup = EndMem;
1187     RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
1188                             EndMem,
1189                             AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
1190     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
1191     EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
1192 
1193     /*
1194      * The existing token has a default DACL only
1195      * if it has an allocated dynamic part.
1196      */
1197     if (Token->DynamicPart && Token->DefaultDacl)
1198     {
1199         AccessToken->DefaultDacl = EndMem;
1200 
1201         RtlCopyMemory(EndMem,
1202                       Token->DefaultDacl,
1203                       Token->DefaultDacl->AclSize);
1204     }
1205 
1206     /*
1207      * Now figure out what does the caller
1208      * want with the privileges.
1209      */
1210     if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE)
1211     {
1212         /*
1213          * The caller wants them disabled, cache this request
1214          * for later operations.
1215          */
1216         WantPrivilegesDisabled = TRUE;
1217     }
1218 
1219     if (PrivilegeFlags & SANDBOX_INERT)
1220     {
1221         /* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */
1222         AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT;
1223     }
1224 
1225     /*
1226      * Now it's time to filter the token's privileges.
1227      * Loop all the privileges in the token.
1228      */
1229     for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++)
1230     {
1231         if (WantPrivilegesDisabled)
1232         {
1233             /*
1234              * We got the acknowledgement that the caller wants
1235              * to disable all the privileges so let's just do it.
1236              * However, as per the general documentation is stated
1237              * that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept
1238              * therefore in that case we must skip this privilege.
1239              */
1240             if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
1241             {
1242                 continue;
1243             }
1244             else
1245             {
1246                 /*
1247                  * The act of disabling privileges actually means
1248                  * "deleting" them from the access token entirely.
1249                  * First we must disable them so that we can update
1250                  * token flags accordingly.
1251                  */
1252                 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
1253                 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
1254 
1255                 /* Remove the privileges now */
1256                 SepRemovePrivilegeToken(AccessToken, PrivsInToken);
1257                 PrivsInToken--;
1258             }
1259         }
1260         else
1261         {
1262             if (PrivilegesToBeDeleted != NULL)
1263             {
1264                 /* Loop the privileges we've got to delete */
1265                 for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++)
1266                 {
1267                     /* Does this privilege exist in the token? */
1268                     if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid,
1269                                      &PrivilegesToBeDeleted[PrivsInList].Luid))
1270                     {
1271                         /* Mark that we found it */
1272                         FoundPrivilege = TRUE;
1273                         break;
1274                     }
1275                 }
1276 
1277                 /* Did we find the privilege? */
1278                 if (PrivsInList == PrivilegesCount)
1279                 {
1280                     /* We didn't, continue with next one */
1281                     continue;
1282                 }
1283             }
1284         }
1285 
1286         /*
1287          * If we have found the target privilege in the token
1288          * based on the privileges list given by the caller
1289          * then begin deleting it.
1290          */
1291         if (FoundPrivilege)
1292         {
1293             /* Disable the privilege and update the flags */
1294             AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
1295             SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
1296 
1297             /* Delete the privilege */
1298             SepRemovePrivilegeToken(AccessToken, PrivsInToken);
1299 
1300             /*
1301              * Adjust the index and reset the FoundPrivilege indicator
1302              * so that we can continue with the next privilege to delete.
1303              */
1304             PrivsInToken--;
1305             FoundPrivilege = FALSE;
1306             continue;
1307         }
1308     }
1309 
1310     /*
1311      * Loop the group SIDs that we want to disable as
1312      * per on the request by the caller.
1313      */
1314     if (SidsToBeDisabled != NULL)
1315     {
1316         for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++)
1317         {
1318             for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++)
1319             {
1320                 /* Does this group SID exist in the token? */
1321                 if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid,
1322                                 &SidsToBeDisabled[GroupsInList].Sid))
1323                 {
1324                     /* Mark that we found it */
1325                     FoundGroup = TRUE;
1326                     break;
1327                 }
1328             }
1329 
1330             /* Did we find the group? */
1331             if (GroupsInList == RegularGroupsSidCount)
1332             {
1333                 /* We didn't, continue with next one */
1334                 continue;
1335             }
1336 
1337             /* If we have found the group, disable it */
1338             if (FoundGroup)
1339             {
1340                 /*
1341                  * If the acess token belongs to the administrators
1342                  * group and this is the target group, we must take
1343                  * away TOKEN_HAS_ADMIN_GROUP flag from the token.
1344                  */
1345                 if (RtlEqualSid(SeAliasAdminsSid,
1346                                 &AccessToken->UserAndGroups[GroupsInToken].Sid))
1347                 {
1348                     AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
1349                 }
1350 
1351                 /*
1352                  * If the target group that we have found it is the
1353                  * owner then from now on it no longer is but the user.
1354                  * Therefore assign the default owner index as the user.
1355                  */
1356                 if (AccessToken->DefaultOwnerIndex == GroupsInToken)
1357                 {
1358                     AccessToken->DefaultOwnerIndex = 0;
1359                 }
1360 
1361                 /*
1362                  * The principle of disabling a group SID is by
1363                  * taking away SE_GROUP_ENABLED_BY_DEFAULT and
1364                  * SE_GROUP_ENABLED attributes and assign
1365                  * SE_GROUP_USE_FOR_DENY_ONLY. This renders
1366                  * SID a "Deny only" SID.
1367                  */
1368                 AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
1369                 AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY;
1370 
1371                 /* Adjust the index and continue with the next group */
1372                 GroupsInToken--;
1373                 FoundGroup = FALSE;
1374                 continue;
1375             }
1376         }
1377     }
1378 
1379     /* We've finally filtered the token, return it to the caller */
1380     *FilteredToken = AccessToken;
1381     Status = STATUS_SUCCESS;
1382     DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n");
1383 
1384 Quit:
1385     if (!NT_SUCCESS(Status))
1386     {
1387         /* Dereference the created token */
1388         ObDereferenceObject(AccessToken);
1389     }
1390 
1391     /* Unlock the source token */
1392     SepReleaseTokenLock(Token);
1393 
1394     return Status;
1395 }
1396 
1397 /* PUBLIC FUNCTIONS ***********************************************************/
1398 
1399 /**
1400  * @brief
1401  * Filters an access token from an existing token, making it more restricted
1402  * than the previous one.
1403  *
1404  * @param[in] ExistingToken
1405  * An existing token for filtering.
1406  *
1407  * @param[in] Flags
1408  * Privilege flag options. This parameter argument influences how the token
1409  * is filtered. Such parameter can be 0. See NtFilterToken syscall for
1410  * more information.
1411  *
1412  * @param[in] SidsToDisable
1413  * Array of SIDs to disable. Such parameter can be NULL.
1414  *
1415  * @param[in] PrivilegesToDelete
1416  * Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified
1417  * in the Flags parameter, PrivilegesToDelete is ignored.
1418  *
1419  * @param[in] RestrictedSids
1420  * An array of restricted SIDs for the new filtered token. Such parameter
1421  * can be NULL.
1422  *
1423  * @param[out] FilteredToken
1424  * The newly filtered token, returned to the caller.
1425  *
1426  * @return
1427  * Returns STATUS_SUCCESS if the function has successfully completed its
1428  * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER
1429  * is returned if one or more of the parameter are not valid. A failure NTSTATUS code
1430  * is returned otherwise.
1431  */
1432 NTSTATUS
1433 NTAPI
1434 SeFilterToken(
1435     _In_ PACCESS_TOKEN ExistingToken,
1436     _In_ ULONG Flags,
1437     _In_opt_ PTOKEN_GROUPS SidsToDisable,
1438     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
1439     _In_opt_ PTOKEN_GROUPS RestrictedSids,
1440     _Out_ PACCESS_TOKEN *FilteredToken)
1441 {
1442     NTSTATUS Status;
1443     PTOKEN AccessToken;
1444     ULONG PrivilegesCount = 0;
1445     ULONG SidsCount = 0;
1446     ULONG RestrictedSidsCount = 0;
1447 
1448     PAGED_CODE();
1449 
1450     /* Begin copying the counters */
1451     if (SidsToDisable != NULL)
1452     {
1453         SidsCount = SidsToDisable->GroupCount;
1454     }
1455 
1456     if (PrivilegesToDelete != NULL)
1457     {
1458         PrivilegesCount = PrivilegesToDelete->PrivilegeCount;
1459     }
1460 
1461     if (RestrictedSids != NULL)
1462     {
1463         RestrictedSidsCount = RestrictedSids->GroupCount;
1464     }
1465 
1466     /* Call the internal API */
1467     Status = SepPerformTokenFiltering(ExistingToken,
1468                                       PrivilegesToDelete->Privileges,
1469                                       SidsToDisable->Groups,
1470                                       RestrictedSids->Groups,
1471                                       PrivilegesCount,
1472                                       SidsCount,
1473                                       RestrictedSidsCount,
1474                                       Flags,
1475                                       KernelMode,
1476                                       &AccessToken);
1477     if (!NT_SUCCESS(Status))
1478     {
1479         DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
1480         return Status;
1481     }
1482 
1483     /* Insert the filtered token */
1484     Status = ObInsertObject(AccessToken,
1485                             NULL,
1486                             0,
1487                             0,
1488                             NULL,
1489                             NULL);
1490     if (!NT_SUCCESS(Status))
1491     {
1492         DPRINT1("SeFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
1493         return Status;
1494     }
1495 
1496     /* Return it to the caller */
1497     *FilteredToken = AccessToken;
1498     return Status;
1499 }
1500 
1501 /* SYSTEM CALLS ***************************************************************/
1502 
1503 /**
1504  * @brief
1505  * Creates an access token.
1506  *
1507  * @param[out] TokenHandle
1508  * The returned created token handle to the caller.
1509  *
1510  * @param[in] DesiredAccess
1511  * The desired access rights for the token that we're creating.
1512  *
1513  * @param[in] ObjectAttributes
1514  * The object attributes for the token object that we're creating.
1515  *
1516  * @param[in] TokenType
1517  * The type of token to assign for the newly created token.
1518  *
1519  * @param[in] AuthenticationId
1520  * Authentication ID that represents the token's identity.
1521  *
1522  * @param[in] ExpirationTime
1523  * Expiration time for the token. If set to -1, the token never expires.
1524  *
1525  * @param[in] TokenUser
1526  * The main user entity for the token to assign.
1527  *
1528  * @param[in] TokenGroups
1529  * Group list of SIDs for the token to assign.
1530  *
1531  * @param[in] TokenPrivileges
1532  * Privileges for the token.
1533  *
1534  * @param[in] TokenOwner
1535  * The main user that owns the newly created token.
1536  *
1537  * @param[in] TokenPrimaryGroup
1538  * The primary group that represents as the main group of the token.
1539  *
1540  * @param[in] TokenDefaultDacl
1541  * Discretionary access control list for the token. This limits on how
1542  * the token can be used, accessed and used by whom.
1543  *
1544  * @param[in] TokenSource
1545  * The source origin of the token who creates it.
1546  *
1547  * @return
1548  * Returns STATUS_SUCCESS if the function has successfully created the token.
1549  * A failure NTSTATUS code is returned otherwise.
1550  */
1551 __kernel_entry
1552 NTSTATUS
1553 NTAPI
1554 NtCreateToken(
1555     _Out_ PHANDLE TokenHandle,
1556     _In_ ACCESS_MASK DesiredAccess,
1557     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1558     _In_ TOKEN_TYPE TokenType,
1559     _In_ PLUID AuthenticationId,
1560     _In_ PLARGE_INTEGER ExpirationTime,
1561     _In_ PTOKEN_USER TokenUser,
1562     _In_ PTOKEN_GROUPS TokenGroups,
1563     _In_ PTOKEN_PRIVILEGES TokenPrivileges,
1564     _In_opt_ PTOKEN_OWNER TokenOwner,
1565     _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
1566     _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
1567     _In_ PTOKEN_SOURCE TokenSource)
1568 {
1569     HANDLE hToken;
1570     KPROCESSOR_MODE PreviousMode;
1571     ULONG PrivilegeCount, GroupCount;
1572     PSID OwnerSid, PrimaryGroupSid;
1573     PACL DefaultDacl;
1574     LARGE_INTEGER LocalExpirationTime = {{0, 0}};
1575     LUID LocalAuthenticationId;
1576     TOKEN_SOURCE LocalTokenSource;
1577     SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
1578     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
1579     PSID_AND_ATTRIBUTES CapturedUser = NULL;
1580     PSID_AND_ATTRIBUTES CapturedGroups = NULL;
1581     PSID CapturedOwnerSid = NULL;
1582     PSID CapturedPrimaryGroupSid = NULL;
1583     PACL CapturedDefaultDacl = NULL;
1584     ULONG PrivilegesLength, UserLength, GroupsLength;
1585     NTSTATUS Status;
1586 
1587     PAGED_CODE();
1588 
1589     PreviousMode = ExGetPreviousMode();
1590 
1591     if (PreviousMode != KernelMode)
1592     {
1593         _SEH2_TRY
1594         {
1595             ProbeForWriteHandle(TokenHandle);
1596 
1597             if (ObjectAttributes != NULL)
1598             {
1599                 ProbeForRead(ObjectAttributes,
1600                              sizeof(OBJECT_ATTRIBUTES),
1601                              sizeof(ULONG));
1602                 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
1603             }
1604 
1605             ProbeForRead(AuthenticationId,
1606                          sizeof(LUID),
1607                          sizeof(ULONG));
1608             LocalAuthenticationId = *AuthenticationId;
1609 
1610             LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
1611 
1612             ProbeForRead(TokenUser,
1613                          sizeof(TOKEN_USER),
1614                          sizeof(ULONG));
1615 
1616             ProbeForRead(TokenGroups,
1617                          sizeof(TOKEN_GROUPS),
1618                          sizeof(ULONG));
1619             GroupCount = TokenGroups->GroupCount;
1620 
1621             ProbeForRead(TokenPrivileges,
1622                          sizeof(TOKEN_PRIVILEGES),
1623                          sizeof(ULONG));
1624             PrivilegeCount = TokenPrivileges->PrivilegeCount;
1625 
1626             if (TokenOwner != NULL)
1627             {
1628                 ProbeForRead(TokenOwner,
1629                              sizeof(TOKEN_OWNER),
1630                              sizeof(ULONG));
1631                 OwnerSid = TokenOwner->Owner;
1632             }
1633             else
1634             {
1635                 OwnerSid = NULL;
1636             }
1637 
1638             ProbeForRead(TokenPrimaryGroup,
1639                          sizeof(TOKEN_PRIMARY_GROUP),
1640                          sizeof(ULONG));
1641             PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
1642 
1643             if (TokenDefaultDacl != NULL)
1644             {
1645                 ProbeForRead(TokenDefaultDacl,
1646                              sizeof(TOKEN_DEFAULT_DACL),
1647                              sizeof(ULONG));
1648                 DefaultDacl = TokenDefaultDacl->DefaultDacl;
1649             }
1650             else
1651             {
1652                 DefaultDacl = NULL;
1653             }
1654 
1655             ProbeForRead(TokenSource,
1656                          sizeof(TOKEN_SOURCE),
1657                          sizeof(ULONG));
1658             LocalTokenSource = *TokenSource;
1659         }
1660         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1661         {
1662             /* Return the exception code */
1663             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1664         }
1665         _SEH2_END;
1666     }
1667     else
1668     {
1669         if (ObjectAttributes != NULL)
1670             LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
1671         LocalAuthenticationId = *AuthenticationId;
1672         LocalExpirationTime = *ExpirationTime;
1673         GroupCount = TokenGroups->GroupCount;
1674         PrivilegeCount = TokenPrivileges->PrivilegeCount;
1675         OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
1676         PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
1677         DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
1678         LocalTokenSource = *TokenSource;
1679     }
1680 
1681     /* Check token type */
1682     if ((TokenType < TokenPrimary) ||
1683         (TokenType > TokenImpersonation))
1684     {
1685         return STATUS_BAD_TOKEN_TYPE;
1686     }
1687 
1688     /* Check for token creation privilege */
1689     if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
1690     {
1691         return STATUS_PRIVILEGE_NOT_HELD;
1692     }
1693 
1694     /* Capture the user SID and attributes */
1695     Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
1696                                             1,
1697                                             PreviousMode,
1698                                             NULL,
1699                                             0,
1700                                             PagedPool,
1701                                             FALSE,
1702                                             &CapturedUser,
1703                                             &UserLength);
1704     if (!NT_SUCCESS(Status))
1705     {
1706         goto Cleanup;
1707     }
1708 
1709     /* Capture the groups SID and attributes array */
1710     Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
1711                                             GroupCount,
1712                                             PreviousMode,
1713                                             NULL,
1714                                             0,
1715                                             PagedPool,
1716                                             FALSE,
1717                                             &CapturedGroups,
1718                                             &GroupsLength);
1719     if (!NT_SUCCESS(Status))
1720     {
1721         goto Cleanup;
1722     }
1723 
1724     /* Capture privileges */
1725     Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
1726                                              PrivilegeCount,
1727                                              PreviousMode,
1728                                              NULL,
1729                                              0,
1730                                              PagedPool,
1731                                              FALSE,
1732                                              &CapturedPrivileges,
1733                                              &PrivilegesLength);
1734     if (!NT_SUCCESS(Status))
1735     {
1736         goto Cleanup;
1737     }
1738 
1739     /* Capture the token owner SID */
1740     if (TokenOwner != NULL)
1741     {
1742         Status = SepCaptureSid(OwnerSid,
1743                                PreviousMode,
1744                                PagedPool,
1745                                FALSE,
1746                                &CapturedOwnerSid);
1747         if (!NT_SUCCESS(Status))
1748         {
1749             goto Cleanup;
1750         }
1751     }
1752 
1753     /* Capture the token primary group SID */
1754     Status = SepCaptureSid(PrimaryGroupSid,
1755                            PreviousMode,
1756                            PagedPool,
1757                            FALSE,
1758                            &CapturedPrimaryGroupSid);
1759     if (!NT_SUCCESS(Status))
1760     {
1761         goto Cleanup;
1762     }
1763 
1764     /* Capture DefaultDacl */
1765     if (DefaultDacl != NULL)
1766     {
1767         Status = SepCaptureAcl(DefaultDacl,
1768                                PreviousMode,
1769                                NonPagedPool,
1770                                FALSE,
1771                                &CapturedDefaultDacl);
1772         if (!NT_SUCCESS(Status))
1773         {
1774             goto Cleanup;
1775         }
1776     }
1777 
1778     /* Call the internal function */
1779     Status = SepCreateToken(&hToken,
1780                             PreviousMode,
1781                             DesiredAccess,
1782                             ObjectAttributes,
1783                             TokenType,
1784                             LocalSecurityQos.ImpersonationLevel,
1785                             &LocalAuthenticationId,
1786                             &LocalExpirationTime,
1787                             CapturedUser,
1788                             GroupCount,
1789                             CapturedGroups,
1790                             GroupsLength,
1791                             PrivilegeCount,
1792                             CapturedPrivileges,
1793                             CapturedOwnerSid,
1794                             CapturedPrimaryGroupSid,
1795                             CapturedDefaultDacl,
1796                             &LocalTokenSource,
1797                             FALSE);
1798     if (NT_SUCCESS(Status))
1799     {
1800         _SEH2_TRY
1801         {
1802             *TokenHandle = hToken;
1803         }
1804         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1805         {
1806             Status = _SEH2_GetExceptionCode();
1807         }
1808         _SEH2_END;
1809     }
1810 
1811 Cleanup:
1812 
1813     /* Release what we captured */
1814     SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
1815     SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
1816     SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
1817     SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
1818     SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
1819     SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
1820 
1821     return Status;
1822 }
1823 
1824 /**
1825  * @brief
1826  * Duplicates a token.
1827  *
1828  * @param[in] ExistingTokenHandle
1829  * An existing token to duplicate.
1830  *
1831  * @param[in] DesiredAccess
1832  * The desired access rights for the new duplicated token.
1833  *
1834  * @param[in] ObjectAttributes
1835  * Object attributes for the new duplicated token.
1836  *
1837  * @param[in] EffectiveOnly
1838  * If set to TRUE, the function removes all the disabled privileges and groups
1839  * of the token to duplicate.
1840  *
1841  * @param[in] TokenType
1842  * Type of token to assign to the duplicated token.
1843  *
1844  * @param[out] NewTokenHandle
1845  * The returned duplicated token handle.
1846  *
1847  * @return
1848  * STATUS_SUCCESS is returned if token duplication has completed successfully.
1849  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants
1850  * to raise the impersonation level even though the conditions do not permit
1851  * it. A failure NTSTATUS code is returned otherwise.
1852  *
1853  * @remarks
1854  * Some sources claim 4th param is ImpersonationLevel, but on W2K
1855  * this is certainly NOT true, although I can't say for sure that EffectiveOnly
1856  * is correct either. -Gunnar
1857  * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
1858  * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
1859  * wrong in that regard, while MSDN documentation is correct.
1860  */
1861 _Must_inspect_result_
1862 __kernel_entry
1863 NTSTATUS
1864 NTAPI
1865 NtDuplicateToken(
1866     _In_ HANDLE ExistingTokenHandle,
1867     _In_ ACCESS_MASK DesiredAccess,
1868     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1869     _In_ BOOLEAN EffectiveOnly,
1870     _In_ TOKEN_TYPE TokenType,
1871     _Out_ PHANDLE NewTokenHandle)
1872 {
1873     KPROCESSOR_MODE PreviousMode;
1874     HANDLE hToken;
1875     PTOKEN Token;
1876     PTOKEN NewToken;
1877     PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService;
1878     BOOLEAN QoSPresent;
1879     OBJECT_HANDLE_INFORMATION HandleInformation;
1880     NTSTATUS Status;
1881 
1882     PAGED_CODE();
1883 
1884     if (TokenType != TokenImpersonation &&
1885         TokenType != TokenPrimary)
1886     {
1887         return STATUS_INVALID_PARAMETER;
1888     }
1889 
1890     PreviousMode = KeGetPreviousMode();
1891 
1892     if (PreviousMode != KernelMode)
1893     {
1894         _SEH2_TRY
1895         {
1896             ProbeForWriteHandle(NewTokenHandle);
1897         }
1898         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1899         {
1900             /* Return the exception code */
1901             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1902         }
1903         _SEH2_END;
1904     }
1905 
1906     Status = SepCaptureSecurityQualityOfService(ObjectAttributes,
1907                                                 PreviousMode,
1908                                                 PagedPool,
1909                                                 FALSE,
1910                                                 &CapturedSecurityQualityOfService,
1911                                                 &QoSPresent);
1912     if (!NT_SUCCESS(Status))
1913     {
1914         DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status);
1915         return Status;
1916     }
1917 
1918     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
1919                                        TOKEN_DUPLICATE,
1920                                        SeTokenObjectType,
1921                                        PreviousMode,
1922                                        (PVOID*)&Token,
1923                                        &HandleInformation);
1924     if (!NT_SUCCESS(Status))
1925     {
1926         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
1927         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1928                                            PreviousMode,
1929                                            FALSE);
1930         return Status;
1931     }
1932 
1933     /*
1934      * Fail, if the original token is an impersonation token and the caller
1935      * tries to raise the impersonation level of the new token above the
1936      * impersonation level of the original token.
1937      */
1938     if (Token->TokenType == TokenImpersonation)
1939     {
1940         if (QoSPresent &&
1941             CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel)
1942         {
1943             ObDereferenceObject(Token);
1944             SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1945                                                PreviousMode,
1946                                                FALSE);
1947             return STATUS_BAD_IMPERSONATION_LEVEL;
1948         }
1949     }
1950 
1951     /*
1952      * Fail, if a primary token is to be created from an impersonation token
1953      * and and the impersonation level of the impersonation token is below SecurityImpersonation.
1954      */
1955     if (Token->TokenType == TokenImpersonation &&
1956         TokenType == TokenPrimary &&
1957         Token->ImpersonationLevel < SecurityImpersonation)
1958     {
1959         ObDereferenceObject(Token);
1960         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1961                                            PreviousMode,
1962                                            FALSE);
1963         return STATUS_BAD_IMPERSONATION_LEVEL;
1964     }
1965 
1966     Status = SepDuplicateToken(Token,
1967                                ObjectAttributes,
1968                                EffectiveOnly,
1969                                TokenType,
1970                                (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous),
1971                                PreviousMode,
1972                                &NewToken);
1973 
1974     ObDereferenceObject(Token);
1975 
1976     if (NT_SUCCESS(Status))
1977     {
1978         Status = ObInsertObject(NewToken,
1979                                 NULL,
1980                                 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess),
1981                                 0,
1982                                 NULL,
1983                                 &hToken);
1984         if (NT_SUCCESS(Status))
1985         {
1986             _SEH2_TRY
1987             {
1988                 *NewTokenHandle = hToken;
1989             }
1990             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1991             {
1992                 Status = _SEH2_GetExceptionCode();
1993             }
1994             _SEH2_END;
1995         }
1996     }
1997 
1998     /* Free the captured structure */
1999     SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
2000                                        PreviousMode,
2001                                        FALSE);
2002 
2003     return Status;
2004 }
2005 
2006 /**
2007  * @brief
2008  * Creates an access token in a restricted form
2009  * from the original existing token, that is, such
2010  * action is called filtering.
2011  *
2012  * @param[in] ExistingTokenHandle
2013  * A handle to an access token which is to be filtered.
2014  *
2015  * @param[in] Flags
2016  * Privilege flag options. This parameter argument influences how the
2017  * token's privileges are filtered. For further details see remarks.
2018  *
2019  * @param[in] SidsToDisable
2020  * Array of SIDs to disable. The action of doing so assigns the
2021  * SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group
2022  * SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT.
2023  * This parameter can be NULL. This can be a UM pointer.
2024  *
2025  * @param[in] PrivilegesToDelete
2026  * Array of privileges to delete. The function will walk within this
2027  * array to determine if the specified privileges do exist in the
2028  * access token. Any missing privileges gets ignored. This parameter
2029  * can be NULL. This can be a UM pointer.
2030  *
2031  * @param[in] RestrictedSids
2032  * An array list of restricted groups SID to be added in the access
2033  * token. A token that is already restricted the newly added restricted
2034  * SIDs are redundant information in addition to the existing restricted
2035  * SIDs in the token. This parameter can be NULL. This can be a UM pointer.
2036  *
2037  * @param[out] NewTokenHandle
2038  * A new handle to the restricted (filtered) access token. This can be a
2039  * UM pointer.
2040  *
2041  * @return
2042  * Returns STATUS_SUCCESS if the routine has successfully filtered the
2043  * access token. STATUS_INVALID_PARAMETER is returned if one or more
2044  * parameters are not valid (see SepPerformTokenFiltering routine call
2045  * for more information). A failure NTSTATUS code is returned otherwise.
2046  *
2047  * @remarks
2048  * The Flags parameter determines the final outcome of how the privileges
2049  * in an access token are filtered. This parameter can take these supported
2050  * values (these can be combined):
2051  *
2052  * 0 -- Filter the token's privileges in the usual way. The function expects
2053  *      that the caller MUST PROVIDE a valid array list of privileges to be
2054  *      deleted (that is, PrivilegesToDelete MUSTN'T BE NULL).
2055  *
2056  * DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege
2057  *                          in the new access token. Bear in mind if this flag is specified
2058  *                          the routine ignores PrivilegesToDelete.
2059  *
2060  * SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token.
2061  *
2062  * LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not
2063  *              supported in Windows Server 2003.
2064  *
2065  * WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are
2066  *                     considered only when evaluating write access onto the token.
2067  *                     This value is not supported in Windows Server 2003.
2068  */
2069 NTSTATUS
2070 NTAPI
2071 NtFilterToken(
2072     _In_ HANDLE ExistingTokenHandle,
2073     _In_ ULONG Flags,
2074     _In_opt_ PTOKEN_GROUPS SidsToDisable,
2075     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
2076     _In_opt_ PTOKEN_GROUPS RestrictedSids,
2077     _Out_ PHANDLE NewTokenHandle)
2078 {
2079     PTOKEN Token, FilteredToken;
2080     HANDLE FilteredTokenHandle;
2081     NTSTATUS Status;
2082     KPROCESSOR_MODE PreviousMode;
2083     OBJECT_HANDLE_INFORMATION HandleInfo;
2084     ULONG ResultLength;
2085     ULONG CapturedSidsCount = 0;
2086     ULONG CapturedPrivilegesCount = 0;
2087     ULONG CapturedRestrictedSidsCount = 0;
2088     ULONG ProbeSize = 0;
2089     PSID_AND_ATTRIBUTES CapturedSids = NULL;
2090     PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL;
2091     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
2092 
2093     PAGED_CODE();
2094 
2095     PreviousMode = ExGetPreviousMode();
2096 
2097     _SEH2_TRY
2098     {
2099         /* Probe SidsToDisable */
2100         if (SidsToDisable != NULL)
2101         {
2102             /* Probe the header */
2103             ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG));
2104 
2105             CapturedSidsCount = SidsToDisable->GroupCount;
2106             ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]);
2107 
2108             ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG));
2109         }
2110 
2111         /* Probe PrivilegesToDelete */
2112         if (PrivilegesToDelete != NULL)
2113         {
2114             /* Probe the header */
2115             ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG));
2116 
2117             CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount;
2118             ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]);
2119 
2120             ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG));
2121         }
2122 
2123         /* Probe RestrictedSids */
2124         if (RestrictedSids != NULL)
2125         {
2126             /* Probe the header */
2127             ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG));
2128 
2129             CapturedRestrictedSidsCount = RestrictedSids->GroupCount;
2130             ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]);
2131 
2132             ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG));
2133         }
2134 
2135         /* Probe the handle */
2136         ProbeForWriteHandle(NewTokenHandle);
2137     }
2138     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2139     {
2140         /* Return the exception code */
2141         _SEH2_YIELD(return _SEH2_GetExceptionCode());
2142     }
2143     _SEH2_END;
2144 
2145     /* Reference the token */
2146     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
2147                                        TOKEN_DUPLICATE,
2148                                        SeTokenObjectType,
2149                                        PreviousMode,
2150                                        (PVOID*)&Token,
2151                                        &HandleInfo);
2152     if (!NT_SUCCESS(Status))
2153     {
2154         DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status);
2155         return Status;
2156     }
2157 
2158     /* Capture the group SIDs */
2159     if (SidsToDisable != NULL)
2160     {
2161         Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups,
2162                                                 CapturedSidsCount,
2163                                                 PreviousMode,
2164                                                 NULL,
2165                                                 0,
2166                                                 PagedPool,
2167                                                 TRUE,
2168                                                 &CapturedSids,
2169                                                 &ResultLength);
2170         if (!NT_SUCCESS(Status))
2171         {
2172             DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status);
2173             goto Quit;
2174         }
2175     }
2176 
2177     /* Capture the privileges */
2178     if (PrivilegesToDelete != NULL)
2179     {
2180         Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges,
2181                                                  CapturedPrivilegesCount,
2182                                                  PreviousMode,
2183                                                  NULL,
2184                                                  0,
2185                                                  PagedPool,
2186                                                  TRUE,
2187                                                  &CapturedPrivileges,
2188                                                  &ResultLength);
2189         if (!NT_SUCCESS(Status))
2190         {
2191             DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status);
2192             goto Quit;
2193         }
2194     }
2195 
2196     /* Capture the restricted SIDs */
2197     if (RestrictedSids != NULL)
2198     {
2199         Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups,
2200                                                 CapturedRestrictedSidsCount,
2201                                                 PreviousMode,
2202                                                 NULL,
2203                                                 0,
2204                                                 PagedPool,
2205                                                 TRUE,
2206                                                 &CapturedRestrictedSids,
2207                                                 &ResultLength);
2208         if (!NT_SUCCESS(Status))
2209         {
2210             DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status);
2211             goto Quit;
2212         }
2213     }
2214 
2215     /* Call the internal API */
2216     Status = SepPerformTokenFiltering(Token,
2217                                       CapturedPrivileges,
2218                                       CapturedSids,
2219                                       CapturedRestrictedSids,
2220                                       CapturedPrivilegesCount,
2221                                       CapturedSidsCount,
2222                                       CapturedRestrictedSidsCount,
2223                                       Flags,
2224                                       PreviousMode,
2225                                       &FilteredToken);
2226     if (!NT_SUCCESS(Status))
2227     {
2228         DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
2229         goto Quit;
2230     }
2231 
2232     /* Insert the filtered token and retrieve a handle to it */
2233     Status = ObInsertObject(FilteredToken,
2234                             NULL,
2235                             HandleInfo.GrantedAccess,
2236                             0,
2237                             NULL,
2238                             &FilteredTokenHandle);
2239     if (!NT_SUCCESS(Status))
2240     {
2241         DPRINT1("NtFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
2242         goto Quit;
2243     }
2244 
2245     /* And return it to the caller once we're done */
2246     _SEH2_TRY
2247     {
2248         *NewTokenHandle = FilteredTokenHandle;
2249     }
2250     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2251     {
2252         Status = _SEH2_GetExceptionCode();
2253         _SEH2_YIELD(goto Quit);
2254     }
2255     _SEH2_END;
2256 
2257 Quit:
2258     /* Dereference the token */
2259     ObDereferenceObject(Token);
2260 
2261     /* Release all the captured data */
2262     if (CapturedSids != NULL)
2263     {
2264         SeReleaseSidAndAttributesArray(CapturedSids,
2265                                        PreviousMode,
2266                                        TRUE);
2267     }
2268 
2269     if (CapturedPrivileges != NULL)
2270     {
2271         SeReleaseLuidAndAttributesArray(CapturedPrivileges,
2272                                         PreviousMode,
2273                                         TRUE);
2274     }
2275 
2276     if (CapturedRestrictedSids != NULL)
2277     {
2278         SeReleaseSidAndAttributesArray(CapturedRestrictedSids,
2279                                        PreviousMode,
2280                                        TRUE);
2281     }
2282 
2283     return Status;
2284 }
2285 
2286 /* EOF */
2287