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