xref: /reactos/ntoskrnl/se/tokencls.c (revision 8b75dce4)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Access token Query/Set information classes implementation
5  * COPYRIGHT:   Copyright David Welch <welch@cwcom.net>
6  *              Copyright 2021-2022 George Bișoc <george.bisoc@reactos.org>
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #include <ntlsa.h>
16 
17 /* INFORMATION CLASSES ********************************************************/
18 
19 static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = {
20 
21     /* Class 0 not used, blame MS! */
22     IQS_NONE,
23 
24     /* TokenUser */
25     IQS_SAME(TOKEN_USER, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
26     /* TokenGroups */
27     IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
28     /* TokenPrivileges */
29     IQS_SAME(TOKEN_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
30     /* TokenOwner */
31     IQS_SAME(TOKEN_OWNER, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
32     /* TokenPrimaryGroup */
33     IQS_SAME(TOKEN_PRIMARY_GROUP, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
34     /* TokenDefaultDacl */
35     IQS_SAME(TOKEN_DEFAULT_DACL, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
36     /* TokenSource */
37     IQS_SAME(TOKEN_SOURCE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
38     /* TokenType */
39     IQS_SAME(TOKEN_TYPE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
40     /* TokenImpersonationLevel */
41     IQS_SAME(SECURITY_IMPERSONATION_LEVEL, ULONG, ICIF_QUERY),
42     /* TokenStatistics */
43     IQS_SAME(TOKEN_STATISTICS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
44     /* TokenRestrictedSids */
45     IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
46     /* TokenSessionId */
47     IQS_SAME(ULONG, ULONG, ICIF_QUERY | ICIF_SET),
48     /* TokenGroupsAndPrivileges */
49     IQS_SAME(TOKEN_GROUPS_AND_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
50     /* TokenSessionReference */
51     IQS_SAME(ULONG, ULONG, ICIF_SET),
52     /* TokenSandBoxInert */
53     IQS_SAME(ULONG, ULONG, ICIF_QUERY),
54     /* TokenAuditPolicy */
55     IQS_SAME(TOKEN_AUDIT_POLICY_INFORMATION, ULONG, ICIF_SET | ICIF_SET_SIZE_VARIABLE),
56     /* TokenOrigin */
57     IQS_SAME(TOKEN_ORIGIN, ULONG, ICIF_QUERY | ICIF_SET),
58 };
59 
60 /* PUBLIC FUNCTIONS *****************************************************************/
61 
62 /**
63  * @brief
64  * Queries information details about the given token to the call. The difference
65  * between NtQueryInformationToken and this routine is that the system call has
66  * user mode buffer data probing and additional protection checks whereas this
67  * routine doesn't have any of these. The routine is used exclusively in kernel
68  * mode.
69  *
70  * @param[in] AccessToken
71  * An access token to be given.
72  *
73  * @param[in] TokenInformationClass
74  * Token information class.
75  *
76  * @param[out] TokenInformation
77  * Buffer with retrieved information. Such information is arbitrary, depending
78  * on the requested information class.
79  *
80  * @return
81  * Returns STATUS_SUCCESS if the operation to query the desired information
82  * has completed successfully. STATUS_INSUFFICIENT_RESOURCES is returned if
83  * pool memory allocation has failed to satisfy an operation. Otherwise
84  * STATUS_INVALID_INFO_CLASS is returned indicating that the information
85  * class provided is not supported by the routine.
86  *
87  * @remarks
88  * Only certain information classes are not implemented in this function and
89  * these are TokenOrigin, TokenGroupsAndPrivileges, TokenRestrictedSids and
90  * TokenSandBoxInert. The following classes are implemented in NtQueryInformationToken
91  * only.
92  */
93 NTSTATUS
94 NTAPI
95 SeQueryInformationToken(
96     _In_ PACCESS_TOKEN AccessToken,
97     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
98     _Outptr_result_buffer_(_Inexpressible_(token-dependent)) PVOID *TokenInformation)
99 {
100     NTSTATUS Status;
101     PTOKEN Token = (PTOKEN)AccessToken;
102     ULONG RequiredLength;
103     union
104     {
105         PSID PSid;
106         ULONG Ulong;
107     } Unused;
108 
109     PAGED_CODE();
110 
111     /* Lock the token */
112     SepAcquireTokenLockShared(Token);
113 
114     switch (TokenInformationClass)
115     {
116         case TokenUser:
117         {
118             PTOKEN_USER tu;
119 
120             DPRINT("SeQueryInformationToken(TokenUser)\n");
121             RequiredLength = sizeof(TOKEN_USER) +
122                 RtlLengthSid(Token->UserAndGroups[0].Sid);
123 
124             /* Allocate the output buffer */
125             tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
126             if (tu == NULL)
127             {
128                 Status = STATUS_INSUFFICIENT_RESOURCES;
129                 break;
130             }
131 
132             Status = RtlCopySidAndAttributesArray(1,
133                                                   &Token->UserAndGroups[0],
134                                                   RequiredLength - sizeof(TOKEN_USER),
135                                                   &tu->User,
136                                                   (PSID)(tu + 1),
137                                                   &Unused.PSid,
138                                                   &Unused.Ulong);
139 
140             /* Return the structure */
141             *TokenInformation = tu;
142             Status = STATUS_SUCCESS;
143             break;
144         }
145 
146         case TokenGroups:
147         {
148             PTOKEN_GROUPS tg;
149             ULONG SidLen;
150             PSID Sid;
151 
152             DPRINT("SeQueryInformationToken(TokenGroups)\n");
153             RequiredLength = sizeof(tg->GroupCount) +
154                 RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
155 
156             SidLen = RequiredLength - sizeof(tg->GroupCount) -
157                 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
158 
159             /* Allocate the output buffer */
160             tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
161             if (tg == NULL)
162             {
163                 Status = STATUS_INSUFFICIENT_RESOURCES;
164                 break;
165             }
166 
167             Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
168                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
169 
170             tg->GroupCount = Token->UserAndGroupCount - 1;
171             Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
172                                                   &Token->UserAndGroups[1],
173                                                   SidLen,
174                                                   &tg->Groups[0],
175                                                   Sid,
176                                                   &Unused.PSid,
177                                                   &Unused.Ulong);
178 
179             /* Return the structure */
180             *TokenInformation = tg;
181             Status = STATUS_SUCCESS;
182             break;
183         }
184 
185         case TokenPrivileges:
186         {
187             PTOKEN_PRIVILEGES tp;
188 
189             DPRINT("SeQueryInformationToken(TokenPrivileges)\n");
190             RequiredLength = sizeof(tp->PrivilegeCount) +
191                 (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
192 
193             /* Allocate the output buffer */
194             tp = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
195             if (tp == NULL)
196             {
197                 Status = STATUS_INSUFFICIENT_RESOURCES;
198                 break;
199             }
200 
201             tp->PrivilegeCount = Token->PrivilegeCount;
202             RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
203                                           Token->Privileges,
204                                           &tp->Privileges[0]);
205 
206             /* Return the structure */
207             *TokenInformation = tp;
208             Status = STATUS_SUCCESS;
209             break;
210         }
211 
212         case TokenOwner:
213         {
214             PTOKEN_OWNER to;
215             ULONG SidLen;
216 
217             DPRINT("SeQueryInformationToken(TokenOwner)\n");
218             SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
219             RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
220 
221             /* Allocate the output buffer */
222             to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
223             if (to == NULL)
224             {
225                 Status = STATUS_INSUFFICIENT_RESOURCES;
226                 break;
227             }
228 
229             to->Owner = (PSID)(to + 1);
230             Status = RtlCopySid(SidLen,
231                                 to->Owner,
232                                 Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
233 
234             /* Return the structure */
235             *TokenInformation = to;
236             Status = STATUS_SUCCESS;
237             break;
238         }
239 
240         case TokenPrimaryGroup:
241         {
242             PTOKEN_PRIMARY_GROUP tpg;
243             ULONG SidLen;
244 
245             DPRINT("SeQueryInformationToken(TokenPrimaryGroup)\n");
246             SidLen = RtlLengthSid(Token->PrimaryGroup);
247             RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
248 
249             /* Allocate the output buffer */
250             tpg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
251             if (tpg == NULL)
252             {
253                 Status = STATUS_INSUFFICIENT_RESOURCES;
254                 break;
255             }
256 
257             tpg->PrimaryGroup = (PSID)(tpg + 1);
258             Status = RtlCopySid(SidLen,
259                                 tpg->PrimaryGroup,
260                                 Token->PrimaryGroup);
261 
262             /* Return the structure */
263             *TokenInformation = tpg;
264             Status = STATUS_SUCCESS;
265             break;
266         }
267 
268         case TokenDefaultDacl:
269         {
270             PTOKEN_DEFAULT_DACL tdd;
271 
272             DPRINT("SeQueryInformationToken(TokenDefaultDacl)\n");
273             RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
274 
275             if (Token->DefaultDacl != NULL)
276                 RequiredLength += Token->DefaultDacl->AclSize;
277 
278             /* Allocate the output buffer */
279             tdd = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
280             if (tdd == NULL)
281             {
282                 Status = STATUS_INSUFFICIENT_RESOURCES;
283                 break;
284             }
285 
286             if (Token->DefaultDacl != NULL)
287             {
288                 tdd->DefaultDacl = (PACL)(tdd + 1);
289                 RtlCopyMemory(tdd->DefaultDacl,
290                               Token->DefaultDacl,
291                               Token->DefaultDacl->AclSize);
292             }
293             else
294             {
295                 tdd->DefaultDacl = NULL;
296             }
297 
298             /* Return the structure */
299             *TokenInformation = tdd;
300             Status = STATUS_SUCCESS;
301             break;
302         }
303 
304         case TokenSource:
305         {
306             PTOKEN_SOURCE ts;
307 
308             DPRINT("SeQueryInformationToken(TokenSource)\n");
309             RequiredLength = sizeof(TOKEN_SOURCE);
310 
311             /* Allocate the output buffer */
312             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
313             if (ts == NULL)
314             {
315                 Status = STATUS_INSUFFICIENT_RESOURCES;
316                 break;
317             }
318 
319             *ts = Token->TokenSource;
320 
321             /* Return the structure */
322             *TokenInformation = ts;
323             Status = STATUS_SUCCESS;
324             break;
325         }
326 
327         case TokenType:
328         {
329             PTOKEN_TYPE tt;
330 
331             DPRINT("SeQueryInformationToken(TokenType)\n");
332             RequiredLength = sizeof(TOKEN_TYPE);
333 
334             /* Allocate the output buffer */
335             tt = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
336             if (tt == NULL)
337             {
338                 Status = STATUS_INSUFFICIENT_RESOURCES;
339                 break;
340             }
341 
342             *tt = Token->TokenType;
343 
344             /* Return the structure */
345             *TokenInformation = tt;
346             Status = STATUS_SUCCESS;
347             break;
348         }
349 
350         case TokenImpersonationLevel:
351         {
352             PSECURITY_IMPERSONATION_LEVEL sil;
353 
354             DPRINT("SeQueryInformationToken(TokenImpersonationLevel)\n");
355             RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
356 
357             /* Fail if the token is not an impersonation token */
358             if (Token->TokenType != TokenImpersonation)
359             {
360                 Status = STATUS_INVALID_INFO_CLASS;
361                 break;
362             }
363 
364             /* Allocate the output buffer */
365             sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
366             if (sil == NULL)
367             {
368                 Status = STATUS_INSUFFICIENT_RESOURCES;
369                 break;
370             }
371 
372             *sil = Token->ImpersonationLevel;
373 
374             /* Return the structure */
375             *TokenInformation = sil;
376             Status = STATUS_SUCCESS;
377             break;
378         }
379 
380         case TokenStatistics:
381         {
382             PTOKEN_STATISTICS ts;
383 
384             DPRINT("SeQueryInformationToken(TokenStatistics)\n");
385             RequiredLength = sizeof(TOKEN_STATISTICS);
386 
387             /* Allocate the output buffer */
388             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
389             if (ts == NULL)
390             {
391                 Status = STATUS_INSUFFICIENT_RESOURCES;
392                 break;
393             }
394 
395             ts->TokenId = Token->TokenId;
396             ts->AuthenticationId = Token->AuthenticationId;
397             ts->ExpirationTime = Token->ExpirationTime;
398             ts->TokenType = Token->TokenType;
399             ts->ImpersonationLevel = Token->ImpersonationLevel;
400             ts->DynamicCharged = Token->DynamicCharged;
401             ts->DynamicAvailable = SepComputeAvailableDynamicSpace(Token->DynamicCharged, Token->PrimaryGroup, Token->DefaultDacl);
402             ts->GroupCount = Token->UserAndGroupCount - 1;
403             ts->PrivilegeCount = Token->PrivilegeCount;
404             ts->ModifiedId = Token->ModifiedId;
405 
406             /* Return the structure */
407             *TokenInformation = ts;
408             Status = STATUS_SUCCESS;
409             break;
410         }
411 
412         case TokenSessionId:
413         {
414             DPRINT("SeQueryInformationToken(TokenSessionId)\n");
415             Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation);
416             break;
417         }
418 
419         default:
420             DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
421             Status = STATUS_INVALID_INFO_CLASS;
422             break;
423     }
424 
425     /* Release the lock of the token */
426     SepReleaseTokenLock(Token);
427 
428     return Status;
429 }
430 
431 /* SYSTEM CALLS ***************************************************************/
432 
433 /**
434  * @brief
435  * Queries a specific type of information in regard of an access token based upon
436  * the information class. The calling thread must have specific access rights in order
437  * to obtain specific information about the token.
438  *
439  * @param[in] TokenHandle
440  * A handle of a token where information is to be gathered.
441  *
442  * @param[in] TokenInformationClass
443  * Token information class.
444  *
445  * @param[out] TokenInformation
446  * A returned output buffer with token information, which information is arbitrarily upon
447  * the information class chosen.
448  *
449  * @param[in] TokenInformationLength
450  * Length of the token information buffer, in bytes.
451  *
452  * @param[out] ReturnLength
453  * A pointer to a variable provided by the caller that receives the actual length
454  * of the buffer pointed by TokenInformation, in bytes. If TokenInformation is NULL
455  * and TokenInformationLength is 0, this parameter receives the required length
456  * needed to store the buffer information in memory. This parameter must not
457  * be NULL!
458  *
459  * @return
460  * Returns STATUS_SUCCESS if information querying has completed successfully.
461  * STATUS_BUFFER_TOO_SMALL is returned if the information length that represents
462  * the token information buffer is not greater than the required length.
463  * STATUS_INVALID_HANDLE is returned if the token handle is not a valid one.
464  * STATUS_INVALID_INFO_CLASS is returned if the information class is not a valid
465  * one (that is, the class doesn't belong to TOKEN_INFORMATION_CLASS).
466  * STATUS_ACCESS_VIOLATION is returned if ReturnLength is NULL. A failure NTSTATUS
467  * code is returned otherwise.
468  */
469 _Must_inspect_result_
470 __kernel_entry
471 NTSTATUS
472 NTAPI
NtQueryInformationToken(_In_ HANDLE TokenHandle,_In_ TOKEN_INFORMATION_CLASS TokenInformationClass,_Out_writes_bytes_to_opt_ (TokenInformationLength,* ReturnLength)PVOID TokenInformation,_In_ ULONG TokenInformationLength,_Out_ PULONG ReturnLength)473 NtQueryInformationToken(
474     _In_ HANDLE TokenHandle,
475     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
476     _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength)
477         PVOID TokenInformation,
478     _In_ ULONG TokenInformationLength,
479     _Out_ PULONG ReturnLength)
480 {
481     NTSTATUS Status;
482     KPROCESSOR_MODE PreviousMode;
483     PTOKEN Token;
484     ULONG RequiredLength = 0;
485     union
486     {
487         PSID PSid;
488         ULONG Ulong;
489     } Unused;
490 
491     PAGED_CODE();
492 
493     PreviousMode = ExGetPreviousMode();
494 
495     /* Check buffers and class validity */
496     Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
497                                          SeTokenInformationClass,
498                                          RTL_NUMBER_OF(SeTokenInformationClass),
499                                          ICIF_PROBE_READ_WRITE | ICIF_FORCE_RETURN_LENGTH_PROBE,
500                                          TokenInformation,
501                                          TokenInformationLength,
502                                          ReturnLength,
503                                          NULL,
504                                          PreviousMode);
505     if (!NT_SUCCESS(Status))
506     {
507         DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status);
508         return Status;
509     }
510 
511     Status = ObReferenceObjectByHandle(TokenHandle,
512                                        (TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY,
513                                        SeTokenObjectType,
514                                        PreviousMode,
515                                        (PVOID*)&Token,
516                                        NULL);
517     if (NT_SUCCESS(Status))
518     {
519         /* Lock the token */
520         SepAcquireTokenLockShared(Token);
521 
522         switch (TokenInformationClass)
523         {
524             case TokenUser:
525             {
526                 PTOKEN_USER tu = (PTOKEN_USER)TokenInformation;
527 
528                 DPRINT("NtQueryInformationToken(TokenUser)\n");
529                 RequiredLength = sizeof(TOKEN_USER) +
530                     RtlLengthSid(Token->UserAndGroups[0].Sid);
531 
532                 _SEH2_TRY
533                 {
534                     if (TokenInformationLength >= RequiredLength)
535                     {
536                         Status = RtlCopySidAndAttributesArray(1,
537                                                               &Token->UserAndGroups[0],
538                                                               RequiredLength - sizeof(TOKEN_USER),
539                                                               &tu->User,
540                                                               (PSID)(tu + 1),
541                                                               &Unused.PSid,
542                                                               &Unused.Ulong);
543                     }
544                     else
545                     {
546                         Status = STATUS_BUFFER_TOO_SMALL;
547                     }
548 
549                     *ReturnLength = RequiredLength;
550                 }
551                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
552                 {
553                     Status = _SEH2_GetExceptionCode();
554                 }
555                 _SEH2_END;
556 
557                 break;
558             }
559 
560             case TokenGroups:
561             {
562                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
563 
564                 DPRINT("NtQueryInformationToken(TokenGroups)\n");
565                 RequiredLength = sizeof(tg->GroupCount) +
566                     RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
567 
568                 _SEH2_TRY
569                 {
570                     if (TokenInformationLength >= RequiredLength)
571                     {
572                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
573                             ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
574                         PSID Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
575                                                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
576 
577                         tg->GroupCount = Token->UserAndGroupCount - 1;
578                         Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
579                                                               &Token->UserAndGroups[1],
580                                                               SidLen,
581                                                               &tg->Groups[0],
582                                                               Sid,
583                                                               &Unused.PSid,
584                                                               &Unused.Ulong);
585                     }
586                     else
587                     {
588                         Status = STATUS_BUFFER_TOO_SMALL;
589                     }
590 
591                     *ReturnLength = RequiredLength;
592                 }
593                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
594                 {
595                     Status = _SEH2_GetExceptionCode();
596                 }
597                 _SEH2_END;
598 
599                 break;
600             }
601 
602             case TokenPrivileges:
603             {
604                 PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation;
605 
606                 DPRINT("NtQueryInformationToken(TokenPrivileges)\n");
607                 RequiredLength = sizeof(tp->PrivilegeCount) +
608                     (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
609 
610                 _SEH2_TRY
611                 {
612                     if (TokenInformationLength >= RequiredLength)
613                     {
614                         tp->PrivilegeCount = Token->PrivilegeCount;
615                         RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
616                                                       Token->Privileges,
617                                                       &tp->Privileges[0]);
618                     }
619                     else
620                     {
621                         Status = STATUS_BUFFER_TOO_SMALL;
622                     }
623 
624                     *ReturnLength = RequiredLength;
625                 }
626                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
627                 {
628                     Status = _SEH2_GetExceptionCode();
629                 }
630                 _SEH2_END;
631 
632                 break;
633             }
634 
635             case TokenOwner:
636             {
637                 PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
638                 ULONG SidLen;
639 
640                 DPRINT("NtQueryInformationToken(TokenOwner)\n");
641                 SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
642                 RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
643 
644                 _SEH2_TRY
645                 {
646                     if (TokenInformationLength >= RequiredLength)
647                     {
648                         to->Owner = (PSID)(to + 1);
649                         Status = RtlCopySid(SidLen,
650                                             to->Owner,
651                                             Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
652                     }
653                     else
654                     {
655                         Status = STATUS_BUFFER_TOO_SMALL;
656                     }
657 
658                    *ReturnLength = RequiredLength;
659                 }
660                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
661                 {
662                     Status = _SEH2_GetExceptionCode();
663                 }
664                 _SEH2_END;
665 
666                 break;
667             }
668 
669             case TokenPrimaryGroup:
670             {
671                 PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
672                 ULONG SidLen;
673 
674                 DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n");
675                 SidLen = RtlLengthSid(Token->PrimaryGroup);
676                 RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
677 
678                 _SEH2_TRY
679                 {
680                     if (TokenInformationLength >= RequiredLength)
681                     {
682                         tpg->PrimaryGroup = (PSID)(tpg + 1);
683                         Status = RtlCopySid(SidLen,
684                                             tpg->PrimaryGroup,
685                                             Token->PrimaryGroup);
686                     }
687                     else
688                     {
689                         Status = STATUS_BUFFER_TOO_SMALL;
690                     }
691 
692                     *ReturnLength = RequiredLength;
693                 }
694                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
695                 {
696                     Status = _SEH2_GetExceptionCode();
697                 }
698                 _SEH2_END;
699 
700                 break;
701             }
702 
703             case TokenDefaultDacl:
704             {
705                 PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
706 
707                 DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n");
708                 RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
709 
710                 if (Token->DefaultDacl != NULL)
711                     RequiredLength += Token->DefaultDacl->AclSize;
712 
713                 _SEH2_TRY
714                 {
715                     if (TokenInformationLength >= RequiredLength)
716                     {
717                         if (Token->DefaultDacl != NULL)
718                         {
719                             tdd->DefaultDacl = (PACL)(tdd + 1);
720                             RtlCopyMemory(tdd->DefaultDacl,
721                                           Token->DefaultDacl,
722                                           Token->DefaultDacl->AclSize);
723                         }
724                         else
725                         {
726                             tdd->DefaultDacl = NULL;
727                         }
728                     }
729                     else
730                     {
731                         Status = STATUS_BUFFER_TOO_SMALL;
732                     }
733 
734                     *ReturnLength = RequiredLength;
735                 }
736                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
737                 {
738                     Status = _SEH2_GetExceptionCode();
739                 }
740                 _SEH2_END;
741 
742                 break;
743             }
744 
745             case TokenSource:
746             {
747                 PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation;
748 
749                 DPRINT("NtQueryInformationToken(TokenSource)\n");
750                 RequiredLength = sizeof(TOKEN_SOURCE);
751 
752                 _SEH2_TRY
753                 {
754                     if (TokenInformationLength >= RequiredLength)
755                     {
756                         *ts = Token->TokenSource;
757                     }
758                     else
759                     {
760                         Status = STATUS_BUFFER_TOO_SMALL;
761                     }
762 
763                    *ReturnLength = RequiredLength;
764                 }
765                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
766                 {
767                     Status = _SEH2_GetExceptionCode();
768                 }
769                 _SEH2_END;
770 
771                 break;
772             }
773 
774             case TokenType:
775             {
776                 PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation;
777 
778                 DPRINT("NtQueryInformationToken(TokenType)\n");
779                 RequiredLength = sizeof(TOKEN_TYPE);
780 
781                 _SEH2_TRY
782                 {
783                     if (TokenInformationLength >= RequiredLength)
784                     {
785                         *tt = Token->TokenType;
786                     }
787                     else
788                     {
789                         Status = STATUS_BUFFER_TOO_SMALL;
790                     }
791 
792                     *ReturnLength = RequiredLength;
793                 }
794                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
795                 {
796                     Status = _SEH2_GetExceptionCode();
797                 }
798                 _SEH2_END;
799 
800                 break;
801             }
802 
803             case TokenImpersonationLevel:
804             {
805                 PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation;
806 
807                 DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n");
808 
809                 /* Fail if the token is not an impersonation token */
810                 if (Token->TokenType != TokenImpersonation)
811                 {
812                     Status = STATUS_INVALID_INFO_CLASS;
813                     break;
814                 }
815 
816                 RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
817 
818                 _SEH2_TRY
819                 {
820                     if (TokenInformationLength >= RequiredLength)
821                     {
822                         *sil = Token->ImpersonationLevel;
823                     }
824                     else
825                     {
826                         Status = STATUS_BUFFER_TOO_SMALL;
827                     }
828 
829                     *ReturnLength = RequiredLength;
830                 }
831                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
832                 {
833                     Status = _SEH2_GetExceptionCode();
834                 }
835                 _SEH2_END;
836 
837                 break;
838             }
839 
840             case TokenStatistics:
841             {
842                 PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation;
843 
844                 DPRINT("NtQueryInformationToken(TokenStatistics)\n");
845                 RequiredLength = sizeof(TOKEN_STATISTICS);
846 
847                 _SEH2_TRY
848                 {
849                     if (TokenInformationLength >= RequiredLength)
850                     {
851                         ts->TokenId = Token->TokenId;
852                         ts->AuthenticationId = Token->AuthenticationId;
853                         ts->ExpirationTime = Token->ExpirationTime;
854                         ts->TokenType = Token->TokenType;
855                         ts->ImpersonationLevel = Token->ImpersonationLevel;
856                         ts->DynamicCharged = Token->DynamicCharged;
857                         ts->DynamicAvailable = SepComputeAvailableDynamicSpace(Token->DynamicCharged, Token->PrimaryGroup, Token->DefaultDacl);
858                         ts->GroupCount = Token->UserAndGroupCount - 1;
859                         ts->PrivilegeCount = Token->PrivilegeCount;
860                         ts->ModifiedId = Token->ModifiedId;
861                     }
862                     else
863                     {
864                         Status = STATUS_BUFFER_TOO_SMALL;
865                     }
866 
867                     *ReturnLength = RequiredLength;
868                 }
869                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
870                 {
871                     Status = _SEH2_GetExceptionCode();
872                 }
873                 _SEH2_END;
874 
875                 break;
876             }
877 
878             case TokenOrigin:
879             {
880                 PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation;
881 
882                 DPRINT("NtQueryInformationToken(TokenOrigin)\n");
883                 RequiredLength = sizeof(TOKEN_ORIGIN);
884 
885                 _SEH2_TRY
886                 {
887                     if (TokenInformationLength >= RequiredLength)
888                     {
889                         to->OriginatingLogonSession = Token->AuthenticationId;
890                     }
891                     else
892                     {
893                         Status = STATUS_BUFFER_TOO_SMALL;
894                     }
895 
896                     *ReturnLength = RequiredLength;
897                 }
898                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
899                 {
900                     Status = _SEH2_GetExceptionCode();
901                 }
902                 _SEH2_END;
903 
904                 break;
905             }
906 
907             case TokenGroupsAndPrivileges:
908             {
909                 PSID Sid, RestrictedSid;
910                 ULONG SidLen, RestrictedSidLen;
911                 ULONG UserGroupLength, RestrictedSidLength, PrivilegeLength;
912                 PTOKEN_GROUPS_AND_PRIVILEGES GroupsAndPrivs = (PTOKEN_GROUPS_AND_PRIVILEGES)TokenInformation;
913 
914                 DPRINT("NtQueryInformationToken(TokenGroupsAndPrivileges)\n");
915                 UserGroupLength = RtlLengthSidAndAttributes(Token->UserAndGroupCount, Token->UserAndGroups);
916                 RestrictedSidLength = RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
917                 PrivilegeLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
918 
919                 RequiredLength = sizeof(TOKEN_GROUPS_AND_PRIVILEGES) +
920                                  UserGroupLength + RestrictedSidLength + PrivilegeLength;
921 
922                 _SEH2_TRY
923                 {
924                     if (TokenInformationLength >= RequiredLength)
925                     {
926                         GroupsAndPrivs->SidCount = Token->UserAndGroupCount;
927                         GroupsAndPrivs->SidLength = UserGroupLength;
928                         GroupsAndPrivs->Sids = (PSID_AND_ATTRIBUTES)(GroupsAndPrivs + 1);
929 
930                         Sid = (PSID)((ULONG_PTR)GroupsAndPrivs->Sids + (Token->UserAndGroupCount * sizeof(SID_AND_ATTRIBUTES)));
931                         SidLen = UserGroupLength - (Token->UserAndGroupCount * sizeof(SID_AND_ATTRIBUTES));
932                         Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount,
933                                                               Token->UserAndGroups,
934                                                               SidLen,
935                                                               GroupsAndPrivs->Sids,
936                                                               Sid,
937                                                               &Unused.PSid,
938                                                               &Unused.Ulong);
939                         NT_ASSERT(NT_SUCCESS(Status));
940 
941                         GroupsAndPrivs->RestrictedSidCount = Token->RestrictedSidCount;
942                         GroupsAndPrivs->RestrictedSidLength = RestrictedSidLength;
943                         GroupsAndPrivs->RestrictedSids = NULL;
944                         if (SeTokenIsRestricted(Token))
945                         {
946                             GroupsAndPrivs->RestrictedSids = (PSID_AND_ATTRIBUTES)((ULONG_PTR)GroupsAndPrivs->Sids + UserGroupLength);
947 
948                             RestrictedSid = (PSID)((ULONG_PTR)GroupsAndPrivs->RestrictedSids + (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
949                             RestrictedSidLen = RestrictedSidLength - (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
950                             Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
951                                                                   Token->RestrictedSids,
952                                                                   RestrictedSidLen,
953                                                                   GroupsAndPrivs->RestrictedSids,
954                                                                   RestrictedSid,
955                                                                   &Unused.PSid,
956                                                                   &Unused.Ulong);
957                             NT_ASSERT(NT_SUCCESS(Status));
958                         }
959 
960                         GroupsAndPrivs->PrivilegeCount = Token->PrivilegeCount;
961                         GroupsAndPrivs->PrivilegeLength = PrivilegeLength;
962                         GroupsAndPrivs->Privileges = (PLUID_AND_ATTRIBUTES)((ULONG_PTR)(GroupsAndPrivs + 1) +
963                                                      UserGroupLength + RestrictedSidLength);
964                         RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
965                                                       Token->Privileges,
966                                                       GroupsAndPrivs->Privileges);
967 
968                         GroupsAndPrivs->AuthenticationId = Token->AuthenticationId;
969                     }
970                     else
971                     {
972                         Status = STATUS_BUFFER_TOO_SMALL;
973                     }
974 
975                     *ReturnLength = RequiredLength;
976                 }
977                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
978                 {
979                     Status = _SEH2_GetExceptionCode();
980                 }
981                 _SEH2_END;
982 
983                 break;
984             }
985 
986             case TokenRestrictedSids:
987             {
988                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
989 
990                 DPRINT("NtQueryInformationToken(TokenRestrictedSids)\n");
991                 RequiredLength = sizeof(tg->GroupCount) +
992                 RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
993 
994                 _SEH2_TRY
995                 {
996                     if (TokenInformationLength >= RequiredLength)
997                     {
998                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
999                             (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
1000                         PSID Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
1001                                           (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
1002 
1003                         tg->GroupCount = Token->RestrictedSidCount;
1004                         Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
1005                                                               Token->RestrictedSids,
1006                                                               SidLen,
1007                                                               &tg->Groups[0],
1008                                                               Sid,
1009                                                               &Unused.PSid,
1010                                                               &Unused.Ulong);
1011                     }
1012                     else
1013                     {
1014                         Status = STATUS_BUFFER_TOO_SMALL;
1015                     }
1016 
1017                     *ReturnLength = RequiredLength;
1018                 }
1019                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1020                 {
1021                     Status = _SEH2_GetExceptionCode();
1022                 }
1023                 _SEH2_END;
1024 
1025                 break;
1026             }
1027 
1028             case TokenSandBoxInert:
1029             {
1030                 ULONG IsTokenSandBoxInert;
1031 
1032                 DPRINT("NtQueryInformationToken(TokenSandBoxInert)\n");
1033 
1034                 IsTokenSandBoxInert = SeTokenIsInert(Token);
1035                 _SEH2_TRY
1036                 {
1037                     /* Buffer size was already verified, no need to check here again */
1038                     *(PULONG)TokenInformation = IsTokenSandBoxInert;
1039                     *ReturnLength = sizeof(ULONG);
1040                 }
1041                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1042                 {
1043                     Status = _SEH2_GetExceptionCode();
1044                 }
1045                 _SEH2_END;
1046 
1047                 break;
1048             }
1049 
1050             case TokenSessionId:
1051             {
1052                 ULONG SessionId = 0;
1053 
1054                 DPRINT("NtQueryInformationToken(TokenSessionId)\n");
1055 
1056                 Status = SeQuerySessionIdToken(Token, &SessionId);
1057                 if (NT_SUCCESS(Status))
1058                 {
1059                     _SEH2_TRY
1060                     {
1061                         /* Buffer size was already verified, no need to check here again */
1062                         *(PULONG)TokenInformation = SessionId;
1063                         *ReturnLength = RequiredLength;
1064                     }
1065                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1066                     {
1067                         Status = _SEH2_GetExceptionCode();
1068                     }
1069                     _SEH2_END;
1070                 }
1071 
1072                 break;
1073             }
1074 
1075             default:
1076                 DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
1077                 Status = STATUS_INVALID_INFO_CLASS;
1078                 break;
1079         }
1080 
1081         /* Unlock and dereference the token */
1082         SepReleaseTokenLock(Token);
1083         ObDereferenceObject(Token);
1084     }
1085 
1086     return Status;
1087 }
1088 
1089 /**
1090  * @unimplemented
1091  * @brief
1092  * Sets (modifies) some specific information in regard of an access token. The
1093  * calling thread must have specific access rights in order to modify token's
1094  * information data.
1095  *
1096  * @param[in] TokenHandle
1097  * A handle of a token where information is to be modified.
1098  *
1099  * @param[in] TokenInformationClass
1100  * Token information class.
1101  *
1102  * @param[in] TokenInformation
1103  * An arbitrary pointer to a buffer with token information to set. Such
1104  * arbitrary buffer depends on the information class chosen that the caller
1105  * wants to modify such information data of a token.
1106  *
1107  * @param[in] TokenInformationLength
1108  * Length of the token information buffer, in bytes.
1109  *
1110  * @return
1111  * Returns STATUS_SUCCESS if information setting has completed successfully.
1112  * STATUS_INFO_LENGTH_MISMATCH is returned if the information length of the
1113  * buffer is less than the required length. STATUS_INSUFFICIENT_RESOURCES is
1114  * returned if memory pool allocation has failed. STATUS_PRIVILEGE_NOT_HELD
1115  * is returned if the calling thread hasn't the required privileges to perform
1116  * the operation in question. A failure NTSTATUS code is returned otherwise.
1117  *
1118  * @remarks
1119  * The function is partly implemented, mainly TokenOrigin.
1120  */
1121 _Must_inspect_result_
1122 __kernel_entry
1123 NTSTATUS
1124 NTAPI
NtSetInformationToken(_In_ HANDLE TokenHandle,_In_ TOKEN_INFORMATION_CLASS TokenInformationClass,_In_reads_bytes_ (TokenInformationLength)PVOID TokenInformation,_In_ ULONG TokenInformationLength)1125 NtSetInformationToken(
1126     _In_ HANDLE TokenHandle,
1127     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
1128     _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
1129     _In_ ULONG TokenInformationLength)
1130 {
1131     NTSTATUS Status;
1132     PTOKEN Token;
1133     KPROCESSOR_MODE PreviousMode;
1134     ULONG NeededAccess = TOKEN_ADJUST_DEFAULT;
1135 
1136     PAGED_CODE();
1137 
1138     PreviousMode = ExGetPreviousMode();
1139 
1140     Status = DefaultSetInfoBufferCheck(TokenInformationClass,
1141                                        SeTokenInformationClass,
1142                                        RTL_NUMBER_OF(SeTokenInformationClass),
1143                                        TokenInformation,
1144                                        TokenInformationLength,
1145                                        PreviousMode);
1146     if (!NT_SUCCESS(Status))
1147     {
1148         /* Invalid buffers */
1149         DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status);
1150         return Status;
1151     }
1152 
1153     if (TokenInformationClass == TokenSessionId)
1154     {
1155         NeededAccess |= TOKEN_ADJUST_SESSIONID;
1156     }
1157 
1158     Status = ObReferenceObjectByHandle(TokenHandle,
1159                                        NeededAccess,
1160                                        SeTokenObjectType,
1161                                        PreviousMode,
1162                                        (PVOID*)&Token,
1163                                        NULL);
1164     if (NT_SUCCESS(Status))
1165     {
1166         switch (TokenInformationClass)
1167         {
1168             case TokenOwner:
1169             {
1170                 if (TokenInformationLength >= sizeof(TOKEN_OWNER))
1171                 {
1172                     PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
1173                     PSID InputSid = NULL, CapturedSid;
1174                     ULONG DefaultOwnerIndex;
1175 
1176                     _SEH2_TRY
1177                     {
1178                         InputSid = to->Owner;
1179                     }
1180                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1181                     {
1182                         Status = _SEH2_GetExceptionCode();
1183                         _SEH2_YIELD(goto Cleanup);
1184                     }
1185                     _SEH2_END;
1186 
1187                     Status = SepCaptureSid(InputSid,
1188                                            PreviousMode,
1189                                            PagedPool,
1190                                            FALSE,
1191                                            &CapturedSid);
1192                     if (NT_SUCCESS(Status))
1193                     {
1194                         /* Lock the token */
1195                         SepAcquireTokenLockExclusive(Token);
1196 
1197                         /* Find the owner amongst the existing token user and groups */
1198                         Status = SepFindPrimaryGroupAndDefaultOwner(Token,
1199                                                                     NULL,
1200                                                                     CapturedSid,
1201                                                                     NULL,
1202                                                                     &DefaultOwnerIndex);
1203                         if (NT_SUCCESS(Status))
1204                         {
1205                             /* Found it */
1206                             Token->DefaultOwnerIndex = DefaultOwnerIndex;
1207                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
1208                         }
1209 
1210                         /* Unlock the token */
1211                         SepReleaseTokenLock(Token);
1212 
1213                         SepReleaseSid(CapturedSid,
1214                                       PreviousMode,
1215                                       FALSE);
1216                     }
1217                 }
1218                 else
1219                 {
1220                     Status = STATUS_INFO_LENGTH_MISMATCH;
1221                 }
1222                 break;
1223             }
1224 
1225             case TokenPrimaryGroup:
1226             {
1227                 if (TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP))
1228                 {
1229                     PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
1230                     ULONG AclSize;
1231                     ULONG_PTR PrimaryGroup;
1232                     PSID InputSid = NULL, CapturedSid;
1233                     ULONG PrimaryGroupIndex, NewDynamicLength;
1234 
1235                     _SEH2_TRY
1236                     {
1237                         InputSid = tpg->PrimaryGroup;
1238                     }
1239                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1240                     {
1241                         Status = _SEH2_GetExceptionCode();
1242                         _SEH2_YIELD(goto Cleanup);
1243                     }
1244                     _SEH2_END;
1245 
1246                     Status = SepCaptureSid(InputSid,
1247                                            PreviousMode,
1248                                            PagedPool,
1249                                            FALSE,
1250                                            &CapturedSid);
1251                     if (NT_SUCCESS(Status))
1252                     {
1253                         /* Lock the token */
1254                         SepAcquireTokenLockExclusive(Token);
1255 
1256                         /*
1257                          * We can whack the token's primary group only if
1258                          * the charged dynamic space boundary allows us
1259                          * to do so. Exceeding this boundary and we're
1260                          * busted out.
1261                          */
1262                         AclSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
1263                         NewDynamicLength = RtlLengthSid(CapturedSid) + AclSize;
1264                         if (NewDynamicLength > Token->DynamicCharged)
1265                         {
1266                             SepReleaseTokenLock(Token);
1267                             SepReleaseSid(CapturedSid, PreviousMode, FALSE);
1268                             Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
1269                             DPRINT1("NtSetInformationToken(): Couldn't assign new primary group, space exceeded (current length %u, new length %lu)\n",
1270                                     Token->DynamicCharged, NewDynamicLength);
1271                             goto Cleanup;
1272                         }
1273 
1274                         /*
1275                          * The dynamic part of the token may require a rebuild
1276                          * if the current dynamic area is too small. If not then
1277                          * we're pretty much good as is.
1278                          */
1279                         Status = SepRebuildDynamicPartOfToken(Token, NewDynamicLength);
1280                         if (NT_SUCCESS(Status))
1281                         {
1282                             /* Find the primary group amongst the existing token user and groups */
1283                             Status = SepFindPrimaryGroupAndDefaultOwner(Token,
1284                                                                         CapturedSid,
1285                                                                         NULL,
1286                                                                         &PrimaryGroupIndex,
1287                                                                         NULL);
1288                             if (NT_SUCCESS(Status))
1289                             {
1290                                 /*
1291                                  * We have found it. Add the length of
1292                                  * the previous primary group SID to the
1293                                  * available dynamic area.
1294                                  */
1295                                 Token->DynamicAvailable += RtlLengthSid(Token->PrimaryGroup);
1296 
1297                                 /*
1298                                  * Move the default DACL if it's not at the
1299                                  * head of the dynamic part.
1300                                  */
1301                                 if ((Token->DefaultDacl) &&
1302                                     ((PULONG)(Token->DefaultDacl) != Token->DynamicPart))
1303                                 {
1304                                     RtlMoveMemory(Token->DynamicPart,
1305                                                   Token->DefaultDacl,
1306                                                   RtlLengthSid(Token->PrimaryGroup));
1307                                     Token->DefaultDacl = (PACL)(Token->DynamicPart);
1308                                 }
1309 
1310                                 /* Take away available space from the dynamic area */
1311                                 Token->DynamicAvailable -= RtlLengthSid(Token->UserAndGroups[PrimaryGroupIndex].Sid);
1312 
1313                                 /*
1314                                  * And assign the new primary group. For that
1315                                  * we have to make sure where the primary group
1316                                  * is going to stay in memory, so if this token
1317                                  * has a default DACL then add up its size with
1318                                  * the address of the dynamic part.
1319                                  */
1320                                 PrimaryGroup = (ULONG_PTR)(Token->DynamicPart) + AclSize;
1321                                 RtlCopySid(RtlLengthSid(Token->UserAndGroups[PrimaryGroupIndex].Sid),
1322                                            (PVOID)PrimaryGroup,
1323                                            Token->UserAndGroups[PrimaryGroupIndex].Sid);
1324                                 Token->PrimaryGroup = (PSID)PrimaryGroup;
1325 
1326                                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
1327                             }
1328                         }
1329 
1330                         /* Unlock the token */
1331                         SepReleaseTokenLock(Token);
1332 
1333                         SepReleaseSid(CapturedSid,
1334                                       PreviousMode,
1335                                       FALSE);
1336                     }
1337                 }
1338                 else
1339                 {
1340                     Status = STATUS_INFO_LENGTH_MISMATCH;
1341                 }
1342                 break;
1343             }
1344 
1345             case TokenDefaultDacl:
1346             {
1347                 if (TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL))
1348                 {
1349                     PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
1350                     PACL InputAcl = NULL;
1351 
1352                     _SEH2_TRY
1353                     {
1354                         InputAcl = tdd->DefaultDacl;
1355                     }
1356                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1357                     {
1358                         Status = _SEH2_GetExceptionCode();
1359                         _SEH2_YIELD(goto Cleanup);
1360                     }
1361                     _SEH2_END;
1362 
1363                     if (InputAcl != NULL)
1364                     {
1365                         PACL CapturedAcl;
1366 
1367                         /* Capture, validate, and copy the DACL */
1368                         Status = SepCaptureAcl(InputAcl,
1369                                                PreviousMode,
1370                                                PagedPool,
1371                                                TRUE,
1372                                                &CapturedAcl);
1373                         if (NT_SUCCESS(Status))
1374                         {
1375                             ULONG NewDynamicLength;
1376                             ULONG_PTR Acl;
1377 
1378                             /* Lock the token */
1379                             SepAcquireTokenLockExclusive(Token);
1380 
1381                             /*
1382                              * We can whack the token's default DACL only if
1383                              * the charged dynamic space boundary allows us
1384                              * to do so. Exceeding this boundary and we're
1385                              * busted out.
1386                              */
1387                             NewDynamicLength = CapturedAcl->AclSize + RtlLengthSid(Token->PrimaryGroup);
1388                             if (NewDynamicLength > Token->DynamicCharged)
1389                             {
1390                                 SepReleaseTokenLock(Token);
1391                                 SepReleaseAcl(CapturedAcl, PreviousMode, TRUE);
1392                                 Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
1393                                 DPRINT1("NtSetInformationToken(): Couldn't assign new default DACL, space exceeded (current length %u, new length %lu)\n",
1394                                         Token->DynamicCharged, NewDynamicLength);
1395                                 goto Cleanup;
1396                             }
1397 
1398                             /*
1399                              * The dynamic part of the token may require a rebuild
1400                              * if the current dynamic area is too small. If not then
1401                              * we're pretty much good as is.
1402                              */
1403                             Status = SepRebuildDynamicPartOfToken(Token, NewDynamicLength);
1404                             if (NT_SUCCESS(Status))
1405                             {
1406                                 /*
1407                                  * Before setting up a new DACL for the
1408                                  * token object we add up the size of
1409                                  * the old DACL to the available dynamic
1410                                  * area
1411                                  */
1412                                 if (Token->DefaultDacl)
1413                                 {
1414                                     Token->DynamicAvailable += Token->DefaultDacl->AclSize;
1415                                 }
1416 
1417                                 /*
1418                                  * Move the primary group if it's not at the
1419                                  * head of the dynamic part.
1420                                  */
1421                                 if ((PULONG)(Token->PrimaryGroup) != Token->DynamicPart)
1422                                 {
1423                                     RtlMoveMemory(Token->DynamicPart,
1424                                                   Token->PrimaryGroup,
1425                                                   RtlLengthSid(Token->PrimaryGroup));
1426                                     Token->PrimaryGroup = (PSID)(Token->DynamicPart);
1427                                 }
1428 
1429                                 /* Take away available space from the dynamic area */
1430                                 Token->DynamicAvailable -= CapturedAcl->AclSize;
1431 
1432                                 /* Set the new dacl */
1433                                 Acl = (ULONG_PTR)(Token->DynamicPart) + RtlLengthSid(Token->PrimaryGroup);
1434                                 RtlCopyMemory((PVOID)Acl,
1435                                               CapturedAcl,
1436                                               CapturedAcl->AclSize);
1437                                 Token->DefaultDacl = (PACL)Acl;
1438 
1439                                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
1440                             }
1441 
1442                             /* Unlock the token and release the ACL */
1443                             SepReleaseTokenLock(Token);
1444                             SepReleaseAcl(CapturedAcl, PreviousMode, TRUE);
1445                         }
1446                     }
1447                     else
1448                     {
1449                         /* Lock the token */
1450                         SepAcquireTokenLockExclusive(Token);
1451 
1452                         /* Clear the default dacl if present */
1453                         if (Token->DefaultDacl != NULL)
1454                         {
1455                             Token->DynamicAvailable += Token->DefaultDacl->AclSize;
1456                             RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize);
1457                             Token->DefaultDacl = NULL;
1458 
1459                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
1460                         }
1461 
1462                         /* Unlock the token */
1463                         SepReleaseTokenLock(Token);
1464                     }
1465                 }
1466                 else
1467                 {
1468                     Status = STATUS_INFO_LENGTH_MISMATCH;
1469                 }
1470                 break;
1471             }
1472 
1473             case TokenSessionId:
1474             {
1475                 ULONG SessionId = 0;
1476 
1477                 _SEH2_TRY
1478                 {
1479                     /* Buffer size was already verified, no need to check here again */
1480                     SessionId = *(PULONG)TokenInformation;
1481                 }
1482                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1483                 {
1484                     Status = _SEH2_GetExceptionCode();
1485                     _SEH2_YIELD(goto Cleanup);
1486                 }
1487                 _SEH2_END;
1488 
1489                 /* Check for TCB privilege */
1490                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
1491                 {
1492                     Status = STATUS_PRIVILEGE_NOT_HELD;
1493                     break;
1494                 }
1495 
1496                 /* Lock the token */
1497                 SepAcquireTokenLockExclusive(Token);
1498 
1499                 Token->SessionId = SessionId;
1500                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
1501 
1502                 /* Unlock the token */
1503                 SepReleaseTokenLock(Token);
1504 
1505                 break;
1506             }
1507 
1508             case TokenSessionReference:
1509             {
1510                 ULONG SessionReference;
1511 
1512                 _SEH2_TRY
1513                 {
1514                     /* Buffer size was already verified, no need to check here again */
1515                     SessionReference = *(PULONG)TokenInformation;
1516                 }
1517                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1518                 {
1519                     Status = _SEH2_GetExceptionCode();
1520                     _SEH2_YIELD(goto Cleanup);
1521                 }
1522                 _SEH2_END;
1523 
1524                 /* Check for TCB privilege */
1525                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
1526                 {
1527                     Status = STATUS_PRIVILEGE_NOT_HELD;
1528                     goto Cleanup;
1529                 }
1530 
1531                 /* Check if it is 0 */
1532                 if (SessionReference == 0)
1533                 {
1534                     ULONG OldTokenFlags;
1535 
1536                     /* Lock the token */
1537                     SepAcquireTokenLockExclusive(Token);
1538 
1539                     /* Atomically set the flag in the token */
1540                     OldTokenFlags = RtlInterlockedSetBits(&Token->TokenFlags,
1541                                                           TOKEN_SESSION_NOT_REFERENCED);
1542                     /*
1543                      * If the flag was already set, do not dereference again
1544                      * the logon session. Use SessionReference as an indicator
1545                      * to know whether to really dereference the session.
1546                      */
1547                     if (OldTokenFlags == Token->TokenFlags)
1548                         SessionReference = ULONG_MAX;
1549 
1550                     /*
1551                      * Otherwise if the flag was never set but just for this first time then
1552                      * remove the referenced logon session data from the token and dereference
1553                      * the logon session when needed.
1554                      */
1555                     if (SessionReference == 0)
1556                     {
1557                         SepRmRemoveLogonSessionFromToken(Token);
1558                         SepRmDereferenceLogonSession(&Token->AuthenticationId);
1559                     }
1560 
1561                     /* Unlock the token */
1562                     SepReleaseTokenLock(Token);
1563                 }
1564                 break;
1565             }
1566 
1567             case TokenAuditPolicy:
1568             {
1569                 PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation =
1570                     (PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation;
1571                 SEP_AUDIT_POLICY AuditPolicy;
1572                 ULONG i;
1573 
1574                 _SEH2_TRY
1575                 {
1576                     ProbeForRead(PolicyInformation,
1577                                  FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION,
1578                                               Policies[PolicyInformation->PolicyCount]),
1579                                  sizeof(ULONG));
1580 
1581                     /* Loop all policies in the structure */
1582                     for (i = 0; i < PolicyInformation->PolicyCount; i++)
1583                     {
1584                         /* Set the corresponding bits in the packed structure */
1585                         switch (PolicyInformation->Policies[i].Category)
1586                         {
1587                             case AuditCategorySystem:
1588                                 AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value;
1589                                 break;
1590 
1591                             case AuditCategoryLogon:
1592                                 AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value;
1593                                 break;
1594 
1595                             case AuditCategoryObjectAccess:
1596                                 AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value;
1597                                 break;
1598 
1599                             case AuditCategoryPrivilegeUse:
1600                                 AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value;
1601                                 break;
1602 
1603                             case AuditCategoryDetailedTracking:
1604                                 AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value;
1605                                 break;
1606 
1607                             case AuditCategoryPolicyChange:
1608                                 AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value;
1609                                 break;
1610 
1611                             case AuditCategoryAccountManagement:
1612                                 AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value;
1613                                 break;
1614 
1615                             case AuditCategoryDirectoryServiceAccess:
1616                                 AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value;
1617                                 break;
1618 
1619                             case AuditCategoryAccountLogon:
1620                                 AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value;
1621                                 break;
1622                         }
1623                     }
1624                 }
1625                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1626                 {
1627                     Status = _SEH2_GetExceptionCode();
1628                     _SEH2_YIELD(goto Cleanup);
1629                 }
1630                 _SEH2_END;
1631 
1632                 /* Check for TCB privilege */
1633                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
1634                 {
1635                     Status = STATUS_PRIVILEGE_NOT_HELD;
1636                     break;
1637                 }
1638 
1639                 /* Lock the token */
1640                 SepAcquireTokenLockExclusive(Token);
1641 
1642                 /* Set the new audit policy */
1643                 Token->AuditPolicy = AuditPolicy;
1644                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
1645 
1646                 /* Unlock the token */
1647                 SepReleaseTokenLock(Token);
1648 
1649                 break;
1650             }
1651 
1652             case TokenOrigin:
1653             {
1654                 TOKEN_ORIGIN TokenOrigin;
1655 
1656                 _SEH2_TRY
1657                 {
1658                     /* Copy the token origin */
1659                     TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation;
1660                 }
1661                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1662                 {
1663                     Status = _SEH2_GetExceptionCode();
1664                     _SEH2_YIELD(goto Cleanup);
1665                 }
1666                 _SEH2_END;
1667 
1668                 /* Check for TCB privilege */
1669                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
1670                 {
1671                     Status = STATUS_PRIVILEGE_NOT_HELD;
1672                     break;
1673                 }
1674 
1675                 /* Lock the token */
1676                 SepAcquireTokenLockExclusive(Token);
1677 
1678                 /* Check if there is no token origin set yet */
1679                 if (RtlIsZeroLuid(&Token->OriginatingLogonSession))
1680                 {
1681                     /* Set the token origin */
1682                     Token->OriginatingLogonSession =
1683                         TokenOrigin.OriginatingLogonSession;
1684 
1685                     ExAllocateLocallyUniqueId(&Token->ModifiedId);
1686                 }
1687 
1688                 /* Unlock the token */
1689                 SepReleaseTokenLock(Token);
1690 
1691                 break;
1692             }
1693 
1694             default:
1695             {
1696                 DPRINT1("Invalid TokenInformationClass: 0x%lx\n",
1697                         TokenInformationClass);
1698                 Status = STATUS_INVALID_INFO_CLASS;
1699                 break;
1700             }
1701         }
1702 Cleanup:
1703         ObDereferenceObject(Token);
1704     }
1705 
1706     if (!NT_SUCCESS(Status))
1707     {
1708         DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status);
1709     }
1710 
1711     return Status;
1712 }
1713 
1714 /* EOF */
1715