xref: /reactos/ntoskrnl/se/tokenlif.c (revision a389f8aa)
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
SepCreateToken(_Out_ PHANDLE TokenHandle,_In_ KPROCESSOR_MODE PreviousMode,_In_ ACCESS_MASK DesiredAccess,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ TOKEN_TYPE TokenType,_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,_In_ PLUID AuthenticationId,_In_ PLARGE_INTEGER ExpirationTime,_In_ PSID_AND_ATTRIBUTES User,_In_ ULONG GroupCount,_In_ PSID_AND_ATTRIBUTES Groups,_In_ ULONG GroupsLength,_In_ ULONG PrivilegeCount,_In_ PLUID_AND_ATTRIBUTES Privileges,_In_opt_ PSID Owner,_In_ PSID PrimaryGroup,_In_opt_ PACL DefaultDacl,_In_ PTOKEN_SOURCE TokenSource,_In_ BOOLEAN SystemToken)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
SepDuplicateToken(_In_ PTOKEN Token,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ BOOLEAN EffectiveOnly,_In_ TOKEN_TYPE TokenType,_In_ SECURITY_IMPERSONATION_LEVEL Level,_In_ KPROCESSOR_MODE PreviousMode,_Out_ PTOKEN * NewAccessToken)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     /* Now allocate the token's dynamic information area and set the data */
669     AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
670                                                      DynamicPartSize,
671                                                      TAG_TOKEN_DYNAMIC);
672     if (AccessToken->DynamicPart == NULL)
673     {
674         Status = STATUS_INSUFFICIENT_RESOURCES;
675         goto Quit;
676     }
677 
678     /* Unused memory in the dynamic area */
679     AccessToken->DynamicAvailable = 0;
680 
681     /*
682      * Assign the primary group to the token
683      * and put it in the dynamic part as well.
684      */
685     EndMem = (PVOID)AccessToken->DynamicPart;
686     AccessToken->PrimaryGroup = EndMem;
687     RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
688                             EndMem,
689                             AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
690     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
691     EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
692 
693     /*
694      * The existing token has a default DACL only
695      * if it has an allocated dynamic part.
696      */
697     if (Token->DynamicPart && Token->DefaultDacl)
698     {
699         AccessToken->DefaultDacl = EndMem;
700 
701         RtlCopyMemory(EndMem,
702                       Token->DefaultDacl,
703                       Token->DefaultDacl->AclSize);
704     }
705 
706     /*
707      * Filter the token by removing the disabled privileges
708      * and groups if the caller wants to duplicate an access
709      * token as effective only.
710      */
711     if (EffectiveOnly)
712     {
713         /*
714          * Begin querying the groups and search for disabled ones. Do not touch the
715          * user which is at the first position because it cannot be disabled, no
716          * matter what attributes it has.
717          */
718         for (GroupsIndex = 1; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++)
719         {
720             /*
721              * A group is considered disabled if its attributes is either
722              * 0 or SE_GROUP_ENABLED is not included in the attributes flags list.
723              * That is because a certain user and/or group can have several attributes
724              * that bear no influence on whether a user/group is enabled or not
725              * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator
726              * that the group has just been enabled by default). A mandatory
727              * group (that is, the group has SE_GROUP_MANDATORY attribute)
728              * by standards it's always enabled and no one can disable it.
729              */
730             if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 ||
731                 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0)
732             {
733                 /*
734                  * If this group is an administrators group
735                  * and the token belongs to such group,
736                  * we've to take away TOKEN_HAS_ADMIN_GROUP
737                  * for the fact that's not enabled and as
738                  * such the token no longer belongs to
739                  * this group.
740                  */
741                 if (RtlEqualSid(SeAliasAdminsSid,
742                                 &AccessToken->UserAndGroups[GroupsIndex].Sid))
743                 {
744                     AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
745                 }
746 
747                 /*
748                  * A group is not enabled, it's time to remove
749                  * from the token and update the groups index
750                  * accordingly and continue with the next group.
751                  */
752                 SepRemoveUserGroupToken(AccessToken, GroupsIndex);
753                 GroupsIndex--;
754             }
755         }
756 
757         /* Begin querying the privileges and search for disabled ones */
758         for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++)
759         {
760             /*
761              * A privilege is considered disabled if its attributes is either
762              * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list.
763              * That is because a certain privilege can have several attributes
764              * that bear no influence on whether a privilege is enabled or not
765              * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator
766              * that the privilege has just been enabled by default).
767              */
768             if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 ||
769                 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0)
770             {
771                 /*
772                  * A privilege is not enabled, therefor it's time
773                  * to strip it from the token and continue with the next
774                  * privilege. Of course we must also want to update the
775                  * privileges index accordingly.
776                  */
777                 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex);
778                 PrivilegesIndex--;
779             }
780         }
781     }
782 
783     /* Return the token to the caller */
784     *NewAccessToken = AccessToken;
785     Status = STATUS_SUCCESS;
786 
787 Quit:
788     if (!NT_SUCCESS(Status))
789     {
790         /* Dereference the token, the delete procedure will clean it up */
791         ObDereferenceObject(AccessToken);
792     }
793 
794     /* Unlock the source token */
795     SepReleaseTokenLock(Token);
796 
797     return Status;
798 }
799 
800 /**
801  * @brief
802  * Private helper function responsible for creating a restricted access
803  * token, that is, a filtered token from privileges and groups and with
804  * restricted SIDs added into the token on demand by the caller.
805  *
806  * @param[in] Token
807  * An existing and valid access token.
808  *
809  * @param[in] PrivilegesToBeDeleted
810  * A list of privileges to be deleted within the token that's going
811  * to be filtered. This parameter is ignored if the caller wants to disable
812  * all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags
813  * parameter.
814  *
815  * @param[in] SidsToBeDisabled
816  * A list of group SIDs to be disabled within the token. This parameter
817  * can be NULL.
818  *
819  * @param[in] RestrictedSidsIntoToken
820  * A list of restricted SIDs to be added into the token. This parameter
821  * can be NULL.
822  *
823  * @param[in] PrivilegesCount
824  * The privilege count of the privileges list.
825  *
826  * @param[in] RegularGroupsSidCount
827  * The SIDs count of the group SIDs list.
828  *
829  * @param[in] RestrictedSidsCount
830  * The restricted SIDs count of restricted SIDs list.
831  *
832  * @param[in] PrivilegeFlags
833  * Influences how the privileges should be filtered in an access
834  * token. See NtFilterToken syscall for more information.
835  *
836  * @param[in] PreviousMode
837  * Processor level access mode.
838  *
839  * @param[out] FilteredToken
840  * The filtered token, returned to the caller.
841  *
842  * @return
843  * Returns STATUS_SUCCESS if token token filtering has completed successfully.
844  * STATUS_INVALID_PARAMETER is returned if one or more of the parameters
845  * do not meet the conditions imposed by the function. A failure NTSTATUS
846  * code is returned otherwise.
847  *
848  * @remarks
849  * The final outcome of privileges and/or SIDs filtering is not always
850  * deterministic. That is, any privileges or SIDs that aren't present
851  * in the access token are ignored and the function continues with the
852  * next privilege or SID to find for filtering. For a fully deterministic
853  * outcome the caller is responsible for querying the information details
854  * of privileges and SIDs present in the token and then afterwards use
855  * such obtained information to do any kind of filtering to the token.
856  */
857 static
858 NTSTATUS
859 SepPerformTokenFiltering(
860     _In_ PTOKEN Token,
861     _In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted,
862     _In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled,
863     _In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken,
864     _When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount,
865     _When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount,
866     _When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount,
867     _In_ ULONG PrivilegeFlags,
868     _In_ KPROCESSOR_MODE PreviousMode,
869     _Out_ PTOKEN *FilteredToken)
870 {
871     NTSTATUS Status;
872     PTOKEN AccessToken;
873     PVOID EndMem;
874     ULONG DynamicPartSize;
875     ULONG RestrictedSidsLength;
876     ULONG PrivilegesLength;
877     ULONG PrimaryGroupIndex;
878     ULONG RestrictedSidsInList;
879     ULONG RestrictedSidsInToken;
880     ULONG VariableLength, TotalSize;
881     ULONG PrivsInToken, PrivsInList;
882     ULONG GroupsInToken, GroupsInList;
883     BOOLEAN WantPrivilegesDisabled;
884     BOOLEAN FoundPrivilege;
885     BOOLEAN FoundGroup;
886 
887     PAGED_CODE();
888 
889     /* Ensure that the source token is valid, and lock it */
890     ASSERT(Token);
891     SepAcquireTokenLockShared(Token);
892 
893     /* Assume the caller doesn't want privileges disabled */
894     WantPrivilegesDisabled = FALSE;
895 
896     /* Assume we haven't found anything */
897     FoundPrivilege = FALSE;
898     FoundGroup = FALSE;
899 
900     /*
901      * Take the size that we need for filtered token
902      * allocation based upon the existing access token
903      * we've been given.
904      */
905     VariableLength = Token->VariableLength;
906 
907     if (RestrictedSidsIntoToken != NULL)
908     {
909         /*
910          * If the caller provided a list of restricted SIDs
911          * to be added onto the filtered access token then
912          * we must compute the size which is the total space
913          * of the current token and the length of the restricted
914          * SIDs for the filtered token.
915          */
916         RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES);
917         RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken);
918         RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID));
919 
920         /*
921          * The variable length of the token is not just
922          * the actual space length of the existing token
923          * but also the sum of the restricted SIDs length.
924          */
925         VariableLength += RestrictedSidsLength;
926         TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength;
927     }
928     else
929     {
930         /* Otherwise the size is of the actual current token */
931         TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
932     }
933 
934     /*
935      * Compute how much size we need to allocate
936      * the dynamic part of the newly duplicated
937      * token.
938      */
939     DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
940     DynamicPartSize += RtlLengthSid(Token->PrimaryGroup);
941 
942     /* Set up a filtered token object */
943     Status = ObCreateObject(PreviousMode,
944                             SeTokenObjectType,
945                             NULL,
946                             PreviousMode,
947                             NULL,
948                             TotalSize,
949                             Token->DynamicCharged,
950                             TotalSize,
951                             (PVOID*)&AccessToken);
952     if (!NT_SUCCESS(Status))
953     {
954         DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status);
955 
956         /* Unlock the source token and bail out */
957         SepReleaseTokenLock(Token);
958         return Status;
959     }
960 
961     /* Initialize the token and begin filling stuff to it */
962     RtlZeroMemory(AccessToken, TotalSize);
963 
964     /* Set up a lock for the new token */
965     Status = SepCreateTokenLock(AccessToken);
966     if (!NT_SUCCESS(Status))
967         goto Quit;
968 
969     /* Allocate new IDs for the token */
970     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
971     ExAllocateLocallyUniqueId(&AccessToken->ModifiedId);
972 
973     /* Copy the type and impersonation level from the token */
974     AccessToken->TokenType = Token->TokenType;
975     AccessToken->ImpersonationLevel = Token->ImpersonationLevel;
976 
977     /* Copy the immutable fields */
978     AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier;
979     RtlCopyMemory(AccessToken->TokenSource.SourceName,
980                   Token->TokenSource.SourceName,
981                   sizeof(Token->TokenSource.SourceName));
982 
983     AccessToken->AuthenticationId = Token->AuthenticationId;
984     AccessToken->ParentTokenId = Token->TokenId;
985     AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
986     AccessToken->DynamicCharged = Token->DynamicCharged;
987 
988     AccessToken->ExpirationTime = Token->ExpirationTime;
989 
990     /* Copy the mutable fields */
991     AccessToken->SessionId = Token->SessionId;
992     AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
993 
994     /* Reference the logon session */
995     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
996     if (!NT_SUCCESS(Status))
997     {
998         /* We failed, bail out*/
999         DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status);
1000         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
1001         goto Quit;
1002     }
1003 
1004     /* Insert the referenced logon session into the token */
1005     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
1006     if (!NT_SUCCESS(Status))
1007     {
1008         /* Failed to insert the logon session into the token, bail out */
1009         DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status);
1010         goto Quit;
1011     }
1012 
1013     /* Fill in token debug information */
1014 #if DBG
1015     RtlCopyMemory(AccessToken->ImageFileName,
1016                   PsGetCurrentProcess()->ImageFileName,
1017                   min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
1018 
1019     AccessToken->ProcessCid = PsGetCurrentProcessId();
1020     AccessToken->ThreadCid = PsGetCurrentThreadId();
1021     AccessToken->CreateMethod = TOKEN_FILTER_METHOD;
1022 #endif
1023 
1024     /* Assign the data that reside in the token's variable information area */
1025     AccessToken->VariableLength = VariableLength;
1026     EndMem = (PVOID)&AccessToken->VariablePart;
1027 
1028     /* Copy the privileges from the existing token */
1029     AccessToken->PrivilegeCount = 0;
1030     AccessToken->Privileges = NULL;
1031     if (Token->Privileges && (Token->PrivilegeCount > 0))
1032     {
1033         PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
1034         PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
1035 
1036         /*
1037          * Ensure that the token can actually hold all
1038          * the privileges from the existing token.
1039          * Otherwise something's seriously wrong and
1040          * we've to guard ourselves.
1041          */
1042         ASSERT(VariableLength >= PrivilegesLength);
1043 
1044         AccessToken->PrivilegeCount = Token->PrivilegeCount;
1045         AccessToken->Privileges = EndMem;
1046         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1047         VariableLength -= PrivilegesLength;
1048 
1049         RtlCopyMemory(AccessToken->Privileges,
1050                       Token->Privileges,
1051                       AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1052     }
1053 
1054     /* Copy the user and groups */
1055     AccessToken->UserAndGroupCount = 0;
1056     AccessToken->UserAndGroups = NULL;
1057     if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
1058     {
1059         AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
1060         AccessToken->UserAndGroups = EndMem;
1061         EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1062         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1063 
1064         Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
1065                                               Token->UserAndGroups,
1066                                               VariableLength,
1067                                               AccessToken->UserAndGroups,
1068                                               EndMem,
1069                                               &EndMem,
1070                                               &VariableLength);
1071         if (!NT_SUCCESS(Status))
1072         {
1073             DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status);
1074             goto Quit;
1075         }
1076     }
1077 
1078     /* Copy the restricted SIDs */
1079     AccessToken->RestrictedSidCount = 0;
1080     AccessToken->RestrictedSids = NULL;
1081     if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
1082     {
1083         AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
1084         AccessToken->RestrictedSids = EndMem;
1085         EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
1086         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
1087 
1088         Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
1089                                               Token->RestrictedSids,
1090                                               VariableLength,
1091                                               AccessToken->RestrictedSids,
1092                                               EndMem,
1093                                               &EndMem,
1094                                               &VariableLength);
1095         if (!NT_SUCCESS(Status))
1096         {
1097             DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status);
1098             goto Quit;
1099         }
1100     }
1101 
1102     /*
1103      * Insert the restricted SIDs into the token on
1104      * the request by the caller.
1105      */
1106     if (RestrictedSidsIntoToken != NULL)
1107     {
1108         for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++)
1109         {
1110             /* Did the caller assign attributes to the restricted SIDs? */
1111             if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0)
1112             {
1113                 /* There mustn't be any attributes, bail out */
1114                 DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n");
1115                 Status = STATUS_INVALID_PARAMETER;
1116                 goto Quit;
1117             }
1118         }
1119 
1120         /*
1121          * Ensure that the token can hold the restricted SIDs
1122          * (the variable length is calculated at the beginning
1123          * of the routine call).
1124          */
1125         ASSERT(VariableLength >= RestrictedSidsLength);
1126 
1127         /*
1128          * Now let's begin inserting the restricted SIDs into the filtered
1129          * access token from the list the caller gave us.
1130          */
1131         AccessToken->RestrictedSidCount = RestrictedSidsCount;
1132         AccessToken->RestrictedSids = EndMem;
1133         EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength);
1134         VariableLength -= RestrictedSidsLength;
1135 
1136         RtlCopyMemory(AccessToken->RestrictedSids,
1137                       RestrictedSidsIntoToken,
1138                       AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
1139 
1140         /*
1141          * As we've copied the restricted SIDs into
1142          * the token, we must assign them the following
1143          * combination of attributes SE_GROUP_ENABLED,
1144          * SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY.
1145          * With such attributes we estabilish that restricting
1146          * SIDs into the token are enabled for access checks.
1147          */
1148         for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++)
1149         {
1150             AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY);
1151         }
1152 
1153         /*
1154          * As we added restricted SIDs into the token, mark
1155          * it as restricted.
1156          */
1157         AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED;
1158     }
1159 
1160     /* Search for the primary group */
1161     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1162                                                 Token->PrimaryGroup,
1163                                                 NULL,
1164                                                 &PrimaryGroupIndex,
1165                                                 NULL);
1166     if (!NT_SUCCESS(Status))
1167     {
1168         DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status);
1169         goto Quit;
1170     }
1171 
1172     /* Now allocate the token's dynamic information area and set the data */
1173     AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1174                                                      DynamicPartSize,
1175                                                      TAG_TOKEN_DYNAMIC);
1176     if (AccessToken->DynamicPart == NULL)
1177     {
1178         Status = STATUS_INSUFFICIENT_RESOURCES;
1179         goto Quit;
1180     }
1181 
1182     /* Unused memory in the dynamic area */
1183     AccessToken->DynamicAvailable = 0;
1184 
1185     /*
1186      * Assign the primary group to the token
1187      * and put it in the dynamic part as well.
1188      */
1189     EndMem = (PVOID)AccessToken->DynamicPart;
1190     AccessToken->PrimaryGroup = EndMem;
1191     RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
1192                             EndMem,
1193                             AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
1194     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
1195     EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
1196 
1197     /*
1198      * The existing token has a default DACL only
1199      * if it has an allocated dynamic part.
1200      */
1201     if (Token->DynamicPart && Token->DefaultDacl)
1202     {
1203         AccessToken->DefaultDacl = EndMem;
1204 
1205         RtlCopyMemory(EndMem,
1206                       Token->DefaultDacl,
1207                       Token->DefaultDacl->AclSize);
1208     }
1209 
1210     /*
1211      * Now figure out what does the caller
1212      * want with the privileges.
1213      */
1214     if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE)
1215     {
1216         /*
1217          * The caller wants them disabled, cache this request
1218          * for later operations.
1219          */
1220         WantPrivilegesDisabled = TRUE;
1221     }
1222 
1223     if (PrivilegeFlags & SANDBOX_INERT)
1224     {
1225         /* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */
1226         AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT;
1227     }
1228 
1229     /*
1230      * Now it's time to filter the token's privileges.
1231      * Loop all the privileges in the token.
1232      */
1233     for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++)
1234     {
1235         if (WantPrivilegesDisabled)
1236         {
1237             /*
1238              * We got the acknowledgement that the caller wants
1239              * to disable all the privileges so let's just do it.
1240              * However, as per the general documentation is stated
1241              * that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept
1242              * therefore in that case we must skip this privilege.
1243              */
1244             if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
1245             {
1246                 continue;
1247             }
1248             else
1249             {
1250                 /*
1251                  * The act of disabling privileges actually means
1252                  * "deleting" them from the access token entirely.
1253                  * First we must disable them so that we can update
1254                  * token flags accordingly.
1255                  */
1256                 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
1257                 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
1258 
1259                 /* Remove the privileges now */
1260                 SepRemovePrivilegeToken(AccessToken, PrivsInToken);
1261                 PrivsInToken--;
1262             }
1263         }
1264         else
1265         {
1266             if (PrivilegesToBeDeleted != NULL)
1267             {
1268                 /* Loop the privileges we've got to delete */
1269                 for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++)
1270                 {
1271                     /* Does this privilege exist in the token? */
1272                     if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid,
1273                                      &PrivilegesToBeDeleted[PrivsInList].Luid))
1274                     {
1275                         /* Mark that we found it */
1276                         FoundPrivilege = TRUE;
1277                         break;
1278                     }
1279                 }
1280 
1281                 /* Did we find the privilege? */
1282                 if (PrivsInList == PrivilegesCount)
1283                 {
1284                     /* We didn't, continue with next one */
1285                     continue;
1286                 }
1287             }
1288         }
1289 
1290         /*
1291          * If we have found the target privilege in the token
1292          * based on the privileges list given by the caller
1293          * then begin deleting it.
1294          */
1295         if (FoundPrivilege)
1296         {
1297             /* Disable the privilege and update the flags */
1298             AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
1299             SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
1300 
1301             /* Delete the privilege */
1302             SepRemovePrivilegeToken(AccessToken, PrivsInToken);
1303 
1304             /*
1305              * Adjust the index and reset the FoundPrivilege indicator
1306              * so that we can continue with the next privilege to delete.
1307              */
1308             PrivsInToken--;
1309             FoundPrivilege = FALSE;
1310             continue;
1311         }
1312     }
1313 
1314     /*
1315      * Loop the group SIDs that we want to disable as
1316      * per on the request by the caller.
1317      */
1318     if (SidsToBeDisabled != NULL)
1319     {
1320         for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++)
1321         {
1322             for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++)
1323             {
1324                 /* Does this group SID exist in the token? */
1325                 if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid,
1326                                 &SidsToBeDisabled[GroupsInList].Sid))
1327                 {
1328                     /* Mark that we found it */
1329                     FoundGroup = TRUE;
1330                     break;
1331                 }
1332             }
1333 
1334             /* Did we find the group? */
1335             if (GroupsInList == RegularGroupsSidCount)
1336             {
1337                 /* We didn't, continue with next one */
1338                 continue;
1339             }
1340 
1341             /* If we have found the group, disable it */
1342             if (FoundGroup)
1343             {
1344                 /*
1345                  * If the acess token belongs to the administrators
1346                  * group and this is the target group, we must take
1347                  * away TOKEN_HAS_ADMIN_GROUP flag from the token.
1348                  */
1349                 if (RtlEqualSid(SeAliasAdminsSid,
1350                                 &AccessToken->UserAndGroups[GroupsInToken].Sid))
1351                 {
1352                     AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
1353                 }
1354 
1355                 /*
1356                  * If the target group that we have found it is the
1357                  * owner then from now on it no longer is but the user.
1358                  * Therefore assign the default owner index as the user.
1359                  */
1360                 if (AccessToken->DefaultOwnerIndex == GroupsInToken)
1361                 {
1362                     AccessToken->DefaultOwnerIndex = 0;
1363                 }
1364 
1365                 /*
1366                  * The principle of disabling a group SID is by
1367                  * taking away SE_GROUP_ENABLED_BY_DEFAULT and
1368                  * SE_GROUP_ENABLED attributes and assign
1369                  * SE_GROUP_USE_FOR_DENY_ONLY. This renders
1370                  * SID a "Deny only" SID.
1371                  */
1372                 AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
1373                 AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY;
1374 
1375                 /* Adjust the index and continue with the next group */
1376                 GroupsInToken--;
1377                 FoundGroup = FALSE;
1378                 continue;
1379             }
1380         }
1381     }
1382 
1383     /* We've finally filtered the token, return it to the caller */
1384     *FilteredToken = AccessToken;
1385     Status = STATUS_SUCCESS;
1386     DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n");
1387 
1388 Quit:
1389     if (!NT_SUCCESS(Status))
1390     {
1391         /* Dereference the created token */
1392         ObDereferenceObject(AccessToken);
1393     }
1394 
1395     /* Unlock the source token */
1396     SepReleaseTokenLock(Token);
1397 
1398     return Status;
1399 }
1400 
1401 /* PUBLIC FUNCTIONS ***********************************************************/
1402 
1403 /**
1404  * @brief
1405  * Filters an access token from an existing token, making it more restricted
1406  * than the previous one.
1407  *
1408  * @param[in] ExistingToken
1409  * An existing token for filtering.
1410  *
1411  * @param[in] Flags
1412  * Privilege flag options. This parameter argument influences how the token
1413  * is filtered. Such parameter can be 0. See NtFilterToken syscall for
1414  * more information.
1415  *
1416  * @param[in] SidsToDisable
1417  * Array of SIDs to disable. Such parameter can be NULL.
1418  *
1419  * @param[in] PrivilegesToDelete
1420  * Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified
1421  * in the Flags parameter, PrivilegesToDelete is ignored.
1422  *
1423  * @param[in] RestrictedSids
1424  * An array of restricted SIDs for the new filtered token. Such parameter
1425  * can be NULL.
1426  *
1427  * @param[out] FilteredToken
1428  * The newly filtered token, returned to the caller.
1429  *
1430  * @return
1431  * Returns STATUS_SUCCESS if the function has successfully completed its
1432  * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER
1433  * is returned if one or more of the parameter are not valid. A failure NTSTATUS code
1434  * is returned otherwise.
1435  */
1436 NTSTATUS
1437 NTAPI
SeFilterToken(_In_ PACCESS_TOKEN ExistingToken,_In_ ULONG Flags,_In_opt_ PTOKEN_GROUPS SidsToDisable,_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,_In_opt_ PTOKEN_GROUPS RestrictedSids,_Out_ PACCESS_TOKEN * FilteredToken)1438 SeFilterToken(
1439     _In_ PACCESS_TOKEN ExistingToken,
1440     _In_ ULONG Flags,
1441     _In_opt_ PTOKEN_GROUPS SidsToDisable,
1442     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
1443     _In_opt_ PTOKEN_GROUPS RestrictedSids,
1444     _Out_ PACCESS_TOKEN *FilteredToken)
1445 {
1446     NTSTATUS Status;
1447     PTOKEN AccessToken;
1448     ULONG PrivilegesCount = 0;
1449     ULONG SidsCount = 0;
1450     ULONG RestrictedSidsCount = 0;
1451 
1452     PAGED_CODE();
1453 
1454     /* Begin copying the counters */
1455     if (SidsToDisable != NULL)
1456     {
1457         SidsCount = SidsToDisable->GroupCount;
1458     }
1459 
1460     if (PrivilegesToDelete != NULL)
1461     {
1462         PrivilegesCount = PrivilegesToDelete->PrivilegeCount;
1463     }
1464 
1465     if (RestrictedSids != NULL)
1466     {
1467         RestrictedSidsCount = RestrictedSids->GroupCount;
1468     }
1469 
1470     /* Call the internal API */
1471     Status = SepPerformTokenFiltering(ExistingToken,
1472                                       PrivilegesToDelete->Privileges,
1473                                       SidsToDisable->Groups,
1474                                       RestrictedSids->Groups,
1475                                       PrivilegesCount,
1476                                       SidsCount,
1477                                       RestrictedSidsCount,
1478                                       Flags,
1479                                       KernelMode,
1480                                       &AccessToken);
1481     if (!NT_SUCCESS(Status))
1482     {
1483         DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
1484         return Status;
1485     }
1486 
1487     /* Insert the filtered token */
1488     Status = ObInsertObject(AccessToken,
1489                             NULL,
1490                             0,
1491                             0,
1492                             NULL,
1493                             NULL);
1494     if (!NT_SUCCESS(Status))
1495     {
1496         DPRINT1("SeFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
1497         return Status;
1498     }
1499 
1500     /* Return it to the caller */
1501     *FilteredToken = AccessToken;
1502     return Status;
1503 }
1504 
1505 /* SYSTEM CALLS ***************************************************************/
1506 
1507 /**
1508  * @brief
1509  * Creates an access token.
1510  *
1511  * @param[out] TokenHandle
1512  * The returned created token handle to the caller.
1513  *
1514  * @param[in] DesiredAccess
1515  * The desired access rights for the token that we're creating.
1516  *
1517  * @param[in] ObjectAttributes
1518  * The object attributes for the token object that we're creating.
1519  *
1520  * @param[in] TokenType
1521  * The type of token to assign for the newly created token.
1522  *
1523  * @param[in] AuthenticationId
1524  * Authentication ID that represents the token's identity.
1525  *
1526  * @param[in] ExpirationTime
1527  * Expiration time for the token. If set to -1, the token never expires.
1528  *
1529  * @param[in] TokenUser
1530  * The main user entity for the token to assign.
1531  *
1532  * @param[in] TokenGroups
1533  * Group list of SIDs for the token to assign.
1534  *
1535  * @param[in] TokenPrivileges
1536  * Privileges for the token.
1537  *
1538  * @param[in] TokenOwner
1539  * The main user that owns the newly created token.
1540  *
1541  * @param[in] TokenPrimaryGroup
1542  * The primary group that represents as the main group of the token.
1543  *
1544  * @param[in] TokenDefaultDacl
1545  * Discretionary access control list for the token. This limits on how
1546  * the token can be used, accessed and used by whom.
1547  *
1548  * @param[in] TokenSource
1549  * The source origin of the token who creates it.
1550  *
1551  * @return
1552  * Returns STATUS_SUCCESS if the function has successfully created the token.
1553  * A failure NTSTATUS code is returned otherwise.
1554  */
1555 __kernel_entry
1556 NTSTATUS
1557 NTAPI
NtCreateToken(_Out_ PHANDLE TokenHandle,_In_ ACCESS_MASK DesiredAccess,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ TOKEN_TYPE TokenType,_In_ PLUID AuthenticationId,_In_ PLARGE_INTEGER ExpirationTime,_In_ PTOKEN_USER TokenUser,_In_ PTOKEN_GROUPS TokenGroups,_In_ PTOKEN_PRIVILEGES TokenPrivileges,_In_opt_ PTOKEN_OWNER TokenOwner,_In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,_In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,_In_ PTOKEN_SOURCE TokenSource)1558 NtCreateToken(
1559     _Out_ PHANDLE TokenHandle,
1560     _In_ ACCESS_MASK DesiredAccess,
1561     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1562     _In_ TOKEN_TYPE TokenType,
1563     _In_ PLUID AuthenticationId,
1564     _In_ PLARGE_INTEGER ExpirationTime,
1565     _In_ PTOKEN_USER TokenUser,
1566     _In_ PTOKEN_GROUPS TokenGroups,
1567     _In_ PTOKEN_PRIVILEGES TokenPrivileges,
1568     _In_opt_ PTOKEN_OWNER TokenOwner,
1569     _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
1570     _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
1571     _In_ PTOKEN_SOURCE TokenSource)
1572 {
1573     HANDLE hToken;
1574     KPROCESSOR_MODE PreviousMode;
1575     ULONG PrivilegeCount, GroupCount;
1576     PSID OwnerSid, PrimaryGroupSid;
1577     PACL DefaultDacl;
1578     LARGE_INTEGER LocalExpirationTime = {{0, 0}};
1579     LUID LocalAuthenticationId;
1580     TOKEN_SOURCE LocalTokenSource;
1581     SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
1582     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
1583     PSID_AND_ATTRIBUTES CapturedUser = NULL;
1584     PSID_AND_ATTRIBUTES CapturedGroups = NULL;
1585     PSID CapturedOwnerSid = NULL;
1586     PSID CapturedPrimaryGroupSid = NULL;
1587     PACL CapturedDefaultDacl = NULL;
1588     ULONG PrivilegesLength, UserLength, GroupsLength;
1589     NTSTATUS Status;
1590 
1591     PAGED_CODE();
1592 
1593     PreviousMode = ExGetPreviousMode();
1594 
1595     if (PreviousMode != KernelMode)
1596     {
1597         _SEH2_TRY
1598         {
1599             ProbeForWriteHandle(TokenHandle);
1600 
1601             if (ObjectAttributes != NULL)
1602             {
1603                 ProbeForRead(ObjectAttributes,
1604                              sizeof(OBJECT_ATTRIBUTES),
1605                              sizeof(ULONG));
1606                 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
1607             }
1608 
1609             ProbeForRead(AuthenticationId,
1610                          sizeof(LUID),
1611                          sizeof(ULONG));
1612             LocalAuthenticationId = *AuthenticationId;
1613 
1614             LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
1615 
1616             ProbeForRead(TokenUser,
1617                          sizeof(TOKEN_USER),
1618                          sizeof(ULONG));
1619 
1620             ProbeForRead(TokenGroups,
1621                          sizeof(TOKEN_GROUPS),
1622                          sizeof(ULONG));
1623             GroupCount = TokenGroups->GroupCount;
1624 
1625             ProbeForRead(TokenPrivileges,
1626                          sizeof(TOKEN_PRIVILEGES),
1627                          sizeof(ULONG));
1628             PrivilegeCount = TokenPrivileges->PrivilegeCount;
1629 
1630             if (TokenOwner != NULL)
1631             {
1632                 ProbeForRead(TokenOwner,
1633                              sizeof(TOKEN_OWNER),
1634                              sizeof(ULONG));
1635                 OwnerSid = TokenOwner->Owner;
1636             }
1637             else
1638             {
1639                 OwnerSid = NULL;
1640             }
1641 
1642             ProbeForRead(TokenPrimaryGroup,
1643                          sizeof(TOKEN_PRIMARY_GROUP),
1644                          sizeof(ULONG));
1645             PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
1646 
1647             if (TokenDefaultDacl != NULL)
1648             {
1649                 ProbeForRead(TokenDefaultDacl,
1650                              sizeof(TOKEN_DEFAULT_DACL),
1651                              sizeof(ULONG));
1652                 DefaultDacl = TokenDefaultDacl->DefaultDacl;
1653             }
1654             else
1655             {
1656                 DefaultDacl = NULL;
1657             }
1658 
1659             ProbeForRead(TokenSource,
1660                          sizeof(TOKEN_SOURCE),
1661                          sizeof(ULONG));
1662             LocalTokenSource = *TokenSource;
1663         }
1664         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1665         {
1666             /* Return the exception code */
1667             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1668         }
1669         _SEH2_END;
1670     }
1671     else
1672     {
1673         if (ObjectAttributes != NULL)
1674             LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
1675         LocalAuthenticationId = *AuthenticationId;
1676         LocalExpirationTime = *ExpirationTime;
1677         GroupCount = TokenGroups->GroupCount;
1678         PrivilegeCount = TokenPrivileges->PrivilegeCount;
1679         OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
1680         PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
1681         DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
1682         LocalTokenSource = *TokenSource;
1683     }
1684 
1685     /* Check token type */
1686     if ((TokenType < TokenPrimary) ||
1687         (TokenType > TokenImpersonation))
1688     {
1689         return STATUS_BAD_TOKEN_TYPE;
1690     }
1691 
1692     /* Check for token creation privilege */
1693     if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
1694     {
1695         return STATUS_PRIVILEGE_NOT_HELD;
1696     }
1697 
1698     /* Capture the user SID and attributes */
1699     Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
1700                                             1,
1701                                             PreviousMode,
1702                                             NULL,
1703                                             0,
1704                                             PagedPool,
1705                                             FALSE,
1706                                             &CapturedUser,
1707                                             &UserLength);
1708     if (!NT_SUCCESS(Status))
1709     {
1710         goto Cleanup;
1711     }
1712 
1713     /* Capture the groups SID and attributes array */
1714     Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
1715                                             GroupCount,
1716                                             PreviousMode,
1717                                             NULL,
1718                                             0,
1719                                             PagedPool,
1720                                             FALSE,
1721                                             &CapturedGroups,
1722                                             &GroupsLength);
1723     if (!NT_SUCCESS(Status))
1724     {
1725         goto Cleanup;
1726     }
1727 
1728     /* Capture privileges */
1729     Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
1730                                              PrivilegeCount,
1731                                              PreviousMode,
1732                                              NULL,
1733                                              0,
1734                                              PagedPool,
1735                                              FALSE,
1736                                              &CapturedPrivileges,
1737                                              &PrivilegesLength);
1738     if (!NT_SUCCESS(Status))
1739     {
1740         goto Cleanup;
1741     }
1742 
1743     /* Capture the token owner SID */
1744     if (TokenOwner != NULL)
1745     {
1746         Status = SepCaptureSid(OwnerSid,
1747                                PreviousMode,
1748                                PagedPool,
1749                                FALSE,
1750                                &CapturedOwnerSid);
1751         if (!NT_SUCCESS(Status))
1752         {
1753             goto Cleanup;
1754         }
1755     }
1756 
1757     /* Capture the token primary group SID */
1758     Status = SepCaptureSid(PrimaryGroupSid,
1759                            PreviousMode,
1760                            PagedPool,
1761                            FALSE,
1762                            &CapturedPrimaryGroupSid);
1763     if (!NT_SUCCESS(Status))
1764     {
1765         goto Cleanup;
1766     }
1767 
1768     /* Capture DefaultDacl */
1769     if (DefaultDacl != NULL)
1770     {
1771         Status = SepCaptureAcl(DefaultDacl,
1772                                PreviousMode,
1773                                NonPagedPool,
1774                                FALSE,
1775                                &CapturedDefaultDacl);
1776         if (!NT_SUCCESS(Status))
1777         {
1778             goto Cleanup;
1779         }
1780     }
1781 
1782     /* Call the internal function */
1783     Status = SepCreateToken(&hToken,
1784                             PreviousMode,
1785                             DesiredAccess,
1786                             ObjectAttributes,
1787                             TokenType,
1788                             LocalSecurityQos.ImpersonationLevel,
1789                             &LocalAuthenticationId,
1790                             &LocalExpirationTime,
1791                             CapturedUser,
1792                             GroupCount,
1793                             CapturedGroups,
1794                             GroupsLength,
1795                             PrivilegeCount,
1796                             CapturedPrivileges,
1797                             CapturedOwnerSid,
1798                             CapturedPrimaryGroupSid,
1799                             CapturedDefaultDacl,
1800                             &LocalTokenSource,
1801                             FALSE);
1802     if (NT_SUCCESS(Status))
1803     {
1804         _SEH2_TRY
1805         {
1806             *TokenHandle = hToken;
1807         }
1808         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1809         {
1810             Status = _SEH2_GetExceptionCode();
1811         }
1812         _SEH2_END;
1813     }
1814 
1815 Cleanup:
1816 
1817     /* Release what we captured */
1818     SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
1819     SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
1820     SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
1821     SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
1822     SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
1823     SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
1824 
1825     return Status;
1826 }
1827 
1828 /**
1829  * @brief
1830  * Duplicates a token.
1831  *
1832  * @param[in] ExistingTokenHandle
1833  * An existing token to duplicate.
1834  *
1835  * @param[in] DesiredAccess
1836  * The desired access rights for the new duplicated token.
1837  *
1838  * @param[in] ObjectAttributes
1839  * Object attributes for the new duplicated token.
1840  *
1841  * @param[in] EffectiveOnly
1842  * If set to TRUE, the function removes all the disabled privileges and groups
1843  * of the token to duplicate.
1844  *
1845  * @param[in] TokenType
1846  * Type of token to assign to the duplicated token.
1847  *
1848  * @param[out] NewTokenHandle
1849  * The returned duplicated token handle.
1850  *
1851  * @return
1852  * STATUS_SUCCESS is returned if token duplication has completed successfully.
1853  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants
1854  * to raise the impersonation level even though the conditions do not permit
1855  * it. A failure NTSTATUS code is returned otherwise.
1856  *
1857  * @remarks
1858  * Some sources claim 4th param is ImpersonationLevel, but on W2K
1859  * this is certainly NOT true, although I can't say for sure that EffectiveOnly
1860  * is correct either. -Gunnar
1861  * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
1862  * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
1863  * wrong in that regard, while MSDN documentation is correct.
1864  */
1865 _Must_inspect_result_
1866 __kernel_entry
1867 NTSTATUS
1868 NTAPI
NtDuplicateToken(_In_ HANDLE ExistingTokenHandle,_In_ ACCESS_MASK DesiredAccess,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ BOOLEAN EffectiveOnly,_In_ TOKEN_TYPE TokenType,_Out_ PHANDLE NewTokenHandle)1869 NtDuplicateToken(
1870     _In_ HANDLE ExistingTokenHandle,
1871     _In_ ACCESS_MASK DesiredAccess,
1872     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1873     _In_ BOOLEAN EffectiveOnly,
1874     _In_ TOKEN_TYPE TokenType,
1875     _Out_ PHANDLE NewTokenHandle)
1876 {
1877     KPROCESSOR_MODE PreviousMode;
1878     HANDLE hToken;
1879     PTOKEN Token;
1880     PTOKEN NewToken;
1881     PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService;
1882     BOOLEAN QoSPresent;
1883     OBJECT_HANDLE_INFORMATION HandleInformation;
1884     NTSTATUS Status;
1885 
1886     PAGED_CODE();
1887 
1888     if (TokenType != TokenImpersonation &&
1889         TokenType != TokenPrimary)
1890     {
1891         return STATUS_INVALID_PARAMETER;
1892     }
1893 
1894     PreviousMode = KeGetPreviousMode();
1895 
1896     if (PreviousMode != KernelMode)
1897     {
1898         _SEH2_TRY
1899         {
1900             ProbeForWriteHandle(NewTokenHandle);
1901         }
1902         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1903         {
1904             /* Return the exception code */
1905             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1906         }
1907         _SEH2_END;
1908     }
1909 
1910     Status = SepCaptureSecurityQualityOfService(ObjectAttributes,
1911                                                 PreviousMode,
1912                                                 PagedPool,
1913                                                 FALSE,
1914                                                 &CapturedSecurityQualityOfService,
1915                                                 &QoSPresent);
1916     if (!NT_SUCCESS(Status))
1917     {
1918         DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status);
1919         return Status;
1920     }
1921 
1922     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
1923                                        TOKEN_DUPLICATE,
1924                                        SeTokenObjectType,
1925                                        PreviousMode,
1926                                        (PVOID*)&Token,
1927                                        &HandleInformation);
1928     if (!NT_SUCCESS(Status))
1929     {
1930         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
1931         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1932                                            PreviousMode,
1933                                            FALSE);
1934         return Status;
1935     }
1936 
1937     /*
1938      * Fail, if the original token is an impersonation token and the caller
1939      * tries to raise the impersonation level of the new token above the
1940      * impersonation level of the original token.
1941      */
1942     if (Token->TokenType == TokenImpersonation)
1943     {
1944         if (QoSPresent &&
1945             CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel)
1946         {
1947             ObDereferenceObject(Token);
1948             SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1949                                                PreviousMode,
1950                                                FALSE);
1951             return STATUS_BAD_IMPERSONATION_LEVEL;
1952         }
1953     }
1954 
1955     /*
1956      * Fail, if a primary token is to be created from an impersonation token
1957      * and and the impersonation level of the impersonation token is below SecurityImpersonation.
1958      */
1959     if (Token->TokenType == TokenImpersonation &&
1960         TokenType == TokenPrimary &&
1961         Token->ImpersonationLevel < SecurityImpersonation)
1962     {
1963         ObDereferenceObject(Token);
1964         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1965                                            PreviousMode,
1966                                            FALSE);
1967         return STATUS_BAD_IMPERSONATION_LEVEL;
1968     }
1969 
1970     Status = SepDuplicateToken(Token,
1971                                ObjectAttributes,
1972                                EffectiveOnly,
1973                                TokenType,
1974                                (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous),
1975                                PreviousMode,
1976                                &NewToken);
1977 
1978     ObDereferenceObject(Token);
1979 
1980     if (NT_SUCCESS(Status))
1981     {
1982         Status = ObInsertObject(NewToken,
1983                                 NULL,
1984                                 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess),
1985                                 0,
1986                                 NULL,
1987                                 &hToken);
1988         if (NT_SUCCESS(Status))
1989         {
1990             _SEH2_TRY
1991             {
1992                 *NewTokenHandle = hToken;
1993             }
1994             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1995             {
1996                 Status = _SEH2_GetExceptionCode();
1997             }
1998             _SEH2_END;
1999         }
2000     }
2001 
2002     /* Free the captured structure */
2003     SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
2004                                        PreviousMode,
2005                                        FALSE);
2006 
2007     return Status;
2008 }
2009 
2010 /**
2011  * @brief
2012  * Creates an access token in a restricted form
2013  * from the original existing token, that is, such
2014  * action is called filtering.
2015  *
2016  * @param[in] ExistingTokenHandle
2017  * A handle to an access token which is to be filtered.
2018  *
2019  * @param[in] Flags
2020  * Privilege flag options. This parameter argument influences how the
2021  * token's privileges are filtered. For further details see remarks.
2022  *
2023  * @param[in] SidsToDisable
2024  * Array of SIDs to disable. The action of doing so assigns the
2025  * SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group
2026  * SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT.
2027  * This parameter can be NULL. This can be a UM pointer.
2028  *
2029  * @param[in] PrivilegesToDelete
2030  * Array of privileges to delete. The function will walk within this
2031  * array to determine if the specified privileges do exist in the
2032  * access token. Any missing privileges gets ignored. This parameter
2033  * can be NULL. This can be a UM pointer.
2034  *
2035  * @param[in] RestrictedSids
2036  * An array list of restricted groups SID to be added in the access
2037  * token. A token that is already restricted the newly added restricted
2038  * SIDs are redundant information in addition to the existing restricted
2039  * SIDs in the token. This parameter can be NULL. This can be a UM pointer.
2040  *
2041  * @param[out] NewTokenHandle
2042  * A new handle to the restricted (filtered) access token. This can be a
2043  * UM pointer.
2044  *
2045  * @return
2046  * Returns STATUS_SUCCESS if the routine has successfully filtered the
2047  * access token. STATUS_INVALID_PARAMETER is returned if one or more
2048  * parameters are not valid (see SepPerformTokenFiltering routine call
2049  * for more information). A failure NTSTATUS code is returned otherwise.
2050  *
2051  * @remarks
2052  * The Flags parameter determines the final outcome of how the privileges
2053  * in an access token are filtered. This parameter can take these supported
2054  * values (these can be combined):
2055  *
2056  * 0 -- Filter the token's privileges in the usual way. The function expects
2057  *      that the caller MUST PROVIDE a valid array list of privileges to be
2058  *      deleted (that is, PrivilegesToDelete MUSTN'T BE NULL).
2059  *
2060  * DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege
2061  *                          in the new access token. Bear in mind if this flag is specified
2062  *                          the routine ignores PrivilegesToDelete.
2063  *
2064  * SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token.
2065  *
2066  * LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not
2067  *              supported in Windows Server 2003.
2068  *
2069  * WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are
2070  *                     considered only when evaluating write access onto the token.
2071  *                     This value is not supported in Windows Server 2003.
2072  */
2073 NTSTATUS
2074 NTAPI
NtFilterToken(_In_ HANDLE ExistingTokenHandle,_In_ ULONG Flags,_In_opt_ PTOKEN_GROUPS SidsToDisable,_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,_In_opt_ PTOKEN_GROUPS RestrictedSids,_Out_ PHANDLE NewTokenHandle)2075 NtFilterToken(
2076     _In_ HANDLE ExistingTokenHandle,
2077     _In_ ULONG Flags,
2078     _In_opt_ PTOKEN_GROUPS SidsToDisable,
2079     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
2080     _In_opt_ PTOKEN_GROUPS RestrictedSids,
2081     _Out_ PHANDLE NewTokenHandle)
2082 {
2083     PTOKEN Token, FilteredToken;
2084     HANDLE FilteredTokenHandle;
2085     NTSTATUS Status;
2086     KPROCESSOR_MODE PreviousMode;
2087     OBJECT_HANDLE_INFORMATION HandleInfo;
2088     ULONG ResultLength;
2089     ULONG CapturedSidsCount = 0;
2090     ULONG CapturedPrivilegesCount = 0;
2091     ULONG CapturedRestrictedSidsCount = 0;
2092     ULONG ProbeSize = 0;
2093     PSID_AND_ATTRIBUTES CapturedSids = NULL;
2094     PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL;
2095     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
2096 
2097     PAGED_CODE();
2098 
2099     PreviousMode = ExGetPreviousMode();
2100 
2101     _SEH2_TRY
2102     {
2103         /* Probe SidsToDisable */
2104         if (SidsToDisable != NULL)
2105         {
2106             /* Probe the header */
2107             ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG));
2108 
2109             CapturedSidsCount = SidsToDisable->GroupCount;
2110             ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]);
2111 
2112             ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG));
2113         }
2114 
2115         /* Probe PrivilegesToDelete */
2116         if (PrivilegesToDelete != NULL)
2117         {
2118             /* Probe the header */
2119             ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG));
2120 
2121             CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount;
2122             ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]);
2123 
2124             ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG));
2125         }
2126 
2127         /* Probe RestrictedSids */
2128         if (RestrictedSids != NULL)
2129         {
2130             /* Probe the header */
2131             ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG));
2132 
2133             CapturedRestrictedSidsCount = RestrictedSids->GroupCount;
2134             ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]);
2135 
2136             ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG));
2137         }
2138 
2139         /* Probe the handle */
2140         ProbeForWriteHandle(NewTokenHandle);
2141     }
2142     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2143     {
2144         /* Return the exception code */
2145         _SEH2_YIELD(return _SEH2_GetExceptionCode());
2146     }
2147     _SEH2_END;
2148 
2149     /* Reference the token */
2150     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
2151                                        TOKEN_DUPLICATE,
2152                                        SeTokenObjectType,
2153                                        PreviousMode,
2154                                        (PVOID*)&Token,
2155                                        &HandleInfo);
2156     if (!NT_SUCCESS(Status))
2157     {
2158         DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status);
2159         return Status;
2160     }
2161 
2162     /* Capture the group SIDs */
2163     if (SidsToDisable != NULL)
2164     {
2165         Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups,
2166                                                 CapturedSidsCount,
2167                                                 PreviousMode,
2168                                                 NULL,
2169                                                 0,
2170                                                 PagedPool,
2171                                                 TRUE,
2172                                                 &CapturedSids,
2173                                                 &ResultLength);
2174         if (!NT_SUCCESS(Status))
2175         {
2176             DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status);
2177             goto Quit;
2178         }
2179     }
2180 
2181     /* Capture the privileges */
2182     if (PrivilegesToDelete != NULL)
2183     {
2184         Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges,
2185                                                  CapturedPrivilegesCount,
2186                                                  PreviousMode,
2187                                                  NULL,
2188                                                  0,
2189                                                  PagedPool,
2190                                                  TRUE,
2191                                                  &CapturedPrivileges,
2192                                                  &ResultLength);
2193         if (!NT_SUCCESS(Status))
2194         {
2195             DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status);
2196             goto Quit;
2197         }
2198     }
2199 
2200     /* Capture the restricted SIDs */
2201     if (RestrictedSids != NULL)
2202     {
2203         Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups,
2204                                                 CapturedRestrictedSidsCount,
2205                                                 PreviousMode,
2206                                                 NULL,
2207                                                 0,
2208                                                 PagedPool,
2209                                                 TRUE,
2210                                                 &CapturedRestrictedSids,
2211                                                 &ResultLength);
2212         if (!NT_SUCCESS(Status))
2213         {
2214             DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status);
2215             goto Quit;
2216         }
2217     }
2218 
2219     /* Call the internal API */
2220     Status = SepPerformTokenFiltering(Token,
2221                                       CapturedPrivileges,
2222                                       CapturedSids,
2223                                       CapturedRestrictedSids,
2224                                       CapturedPrivilegesCount,
2225                                       CapturedSidsCount,
2226                                       CapturedRestrictedSidsCount,
2227                                       Flags,
2228                                       PreviousMode,
2229                                       &FilteredToken);
2230     if (!NT_SUCCESS(Status))
2231     {
2232         DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
2233         goto Quit;
2234     }
2235 
2236     /* Insert the filtered token and retrieve a handle to it */
2237     Status = ObInsertObject(FilteredToken,
2238                             NULL,
2239                             HandleInfo.GrantedAccess,
2240                             0,
2241                             NULL,
2242                             &FilteredTokenHandle);
2243     if (!NT_SUCCESS(Status))
2244     {
2245         DPRINT1("NtFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
2246         goto Quit;
2247     }
2248 
2249     /* And return it to the caller once we're done */
2250     _SEH2_TRY
2251     {
2252         *NewTokenHandle = FilteredTokenHandle;
2253     }
2254     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2255     {
2256         Status = _SEH2_GetExceptionCode();
2257         _SEH2_YIELD(goto Quit);
2258     }
2259     _SEH2_END;
2260 
2261 Quit:
2262     /* Dereference the token */
2263     ObDereferenceObject(Token);
2264 
2265     /* Release all the captured data */
2266     if (CapturedSids != NULL)
2267     {
2268         SeReleaseSidAndAttributesArray(CapturedSids,
2269                                        PreviousMode,
2270                                        TRUE);
2271     }
2272 
2273     if (CapturedPrivileges != NULL)
2274     {
2275         SeReleaseLuidAndAttributesArray(CapturedPrivileges,
2276                                         PreviousMode,
2277                                         TRUE);
2278     }
2279 
2280     if (CapturedRestrictedSids != NULL)
2281     {
2282         SeReleaseSidAndAttributesArray(CapturedRestrictedSids,
2283                                        PreviousMode,
2284                                        TRUE);
2285     }
2286 
2287     return Status;
2288 }
2289 
2290 /* EOF */
2291