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