xref: /reactos/ntoskrnl/se/token.c (revision 8a92b556)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/se/token.c
5  * PURPOSE:         Security manager
6  *
7  * PROGRAMMERS:     David Welch <welch@cwcom.net>
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 #include <ntlsa.h>
16 
17 typedef struct _TOKEN_AUDIT_POLICY_INFORMATION
18 {
19     ULONG PolicyCount;
20     struct
21     {
22         ULONG Category;
23         UCHAR Value;
24     } Policies[1];
25 } TOKEN_AUDIT_POLICY_INFORMATION, *PTOKEN_AUDIT_POLICY_INFORMATION;
26 
27 /* GLOBALS ********************************************************************/
28 
29 POBJECT_TYPE SeTokenObjectType = NULL;
30 ERESOURCE SepTokenLock; // FIXME: Global lock!
31 
32 TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}};
33 LUID SeSystemAuthenticationId = SYSTEM_LUID;
34 LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID;
35 
36 static GENERIC_MAPPING SepTokenMapping = {
37     TOKEN_READ,
38     TOKEN_WRITE,
39     TOKEN_EXECUTE,
40     TOKEN_ALL_ACCESS
41 };
42 
43 static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = {
44 
45     /* Class 0 not used, blame MS! */
46     ICI_SQ_SAME( 0, 0, 0),
47 
48     /* TokenUser */
49     ICI_SQ_SAME( sizeof(TOKEN_USER),                   sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
50     /* TokenGroups */
51     ICI_SQ_SAME( sizeof(TOKEN_GROUPS),                 sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
52     /* TokenPrivileges */
53     ICI_SQ_SAME( sizeof(TOKEN_PRIVILEGES),             sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
54     /* TokenOwner */
55     ICI_SQ_SAME( sizeof(TOKEN_OWNER),                  sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
56     /* TokenPrimaryGroup */
57     ICI_SQ_SAME( sizeof(TOKEN_PRIMARY_GROUP),          sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
58     /* TokenDefaultDacl */
59     ICI_SQ_SAME( sizeof(TOKEN_DEFAULT_DACL),           sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
60     /* TokenSource */
61     ICI_SQ_SAME( sizeof(TOKEN_SOURCE),                 sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
62     /* TokenType */
63     ICI_SQ_SAME( sizeof(TOKEN_TYPE),                   sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
64     /* TokenImpersonationLevel */
65     ICI_SQ_SAME( sizeof(SECURITY_IMPERSONATION_LEVEL), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
66     /* TokenStatistics */
67     ICI_SQ_SAME( sizeof(TOKEN_STATISTICS),             sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
68     /* TokenRestrictedSids */
69     ICI_SQ_SAME( sizeof(TOKEN_GROUPS),                 sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
70     /* TokenSessionId */
71     ICI_SQ_SAME( sizeof(ULONG),                        sizeof(ULONG), ICIF_QUERY | ICIF_SET ),
72     /* TokenGroupsAndPrivileges */
73     ICI_SQ_SAME( sizeof(TOKEN_GROUPS_AND_PRIVILEGES),  sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
74     /* TokenSessionReference */
75     ICI_SQ_SAME( sizeof(ULONG),                        sizeof(ULONG), ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
76     /* TokenSandBoxInert */
77     ICI_SQ_SAME( sizeof(ULONG),                        sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
78     /* TokenAuditPolicy */
79     ICI_SQ_SAME( /* FIXME */0,                         sizeof(ULONG), ICIF_QUERY | ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
80     /* TokenOrigin */
81     ICI_SQ_SAME( sizeof(TOKEN_ORIGIN),                 sizeof(ULONG), ICIF_QUERY | ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
82 };
83 
84 /* FUNCTIONS *****************************************************************/
85 
86 static NTSTATUS
87 SepCompareTokens(IN PTOKEN FirstToken,
88                  IN PTOKEN SecondToken,
89                  OUT PBOOLEAN Equal)
90 {
91     BOOLEAN Restricted, IsEqual = FALSE;
92 
93     ASSERT(FirstToken != SecondToken);
94 
95     /* Lock the tokens */
96     SepAcquireTokenLockShared(FirstToken);
97     SepAcquireTokenLockShared(SecondToken);
98 
99     /* FIXME: Check if every SID that is present in either token is also present in the other one */
100 
101     Restricted = SeTokenIsRestricted(FirstToken);
102     if (Restricted == SeTokenIsRestricted(SecondToken))
103     {
104         if (Restricted)
105         {
106             /* FIXME: Check if every SID that is restricted in either token is also restricted in the other one */
107         }
108 
109         /* FIXME: Check if every privilege that is present in either token is also present in the other one */
110         DPRINT1("FIXME: Pretending tokens are equal!\n");
111         IsEqual = TRUE;
112     }
113 
114     /* Unlock the tokens */
115     SepReleaseTokenLock(SecondToken);
116     SepReleaseTokenLock(FirstToken);
117 
118     *Equal = IsEqual;
119     return STATUS_SUCCESS;
120 }
121 
122 static
123 VOID
124 SepUpdateSinglePrivilegeFlagToken(
125     _Inout_ PTOKEN Token,
126     _In_ ULONG Index)
127 {
128     ULONG TokenFlag;
129     ASSERT(Index < Token->PrivilegeCount);
130 
131     /* The high part of all values we are interested in is 0 */
132     if (Token->Privileges[Index].Luid.HighPart != 0)
133     {
134         return;
135     }
136 
137     /* Check for certain privileges to update flags */
138     if (Token->Privileges[Index].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
139     {
140         TokenFlag = TOKEN_HAS_TRAVERSE_PRIVILEGE;
141     }
142     else if (Token->Privileges[Index].Luid.LowPart == SE_BACKUP_PRIVILEGE)
143     {
144         TokenFlag = TOKEN_HAS_BACKUP_PRIVILEGE;
145     }
146     else if (Token->Privileges[Index].Luid.LowPart == SE_RESTORE_PRIVILEGE)
147     {
148         TokenFlag = TOKEN_HAS_RESTORE_PRIVILEGE;
149     }
150     else if (Token->Privileges[Index].Luid.LowPart == SE_IMPERSONATE_PRIVILEGE)
151     {
152         TokenFlag = TOKEN_HAS_IMPERSONATE_PRIVILEGE;
153     }
154     else
155     {
156         /* Nothing to do */
157         return;
158     }
159 
160     /* Check if the specified privilege is enabled */
161     if (Token->Privileges[Index].Attributes & SE_PRIVILEGE_ENABLED)
162     {
163         /* It is enabled, so set the flag */
164         Token->TokenFlags |= TokenFlag;
165     }
166     else
167     {
168         /* Is is disabled, so remove the flag */
169         Token->TokenFlags &= ~TokenFlag;
170     }
171 }
172 
173 static
174 VOID
175 SepUpdatePrivilegeFlagsToken(
176     _Inout_ PTOKEN Token)
177 {
178     ULONG i;
179 
180     /* Loop all privileges */
181     for (i = 0; i < Token->PrivilegeCount; i++)
182     {
183         /* Updates the flags dor this privilege */
184         SepUpdateSinglePrivilegeFlagToken(Token, i);
185     }
186 }
187 
188 static
189 VOID
190 SepRemovePrivilegeToken(
191     _Inout_ PTOKEN Token,
192     _In_ ULONG Index)
193 {
194     ULONG MoveCount;
195     ASSERT(Index < Token->PrivilegeCount);
196 
197     /* Calculate the number of trailing privileges */
198     MoveCount = Token->PrivilegeCount - Index - 1;
199     if (MoveCount != 0)
200     {
201         /* Move them one location ahead */
202         RtlMoveMemory(&Token->Privileges[Index],
203                       &Token->Privileges[Index + 1],
204                       MoveCount * sizeof(LUID_AND_ATTRIBUTES));
205     }
206 
207     /* Update privilege count */
208     Token->PrivilegeCount--;
209 }
210 
211 VOID
212 NTAPI
213 SepFreeProxyData(PVOID ProxyData)
214 {
215     UNIMPLEMENTED;
216 }
217 
218 NTSTATUS
219 NTAPI
220 SepCopyProxyData(PVOID* Dest,
221                  PVOID Src)
222 {
223     UNIMPLEMENTED;
224     return STATUS_NOT_IMPLEMENTED;
225 }
226 
227 NTSTATUS
228 NTAPI
229 SeExchangePrimaryToken(
230     _In_ PEPROCESS Process,
231     _In_ PACCESS_TOKEN NewAccessToken,
232     _Out_ PACCESS_TOKEN* OldAccessToken)
233 {
234     PTOKEN OldToken;
235     PTOKEN NewToken = (PTOKEN)NewAccessToken;
236 
237     PAGED_CODE();
238 
239     if (NewToken->TokenType != TokenPrimary)
240         return STATUS_BAD_TOKEN_TYPE;
241 
242     if (NewToken->TokenInUse)
243     {
244         BOOLEAN IsEqual;
245         NTSTATUS Status;
246 
247         /* Maybe we're trying to set the same token */
248         OldToken = PsReferencePrimaryToken(Process);
249         if (OldToken == NewToken)
250         {
251             /* So it's a nop. */
252             *OldAccessToken = OldToken;
253             return STATUS_SUCCESS;
254         }
255 
256         Status = SepCompareTokens(OldToken, NewToken, &IsEqual);
257         if (!NT_SUCCESS(Status))
258         {
259             PsDereferencePrimaryToken(OldToken);
260             *OldAccessToken = NULL;
261             return Status;
262         }
263 
264         if (!IsEqual)
265         {
266             PsDereferencePrimaryToken(OldToken);
267             *OldAccessToken = NULL;
268             return STATUS_TOKEN_ALREADY_IN_USE;
269         }
270         /* Silently return STATUS_SUCCESS but do not set the new token,
271          * as it's already in use elsewhere. */
272         *OldAccessToken = OldToken;
273         return STATUS_SUCCESS;
274     }
275 
276     /* Lock the new token */
277     SepAcquireTokenLockExclusive(NewToken);
278 
279     /* Mark new token in use */
280     NewToken->TokenInUse = TRUE;
281 
282     // TODO: Set a correct SessionId for NewToken
283 
284     /* Unlock the new token */
285     SepReleaseTokenLock(NewToken);
286 
287     /* Reference the new token */
288     ObReferenceObject(NewToken);
289 
290     /* Replace the old with the new */
291     OldToken = ObFastReplaceObject(&Process->Token, NewToken);
292 
293     /* Lock the old token */
294     SepAcquireTokenLockExclusive(OldToken);
295 
296     /* Mark the old token as free */
297     OldToken->TokenInUse = FALSE;
298 
299     /* Unlock the old token */
300     SepReleaseTokenLock(OldToken);
301 
302     *OldAccessToken = (PACCESS_TOKEN)OldToken;
303     return STATUS_SUCCESS;
304 }
305 
306 VOID
307 NTAPI
308 SeDeassignPrimaryToken(PEPROCESS Process)
309 {
310     PTOKEN OldToken;
311 
312     /* Remove the Token */
313     OldToken = ObFastReplaceObject(&Process->Token, NULL);
314 
315     /* Mark the Old Token as free */
316     OldToken->TokenInUse = FALSE;
317 
318     /* Dereference the Token */
319     ObDereferenceObject(OldToken);
320 }
321 
322 static ULONG
323 RtlLengthSidAndAttributes(ULONG Count,
324                           PSID_AND_ATTRIBUTES Src)
325 {
326     ULONG i;
327     ULONG uLength;
328 
329     PAGED_CODE();
330 
331     uLength = Count * sizeof(SID_AND_ATTRIBUTES);
332     for (i = 0; i < Count; i++)
333         uLength += RtlLengthSid(Src[i].Sid);
334 
335     return uLength;
336 }
337 
338 
339 static NTSTATUS
340 SepFindPrimaryGroupAndDefaultOwner(
341     _In_ PTOKEN Token,
342     _In_ PSID PrimaryGroup,
343     _In_opt_ PSID DefaultOwner,
344     _Out_opt_ PULONG PrimaryGroupIndex,
345     _Out_opt_ PULONG DefaultOwnerIndex)
346 {
347     ULONG i;
348 
349     /* We should return at least a search result */
350     if (!PrimaryGroupIndex && !DefaultOwnerIndex)
351         return STATUS_INVALID_PARAMETER;
352 
353     if (PrimaryGroupIndex)
354     {
355         /* Initialize with an invalid index */
356         // Token->PrimaryGroup = NULL;
357         *PrimaryGroupIndex = Token->UserAndGroupCount;
358     }
359 
360     if (DefaultOwnerIndex)
361     {
362         if (DefaultOwner)
363         {
364             /* An owner is specified: check whether this is actually the user */
365             if (RtlEqualSid(Token->UserAndGroups[0].Sid, DefaultOwner))
366             {
367                 /*
368                  * It's the user (first element in array): set it
369                  * as the owner and stop the search for it.
370                  */
371                 *DefaultOwnerIndex = 0;
372                 DefaultOwnerIndex = NULL;
373             }
374             else
375             {
376                 /* An owner is specified: initialize with an invalid index */
377                 *DefaultOwnerIndex = Token->UserAndGroupCount;
378             }
379         }
380         else
381         {
382             /*
383              * No owner specified: set the user (first element in array)
384              * as the owner and stop the search for it.
385              */
386             *DefaultOwnerIndex = 0;
387             DefaultOwnerIndex = NULL;
388         }
389     }
390 
391     /* Validate and set the primary group and default owner indices */
392     for (i = 0; i < Token->UserAndGroupCount; i++)
393     {
394         /* Stop the search if we have found what we searched for */
395         if (!PrimaryGroupIndex && !DefaultOwnerIndex)
396             break;
397 
398         if (DefaultOwnerIndex && DefaultOwner &&
399             RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner) &&
400             (Token->UserAndGroups[i].Attributes & SE_GROUP_OWNER))
401         {
402             /* Owner is found, stop the search for it */
403             *DefaultOwnerIndex = i;
404             DefaultOwnerIndex = NULL;
405         }
406 
407         if (PrimaryGroupIndex &&
408             RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup))
409         {
410             /* Primary group is found, stop the search for it */
411             // Token->PrimaryGroup = Token->UserAndGroups[i].Sid;
412             *PrimaryGroupIndex = i;
413             PrimaryGroupIndex = NULL;
414         }
415     }
416 
417     if (DefaultOwnerIndex)
418     {
419         if (*DefaultOwnerIndex == Token->UserAndGroupCount)
420             return STATUS_INVALID_OWNER;
421     }
422 
423     if (PrimaryGroupIndex)
424     {
425         if (*PrimaryGroupIndex == Token->UserAndGroupCount)
426         // if (Token->PrimaryGroup == NULL)
427             return STATUS_INVALID_PRIMARY_GROUP;
428     }
429 
430     return STATUS_SUCCESS;
431 }
432 
433 
434 NTSTATUS
435 NTAPI
436 SepDuplicateToken(
437     _In_ PTOKEN Token,
438     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
439     _In_ BOOLEAN EffectiveOnly,
440     _In_ TOKEN_TYPE TokenType,
441     _In_ SECURITY_IMPERSONATION_LEVEL Level,
442     _In_ KPROCESSOR_MODE PreviousMode,
443     _Out_ PTOKEN* NewAccessToken)
444 {
445     NTSTATUS Status;
446     PTOKEN AccessToken;
447     PVOID EndMem;
448     ULONG VariableLength;
449     ULONG TotalSize;
450 
451     PAGED_CODE();
452 
453     /* Compute how much size we need to allocate for the token */
454     VariableLength = Token->VariableLength;
455     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
456 
457     Status = ObCreateObject(PreviousMode,
458                             SeTokenObjectType,
459                             ObjectAttributes,
460                             PreviousMode,
461                             NULL,
462                             TotalSize,
463                             0,
464                             0,
465                             (PVOID*)&AccessToken);
466     if (!NT_SUCCESS(Status))
467     {
468         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
469         return Status;
470     }
471 
472     /* Zero out the buffer and initialize the token */
473     RtlZeroMemory(AccessToken, TotalSize);
474 
475     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
476 
477     AccessToken->TokenType = TokenType;
478     AccessToken->ImpersonationLevel = Level;
479 
480     AccessToken->TokenLock = &SepTokenLock; // FIXME: Global lock!
481 
482     /* Copy the immutable fields */
483     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
484                 &Token->TokenSource.SourceIdentifier);
485     RtlCopyMemory(AccessToken->TokenSource.SourceName,
486                   Token->TokenSource.SourceName,
487                   sizeof(Token->TokenSource.SourceName));
488 
489     AccessToken->AuthenticationId = Token->AuthenticationId;
490     AccessToken->ParentTokenId = Token->ParentTokenId;
491     AccessToken->ExpirationTime = Token->ExpirationTime;
492     AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
493 
494 
495     /* Lock the source token and copy the mutable fields */
496     SepAcquireTokenLockExclusive(Token);
497 
498     AccessToken->SessionId = Token->SessionId;
499     RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId);
500 
501     AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
502 
503     /* Copy and reference the logon session */
504     // RtlCopyLuid(&AccessToken->AuthenticationId, &Token->AuthenticationId);
505     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
506     if (!NT_SUCCESS(Status))
507     {
508         /* No logon session could be found, bail out */
509         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
510         /* Set the flag for proper cleanup by the delete procedure */
511         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
512         goto Quit;
513     }
514 
515     /* Assign the data that reside in the TOKEN's variable information area */
516     AccessToken->VariableLength = VariableLength;
517     EndMem = (PVOID)&AccessToken->VariablePart;
518 
519     /* Copy the privileges */
520     AccessToken->PrivilegeCount = 0;
521     AccessToken->Privileges = NULL;
522     if (Token->Privileges && (Token->PrivilegeCount > 0))
523     {
524         ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
525         PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
526 
527         ASSERT(VariableLength >= PrivilegesLength);
528 
529         AccessToken->PrivilegeCount = Token->PrivilegeCount;
530         AccessToken->Privileges = EndMem;
531         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
532         VariableLength -= PrivilegesLength;
533 
534         RtlCopyMemory(AccessToken->Privileges,
535                       Token->Privileges,
536                       AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
537     }
538 
539     /* Copy the user and groups */
540     AccessToken->UserAndGroupCount = 0;
541     AccessToken->UserAndGroups = NULL;
542     if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
543     {
544         AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
545         AccessToken->UserAndGroups = EndMem;
546         EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
547         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
548 
549         Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
550                                               Token->UserAndGroups,
551                                               VariableLength,
552                                               AccessToken->UserAndGroups,
553                                               EndMem,
554                                               &EndMem,
555                                               &VariableLength);
556         if (!NT_SUCCESS(Status))
557         {
558             DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status);
559             goto Quit;
560         }
561     }
562 
563 #if 1
564     {
565     ULONG PrimaryGroupIndex;
566 
567     /* Find the token primary group */
568     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
569                                                 Token->PrimaryGroup,
570                                                 NULL,
571                                                 &PrimaryGroupIndex,
572                                                 NULL);
573     if (!NT_SUCCESS(Status))
574     {
575         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
576         goto Quit;
577     }
578     AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
579     }
580 #else
581     AccessToken->PrimaryGroup = (PVOID)((ULONG_PTR)AccessToken + (ULONG_PTR)Token->PrimaryGroup - (ULONG_PTR)Token->UserAndGroups);
582 #endif
583     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
584 
585     /* Copy the restricted SIDs */
586     AccessToken->RestrictedSidCount = 0;
587     AccessToken->RestrictedSids = NULL;
588     if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
589     {
590         AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
591         AccessToken->RestrictedSids = EndMem;
592         EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
593         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
594 
595         Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
596                                               Token->RestrictedSids,
597                                               VariableLength,
598                                               AccessToken->RestrictedSids,
599                                               EndMem,
600                                               &EndMem,
601                                               &VariableLength);
602         if (!NT_SUCCESS(Status))
603         {
604             DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status);
605             goto Quit;
606         }
607     }
608 
609 
610     //
611     // FIXME: Implement the "EffectiveOnly" option, that removes all
612     // the disabled parts (privileges and groups) of the token.
613     //
614 
615 
616     //
617     // NOTE: So far our dynamic area only contains
618     // the default dacl, so this makes the following
619     // code pretty simple. The day where it stores
620     // other data, the code will require adaptations.
621     //
622 
623     /* Now allocate the TOKEN's dynamic information area and set the data */
624     AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
625     AccessToken->DynamicPart = NULL;
626     if (Token->DynamicPart && Token->DefaultDacl)
627     {
628         AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
629                                                          Token->DefaultDacl->AclSize,
630                                                          TAG_TOKEN_DYNAMIC);
631         if (AccessToken->DynamicPart == NULL)
632         {
633             Status = STATUS_INSUFFICIENT_RESOURCES;
634             goto Quit;
635         }
636         EndMem = (PVOID)AccessToken->DynamicPart;
637 
638         AccessToken->DefaultDacl = EndMem;
639 
640         RtlCopyMemory(AccessToken->DefaultDacl,
641                       Token->DefaultDacl,
642                       Token->DefaultDacl->AclSize);
643     }
644 
645     /* Unlock the source token */
646     SepReleaseTokenLock(Token);
647 
648     /* Return the token */
649     *NewAccessToken = AccessToken;
650     Status = STATUS_SUCCESS;
651 
652 Quit:
653     if (!NT_SUCCESS(Status))
654     {
655         /* Unlock the source token */
656         SepReleaseTokenLock(Token);
657 
658         /* Dereference the token, the delete procedure will clean it up */
659         ObDereferenceObject(AccessToken);
660     }
661 
662     return Status;
663 }
664 
665 NTSTATUS
666 NTAPI
667 SeSubProcessToken(IN PTOKEN ParentToken,
668                   OUT PTOKEN *Token,
669                   IN BOOLEAN InUse,
670                   IN ULONG SessionId)
671 {
672     PTOKEN NewToken;
673     OBJECT_ATTRIBUTES ObjectAttributes;
674     NTSTATUS Status;
675 
676     /* Initialize the attributes and duplicate it */
677     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
678     Status = SepDuplicateToken(ParentToken,
679                                &ObjectAttributes,
680                                FALSE,
681                                TokenPrimary,
682                                ParentToken->ImpersonationLevel,
683                                KernelMode,
684                                &NewToken);
685     if (NT_SUCCESS(Status))
686     {
687         /* Insert it */
688         Status = ObInsertObject(NewToken,
689                                 NULL,
690                                 0,
691                                 0,
692                                 NULL,
693                                 NULL);
694         if (NT_SUCCESS(Status))
695         {
696             /* Set the session ID */
697             NewToken->SessionId = SessionId;
698             NewToken->TokenInUse = InUse;
699 
700             /* Return the token */
701             *Token = NewToken;
702         }
703     }
704 
705     /* Return status */
706     return Status;
707 }
708 
709 NTSTATUS
710 NTAPI
711 SeIsTokenChild(IN PTOKEN Token,
712                OUT PBOOLEAN IsChild)
713 {
714     PTOKEN ProcessToken;
715     LUID ProcessTokenId, CallerParentId;
716 
717     /* Assume failure */
718     *IsChild = FALSE;
719 
720     /* Reference the process token */
721     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
722     if (!ProcessToken)
723         return STATUS_UNSUCCESSFUL;
724 
725     /* Get its token ID */
726     ProcessTokenId = ProcessToken->TokenId;
727 
728     /* Dereference the token */
729     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
730 
731     /* Get our parent token ID */
732     CallerParentId = Token->ParentTokenId;
733 
734     /* Compare the token IDs */
735     if (RtlEqualLuid(&CallerParentId, &ProcessTokenId))
736         *IsChild = TRUE;
737 
738     /* Return success */
739     return STATUS_SUCCESS;
740 }
741 
742 NTSTATUS
743 NTAPI
744 SeIsTokenSibling(IN PTOKEN Token,
745                  OUT PBOOLEAN IsSibling)
746 {
747     PTOKEN ProcessToken;
748     LUID ProcessParentId, ProcessAuthId;
749     LUID CallerParentId, CallerAuthId;
750 
751     /* Assume failure */
752     *IsSibling = FALSE;
753 
754     /* Reference the process token */
755     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
756     if (!ProcessToken)
757         return STATUS_UNSUCCESSFUL;
758 
759     /* Get its parent and authentication IDs */
760     ProcessParentId = ProcessToken->ParentTokenId;
761     ProcessAuthId = ProcessToken->AuthenticationId;
762 
763     /* Dereference the token */
764     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
765 
766     /* Get our parent and authentication IDs */
767     CallerParentId = Token->ParentTokenId;
768     CallerAuthId = Token->AuthenticationId;
769 
770     /* Compare the token IDs */
771     if (RtlEqualLuid(&CallerParentId, &ProcessParentId) &&
772         RtlEqualLuid(&CallerAuthId, &ProcessAuthId))
773     {
774         *IsSibling = TRUE;
775     }
776 
777     /* Return success */
778     return STATUS_SUCCESS;
779 }
780 
781 NTSTATUS
782 NTAPI
783 SeCopyClientToken(IN PACCESS_TOKEN Token,
784                   IN SECURITY_IMPERSONATION_LEVEL Level,
785                   IN KPROCESSOR_MODE PreviousMode,
786                   OUT PACCESS_TOKEN* NewToken)
787 {
788     NTSTATUS Status;
789     OBJECT_ATTRIBUTES ObjectAttributes;
790 
791     PAGED_CODE();
792 
793     InitializeObjectAttributes(&ObjectAttributes,
794                                NULL,
795                                0,
796                                NULL,
797                                NULL);
798 
799     Status = SepDuplicateToken(Token,
800                                &ObjectAttributes,
801                                FALSE,
802                                TokenImpersonation,
803                                Level,
804                                PreviousMode,
805                                (PTOKEN*)NewToken);
806 
807     return Status;
808 }
809 
810 VOID
811 NTAPI
812 SepDeleteToken(PVOID ObjectBody)
813 {
814     PTOKEN AccessToken = (PTOKEN)ObjectBody;
815 
816     DPRINT("SepDeleteToken()\n");
817 
818     /* Dereference the logon session */
819     if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0)
820         SepRmDereferenceLogonSession(&AccessToken->AuthenticationId);
821 
822     /* Delete the dynamic information area */
823     if (AccessToken->DynamicPart)
824         ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC);
825 }
826 
827 
828 CODE_SEG("INIT")
829 VOID
830 NTAPI
831 SepInitializeTokenImplementation(VOID)
832 {
833     UNICODE_STRING Name;
834     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
835 
836     ExInitializeResource(&SepTokenLock); // FIXME: Global lock!
837 
838     DPRINT("Creating Token Object Type\n");
839 
840     /* Initialize the Token type */
841     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
842     RtlInitUnicodeString(&Name, L"Token");
843     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
844     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
845     ObjectTypeInitializer.SecurityRequired = TRUE;
846     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN);
847     ObjectTypeInitializer.GenericMapping = SepTokenMapping;
848     ObjectTypeInitializer.PoolType = PagedPool;
849     ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS;
850     ObjectTypeInitializer.UseDefaultObject = TRUE;
851     ObjectTypeInitializer.DeleteProcedure = SepDeleteToken;
852     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SeTokenObjectType);
853 }
854 
855 VOID
856 NTAPI
857 SeAssignPrimaryToken(IN PEPROCESS Process,
858                      IN PTOKEN Token)
859 {
860     PAGED_CODE();
861 
862     /* Sanity checks */
863     ASSERT(Token->TokenType == TokenPrimary);
864     ASSERT(!Token->TokenInUse);
865 
866     /* Clean any previous token */
867     if (Process->Token.Object) SeDeassignPrimaryToken(Process);
868 
869     /* Set the new token */
870     ObReferenceObject(Token);
871     Token->TokenInUse = TRUE;
872     ObInitializeFastReference(&Process->Token, Token);
873 }
874 
875 NTSTATUS
876 NTAPI
877 SepCreateToken(
878     _Out_ PHANDLE TokenHandle,
879     _In_ KPROCESSOR_MODE PreviousMode,
880     _In_ ACCESS_MASK DesiredAccess,
881     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
882     _In_ TOKEN_TYPE TokenType,
883     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
884     _In_ PLUID AuthenticationId,
885     _In_ PLARGE_INTEGER ExpirationTime,
886     _In_ PSID_AND_ATTRIBUTES User,
887     _In_ ULONG GroupCount,
888     _In_ PSID_AND_ATTRIBUTES Groups,
889     _In_ ULONG GroupsLength,
890     _In_ ULONG PrivilegeCount,
891     _In_ PLUID_AND_ATTRIBUTES Privileges,
892     _In_opt_ PSID Owner,
893     _In_ PSID PrimaryGroup,
894     _In_opt_ PACL DefaultDacl,
895     _In_ PTOKEN_SOURCE TokenSource,
896     _In_ BOOLEAN SystemToken)
897 {
898     NTSTATUS Status;
899     PTOKEN AccessToken;
900     ULONG TokenFlags = 0;
901     ULONG PrimaryGroupIndex, DefaultOwnerIndex;
902     LUID TokenId;
903     LUID ModifiedId;
904     PVOID EndMem;
905     ULONG PrivilegesLength;
906     ULONG UserGroupsLength;
907     ULONG VariableLength;
908     ULONG TotalSize;
909     ULONG i;
910 
911     PAGED_CODE();
912 
913     /* Loop all groups */
914     for (i = 0; i < GroupCount; i++)
915     {
916         /* Check for mandatory groups */
917         if (Groups[i].Attributes & SE_GROUP_MANDATORY)
918         {
919             /* Force them to be enabled */
920             Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
921         }
922 
923         /* Check of the group is an admin group */
924         if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid))
925         {
926             /* Remember this so we can optimize queries later */
927             TokenFlags |= TOKEN_HAS_ADMIN_GROUP;
928         }
929     }
930 
931     /* Allocate unique IDs for the token */
932     ExAllocateLocallyUniqueId(&TokenId);
933     ExAllocateLocallyUniqueId(&ModifiedId);
934 
935     /* Compute how much size we need to allocate for the token */
936 
937     /* Privileges size */
938     PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
939     PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
940 
941     /* User and groups size */
942     UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES);
943     UserGroupsLength += RtlLengthSid(User->Sid);
944     for (i = 0; i < GroupCount; i++)
945     {
946         UserGroupsLength += RtlLengthSid(Groups[i].Sid);
947     }
948     UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID));
949 
950     /* Add the additional groups array length */
951     UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID));
952 
953     VariableLength = PrivilegesLength + UserGroupsLength;
954     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
955 
956     Status = ObCreateObject(PreviousMode,
957                             SeTokenObjectType,
958                             ObjectAttributes,
959                             PreviousMode,
960                             NULL,
961                             TotalSize,
962                             0,
963                             0,
964                             (PVOID*)&AccessToken);
965     if (!NT_SUCCESS(Status))
966     {
967         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
968         return Status;
969     }
970 
971     /* Zero out the buffer and initialize the token */
972     RtlZeroMemory(AccessToken, TotalSize);
973 
974     RtlCopyLuid(&AccessToken->TokenId, &TokenId);
975 
976     AccessToken->TokenType = TokenType;
977     AccessToken->ImpersonationLevel = ImpersonationLevel;
978 
979     AccessToken->TokenLock = &SepTokenLock; // FIXME: Global lock!
980 
981     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
982                 &TokenSource->SourceIdentifier);
983     RtlCopyMemory(AccessToken->TokenSource.SourceName,
984                   TokenSource->SourceName,
985                   sizeof(TokenSource->SourceName));
986 
987     AccessToken->ExpirationTime = *ExpirationTime;
988     RtlCopyLuid(&AccessToken->ModifiedId, &ModifiedId);
989 
990     AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
991 
992     /* Copy and reference the logon session */
993     RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId);
994     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
995     if (!NT_SUCCESS(Status))
996     {
997         /* No logon session could be found, bail out */
998         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
999         /* Set the flag for proper cleanup by the delete procedure */
1000         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
1001         goto Quit;
1002     }
1003 
1004     /* Assign the data that reside in the TOKEN's variable information area */
1005     AccessToken->VariableLength = VariableLength;
1006     EndMem = (PVOID)&AccessToken->VariablePart;
1007 
1008     /* Copy the privileges */
1009     AccessToken->PrivilegeCount = PrivilegeCount;
1010     AccessToken->Privileges = NULL;
1011     if (PrivilegeCount > 0)
1012     {
1013         AccessToken->Privileges = EndMem;
1014         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1015         VariableLength -= PrivilegesLength;
1016 
1017         if (PreviousMode != KernelMode)
1018         {
1019             _SEH2_TRY
1020             {
1021                 RtlCopyMemory(AccessToken->Privileges,
1022                               Privileges,
1023                               PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1024             }
1025             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1026             {
1027                 Status = _SEH2_GetExceptionCode();
1028             }
1029             _SEH2_END;
1030         }
1031         else
1032         {
1033             RtlCopyMemory(AccessToken->Privileges,
1034                           Privileges,
1035                           PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1036         }
1037 
1038         if (!NT_SUCCESS(Status))
1039             goto Quit;
1040     }
1041 
1042     /* Update the privilege flags */
1043     SepUpdatePrivilegeFlagsToken(AccessToken);
1044 
1045     /* Copy the user and groups */
1046     AccessToken->UserAndGroupCount = 1 + GroupCount;
1047     AccessToken->UserAndGroups = EndMem;
1048     EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1049     VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1050 
1051     Status = RtlCopySidAndAttributesArray(1,
1052                                           User,
1053                                           VariableLength,
1054                                           &AccessToken->UserAndGroups[0],
1055                                           EndMem,
1056                                           &EndMem,
1057                                           &VariableLength);
1058     if (!NT_SUCCESS(Status))
1059         goto Quit;
1060 
1061     Status = RtlCopySidAndAttributesArray(GroupCount,
1062                                           Groups,
1063                                           VariableLength,
1064                                           &AccessToken->UserAndGroups[1],
1065                                           EndMem,
1066                                           &EndMem,
1067                                           &VariableLength);
1068     if (!NT_SUCCESS(Status))
1069         goto Quit;
1070 
1071     /* Find the token primary group and default owner */
1072     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1073                                                 PrimaryGroup,
1074                                                 Owner,
1075                                                 &PrimaryGroupIndex,
1076                                                 &DefaultOwnerIndex);
1077     if (!NT_SUCCESS(Status))
1078     {
1079         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
1080         goto Quit;
1081     }
1082 
1083     AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
1084     AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
1085 
1086     /* Now allocate the TOKEN's dynamic information area and set the data */
1087     AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
1088     AccessToken->DynamicPart = NULL;
1089     if (DefaultDacl != NULL)
1090     {
1091         AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1092                                                          DefaultDacl->AclSize,
1093                                                          TAG_TOKEN_DYNAMIC);
1094         if (AccessToken->DynamicPart == NULL)
1095         {
1096             Status = STATUS_INSUFFICIENT_RESOURCES;
1097             goto Quit;
1098         }
1099         EndMem = (PVOID)AccessToken->DynamicPart;
1100 
1101         AccessToken->DefaultDacl = EndMem;
1102 
1103         RtlCopyMemory(AccessToken->DefaultDacl,
1104                       DefaultDacl,
1105                       DefaultDacl->AclSize);
1106     }
1107 
1108     /* Insert the token only if it's not the system token, otherwise return it directly */
1109     if (!SystemToken)
1110     {
1111         Status = ObInsertObject(AccessToken,
1112                                 NULL,
1113                                 DesiredAccess,
1114                                 0,
1115                                 NULL,
1116                                 TokenHandle);
1117         if (!NT_SUCCESS(Status))
1118         {
1119             DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status);
1120         }
1121     }
1122     else
1123     {
1124         /* Return pointer instead of handle */
1125         *TokenHandle = (HANDLE)AccessToken;
1126     }
1127 
1128 Quit:
1129     if (!NT_SUCCESS(Status))
1130     {
1131         /* Dereference the token, the delete procedure will clean it up */
1132         ObDereferenceObject(AccessToken);
1133     }
1134 
1135     return Status;
1136 }
1137 
1138 PTOKEN
1139 NTAPI
1140 SepCreateSystemProcessToken(VOID)
1141 {
1142     LUID_AND_ATTRIBUTES Privileges[25];
1143     ULONG GroupAttributes, OwnerAttributes;
1144     SID_AND_ATTRIBUTES Groups[32];
1145     LARGE_INTEGER Expiration;
1146     SID_AND_ATTRIBUTES UserSid;
1147     ULONG GroupsLength;
1148     PSID PrimaryGroup;
1149     OBJECT_ATTRIBUTES ObjectAttributes;
1150     PSID Owner;
1151     ULONG i;
1152     PTOKEN Token;
1153     NTSTATUS Status;
1154 
1155     /* Don't ever expire */
1156     Expiration.QuadPart = -1;
1157 
1158     /* All groups mandatory and enabled */
1159     GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
1160     OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT;
1161 
1162     /* User is Local System */
1163     UserSid.Sid = SeLocalSystemSid;
1164     UserSid.Attributes = 0;
1165 
1166     /* Primary group is Local System */
1167     PrimaryGroup = SeLocalSystemSid;
1168 
1169     /* Owner is Administrators */
1170     Owner = SeAliasAdminsSid;
1171 
1172     /* Groups are Administrators, World, and Authenticated Users */
1173     Groups[0].Sid = SeAliasAdminsSid;
1174     Groups[0].Attributes = OwnerAttributes;
1175     Groups[1].Sid = SeWorldSid;
1176     Groups[1].Attributes = GroupAttributes;
1177     Groups[2].Sid = SeAuthenticatedUsersSid;
1178     Groups[2].Attributes = GroupAttributes;
1179     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
1180                    SeLengthSid(Groups[0].Sid) +
1181                    SeLengthSid(Groups[1].Sid) +
1182                    SeLengthSid(Groups[2].Sid);
1183     ASSERT(GroupsLength <= sizeof(Groups));
1184 
1185     /* Setup the privileges */
1186     i = 0;
1187     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1188     Privileges[i++].Luid = SeTcbPrivilege;
1189 
1190     Privileges[i].Attributes = 0;
1191     Privileges[i++].Luid = SeCreateTokenPrivilege;
1192 
1193     Privileges[i].Attributes = 0;
1194     Privileges[i++].Luid = SeTakeOwnershipPrivilege;
1195 
1196     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1197     Privileges[i++].Luid = SeCreatePagefilePrivilege;
1198 
1199     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1200     Privileges[i++].Luid = SeLockMemoryPrivilege;
1201 
1202     Privileges[i].Attributes = 0;
1203     Privileges[i++].Luid = SeAssignPrimaryTokenPrivilege;
1204 
1205     Privileges[i].Attributes = 0;
1206     Privileges[i++].Luid = SeIncreaseQuotaPrivilege;
1207 
1208     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1209     Privileges[i++].Luid = SeIncreaseBasePriorityPrivilege;
1210 
1211     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1212     Privileges[i++].Luid = SeCreatePermanentPrivilege;
1213 
1214     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1215     Privileges[i++].Luid = SeDebugPrivilege;
1216 
1217     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1218     Privileges[i++].Luid = SeAuditPrivilege;
1219 
1220     Privileges[i].Attributes = 0;
1221     Privileges[i++].Luid = SeSecurityPrivilege;
1222 
1223     Privileges[i].Attributes = 0;
1224     Privileges[i++].Luid = SeSystemEnvironmentPrivilege;
1225 
1226     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1227     Privileges[i++].Luid = SeChangeNotifyPrivilege;
1228 
1229     Privileges[i].Attributes = 0;
1230     Privileges[i++].Luid = SeBackupPrivilege;
1231 
1232     Privileges[i].Attributes = 0;
1233     Privileges[i++].Luid = SeRestorePrivilege;
1234 
1235     Privileges[i].Attributes = 0;
1236     Privileges[i++].Luid = SeShutdownPrivilege;
1237 
1238     Privileges[i].Attributes = 0;
1239     Privileges[i++].Luid = SeLoadDriverPrivilege;
1240 
1241     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
1242     Privileges[i++].Luid = SeProfileSingleProcessPrivilege;
1243 
1244     Privileges[i].Attributes = 0;
1245     Privileges[i++].Luid = SeSystemtimePrivilege;
1246     ASSERT(i == 20);
1247 
1248     /* Setup the object attributes */
1249     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
1250     ASSERT(SeSystemDefaultDacl != NULL);
1251 
1252     /* Create the token */
1253     Status = SepCreateToken((PHANDLE)&Token,
1254                             KernelMode,
1255                             0,
1256                             &ObjectAttributes,
1257                             TokenPrimary,
1258                             SecurityAnonymous,
1259                             &SeSystemAuthenticationId,
1260                             &Expiration,
1261                             &UserSid,
1262                             3,
1263                             Groups,
1264                             GroupsLength,
1265                             20,
1266                             Privileges,
1267                             Owner,
1268                             PrimaryGroup,
1269                             SeSystemDefaultDacl,
1270                             &SeSystemTokenSource,
1271                             TRUE);
1272     ASSERT(Status == STATUS_SUCCESS);
1273 
1274     /* Return the token */
1275     return Token;
1276 }
1277 
1278 /* PUBLIC FUNCTIONS ***********************************************************/
1279 
1280 /*
1281  * @unimplemented
1282  */
1283 NTSTATUS
1284 NTAPI
1285 SeFilterToken(IN PACCESS_TOKEN ExistingToken,
1286               IN ULONG Flags,
1287               IN PTOKEN_GROUPS SidsToDisable OPTIONAL,
1288               IN PTOKEN_PRIVILEGES PrivilegesToDelete OPTIONAL,
1289               IN PTOKEN_GROUPS RestrictedSids OPTIONAL,
1290               OUT PACCESS_TOKEN * FilteredToken)
1291 {
1292     UNIMPLEMENTED;
1293     return STATUS_NOT_IMPLEMENTED;
1294 }
1295 
1296 /*
1297  * @implemented
1298  *
1299  * NOTE: SeQueryInformationToken is just NtQueryInformationToken without all
1300  * the bells and whistles needed for user-mode buffer access protection.
1301  */
1302 NTSTATUS
1303 NTAPI
1304 SeQueryInformationToken(IN PACCESS_TOKEN AccessToken,
1305                         IN TOKEN_INFORMATION_CLASS TokenInformationClass,
1306                         OUT PVOID *TokenInformation)
1307 {
1308     NTSTATUS Status;
1309     PTOKEN Token = (PTOKEN)AccessToken;
1310     ULONG RequiredLength;
1311     union
1312     {
1313         PSID PSid;
1314         ULONG Ulong;
1315     } Unused;
1316 
1317     PAGED_CODE();
1318 
1319     if (TokenInformationClass >= MaxTokenInfoClass)
1320     {
1321         DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
1322         return STATUS_INVALID_INFO_CLASS;
1323     }
1324 
1325     // TODO: Lock the token
1326 
1327     switch (TokenInformationClass)
1328     {
1329         case TokenUser:
1330         {
1331             PTOKEN_USER tu;
1332 
1333             DPRINT("SeQueryInformationToken(TokenUser)\n");
1334             RequiredLength = sizeof(TOKEN_USER) +
1335                 RtlLengthSid(Token->UserAndGroups[0].Sid);
1336 
1337             /* Allocate the output buffer */
1338             tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1339             if (tu == NULL)
1340             {
1341                 Status = STATUS_INSUFFICIENT_RESOURCES;
1342                 break;
1343             }
1344 
1345             Status = RtlCopySidAndAttributesArray(1,
1346                                                   &Token->UserAndGroups[0],
1347                                                   RequiredLength - sizeof(TOKEN_USER),
1348                                                   &tu->User,
1349                                                   (PSID)(tu + 1),
1350                                                   &Unused.PSid,
1351                                                   &Unused.Ulong);
1352 
1353             /* Return the structure */
1354             *TokenInformation = tu;
1355             Status = STATUS_SUCCESS;
1356             break;
1357         }
1358 
1359         case TokenGroups:
1360         {
1361             PTOKEN_GROUPS tg;
1362             ULONG SidLen;
1363             PSID Sid;
1364 
1365             DPRINT("SeQueryInformationToken(TokenGroups)\n");
1366             RequiredLength = sizeof(tg->GroupCount) +
1367                 RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
1368 
1369             SidLen = RequiredLength - sizeof(tg->GroupCount) -
1370                 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
1371 
1372             /* Allocate the output buffer */
1373             tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1374             if (tg == NULL)
1375             {
1376                 Status = STATUS_INSUFFICIENT_RESOURCES;
1377                 break;
1378             }
1379 
1380             Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
1381                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
1382 
1383             tg->GroupCount = Token->UserAndGroupCount - 1;
1384             Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
1385                                                   &Token->UserAndGroups[1],
1386                                                   SidLen,
1387                                                   &tg->Groups[0],
1388                                                   Sid,
1389                                                   &Unused.PSid,
1390                                                   &Unused.Ulong);
1391 
1392             /* Return the structure */
1393             *TokenInformation = tg;
1394             Status = STATUS_SUCCESS;
1395             break;
1396         }
1397 
1398         case TokenPrivileges:
1399         {
1400             PTOKEN_PRIVILEGES tp;
1401 
1402             DPRINT("SeQueryInformationToken(TokenPrivileges)\n");
1403             RequiredLength = sizeof(tp->PrivilegeCount) +
1404                 (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1405 
1406             /* Allocate the output buffer */
1407             tp = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1408             if (tp == NULL)
1409             {
1410                 Status = STATUS_INSUFFICIENT_RESOURCES;
1411                 break;
1412             }
1413 
1414             tp->PrivilegeCount = Token->PrivilegeCount;
1415             RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
1416                                           Token->Privileges,
1417                                           &tp->Privileges[0]);
1418 
1419             /* Return the structure */
1420             *TokenInformation = tp;
1421             Status = STATUS_SUCCESS;
1422             break;
1423         }
1424 
1425         case TokenOwner:
1426         {
1427             PTOKEN_OWNER to;
1428             ULONG SidLen;
1429 
1430             DPRINT("SeQueryInformationToken(TokenOwner)\n");
1431             SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
1432             RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
1433 
1434             /* Allocate the output buffer */
1435             to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1436             if (to == NULL)
1437             {
1438                 Status = STATUS_INSUFFICIENT_RESOURCES;
1439                 break;
1440             }
1441 
1442             to->Owner = (PSID)(to + 1);
1443             Status = RtlCopySid(SidLen,
1444                                 to->Owner,
1445                                 Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
1446 
1447             /* Return the structure */
1448             *TokenInformation = to;
1449             Status = STATUS_SUCCESS;
1450             break;
1451         }
1452 
1453         case TokenPrimaryGroup:
1454         {
1455             PTOKEN_PRIMARY_GROUP tpg;
1456             ULONG SidLen;
1457 
1458             DPRINT("SeQueryInformationToken(TokenPrimaryGroup)\n");
1459             SidLen = RtlLengthSid(Token->PrimaryGroup);
1460             RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
1461 
1462             /* Allocate the output buffer */
1463             tpg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1464             if (tpg == NULL)
1465             {
1466                 Status = STATUS_INSUFFICIENT_RESOURCES;
1467                 break;
1468             }
1469 
1470             tpg->PrimaryGroup = (PSID)(tpg + 1);
1471             Status = RtlCopySid(SidLen,
1472                                 tpg->PrimaryGroup,
1473                                 Token->PrimaryGroup);
1474 
1475             /* Return the structure */
1476             *TokenInformation = tpg;
1477             Status = STATUS_SUCCESS;
1478             break;
1479         }
1480 
1481         case TokenDefaultDacl:
1482         {
1483             PTOKEN_DEFAULT_DACL tdd;
1484 
1485             DPRINT("SeQueryInformationToken(TokenDefaultDacl)\n");
1486             RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
1487 
1488             if (Token->DefaultDacl != NULL)
1489                 RequiredLength += Token->DefaultDacl->AclSize;
1490 
1491             /* Allocate the output buffer */
1492             tdd = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1493             if (tdd == NULL)
1494             {
1495                 Status = STATUS_INSUFFICIENT_RESOURCES;
1496                 break;
1497             }
1498 
1499             if (Token->DefaultDacl != NULL)
1500             {
1501                 tdd->DefaultDacl = (PACL)(tdd + 1);
1502                 RtlCopyMemory(tdd->DefaultDacl,
1503                               Token->DefaultDacl,
1504                               Token->DefaultDacl->AclSize);
1505             }
1506             else
1507             {
1508                 tdd->DefaultDacl = NULL;
1509             }
1510 
1511             /* Return the structure */
1512             *TokenInformation = tdd;
1513             Status = STATUS_SUCCESS;
1514             break;
1515         }
1516 
1517         case TokenSource:
1518         {
1519             PTOKEN_SOURCE ts;
1520 
1521             DPRINT("SeQueryInformationToken(TokenSource)\n");
1522             RequiredLength = sizeof(TOKEN_SOURCE);
1523 
1524             /* Allocate the output buffer */
1525             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1526             if (ts == NULL)
1527             {
1528                 Status = STATUS_INSUFFICIENT_RESOURCES;
1529                 break;
1530             }
1531 
1532             *ts = Token->TokenSource;
1533 
1534             /* Return the structure */
1535             *TokenInformation = ts;
1536             Status = STATUS_SUCCESS;
1537             break;
1538         }
1539 
1540         case TokenType:
1541         {
1542             PTOKEN_TYPE tt;
1543 
1544             DPRINT("SeQueryInformationToken(TokenType)\n");
1545             RequiredLength = sizeof(TOKEN_TYPE);
1546 
1547             /* Allocate the output buffer */
1548             tt = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1549             if (tt == NULL)
1550             {
1551                 Status = STATUS_INSUFFICIENT_RESOURCES;
1552                 break;
1553             }
1554 
1555             *tt = Token->TokenType;
1556 
1557             /* Return the structure */
1558             *TokenInformation = tt;
1559             Status = STATUS_SUCCESS;
1560             break;
1561         }
1562 
1563         case TokenImpersonationLevel:
1564         {
1565             PSECURITY_IMPERSONATION_LEVEL sil;
1566 
1567             DPRINT("SeQueryInformationToken(TokenImpersonationLevel)\n");
1568             RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
1569 
1570             /* Fail if the token is not an impersonation token */
1571             if (Token->TokenType != TokenImpersonation)
1572             {
1573                 Status = STATUS_INVALID_INFO_CLASS;
1574                 break;
1575             }
1576 
1577             /* Allocate the output buffer */
1578             sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1579             if (sil == NULL)
1580             {
1581                 Status = STATUS_INSUFFICIENT_RESOURCES;
1582                 break;
1583             }
1584 
1585             *sil = Token->ImpersonationLevel;
1586 
1587             /* Return the structure */
1588             *TokenInformation = sil;
1589             Status = STATUS_SUCCESS;
1590             break;
1591         }
1592 
1593         case TokenStatistics:
1594         {
1595             PTOKEN_STATISTICS ts;
1596 
1597             DPRINT("SeQueryInformationToken(TokenStatistics)\n");
1598             RequiredLength = sizeof(TOKEN_STATISTICS);
1599 
1600             /* Allocate the output buffer */
1601             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1602             if (ts == NULL)
1603             {
1604                 Status = STATUS_INSUFFICIENT_RESOURCES;
1605                 break;
1606             }
1607 
1608             ts->TokenId = Token->TokenId;
1609             ts->AuthenticationId = Token->AuthenticationId;
1610             ts->ExpirationTime = Token->ExpirationTime;
1611             ts->TokenType = Token->TokenType;
1612             ts->ImpersonationLevel = Token->ImpersonationLevel;
1613             ts->DynamicCharged = Token->DynamicCharged;
1614             ts->DynamicAvailable = Token->DynamicAvailable;
1615             ts->GroupCount = Token->UserAndGroupCount - 1;
1616             ts->PrivilegeCount = Token->PrivilegeCount;
1617             ts->ModifiedId = Token->ModifiedId;
1618 
1619             /* Return the structure */
1620             *TokenInformation = ts;
1621             Status = STATUS_SUCCESS;
1622             break;
1623         }
1624 
1625 /*
1626  * The following 4 cases are only implemented in NtQueryInformationToken
1627  */
1628 #if 0
1629 
1630         case TokenOrigin:
1631         {
1632             PTOKEN_ORIGIN to;
1633 
1634             DPRINT("SeQueryInformationToken(TokenOrigin)\n");
1635             RequiredLength = sizeof(TOKEN_ORIGIN);
1636 
1637             /* Allocate the output buffer */
1638             to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1639             if (to == NULL)
1640             {
1641                 Status = STATUS_INSUFFICIENT_RESOURCES;
1642                 break;
1643             }
1644 
1645             RtlCopyLuid(&to->OriginatingLogonSession,
1646                         &Token->AuthenticationId);
1647 
1648             /* Return the structure */
1649             *TokenInformation = to;
1650             Status = STATUS_SUCCESS;
1651             break;
1652         }
1653 
1654         case TokenGroupsAndPrivileges:
1655             DPRINT1("SeQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n");
1656             Status = STATUS_NOT_IMPLEMENTED;
1657             break;
1658 
1659         case TokenRestrictedSids:
1660         {
1661             PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
1662             ULONG SidLen;
1663             PSID Sid;
1664 
1665             DPRINT("SeQueryInformationToken(TokenRestrictedSids)\n");
1666             RequiredLength = sizeof(tg->GroupCount) +
1667             RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
1668 
1669             SidLen = RequiredLength - sizeof(tg->GroupCount) -
1670                 (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
1671 
1672             /* Allocate the output buffer */
1673             tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
1674             if (tg == NULL)
1675             {
1676                 Status = STATUS_INSUFFICIENT_RESOURCES;
1677                 break;
1678             }
1679 
1680             Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
1681                          (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
1682 
1683             tg->GroupCount = Token->RestrictedSidCount;
1684             Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
1685                                                   Token->RestrictedSids,
1686                                                   SidLen,
1687                                                   &tg->Groups[0],
1688                                                   Sid,
1689                                                   &Unused.PSid,
1690                                                   &Unused.Ulong);
1691 
1692             /* Return the structure */
1693             *TokenInformation = tg;
1694             Status = STATUS_SUCCESS;
1695             break;
1696         }
1697 
1698         case TokenSandBoxInert:
1699             DPRINT1("SeQueryInformationToken(TokenSandboxInert) not implemented\n");
1700             Status = STATUS_NOT_IMPLEMENTED;
1701             break;
1702 
1703 #endif
1704 
1705         case TokenSessionId:
1706         {
1707             DPRINT("SeQueryInformationToken(TokenSessionId)\n");
1708             Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation);
1709             break;
1710         }
1711 
1712         default:
1713             DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
1714             Status = STATUS_INVALID_INFO_CLASS;
1715             break;
1716     }
1717 
1718     return Status;
1719 }
1720 
1721 /*
1722  * @implemented
1723  */
1724 NTSTATUS
1725 NTAPI
1726 SeQuerySessionIdToken(IN PACCESS_TOKEN Token,
1727                       IN PULONG pSessionId)
1728 {
1729     PAGED_CODE();
1730 
1731     /* Lock the token */
1732     SepAcquireTokenLockShared(Token);
1733 
1734     *pSessionId = ((PTOKEN)Token)->SessionId;
1735 
1736     /* Unlock the token */
1737     SepReleaseTokenLock(Token);
1738 
1739     return STATUS_SUCCESS;
1740 }
1741 
1742 /*
1743  * @implemented
1744  */
1745 NTSTATUS
1746 NTAPI
1747 SeQueryAuthenticationIdToken(IN PACCESS_TOKEN Token,
1748                              OUT PLUID LogonId)
1749 {
1750     PAGED_CODE();
1751 
1752     *LogonId = ((PTOKEN)Token)->AuthenticationId;
1753 
1754     return STATUS_SUCCESS;
1755 }
1756 
1757 
1758 /*
1759  * @implemented
1760  */
1761 SECURITY_IMPERSONATION_LEVEL
1762 NTAPI
1763 SeTokenImpersonationLevel(IN PACCESS_TOKEN Token)
1764 {
1765     PAGED_CODE();
1766 
1767     return ((PTOKEN)Token)->ImpersonationLevel;
1768 }
1769 
1770 
1771 /*
1772  * @implemented
1773  */
1774 TOKEN_TYPE NTAPI
1775 SeTokenType(IN PACCESS_TOKEN Token)
1776 {
1777     PAGED_CODE();
1778 
1779     return ((PTOKEN)Token)->TokenType;
1780 }
1781 
1782 
1783 /*
1784  * @implemented
1785  */
1786 BOOLEAN
1787 NTAPI
1788 SeTokenIsAdmin(IN PACCESS_TOKEN Token)
1789 {
1790     PAGED_CODE();
1791 
1792     // NOTE: Win7+ instead really checks the list of groups in the token
1793     // (since TOKEN_HAS_ADMIN_GROUP == TOKEN_WRITE_RESTRICTED ...)
1794     return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_ADMIN_GROUP) != 0;
1795 }
1796 
1797 /*
1798  * @implemented
1799  */
1800 BOOLEAN
1801 NTAPI
1802 SeTokenIsRestricted(IN PACCESS_TOKEN Token)
1803 {
1804     PAGED_CODE();
1805 
1806     return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0;
1807 }
1808 
1809 /*
1810  * @implemented
1811  * @note First introduced in NT 5.1 SP2 x86 (5.1.2600.2622), absent in NT 5.2,
1812  *       then finally re-introduced in Vista+.
1813  */
1814 BOOLEAN
1815 NTAPI
1816 SeTokenIsWriteRestricted(IN PACCESS_TOKEN Token)
1817 {
1818     PAGED_CODE();
1819 
1820     // NOTE: NT 5.1 SP2 x86 checks the SE_BACKUP_PRIVILEGES_CHECKED flag
1821     // while Vista+ checks the TOKEN_WRITE_RESTRICTED flag as one expects.
1822     return (((PTOKEN)Token)->TokenFlags & SE_BACKUP_PRIVILEGES_CHECKED) != 0;
1823 }
1824 
1825 /* SYSTEM CALLS ***************************************************************/
1826 
1827 /*
1828  * @implemented
1829  */
1830 _Must_inspect_result_
1831 __kernel_entry
1832 NTSTATUS
1833 NTAPI
1834 NtQueryInformationToken(
1835     _In_ HANDLE TokenHandle,
1836     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
1837     _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength)
1838         PVOID TokenInformation,
1839     _In_ ULONG TokenInformationLength,
1840     _Out_ PULONG ReturnLength)
1841 {
1842     NTSTATUS Status;
1843     KPROCESSOR_MODE PreviousMode;
1844     PTOKEN Token;
1845     ULONG RequiredLength;
1846     union
1847     {
1848         PSID PSid;
1849         ULONG Ulong;
1850     } Unused;
1851 
1852     PAGED_CODE();
1853 
1854     PreviousMode = ExGetPreviousMode();
1855 
1856     /* Check buffers and class validity */
1857     Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
1858                                          SeTokenInformationClass,
1859                                          RTL_NUMBER_OF(SeTokenInformationClass),
1860                                          TokenInformation,
1861                                          TokenInformationLength,
1862                                          ReturnLength,
1863                                          NULL,
1864                                          PreviousMode);
1865     if (!NT_SUCCESS(Status))
1866     {
1867         DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status);
1868         return Status;
1869     }
1870 
1871     Status = ObReferenceObjectByHandle(TokenHandle,
1872                                        (TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY,
1873                                        SeTokenObjectType,
1874                                        PreviousMode,
1875                                        (PVOID*)&Token,
1876                                        NULL);
1877     if (NT_SUCCESS(Status))
1878     {
1879         /* Lock the token */
1880         SepAcquireTokenLockShared(Token);
1881 
1882         switch (TokenInformationClass)
1883         {
1884             case TokenUser:
1885             {
1886                 PTOKEN_USER tu = (PTOKEN_USER)TokenInformation;
1887 
1888                 DPRINT("NtQueryInformationToken(TokenUser)\n");
1889                 RequiredLength = sizeof(TOKEN_USER) +
1890                     RtlLengthSid(Token->UserAndGroups[0].Sid);
1891 
1892                 _SEH2_TRY
1893                 {
1894                     if (TokenInformationLength >= RequiredLength)
1895                     {
1896                         Status = RtlCopySidAndAttributesArray(1,
1897                                                               &Token->UserAndGroups[0],
1898                                                               RequiredLength - sizeof(TOKEN_USER),
1899                                                               &tu->User,
1900                                                               (PSID)(tu + 1),
1901                                                               &Unused.PSid,
1902                                                               &Unused.Ulong);
1903                     }
1904                     else
1905                     {
1906                         Status = STATUS_BUFFER_TOO_SMALL;
1907                     }
1908 
1909                     if (ReturnLength != NULL)
1910                     {
1911                         *ReturnLength = RequiredLength;
1912                     }
1913                 }
1914                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1915                 {
1916                     Status = _SEH2_GetExceptionCode();
1917                 }
1918                 _SEH2_END;
1919 
1920                 break;
1921             }
1922 
1923             case TokenGroups:
1924             {
1925                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
1926 
1927                 DPRINT("NtQueryInformationToken(TokenGroups)\n");
1928                 RequiredLength = sizeof(tg->GroupCount) +
1929                     RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
1930 
1931                 _SEH2_TRY
1932                 {
1933                     if (TokenInformationLength >= RequiredLength)
1934                     {
1935                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
1936                             ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
1937                         PSID Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
1938                                                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
1939 
1940                         tg->GroupCount = Token->UserAndGroupCount - 1;
1941                         Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
1942                                                               &Token->UserAndGroups[1],
1943                                                               SidLen,
1944                                                               &tg->Groups[0],
1945                                                               Sid,
1946                                                               &Unused.PSid,
1947                                                               &Unused.Ulong);
1948                     }
1949                     else
1950                     {
1951                         Status = STATUS_BUFFER_TOO_SMALL;
1952                     }
1953 
1954                     if (ReturnLength != NULL)
1955                     {
1956                         *ReturnLength = RequiredLength;
1957                     }
1958                 }
1959                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1960                 {
1961                     Status = _SEH2_GetExceptionCode();
1962                 }
1963                 _SEH2_END;
1964 
1965                 break;
1966             }
1967 
1968             case TokenPrivileges:
1969             {
1970                 PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation;
1971 
1972                 DPRINT("NtQueryInformationToken(TokenPrivileges)\n");
1973                 RequiredLength = sizeof(tp->PrivilegeCount) +
1974                     (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1975 
1976                 _SEH2_TRY
1977                 {
1978                     if (TokenInformationLength >= RequiredLength)
1979                     {
1980                         tp->PrivilegeCount = Token->PrivilegeCount;
1981                         RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
1982                                                       Token->Privileges,
1983                                                       &tp->Privileges[0]);
1984                     }
1985                     else
1986                     {
1987                         Status = STATUS_BUFFER_TOO_SMALL;
1988                     }
1989 
1990                     if (ReturnLength != NULL)
1991                     {
1992                         *ReturnLength = RequiredLength;
1993                     }
1994                 }
1995                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1996                 {
1997                     Status = _SEH2_GetExceptionCode();
1998                 }
1999                 _SEH2_END;
2000 
2001                 break;
2002             }
2003 
2004             case TokenOwner:
2005             {
2006                 PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
2007                 ULONG SidLen;
2008 
2009                 DPRINT("NtQueryInformationToken(TokenOwner)\n");
2010                 SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
2011                 RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
2012 
2013                 _SEH2_TRY
2014                 {
2015                     if (TokenInformationLength >= RequiredLength)
2016                     {
2017                         to->Owner = (PSID)(to + 1);
2018                         Status = RtlCopySid(SidLen,
2019                                             to->Owner,
2020                                             Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
2021                     }
2022                     else
2023                     {
2024                         Status = STATUS_BUFFER_TOO_SMALL;
2025                     }
2026 
2027                     if (ReturnLength != NULL)
2028                     {
2029                         *ReturnLength = RequiredLength;
2030                     }
2031                 }
2032                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2033                 {
2034                     Status = _SEH2_GetExceptionCode();
2035                 }
2036                 _SEH2_END;
2037 
2038                 break;
2039             }
2040 
2041             case TokenPrimaryGroup:
2042             {
2043                 PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
2044                 ULONG SidLen;
2045 
2046                 DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n");
2047                 SidLen = RtlLengthSid(Token->PrimaryGroup);
2048                 RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
2049 
2050                 _SEH2_TRY
2051                 {
2052                     if (TokenInformationLength >= RequiredLength)
2053                     {
2054                         tpg->PrimaryGroup = (PSID)(tpg + 1);
2055                         Status = RtlCopySid(SidLen,
2056                                             tpg->PrimaryGroup,
2057                                             Token->PrimaryGroup);
2058                     }
2059                     else
2060                     {
2061                         Status = STATUS_BUFFER_TOO_SMALL;
2062                     }
2063 
2064                     if (ReturnLength != NULL)
2065                     {
2066                         *ReturnLength = RequiredLength;
2067                     }
2068                 }
2069                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2070                 {
2071                     Status = _SEH2_GetExceptionCode();
2072                 }
2073                 _SEH2_END;
2074 
2075                 break;
2076             }
2077 
2078             case TokenDefaultDacl:
2079             {
2080                 PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
2081 
2082                 DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n");
2083                 RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
2084 
2085                 if (Token->DefaultDacl != NULL)
2086                     RequiredLength += Token->DefaultDacl->AclSize;
2087 
2088                 _SEH2_TRY
2089                 {
2090                     if (TokenInformationLength >= RequiredLength)
2091                     {
2092                         if (Token->DefaultDacl != NULL)
2093                         {
2094                             tdd->DefaultDacl = (PACL)(tdd + 1);
2095                             RtlCopyMemory(tdd->DefaultDacl,
2096                                           Token->DefaultDacl,
2097                                           Token->DefaultDacl->AclSize);
2098                         }
2099                         else
2100                         {
2101                             tdd->DefaultDacl = NULL;
2102                         }
2103                     }
2104                     else
2105                     {
2106                         Status = STATUS_BUFFER_TOO_SMALL;
2107                     }
2108 
2109                     if (ReturnLength != NULL)
2110                     {
2111                         *ReturnLength = RequiredLength;
2112                     }
2113                 }
2114                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2115                 {
2116                     Status = _SEH2_GetExceptionCode();
2117                 }
2118                 _SEH2_END;
2119 
2120                 break;
2121             }
2122 
2123             case TokenSource:
2124             {
2125                 PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation;
2126 
2127                 DPRINT("NtQueryInformationToken(TokenSource)\n");
2128                 RequiredLength = sizeof(TOKEN_SOURCE);
2129 
2130                 _SEH2_TRY
2131                 {
2132                     if (TokenInformationLength >= RequiredLength)
2133                     {
2134                         *ts = Token->TokenSource;
2135                     }
2136                     else
2137                     {
2138                         Status = STATUS_BUFFER_TOO_SMALL;
2139                     }
2140 
2141                     if (ReturnLength != NULL)
2142                     {
2143                         *ReturnLength = RequiredLength;
2144                     }
2145                 }
2146                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2147                 {
2148                     Status = _SEH2_GetExceptionCode();
2149                 }
2150                 _SEH2_END;
2151 
2152                 break;
2153             }
2154 
2155             case TokenType:
2156             {
2157                 PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation;
2158 
2159                 DPRINT("NtQueryInformationToken(TokenType)\n");
2160                 RequiredLength = sizeof(TOKEN_TYPE);
2161 
2162                 _SEH2_TRY
2163                 {
2164                     if (TokenInformationLength >= RequiredLength)
2165                     {
2166                         *tt = Token->TokenType;
2167                     }
2168                     else
2169                     {
2170                         Status = STATUS_BUFFER_TOO_SMALL;
2171                     }
2172 
2173                     if (ReturnLength != NULL)
2174                     {
2175                         *ReturnLength = RequiredLength;
2176                     }
2177                 }
2178                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2179                 {
2180                     Status = _SEH2_GetExceptionCode();
2181                 }
2182                 _SEH2_END;
2183 
2184                 break;
2185             }
2186 
2187             case TokenImpersonationLevel:
2188             {
2189                 PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation;
2190 
2191                 DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n");
2192 
2193                 /* Fail if the token is not an impersonation token */
2194                 if (Token->TokenType != TokenImpersonation)
2195                 {
2196                     Status = STATUS_INVALID_INFO_CLASS;
2197                     break;
2198                 }
2199 
2200                 RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
2201 
2202                 _SEH2_TRY
2203                 {
2204                     if (TokenInformationLength >= RequiredLength)
2205                     {
2206                         *sil = Token->ImpersonationLevel;
2207                     }
2208                     else
2209                     {
2210                         Status = STATUS_BUFFER_TOO_SMALL;
2211                     }
2212 
2213                     if (ReturnLength != NULL)
2214                     {
2215                         *ReturnLength = RequiredLength;
2216                     }
2217                 }
2218                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2219                 {
2220                     Status = _SEH2_GetExceptionCode();
2221                 }
2222                 _SEH2_END;
2223 
2224                 break;
2225             }
2226 
2227             case TokenStatistics:
2228             {
2229                 PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation;
2230 
2231                 DPRINT("NtQueryInformationToken(TokenStatistics)\n");
2232                 RequiredLength = sizeof(TOKEN_STATISTICS);
2233 
2234                 _SEH2_TRY
2235                 {
2236                     if (TokenInformationLength >= RequiredLength)
2237                     {
2238                         ts->TokenId = Token->TokenId;
2239                         ts->AuthenticationId = Token->AuthenticationId;
2240                         ts->ExpirationTime = Token->ExpirationTime;
2241                         ts->TokenType = Token->TokenType;
2242                         ts->ImpersonationLevel = Token->ImpersonationLevel;
2243                         ts->DynamicCharged = Token->DynamicCharged;
2244                         ts->DynamicAvailable = Token->DynamicAvailable;
2245                         ts->GroupCount = Token->UserAndGroupCount - 1;
2246                         ts->PrivilegeCount = Token->PrivilegeCount;
2247                         ts->ModifiedId = Token->ModifiedId;
2248                     }
2249                     else
2250                     {
2251                         Status = STATUS_BUFFER_TOO_SMALL;
2252                     }
2253 
2254                     if (ReturnLength != NULL)
2255                     {
2256                         *ReturnLength = RequiredLength;
2257                     }
2258                 }
2259                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2260                 {
2261                     Status = _SEH2_GetExceptionCode();
2262                 }
2263                 _SEH2_END;
2264 
2265                 break;
2266             }
2267 
2268             case TokenOrigin:
2269             {
2270                 PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation;
2271 
2272                 DPRINT("NtQueryInformationToken(TokenOrigin)\n");
2273                 RequiredLength = sizeof(TOKEN_ORIGIN);
2274 
2275                 _SEH2_TRY
2276                 {
2277                     if (TokenInformationLength >= RequiredLength)
2278                     {
2279                         RtlCopyLuid(&to->OriginatingLogonSession,
2280                                     &Token->AuthenticationId);
2281                     }
2282                     else
2283                     {
2284                         Status = STATUS_BUFFER_TOO_SMALL;
2285                     }
2286 
2287                     if (ReturnLength != NULL)
2288                     {
2289                         *ReturnLength = RequiredLength;
2290                     }
2291                 }
2292                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2293                 {
2294                     Status = _SEH2_GetExceptionCode();
2295                 }
2296                 _SEH2_END;
2297 
2298                 break;
2299             }
2300 
2301             case TokenGroupsAndPrivileges:
2302                 DPRINT1("NtQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n");
2303                 Status = STATUS_NOT_IMPLEMENTED;
2304                 break;
2305 
2306             case TokenRestrictedSids:
2307             {
2308                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
2309 
2310                 DPRINT("NtQueryInformationToken(TokenRestrictedSids)\n");
2311                 RequiredLength = sizeof(tg->GroupCount) +
2312                 RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
2313 
2314                 _SEH2_TRY
2315                 {
2316                     if (TokenInformationLength >= RequiredLength)
2317                     {
2318                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
2319                             (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
2320                         PSID Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
2321                                           (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
2322 
2323                         tg->GroupCount = Token->RestrictedSidCount;
2324                         Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
2325                                                               Token->RestrictedSids,
2326                                                               SidLen,
2327                                                               &tg->Groups[0],
2328                                                               Sid,
2329                                                               &Unused.PSid,
2330                                                               &Unused.Ulong);
2331                     }
2332                     else
2333                     {
2334                         Status = STATUS_BUFFER_TOO_SMALL;
2335                     }
2336 
2337                     if (ReturnLength != NULL)
2338                     {
2339                         *ReturnLength = RequiredLength;
2340                     }
2341                 }
2342                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2343                 {
2344                     Status = _SEH2_GetExceptionCode();
2345                 }
2346                 _SEH2_END;
2347 
2348                 break;
2349             }
2350 
2351             case TokenSandBoxInert:
2352                 DPRINT1("NtQueryInformationToken(TokenSandboxInert) not implemented\n");
2353                 Status = STATUS_NOT_IMPLEMENTED;
2354                 break;
2355 
2356             case TokenSessionId:
2357             {
2358                 ULONG SessionId = 0;
2359 
2360                 DPRINT("NtQueryInformationToken(TokenSessionId)\n");
2361 
2362                 Status = SeQuerySessionIdToken(Token, &SessionId);
2363                 if (NT_SUCCESS(Status))
2364                 {
2365                     _SEH2_TRY
2366                     {
2367                         /* Buffer size was already verified, no need to check here again */
2368                         *(PULONG)TokenInformation = SessionId;
2369 
2370                         if (ReturnLength != NULL)
2371                         {
2372                             *ReturnLength = sizeof(ULONG);
2373                         }
2374                     }
2375                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2376                     {
2377                         Status = _SEH2_GetExceptionCode();
2378                     }
2379                     _SEH2_END;
2380                 }
2381 
2382                 break;
2383             }
2384 
2385             default:
2386                 DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
2387                 Status = STATUS_INVALID_INFO_CLASS;
2388                 break;
2389         }
2390 
2391         /* Unlock and dereference the token */
2392         SepReleaseTokenLock(Token);
2393         ObDereferenceObject(Token);
2394     }
2395 
2396     return Status;
2397 }
2398 
2399 
2400 /*
2401  * NtSetTokenInformation: Partly implemented.
2402  * Unimplemented:
2403  *  TokenOrigin, TokenDefaultDacl
2404  */
2405 _Must_inspect_result_
2406 __kernel_entry
2407 NTSTATUS
2408 NTAPI
2409 NtSetInformationToken(
2410     _In_ HANDLE TokenHandle,
2411     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
2412     _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
2413     _In_ ULONG TokenInformationLength)
2414 {
2415     NTSTATUS Status;
2416     PTOKEN Token;
2417     KPROCESSOR_MODE PreviousMode;
2418     ULONG NeededAccess = TOKEN_ADJUST_DEFAULT;
2419 
2420     PAGED_CODE();
2421 
2422     PreviousMode = ExGetPreviousMode();
2423 
2424     Status = DefaultSetInfoBufferCheck(TokenInformationClass,
2425                                        SeTokenInformationClass,
2426                                        RTL_NUMBER_OF(SeTokenInformationClass),
2427                                        TokenInformation,
2428                                        TokenInformationLength,
2429                                        PreviousMode);
2430     if (!NT_SUCCESS(Status))
2431     {
2432         /* Invalid buffers */
2433         DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status);
2434         return Status;
2435     }
2436 
2437     if (TokenInformationClass == TokenSessionId)
2438     {
2439         NeededAccess |= TOKEN_ADJUST_SESSIONID;
2440     }
2441 
2442     Status = ObReferenceObjectByHandle(TokenHandle,
2443                                        NeededAccess,
2444                                        SeTokenObjectType,
2445                                        PreviousMode,
2446                                        (PVOID*)&Token,
2447                                        NULL);
2448     if (NT_SUCCESS(Status))
2449     {
2450         switch (TokenInformationClass)
2451         {
2452             case TokenOwner:
2453             {
2454                 if (TokenInformationLength >= sizeof(TOKEN_OWNER))
2455                 {
2456                     PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
2457                     PSID InputSid = NULL, CapturedSid;
2458                     ULONG DefaultOwnerIndex;
2459 
2460                     _SEH2_TRY
2461                     {
2462                         InputSid = to->Owner;
2463                     }
2464                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2465                     {
2466                         Status = _SEH2_GetExceptionCode();
2467                         _SEH2_YIELD(goto Cleanup);
2468                     }
2469                     _SEH2_END;
2470 
2471                     Status = SepCaptureSid(InputSid,
2472                                            PreviousMode,
2473                                            PagedPool,
2474                                            FALSE,
2475                                            &CapturedSid);
2476                     if (NT_SUCCESS(Status))
2477                     {
2478                         /* Lock the token */
2479                         SepAcquireTokenLockExclusive(Token);
2480 
2481                         /* Find the owner amongst the existing token user and groups */
2482                         Status = SepFindPrimaryGroupAndDefaultOwner(Token,
2483                                                                     NULL,
2484                                                                     CapturedSid,
2485                                                                     NULL,
2486                                                                     &DefaultOwnerIndex);
2487                         if (NT_SUCCESS(Status))
2488                         {
2489                             /* Found it */
2490                             Token->DefaultOwnerIndex = DefaultOwnerIndex;
2491                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
2492                         }
2493 
2494                         /* Unlock the token */
2495                         SepReleaseTokenLock(Token);
2496 
2497                         SepReleaseSid(CapturedSid,
2498                                       PreviousMode,
2499                                       FALSE);
2500                     }
2501                 }
2502                 else
2503                 {
2504                     Status = STATUS_INFO_LENGTH_MISMATCH;
2505                 }
2506                 break;
2507             }
2508 
2509             case TokenPrimaryGroup:
2510             {
2511                 if (TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP))
2512                 {
2513                     PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
2514                     PSID InputSid = NULL, CapturedSid;
2515                     ULONG PrimaryGroupIndex;
2516 
2517                     _SEH2_TRY
2518                     {
2519                         InputSid = tpg->PrimaryGroup;
2520                     }
2521                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2522                     {
2523                         Status = _SEH2_GetExceptionCode();
2524                         _SEH2_YIELD(goto Cleanup);
2525                     }
2526                     _SEH2_END;
2527 
2528                     Status = SepCaptureSid(InputSid,
2529                                            PreviousMode,
2530                                            PagedPool,
2531                                            FALSE,
2532                                            &CapturedSid);
2533                     if (NT_SUCCESS(Status))
2534                     {
2535                         /* Lock the token */
2536                         SepAcquireTokenLockExclusive(Token);
2537 
2538                         /* Find the primary group amongst the existing token user and groups */
2539                         Status = SepFindPrimaryGroupAndDefaultOwner(Token,
2540                                                                     CapturedSid,
2541                                                                     NULL,
2542                                                                     &PrimaryGroupIndex,
2543                                                                     NULL);
2544                         if (NT_SUCCESS(Status))
2545                         {
2546                             /* Found it */
2547                             Token->PrimaryGroup = Token->UserAndGroups[PrimaryGroupIndex].Sid;
2548                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
2549                         }
2550 
2551                         /* Unlock the token */
2552                         SepReleaseTokenLock(Token);
2553 
2554                         SepReleaseSid(CapturedSid,
2555                                       PreviousMode,
2556                                       FALSE);
2557                     }
2558                 }
2559                 else
2560                 {
2561                     Status = STATUS_INFO_LENGTH_MISMATCH;
2562                 }
2563                 break;
2564             }
2565 
2566             case TokenDefaultDacl:
2567             {
2568                 if (TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL))
2569                 {
2570                     PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
2571                     PACL InputAcl = NULL;
2572 
2573                     _SEH2_TRY
2574                     {
2575                         InputAcl = tdd->DefaultDacl;
2576                     }
2577                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2578                     {
2579                         Status = _SEH2_GetExceptionCode();
2580                         _SEH2_YIELD(goto Cleanup);
2581                     }
2582                     _SEH2_END;
2583 
2584                     if (InputAcl != NULL)
2585                     {
2586                         PACL CapturedAcl;
2587 
2588                         /* Capture and copy the dacl */
2589                         Status = SepCaptureAcl(InputAcl,
2590                                                PreviousMode,
2591                                                PagedPool,
2592                                                TRUE,
2593                                                &CapturedAcl);
2594                         if (NT_SUCCESS(Status))
2595                         {
2596                             ULONG DynamicLength;
2597 
2598                             /* Lock the token */
2599                             SepAcquireTokenLockExclusive(Token);
2600 
2601                             //
2602                             // NOTE: So far our dynamic area only contains
2603                             // the default dacl, so this makes the following
2604                             // code pretty simple. The day where it stores
2605                             // other data, the code will require adaptations.
2606                             //
2607 
2608                             DynamicLength = Token->DynamicAvailable;
2609                             // Add here any other data length present in the dynamic area...
2610                             if (Token->DefaultDacl)
2611                                 DynamicLength += Token->DefaultDacl->AclSize;
2612 
2613                             /* Reallocate the dynamic area if it is too small */
2614                             Status = STATUS_SUCCESS;
2615                             if ((DynamicLength < CapturedAcl->AclSize) ||
2616                                 (Token->DynamicPart == NULL))
2617                             {
2618                                 PVOID NewDynamicPart;
2619 
2620                                 NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
2621                                                                        CapturedAcl->AclSize,
2622                                                                        TAG_TOKEN_DYNAMIC);
2623                                 if (NewDynamicPart == NULL)
2624                                 {
2625                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2626                                 }
2627                                 else
2628                                 {
2629                                     if (Token->DynamicPart != NULL)
2630                                     {
2631                                         // RtlCopyMemory(NewDynamicPart, Token->DynamicPart, DynamicLength);
2632                                         ExFreePoolWithTag(Token->DynamicPart, TAG_TOKEN_DYNAMIC);
2633                                     }
2634                                     Token->DynamicPart = NewDynamicPart;
2635                                     Token->DynamicAvailable = 0;
2636                                 }
2637                             }
2638                             else
2639                             {
2640                                 Token->DynamicAvailable = DynamicLength - CapturedAcl->AclSize;
2641                             }
2642 
2643                             if (NT_SUCCESS(Status))
2644                             {
2645                                 /* Set the new dacl */
2646                                 Token->DefaultDacl = (PVOID)Token->DynamicPart;
2647                                 RtlCopyMemory(Token->DefaultDacl,
2648                                               CapturedAcl,
2649                                               CapturedAcl->AclSize);
2650 
2651                                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
2652                             }
2653 
2654                             /* Unlock the token */
2655                             SepReleaseTokenLock(Token);
2656 
2657                             ExFreePoolWithTag(CapturedAcl, TAG_ACL);
2658                         }
2659                     }
2660                     else
2661                     {
2662                         /* Lock the token */
2663                         SepAcquireTokenLockExclusive(Token);
2664 
2665                         /* Clear the default dacl if present */
2666                         if (Token->DefaultDacl != NULL)
2667                         {
2668                             Token->DynamicAvailable += Token->DefaultDacl->AclSize;
2669                             RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize);
2670                             Token->DefaultDacl = NULL;
2671 
2672                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
2673                         }
2674 
2675                         /* Unlock the token */
2676                         SepReleaseTokenLock(Token);
2677                     }
2678                 }
2679                 else
2680                 {
2681                     Status = STATUS_INFO_LENGTH_MISMATCH;
2682                 }
2683                 break;
2684             }
2685 
2686             case TokenSessionId:
2687             {
2688                 ULONG SessionId = 0;
2689 
2690                 _SEH2_TRY
2691                 {
2692                     /* Buffer size was already verified, no need to check here again */
2693                     SessionId = *(PULONG)TokenInformation;
2694                 }
2695                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2696                 {
2697                     Status = _SEH2_GetExceptionCode();
2698                     _SEH2_YIELD(goto Cleanup);
2699                 }
2700                 _SEH2_END;
2701 
2702                 /* Check for TCB privilege */
2703                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
2704                 {
2705                     Status = STATUS_PRIVILEGE_NOT_HELD;
2706                     break;
2707                 }
2708 
2709                 /* Lock the token */
2710                 SepAcquireTokenLockExclusive(Token);
2711 
2712                 Token->SessionId = SessionId;
2713                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
2714 
2715                 /* Unlock the token */
2716                 SepReleaseTokenLock(Token);
2717 
2718                 break;
2719             }
2720 
2721             case TokenSessionReference:
2722             {
2723                 ULONG SessionReference;
2724 
2725                 _SEH2_TRY
2726                 {
2727                     /* Buffer size was already verified, no need to check here again */
2728                     SessionReference = *(PULONG)TokenInformation;
2729                 }
2730                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2731                 {
2732                     Status = _SEH2_GetExceptionCode();
2733                     _SEH2_YIELD(goto Cleanup);
2734                 }
2735                 _SEH2_END;
2736 
2737                 /* Check for TCB privilege */
2738                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
2739                 {
2740                     Status = STATUS_PRIVILEGE_NOT_HELD;
2741                     goto Cleanup;
2742                 }
2743 
2744                 /* Check if it is 0 */
2745                 if (SessionReference == 0)
2746                 {
2747                     ULONG OldTokenFlags;
2748 
2749                     /* Lock the token */
2750                     SepAcquireTokenLockExclusive(Token);
2751 
2752                     /* Atomically set the flag in the token */
2753                     OldTokenFlags = RtlInterlockedSetBits(&Token->TokenFlags,
2754                                                           TOKEN_SESSION_NOT_REFERENCED);
2755                     /*
2756                      * If the flag was already set, do not dereference again
2757                      * the logon session. Use SessionReference as an indicator
2758                      * to know whether to really dereference the session.
2759                      */
2760                     if (OldTokenFlags == Token->TokenFlags)
2761                         SessionReference = ULONG_MAX;
2762 
2763                     /* Unlock the token */
2764                     SepReleaseTokenLock(Token);
2765                 }
2766 
2767                 /* Dereference the logon session if needed */
2768                 if (SessionReference == 0)
2769                     SepRmDereferenceLogonSession(&Token->AuthenticationId);
2770 
2771                 break;
2772             }
2773 
2774             case TokenAuditPolicy:
2775             {
2776                 PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation =
2777                     (PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation;
2778                 SEP_AUDIT_POLICY AuditPolicy;
2779                 ULONG i;
2780 
2781                 _SEH2_TRY
2782                 {
2783                     ProbeForRead(PolicyInformation,
2784                                  FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION,
2785                                               Policies[PolicyInformation->PolicyCount]),
2786                                  sizeof(ULONG));
2787 
2788                     /* Loop all policies in the structure */
2789                     for (i = 0; i < PolicyInformation->PolicyCount; i++)
2790                     {
2791                         /* Set the corresponding bits in the packed structure */
2792                         switch (PolicyInformation->Policies[i].Category)
2793                         {
2794                             case AuditCategorySystem:
2795                                 AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value;
2796                                 break;
2797 
2798                             case AuditCategoryLogon:
2799                                 AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value;
2800                                 break;
2801 
2802                             case AuditCategoryObjectAccess:
2803                                 AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value;
2804                                 break;
2805 
2806                             case AuditCategoryPrivilegeUse:
2807                                 AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value;
2808                                 break;
2809 
2810                             case AuditCategoryDetailedTracking:
2811                                 AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value;
2812                                 break;
2813 
2814                             case AuditCategoryPolicyChange:
2815                                 AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value;
2816                                 break;
2817 
2818                             case AuditCategoryAccountManagement:
2819                                 AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value;
2820                                 break;
2821 
2822                             case AuditCategoryDirectoryServiceAccess:
2823                                 AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value;
2824                                 break;
2825 
2826                             case AuditCategoryAccountLogon:
2827                                 AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value;
2828                                 break;
2829                         }
2830                     }
2831                 }
2832                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2833                 {
2834                     Status = _SEH2_GetExceptionCode();
2835                     _SEH2_YIELD(goto Cleanup);
2836                 }
2837                 _SEH2_END;
2838 
2839                 /* Check for TCB privilege */
2840                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
2841                 {
2842                     Status = STATUS_PRIVILEGE_NOT_HELD;
2843                     break;
2844                 }
2845 
2846                 /* Lock the token */
2847                 SepAcquireTokenLockExclusive(Token);
2848 
2849                 /* Set the new audit policy */
2850                 Token->AuditPolicy = AuditPolicy;
2851                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
2852 
2853                 /* Unlock the token */
2854                 SepReleaseTokenLock(Token);
2855 
2856                 break;
2857             }
2858 
2859             case TokenOrigin:
2860             {
2861                 TOKEN_ORIGIN TokenOrigin;
2862 
2863                 _SEH2_TRY
2864                 {
2865                     /* Copy the token origin */
2866                     TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation;
2867                 }
2868                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2869                 {
2870                     Status = _SEH2_GetExceptionCode();
2871                     _SEH2_YIELD(goto Cleanup);
2872                 }
2873                 _SEH2_END;
2874 
2875                 /* Check for TCB privilege */
2876                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
2877                 {
2878                     Status = STATUS_PRIVILEGE_NOT_HELD;
2879                     break;
2880                 }
2881 
2882                 /* Lock the token */
2883                 SepAcquireTokenLockExclusive(Token);
2884 
2885                 /* Check if there is no token origin set yet */
2886                 if (RtlIsZeroLuid(&Token->OriginatingLogonSession))
2887                 {
2888                     /* Set the token origin */
2889                     Token->OriginatingLogonSession =
2890                         TokenOrigin.OriginatingLogonSession;
2891 
2892                     ExAllocateLocallyUniqueId(&Token->ModifiedId);
2893                 }
2894 
2895                 /* Unlock the token */
2896                 SepReleaseTokenLock(Token);
2897 
2898                 break;
2899             }
2900 
2901             default:
2902             {
2903                 DPRINT1("Invalid TokenInformationClass: 0x%lx\n",
2904                         TokenInformationClass);
2905                 Status = STATUS_INVALID_INFO_CLASS;
2906                 break;
2907             }
2908         }
2909 Cleanup:
2910         ObDereferenceObject(Token);
2911     }
2912 
2913     if (!NT_SUCCESS(Status))
2914     {
2915         DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status);
2916     }
2917 
2918     return Status;
2919 }
2920 
2921 
2922 /*
2923  * @implemented
2924  *
2925  * NOTE: Some sources claim 4th param is ImpersonationLevel, but on W2K
2926  * this is certainly NOT true, although I can't say for sure that EffectiveOnly
2927  * is correct either. -Gunnar
2928  * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
2929  * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
2930  * wrong in that regard, while MSDN documentation is correct.
2931  */
2932 _Must_inspect_result_
2933 __kernel_entry
2934 NTSTATUS
2935 NTAPI
2936 NtDuplicateToken(
2937     _In_ HANDLE ExistingTokenHandle,
2938     _In_ ACCESS_MASK DesiredAccess,
2939     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
2940     _In_ BOOLEAN EffectiveOnly,
2941     _In_ TOKEN_TYPE TokenType,
2942     _Out_ PHANDLE NewTokenHandle)
2943 {
2944     KPROCESSOR_MODE PreviousMode;
2945     HANDLE hToken;
2946     PTOKEN Token;
2947     PTOKEN NewToken;
2948     PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService;
2949     BOOLEAN QoSPresent;
2950     OBJECT_HANDLE_INFORMATION HandleInformation;
2951     NTSTATUS Status;
2952 
2953     PAGED_CODE();
2954 
2955     if (TokenType != TokenImpersonation &&
2956         TokenType != TokenPrimary)
2957     {
2958         return STATUS_INVALID_PARAMETER;
2959     }
2960 
2961     PreviousMode = KeGetPreviousMode();
2962 
2963     if (PreviousMode != KernelMode)
2964     {
2965         _SEH2_TRY
2966         {
2967             ProbeForWriteHandle(NewTokenHandle);
2968         }
2969         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2970         {
2971             /* Return the exception code */
2972             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2973         }
2974         _SEH2_END;
2975     }
2976 
2977     Status = SepCaptureSecurityQualityOfService(ObjectAttributes,
2978                                                 PreviousMode,
2979                                                 PagedPool,
2980                                                 FALSE,
2981                                                 &CapturedSecurityQualityOfService,
2982                                                 &QoSPresent);
2983     if (!NT_SUCCESS(Status))
2984     {
2985         DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status);
2986         return Status;
2987     }
2988 
2989     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
2990                                        TOKEN_DUPLICATE,
2991                                        SeTokenObjectType,
2992                                        PreviousMode,
2993                                        (PVOID*)&Token,
2994                                        &HandleInformation);
2995     if (!NT_SUCCESS(Status))
2996     {
2997         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
2998         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
2999                                            PreviousMode,
3000                                            FALSE);
3001         return Status;
3002     }
3003 
3004     /*
3005      * Fail, if the original token is an impersonation token and the caller
3006      * tries to raise the impersonation level of the new token above the
3007      * impersonation level of the original token.
3008      */
3009     if (Token->TokenType == TokenImpersonation)
3010     {
3011         if (QoSPresent &&
3012             CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel)
3013         {
3014             ObDereferenceObject(Token);
3015             SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
3016                                                PreviousMode,
3017                                                FALSE);
3018             return STATUS_BAD_IMPERSONATION_LEVEL;
3019         }
3020     }
3021 
3022     /*
3023      * Fail, if a primary token is to be created from an impersonation token
3024      * and and the impersonation level of the impersonation token is below SecurityImpersonation.
3025      */
3026     if (Token->TokenType == TokenImpersonation &&
3027         TokenType == TokenPrimary &&
3028         Token->ImpersonationLevel < SecurityImpersonation)
3029     {
3030         ObDereferenceObject(Token);
3031         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
3032                                            PreviousMode,
3033                                            FALSE);
3034         return STATUS_BAD_IMPERSONATION_LEVEL;
3035     }
3036 
3037     Status = SepDuplicateToken(Token,
3038                                ObjectAttributes,
3039                                EffectiveOnly,
3040                                TokenType,
3041                                (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous),
3042                                PreviousMode,
3043                                &NewToken);
3044 
3045     ObDereferenceObject(Token);
3046 
3047     if (NT_SUCCESS(Status))
3048     {
3049         Status = ObInsertObject(NewToken,
3050                                 NULL,
3051                                 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess),
3052                                 0,
3053                                 NULL,
3054                                 &hToken);
3055         if (NT_SUCCESS(Status))
3056         {
3057             _SEH2_TRY
3058             {
3059                 *NewTokenHandle = hToken;
3060             }
3061             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3062             {
3063                 Status = _SEH2_GetExceptionCode();
3064             }
3065             _SEH2_END;
3066         }
3067     }
3068 
3069     /* Free the captured structure */
3070     SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
3071                                        PreviousMode,
3072                                        FALSE);
3073 
3074     return Status;
3075 }
3076 
3077 NTSTATUS NTAPI
3078 NtAdjustGroupsToken(IN HANDLE TokenHandle,
3079                     IN BOOLEAN ResetToDefault,
3080                     IN PTOKEN_GROUPS NewState,
3081                     IN ULONG BufferLength,
3082                     OUT PTOKEN_GROUPS PreviousState OPTIONAL,
3083                     OUT PULONG ReturnLength)
3084 {
3085     UNIMPLEMENTED;
3086     return STATUS_NOT_IMPLEMENTED;
3087 }
3088 
3089 
3090 static
3091 NTSTATUS
3092 SepAdjustPrivileges(
3093     _Inout_ PTOKEN Token,
3094     _In_ BOOLEAN DisableAllPrivileges,
3095     _In_opt_ PLUID_AND_ATTRIBUTES NewState,
3096     _In_ ULONG NewStateCount,
3097     _Out_opt_ PTOKEN_PRIVILEGES PreviousState,
3098     _In_ BOOLEAN ApplyChanges,
3099     _Out_ PULONG ChangedPrivileges,
3100     _Out_ PBOOLEAN ChangesMade)
3101 {
3102     ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes;
3103 
3104     /* Count the found privileges and those that need to be changed */
3105     PrivilegeCount = 0;
3106     ChangeCount = 0;
3107     *ChangesMade = FALSE;
3108 
3109     /* Loop all privileges in the token */
3110     for (i = 0; i < Token->PrivilegeCount; i++)
3111     {
3112         /* Shall all of them be disabled? */
3113         if (DisableAllPrivileges)
3114         {
3115             /* The new attributes are the old ones, but disabled */
3116             NewAttributes = Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
3117         }
3118         else
3119         {
3120             /* Otherwise loop all provided privileges */
3121             for (j = 0; j < NewStateCount; j++)
3122             {
3123                 /* Check if this is the LUID we are looking for */
3124                 if (RtlEqualLuid(&Token->Privileges[i].Luid, &NewState[j].Luid))
3125                 {
3126                     DPRINT("Found privilege\n");
3127 
3128                     /* Copy SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED */
3129                     NewAttributes = NewState[j].Attributes;
3130                     NewAttributes &= (SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED);
3131                     NewAttributes |= Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
3132 
3133                     /* Stop looking */
3134                     break;
3135                 }
3136             }
3137 
3138             /* Check if we didn't find the privilege */
3139             if (j == NewStateCount)
3140             {
3141                 /* Continue with the token's next privilege */
3142                 continue;
3143             }
3144         }
3145 
3146         /* We found a privilege, count it */
3147         PrivilegeCount++;
3148 
3149         /* Does the privilege need to be changed? */
3150         if (Token->Privileges[i].Attributes != NewAttributes)
3151         {
3152             /* Does the caller want the old privileges? */
3153             if (PreviousState != NULL)
3154             {
3155                 /* Copy the old privilege */
3156                 PreviousState->Privileges[ChangeCount] = Token->Privileges[i];
3157             }
3158 
3159             /* Does the caller want to apply the changes? */
3160             if (ApplyChanges)
3161             {
3162                 /* Shall we remove the privilege? */
3163                 if (NewAttributes & SE_PRIVILEGE_REMOVED)
3164                 {
3165                     /* Set the token as disabled and update flags for it */
3166                     Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
3167                     SepUpdateSinglePrivilegeFlagToken(Token, i);
3168 
3169                     /* Remove the privilege */
3170                     SepRemovePrivilegeToken(Token, i);
3171 
3172                     *ChangesMade = TRUE;
3173 
3174                     /* Fix the running index and continue with next one */
3175                     i--;
3176                     continue;
3177                 }
3178 
3179                 /* Set the new attributes and update flags */
3180                 Token->Privileges[i].Attributes = NewAttributes;
3181                 SepUpdateSinglePrivilegeFlagToken(Token, i);
3182                 *ChangesMade = TRUE;
3183             }
3184 
3185             /* Increment the change count */
3186             ChangeCount++;
3187         }
3188     }
3189 
3190     /* Set the number of saved privileges */
3191     if (PreviousState != NULL)
3192         PreviousState->PrivilegeCount = ChangeCount;
3193 
3194     /* Return the number of changed privileges */
3195     *ChangedPrivileges = ChangeCount;
3196 
3197     /* Check if we missed some */
3198     if (!DisableAllPrivileges && (PrivilegeCount < NewStateCount))
3199     {
3200         return STATUS_NOT_ALL_ASSIGNED;
3201     }
3202 
3203     return STATUS_SUCCESS;
3204 }
3205 
3206 
3207 /*
3208  * @implemented
3209  */
3210 _Must_inspect_result_
3211 __kernel_entry
3212 NTSTATUS
3213 NTAPI
3214 NtAdjustPrivilegesToken(
3215     _In_ HANDLE TokenHandle,
3216     _In_ BOOLEAN DisableAllPrivileges,
3217     _In_opt_ PTOKEN_PRIVILEGES NewState,
3218     _In_ ULONG BufferLength,
3219     _Out_writes_bytes_to_opt_(BufferLength,*ReturnLength)
3220         PTOKEN_PRIVILEGES PreviousState,
3221     _When_(PreviousState!=NULL, _Out_) PULONG ReturnLength)
3222 {
3223     NTSTATUS Status;
3224     KPROCESSOR_MODE PreviousMode;
3225     PTOKEN Token;
3226     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
3227     ULONG CapturedCount = 0;
3228     ULONG CapturedLength = 0;
3229     ULONG NewStateSize = 0;
3230     ULONG ChangeCount;
3231     ULONG RequiredLength;
3232     BOOLEAN ChangesMade = FALSE;
3233 
3234     PAGED_CODE();
3235 
3236     DPRINT("NtAdjustPrivilegesToken() called\n");
3237 
3238     /* Fail, if we do not disable all privileges but NewState is NULL */
3239     if (DisableAllPrivileges == FALSE && NewState == NULL)
3240         return STATUS_INVALID_PARAMETER;
3241 
3242     PreviousMode = KeGetPreviousMode();
3243     if (PreviousMode != KernelMode)
3244     {
3245         _SEH2_TRY
3246         {
3247             /* Probe NewState */
3248             if (DisableAllPrivileges == FALSE)
3249             {
3250                 /* First probe the header */
3251                 ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG));
3252 
3253                 CapturedCount = NewState->PrivilegeCount;
3254                 NewStateSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedCount]);
3255 
3256                 ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
3257             }
3258 
3259             /* Probe PreviousState and ReturnLength */
3260             if (PreviousState != NULL)
3261             {
3262                 ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
3263                 ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
3264             }
3265         }
3266         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3267         {
3268             /* Return the exception code */
3269             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3270         }
3271         _SEH2_END;
3272     }
3273     else
3274     {
3275         /* This is kernel mode, we trust the caller */
3276         if (DisableAllPrivileges == FALSE)
3277             CapturedCount = NewState->PrivilegeCount;
3278     }
3279 
3280     /* Do we need to capture the new state? */
3281     if (DisableAllPrivileges == FALSE)
3282     {
3283         _SEH2_TRY
3284         {
3285             /* Capture the new state array of privileges */
3286             Status = SeCaptureLuidAndAttributesArray(NewState->Privileges,
3287                                                      CapturedCount,
3288                                                      PreviousMode,
3289                                                      NULL,
3290                                                      0,
3291                                                      PagedPool,
3292                                                      TRUE,
3293                                                      &CapturedPrivileges,
3294                                                      &CapturedLength);
3295         }
3296         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3297         {
3298             /* Return the exception code */
3299             Status = _SEH2_GetExceptionCode();
3300         }
3301         _SEH2_END;
3302 
3303         if (!NT_SUCCESS(Status))
3304             return Status;
3305     }
3306 
3307     /* Reference the token */
3308     Status = ObReferenceObjectByHandle(TokenHandle,
3309                                        TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0),
3310                                        SeTokenObjectType,
3311                                        PreviousMode,
3312                                        (PVOID*)&Token,
3313                                        NULL);
3314     if (!NT_SUCCESS(Status))
3315     {
3316         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
3317 
3318         /* Release the captured privileges */
3319         if (CapturedPrivileges != NULL)
3320         {
3321             SeReleaseLuidAndAttributesArray(CapturedPrivileges,
3322                                             PreviousMode,
3323                                             TRUE);
3324         }
3325 
3326         return Status;
3327     }
3328 
3329     /* Lock the token */
3330     SepAcquireTokenLockExclusive(Token);
3331 
3332     /* Count the privileges that need to be changed, do not apply them yet */
3333     Status = SepAdjustPrivileges(Token,
3334                                  DisableAllPrivileges,
3335                                  CapturedPrivileges,
3336                                  CapturedCount,
3337                                  NULL,
3338                                  FALSE,
3339                                  &ChangeCount,
3340                                  &ChangesMade);
3341 
3342     /* Check if the caller asked for the previous state */
3343     if (PreviousState != NULL)
3344     {
3345         /* Calculate the required length */
3346         RequiredLength = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[ChangeCount]);
3347 
3348         /* Try to return the required buffer length */
3349         _SEH2_TRY
3350         {
3351             *ReturnLength = RequiredLength;
3352         }
3353         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3354         {
3355             /* Do cleanup and return the exception code */
3356             Status = _SEH2_GetExceptionCode();
3357             _SEH2_YIELD(goto Cleanup);
3358         }
3359         _SEH2_END;
3360 
3361         /* Fail, if the buffer length is smaller than the required length */
3362         if (BufferLength < RequiredLength)
3363         {
3364             Status = STATUS_BUFFER_TOO_SMALL;
3365             goto Cleanup;
3366         }
3367     }
3368 
3369     /* Now enter SEH, since we might return the old privileges */
3370     _SEH2_TRY
3371     {
3372         /* This time apply the changes */
3373         Status = SepAdjustPrivileges(Token,
3374                                      DisableAllPrivileges,
3375                                      CapturedPrivileges,
3376                                      CapturedCount,
3377                                      PreviousState,
3378                                      TRUE,
3379                                      &ChangeCount,
3380                                      &ChangesMade);
3381     }
3382     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3383     {
3384         /* Do cleanup and return the exception code */
3385         Status = _SEH2_GetExceptionCode();
3386         ChangesMade = TRUE; // Force write.
3387         _SEH2_YIELD(goto Cleanup);
3388     }
3389     _SEH2_END;
3390 
3391 Cleanup:
3392     /* Touch the token if we made changes */
3393     if (ChangesMade)
3394         ExAllocateLocallyUniqueId(&Token->ModifiedId);
3395 
3396     /* Unlock and dereference the token */
3397     SepReleaseTokenLock(Token);
3398     ObDereferenceObject(Token);
3399 
3400     /* Release the captured privileges */
3401     if (CapturedPrivileges != NULL)
3402     {
3403         SeReleaseLuidAndAttributesArray(CapturedPrivileges,
3404                                         PreviousMode,
3405                                         TRUE);
3406     }
3407 
3408     DPRINT ("NtAdjustPrivilegesToken() done\n");
3409     return Status;
3410 }
3411 
3412 __kernel_entry
3413 NTSTATUS
3414 NTAPI
3415 NtCreateToken(
3416     _Out_ PHANDLE TokenHandle,
3417     _In_ ACCESS_MASK DesiredAccess,
3418     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
3419     _In_ TOKEN_TYPE TokenType,
3420     _In_ PLUID AuthenticationId,
3421     _In_ PLARGE_INTEGER ExpirationTime,
3422     _In_ PTOKEN_USER TokenUser,
3423     _In_ PTOKEN_GROUPS TokenGroups,
3424     _In_ PTOKEN_PRIVILEGES TokenPrivileges,
3425     _In_opt_ PTOKEN_OWNER TokenOwner,
3426     _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
3427     _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
3428     _In_ PTOKEN_SOURCE TokenSource)
3429 {
3430     HANDLE hToken;
3431     KPROCESSOR_MODE PreviousMode;
3432     ULONG PrivilegeCount, GroupCount;
3433     PSID OwnerSid, PrimaryGroupSid;
3434     PACL DefaultDacl;
3435     LARGE_INTEGER LocalExpirationTime = {{0, 0}};
3436     LUID LocalAuthenticationId;
3437     TOKEN_SOURCE LocalTokenSource;
3438     SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
3439     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
3440     PSID_AND_ATTRIBUTES CapturedUser = NULL;
3441     PSID_AND_ATTRIBUTES CapturedGroups = NULL;
3442     PSID CapturedOwnerSid = NULL;
3443     PSID CapturedPrimaryGroupSid = NULL;
3444     PACL CapturedDefaultDacl = NULL;
3445     ULONG PrivilegesLength, UserLength, GroupsLength;
3446     NTSTATUS Status;
3447 
3448     PAGED_CODE();
3449 
3450     PreviousMode = ExGetPreviousMode();
3451 
3452     if (PreviousMode != KernelMode)
3453     {
3454         _SEH2_TRY
3455         {
3456             ProbeForWriteHandle(TokenHandle);
3457 
3458             if (ObjectAttributes != NULL)
3459             {
3460                 ProbeForRead(ObjectAttributes,
3461                              sizeof(OBJECT_ATTRIBUTES),
3462                              sizeof(ULONG));
3463                 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
3464             }
3465 
3466             ProbeForRead(AuthenticationId,
3467                          sizeof(LUID),
3468                          sizeof(ULONG));
3469             LocalAuthenticationId = *AuthenticationId;
3470 
3471             LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
3472 
3473             ProbeForRead(TokenUser,
3474                          sizeof(TOKEN_USER),
3475                          sizeof(ULONG));
3476 
3477             ProbeForRead(TokenGroups,
3478                          sizeof(TOKEN_GROUPS),
3479                          sizeof(ULONG));
3480             GroupCount = TokenGroups->GroupCount;
3481 
3482             ProbeForRead(TokenPrivileges,
3483                          sizeof(TOKEN_PRIVILEGES),
3484                          sizeof(ULONG));
3485             PrivilegeCount = TokenPrivileges->PrivilegeCount;
3486 
3487             if (TokenOwner != NULL)
3488             {
3489                 ProbeForRead(TokenOwner,
3490                              sizeof(TOKEN_OWNER),
3491                              sizeof(ULONG));
3492                 OwnerSid = TokenOwner->Owner;
3493             }
3494             else
3495             {
3496                 OwnerSid = NULL;
3497             }
3498 
3499             ProbeForRead(TokenPrimaryGroup,
3500                          sizeof(TOKEN_PRIMARY_GROUP),
3501                          sizeof(ULONG));
3502             PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
3503 
3504             if (TokenDefaultDacl != NULL)
3505             {
3506                 ProbeForRead(TokenDefaultDacl,
3507                              sizeof(TOKEN_DEFAULT_DACL),
3508                              sizeof(ULONG));
3509                 DefaultDacl = TokenDefaultDacl->DefaultDacl;
3510             }
3511             else
3512             {
3513                 DefaultDacl = NULL;
3514             }
3515 
3516             ProbeForRead(TokenSource,
3517                          sizeof(TOKEN_SOURCE),
3518                          sizeof(ULONG));
3519             LocalTokenSource = *TokenSource;
3520         }
3521         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3522         {
3523             /* Return the exception code */
3524             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3525         }
3526         _SEH2_END;
3527     }
3528     else
3529     {
3530         if (ObjectAttributes != NULL)
3531             LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
3532         LocalAuthenticationId = *AuthenticationId;
3533         LocalExpirationTime = *ExpirationTime;
3534         GroupCount = TokenGroups->GroupCount;
3535         PrivilegeCount = TokenPrivileges->PrivilegeCount;
3536         OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
3537         PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
3538         DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
3539         LocalTokenSource = *TokenSource;
3540     }
3541 
3542     /* Check token type */
3543     if ((TokenType < TokenPrimary) ||
3544         (TokenType > TokenImpersonation))
3545     {
3546         return STATUS_BAD_TOKEN_TYPE;
3547     }
3548 
3549     /* Check for token creation privilege */
3550     if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
3551     {
3552         return STATUS_PRIVILEGE_NOT_HELD;
3553     }
3554 
3555     /* Capture the user SID and attributes */
3556     Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
3557                                             1,
3558                                             PreviousMode,
3559                                             NULL,
3560                                             0,
3561                                             PagedPool,
3562                                             FALSE,
3563                                             &CapturedUser,
3564                                             &UserLength);
3565     if (!NT_SUCCESS(Status))
3566     {
3567         goto Cleanup;
3568     }
3569 
3570     /* Capture the groups SID and attributes array */
3571     Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
3572                                             GroupCount,
3573                                             PreviousMode,
3574                                             NULL,
3575                                             0,
3576                                             PagedPool,
3577                                             FALSE,
3578                                             &CapturedGroups,
3579                                             &GroupsLength);
3580     if (!NT_SUCCESS(Status))
3581     {
3582         goto Cleanup;
3583     }
3584 
3585     /* Capture privileges */
3586     Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
3587                                              PrivilegeCount,
3588                                              PreviousMode,
3589                                              NULL,
3590                                              0,
3591                                              PagedPool,
3592                                              FALSE,
3593                                              &CapturedPrivileges,
3594                                              &PrivilegesLength);
3595     if (!NT_SUCCESS(Status))
3596     {
3597         goto Cleanup;
3598     }
3599 
3600     /* Capture the token owner SID */
3601     if (TokenOwner != NULL)
3602     {
3603         Status = SepCaptureSid(OwnerSid,
3604                                PreviousMode,
3605                                PagedPool,
3606                                FALSE,
3607                                &CapturedOwnerSid);
3608         if (!NT_SUCCESS(Status))
3609         {
3610             goto Cleanup;
3611         }
3612     }
3613 
3614     /* Capture the token primary group SID */
3615     Status = SepCaptureSid(PrimaryGroupSid,
3616                            PreviousMode,
3617                            PagedPool,
3618                            FALSE,
3619                            &CapturedPrimaryGroupSid);
3620     if (!NT_SUCCESS(Status))
3621     {
3622         goto Cleanup;
3623     }
3624 
3625     /* Capture DefaultDacl */
3626     if (DefaultDacl != NULL)
3627     {
3628         Status = SepCaptureAcl(DefaultDacl,
3629                                PreviousMode,
3630                                NonPagedPool,
3631                                FALSE,
3632                                &CapturedDefaultDacl);
3633         if (!NT_SUCCESS(Status))
3634         {
3635             goto Cleanup;
3636         }
3637     }
3638 
3639     /* Call the internal function */
3640     Status = SepCreateToken(&hToken,
3641                             PreviousMode,
3642                             DesiredAccess,
3643                             ObjectAttributes,
3644                             TokenType,
3645                             LocalSecurityQos.ImpersonationLevel,
3646                             &LocalAuthenticationId,
3647                             &LocalExpirationTime,
3648                             CapturedUser,
3649                             GroupCount,
3650                             CapturedGroups,
3651                             0, // FIXME: Should capture
3652                             PrivilegeCount,
3653                             CapturedPrivileges,
3654                             CapturedOwnerSid,
3655                             CapturedPrimaryGroupSid,
3656                             CapturedDefaultDacl,
3657                             &LocalTokenSource,
3658                             FALSE);
3659     if (NT_SUCCESS(Status))
3660     {
3661         _SEH2_TRY
3662         {
3663             *TokenHandle = hToken;
3664         }
3665         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3666         {
3667             Status = _SEH2_GetExceptionCode();
3668         }
3669         _SEH2_END;
3670     }
3671 
3672 Cleanup:
3673 
3674     /* Release what we captured */
3675     SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
3676     SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
3677     SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
3678     SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
3679     SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
3680     SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
3681 
3682     return Status;
3683 }
3684 
3685 /*
3686  * @implemented
3687  */
3688 NTSTATUS
3689 NTAPI
3690 NtOpenThreadTokenEx(IN HANDLE ThreadHandle,
3691                     IN ACCESS_MASK DesiredAccess,
3692                     IN BOOLEAN OpenAsSelf,
3693                     IN ULONG HandleAttributes,
3694                     OUT PHANDLE TokenHandle)
3695 {
3696     PETHREAD Thread, NewThread;
3697     HANDLE hToken;
3698     PTOKEN Token, NewToken = NULL, PrimaryToken;
3699     BOOLEAN CopyOnOpen, EffectiveOnly;
3700     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
3701     SE_IMPERSONATION_STATE ImpersonationState;
3702     OBJECT_ATTRIBUTES ObjectAttributes;
3703     SECURITY_DESCRIPTOR SecurityDescriptor;
3704     PACL Dacl = NULL;
3705     KPROCESSOR_MODE PreviousMode;
3706     NTSTATUS Status;
3707     BOOLEAN RestoreImpersonation = FALSE;
3708 
3709     PAGED_CODE();
3710 
3711     PreviousMode = ExGetPreviousMode();
3712 
3713     if (PreviousMode != KernelMode)
3714     {
3715         _SEH2_TRY
3716         {
3717             ProbeForWriteHandle(TokenHandle);
3718         }
3719         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3720         {
3721             /* Return the exception code */
3722             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3723         }
3724         _SEH2_END;
3725     }
3726 
3727     /* Validate object attributes */
3728     HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode);
3729 
3730     /*
3731      * At first open the thread token for information access and verify
3732      * that the token associated with thread is valid.
3733      */
3734 
3735     Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION,
3736                                        PsThreadType, PreviousMode, (PVOID*)&Thread,
3737                                        NULL);
3738     if (!NT_SUCCESS(Status))
3739     {
3740         return Status;
3741     }
3742 
3743     Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly,
3744                                           &ImpersonationLevel);
3745     if (Token == NULL)
3746     {
3747         ObDereferenceObject(Thread);
3748         return STATUS_NO_TOKEN;
3749     }
3750 
3751     if (ImpersonationLevel == SecurityAnonymous)
3752     {
3753         PsDereferenceImpersonationToken(Token);
3754         ObDereferenceObject(Thread);
3755         return STATUS_CANT_OPEN_ANONYMOUS;
3756     }
3757 
3758     /*
3759      * Revert to self if OpenAsSelf is specified.
3760      */
3761 
3762     if (OpenAsSelf)
3763     {
3764         RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(),
3765                                                       &ImpersonationState);
3766     }
3767 
3768     if (CopyOnOpen)
3769     {
3770         Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_ALL_ACCESS,
3771                                            PsThreadType, KernelMode,
3772                                            (PVOID*)&NewThread, NULL);
3773         if (NT_SUCCESS(Status))
3774         {
3775             PrimaryToken = PsReferencePrimaryToken(NewThread->ThreadsProcess);
3776 
3777             Status = SepCreateImpersonationTokenDacl(Token, PrimaryToken, &Dacl);
3778 
3779             ObFastDereferenceObject(&NewThread->ThreadsProcess->Token, PrimaryToken);
3780 
3781             if (NT_SUCCESS(Status))
3782             {
3783                 if (Dacl)
3784                 {
3785                     RtlCreateSecurityDescriptor(&SecurityDescriptor,
3786                                                 SECURITY_DESCRIPTOR_REVISION);
3787                     RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl,
3788                                                  FALSE);
3789                 }
3790 
3791                 InitializeObjectAttributes(&ObjectAttributes, NULL, HandleAttributes,
3792                                            NULL, Dacl ? &SecurityDescriptor : NULL);
3793 
3794                 Status = SepDuplicateToken(Token, &ObjectAttributes, EffectiveOnly,
3795                                            TokenImpersonation, ImpersonationLevel,
3796                                            KernelMode, &NewToken);
3797                 if (NT_SUCCESS(Status))
3798                 {
3799                     ObReferenceObject(NewToken);
3800                     Status = ObInsertObject(NewToken, NULL, DesiredAccess, 0, NULL,
3801                                             &hToken);
3802                 }
3803             }
3804         }
3805     }
3806     else
3807     {
3808         Status = ObOpenObjectByPointer(Token, HandleAttributes,
3809                                        NULL, DesiredAccess, SeTokenObjectType,
3810                                        PreviousMode, &hToken);
3811     }
3812 
3813     if (Dacl) ExFreePoolWithTag(Dacl, TAG_ACL);
3814 
3815     if (RestoreImpersonation)
3816     {
3817         PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState);
3818     }
3819 
3820     ObDereferenceObject(Token);
3821 
3822     if (NT_SUCCESS(Status) && CopyOnOpen)
3823     {
3824         PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel);
3825     }
3826 
3827     if (NewToken) ObDereferenceObject(NewToken);
3828 
3829     if (CopyOnOpen && NewThread) ObDereferenceObject(NewThread);
3830 
3831     ObDereferenceObject(Thread);
3832 
3833     if (NT_SUCCESS(Status))
3834     {
3835         _SEH2_TRY
3836         {
3837             *TokenHandle = hToken;
3838         }
3839         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3840         {
3841             Status = _SEH2_GetExceptionCode();
3842         }
3843         _SEH2_END;
3844     }
3845 
3846     return Status;
3847 }
3848 
3849 /*
3850  * @implemented
3851  */
3852 NTSTATUS NTAPI
3853 NtOpenThreadToken(IN HANDLE ThreadHandle,
3854                   IN ACCESS_MASK DesiredAccess,
3855                   IN BOOLEAN OpenAsSelf,
3856                   OUT PHANDLE TokenHandle)
3857 {
3858     return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0,
3859                                TokenHandle);
3860 }
3861 
3862 /*
3863  * @unimplemented
3864  */
3865 NTSTATUS
3866 NTAPI
3867 NtCompareTokens(IN HANDLE FirstTokenHandle,
3868                 IN HANDLE SecondTokenHandle,
3869                 OUT PBOOLEAN Equal)
3870 {
3871     KPROCESSOR_MODE PreviousMode;
3872     PTOKEN FirstToken, SecondToken;
3873     BOOLEAN IsEqual;
3874     NTSTATUS Status;
3875 
3876     PAGED_CODE();
3877 
3878     PreviousMode = ExGetPreviousMode();
3879 
3880     if (PreviousMode != KernelMode)
3881     {
3882         _SEH2_TRY
3883         {
3884             ProbeForWriteBoolean(Equal);
3885         }
3886         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3887         {
3888             /* Return the exception code */
3889             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3890         }
3891         _SEH2_END;
3892     }
3893 
3894     Status = ObReferenceObjectByHandle(FirstTokenHandle,
3895                                        TOKEN_QUERY,
3896                                        SeTokenObjectType,
3897                                        PreviousMode,
3898                                        (PVOID*)&FirstToken,
3899                                        NULL);
3900     if (!NT_SUCCESS(Status))
3901         return Status;
3902 
3903     Status = ObReferenceObjectByHandle(SecondTokenHandle,
3904                                        TOKEN_QUERY,
3905                                        SeTokenObjectType,
3906                                        PreviousMode,
3907                                        (PVOID*)&SecondToken,
3908                                        NULL);
3909     if (!NT_SUCCESS(Status))
3910     {
3911         ObDereferenceObject(FirstToken);
3912         return Status;
3913     }
3914 
3915     if (FirstToken != SecondToken)
3916     {
3917         Status = SepCompareTokens(FirstToken,
3918                                   SecondToken,
3919                                   &IsEqual);
3920     }
3921     else
3922     {
3923         IsEqual = TRUE;
3924     }
3925 
3926     ObDereferenceObject(SecondToken);
3927     ObDereferenceObject(FirstToken);
3928 
3929     if (NT_SUCCESS(Status))
3930     {
3931         _SEH2_TRY
3932         {
3933             *Equal = IsEqual;
3934         }
3935         _SEH2_EXCEPT(ExSystemExceptionFilter())
3936         {
3937             Status = _SEH2_GetExceptionCode();
3938         }
3939         _SEH2_END;
3940     }
3941 
3942     return Status;
3943 }
3944 
3945 NTSTATUS
3946 NTAPI
3947 NtFilterToken(IN HANDLE ExistingTokenHandle,
3948               IN ULONG Flags,
3949               IN PTOKEN_GROUPS SidsToDisable OPTIONAL,
3950               IN PTOKEN_PRIVILEGES PrivilegesToDelete OPTIONAL,
3951               IN PTOKEN_GROUPS RestrictedSids OPTIONAL,
3952               OUT PHANDLE NewTokenHandle)
3953 {
3954     UNIMPLEMENTED;
3955     return STATUS_NOT_IMPLEMENTED;
3956 }
3957 
3958 /*
3959  * @unimplemented
3960  */
3961 NTSTATUS
3962 NTAPI
3963 NtImpersonateAnonymousToken(IN HANDLE Thread)
3964 {
3965     UNIMPLEMENTED;
3966     return STATUS_NOT_IMPLEMENTED;
3967 }
3968 
3969 /* EOF */
3970