1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Access token lifetime management implementation (Creation/Duplication/Filtering)
5 * COPYRIGHT: Copyright David Welch <welch@cwcom.net>
6 * Copyright 2021-2022 George Bișoc <george.bisoc@reactos.org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* DEFINES ********************************************************************/
16
17 #define SE_TOKEN_DYNAMIC_SLIM 500
18
19 /* PRIVATE FUNCTIONS *********************************************************/
20
21 /**
22 * @brief
23 * Internal function responsible for access token object creation in the kernel.
24 * A fully created token objected is inserted into the token handle, thus the handle
25 * becoming a valid handle to an access token object and ready for use.
26 *
27 * @param[out] TokenHandle
28 * Valid token handle that's ready for use after token creation and object insertion.
29 *
30 * @param[in] PreviousMode
31 * Processor request level mode.
32 *
33 * @param[in] DesiredAccess
34 * Desired access right for the token object to be granted. This kind of access right
35 * impacts how the token can be used and who.
36 *
37 * @param[in] ObjectAttributes
38 * Object attributes for the token to be created.
39 *
40 * @param[in] TokenType
41 * Type of token to assign upon creation.
42 *
43 * @param[in] ImpersonationLevel
44 * Security impersonation level of token to assign upon creation.
45 *
46 * @param[in] AuthenticationId
47 * Authentication ID that represents the authentication information of the token.
48 *
49 * @param[in] ExpirationTime
50 * Expiration time of the token to assign. A value of -1 means that the token never
51 * expires and its life depends upon the amount of references this token object has.
52 *
53 * @param[in] User
54 * User entry to assign to the token.
55 *
56 * @param[in] GroupCount
57 * The total number of groups count for the token.
58 *
59 * @param[in] Groups
60 * The group entries for the token.
61 *
62 * @param[in] GroupsLength
63 * The length size of the groups array, pointed by the Groups parameter.
64 *
65 * @param[in] PrivilegeCount
66 * The total number of priivleges that the newly created token has.
67 *
68 * @param[in] Privileges
69 * The privileges for the token.
70 *
71 * @param[in] Owner
72 * The main user (or also owner) that represents the token that we create.
73 *
74 * @param[in] PrimaryGroup
75 * The main group that represents the token that we create.
76 *
77 * @param[in] DefaultDacl
78 * A discretionary access control list for the token.
79 *
80 * @param[in] TokenSource
81 * Source (or the origin) of the access token that creates it.
82 *
83 * @param[in] SystemToken
84 * If set to TRUE, the newly created token is a system token and only in charge
85 * by the internal system. The function directly returns a pointer to the
86 * created token object for system kernel use. Otherwise if set to FALSE, the
87 * function inserts the object to a handle making it a regular access token.
88 *
89 * @return
90 * Returns STATUS_SUCCESS if token creation has completed successfully.
91 * STATUS_INSUFFICIENT_RESOURCES is returned if the dynamic area of memory of the
92 * token hasn't been allocated because of lack of memory resources. A failure
93 * NTSTATUS code is returned otherwise.
94 */
95 NTSTATUS
96 NTAPI
SepCreateToken(_Out_ PHANDLE TokenHandle,_In_ KPROCESSOR_MODE PreviousMode,_In_ ACCESS_MASK DesiredAccess,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ TOKEN_TYPE TokenType,_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,_In_ PLUID AuthenticationId,_In_ PLARGE_INTEGER ExpirationTime,_In_ PSID_AND_ATTRIBUTES User,_In_ ULONG GroupCount,_In_ PSID_AND_ATTRIBUTES Groups,_In_ ULONG GroupsLength,_In_ ULONG PrivilegeCount,_In_ PLUID_AND_ATTRIBUTES Privileges,_In_opt_ PSID Owner,_In_ PSID PrimaryGroup,_In_opt_ PACL DefaultDacl,_In_ PTOKEN_SOURCE TokenSource,_In_ BOOLEAN SystemToken)97 SepCreateToken(
98 _Out_ PHANDLE TokenHandle,
99 _In_ KPROCESSOR_MODE PreviousMode,
100 _In_ ACCESS_MASK DesiredAccess,
101 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
102 _In_ TOKEN_TYPE TokenType,
103 _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
104 _In_ PLUID AuthenticationId,
105 _In_ PLARGE_INTEGER ExpirationTime,
106 _In_ PSID_AND_ATTRIBUTES User,
107 _In_ ULONG GroupCount,
108 _In_ PSID_AND_ATTRIBUTES Groups,
109 _In_ ULONG GroupsLength,
110 _In_ ULONG PrivilegeCount,
111 _In_ PLUID_AND_ATTRIBUTES Privileges,
112 _In_opt_ PSID Owner,
113 _In_ PSID PrimaryGroup,
114 _In_opt_ PACL DefaultDacl,
115 _In_ PTOKEN_SOURCE TokenSource,
116 _In_ BOOLEAN SystemToken)
117 {
118 NTSTATUS Status;
119 PTOKEN AccessToken;
120 ULONG TokenFlags = 0;
121 ULONG PrimaryGroupIndex, DefaultOwnerIndex;
122 LUID TokenId;
123 LUID ModifiedId;
124 PVOID EndMem;
125 ULONG PrivilegesLength;
126 ULONG UserGroupsLength;
127 ULONG VariableLength;
128 ULONG DynamicPartSize, TotalSize;
129 ULONG TokenPagedCharges;
130 ULONG i;
131
132 PAGED_CODE();
133
134 /* Loop all groups */
135 for (i = 0; i < GroupCount; i++)
136 {
137 /* Check for mandatory groups */
138 if (Groups[i].Attributes & SE_GROUP_MANDATORY)
139 {
140 /* Force them to be enabled */
141 Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
142 }
143
144 /* Check of the group is an admin group */
145 if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid))
146 {
147 /* Remember this so we can optimize queries later */
148 TokenFlags |= TOKEN_HAS_ADMIN_GROUP;
149 }
150 }
151
152 /* Allocate unique IDs for the token */
153 ExAllocateLocallyUniqueId(&TokenId);
154 ExAllocateLocallyUniqueId(&ModifiedId);
155
156 /* Compute how much size we need to allocate for the token */
157
158 /* Privileges size */
159 PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
160 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
161
162 /* User and groups size */
163 UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES);
164 UserGroupsLength += RtlLengthSid(User->Sid);
165 for (i = 0; i < GroupCount; i++)
166 {
167 UserGroupsLength += RtlLengthSid(Groups[i].Sid);
168 }
169 UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID));
170
171 /* Add the additional groups array length */
172 UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID));
173
174 VariableLength = PrivilegesLength + UserGroupsLength;
175 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
176
177 /*
178 * A token is considered slim if it has the default dynamic
179 * contents, or in other words, the primary group and ACL.
180 * We judge if such contents are default by checking their
181 * total size if it's over the range. On Windows this range
182 * is 0x1F4 (aka 500). If the size of the whole dynamic contents
183 * is over that range then the token is considered fat and
184 * the token will be charged the whole of its token body length
185 * plus the dynamic size.
186 */
187 DynamicPartSize = DefaultDacl ? DefaultDacl->AclSize : 0;
188 DynamicPartSize += RtlLengthSid(PrimaryGroup);
189 if (DynamicPartSize > SE_TOKEN_DYNAMIC_SLIM)
190 {
191 TokenPagedCharges = DynamicPartSize + TotalSize;
192 }
193 else
194 {
195 TokenPagedCharges = SE_TOKEN_DYNAMIC_SLIM + TotalSize;
196 }
197
198 Status = ObCreateObject(PreviousMode,
199 SeTokenObjectType,
200 ObjectAttributes,
201 PreviousMode,
202 NULL,
203 TotalSize,
204 TokenPagedCharges,
205 0,
206 (PVOID*)&AccessToken);
207 if (!NT_SUCCESS(Status))
208 {
209 DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
210 return Status;
211 }
212
213 /* Zero out the buffer and initialize the token */
214 RtlZeroMemory(AccessToken, TotalSize);
215
216 AccessToken->TokenId = TokenId;
217 AccessToken->TokenType = TokenType;
218 AccessToken->ImpersonationLevel = ImpersonationLevel;
219
220 /* Initialise the lock for the access token */
221 Status = SepCreateTokenLock(AccessToken);
222 if (!NT_SUCCESS(Status))
223 goto Quit;
224
225 AccessToken->TokenSource.SourceIdentifier = TokenSource->SourceIdentifier;
226 RtlCopyMemory(AccessToken->TokenSource.SourceName,
227 TokenSource->SourceName,
228 sizeof(TokenSource->SourceName));
229
230 AccessToken->ExpirationTime = *ExpirationTime;
231 AccessToken->ModifiedId = ModifiedId;
232 AccessToken->DynamicCharged = TokenPagedCharges - TotalSize;
233
234 AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
235
236 /* Copy and reference the logon session */
237 AccessToken->AuthenticationId = *AuthenticationId;
238 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
239 if (!NT_SUCCESS(Status))
240 {
241 /* No logon session could be found, bail out */
242 DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
243 /* Set the flag for proper cleanup by the delete procedure */
244 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
245 goto Quit;
246 }
247
248 /* Insert the referenced logon session into the token */
249 Status = SepRmInsertLogonSessionIntoToken(AccessToken);
250 if (!NT_SUCCESS(Status))
251 {
252 /* Failed to insert the logon session into the token, bail out */
253 DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
254 goto Quit;
255 }
256
257 /* Fill in token debug information */
258 #if DBG
259 /*
260 * We must determine ourselves that the current
261 * process is not the initial CPU one. The initial
262 * process is not a "real" process, that is, the
263 * Process Manager has not yet been initialized and
264 * as a matter of fact we are creating a token before
265 * any process gets created by Ps. If it turns out
266 * that the current process is the initial CPU process
267 * where token creation execution takes place, don't
268 * do anything.
269 */
270 if (PsGetCurrentProcess() != &KiInitialProcess)
271 {
272 RtlCopyMemory(AccessToken->ImageFileName,
273 PsGetCurrentProcess()->ImageFileName,
274 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
275
276 AccessToken->ProcessCid = PsGetCurrentProcessId();
277 AccessToken->ThreadCid = PsGetCurrentThreadId();
278 }
279
280 AccessToken->CreateMethod = TOKEN_CREATE_METHOD;
281 #endif
282
283 /* Assign the data that reside in the token's variable information area */
284 AccessToken->VariableLength = VariableLength;
285 EndMem = (PVOID)&AccessToken->VariablePart;
286
287 /* Copy the privileges */
288 AccessToken->PrivilegeCount = PrivilegeCount;
289 AccessToken->Privileges = NULL;
290 if (PrivilegeCount > 0)
291 {
292 AccessToken->Privileges = EndMem;
293 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
294 VariableLength -= PrivilegesLength;
295
296 if (PreviousMode != KernelMode)
297 {
298 _SEH2_TRY
299 {
300 RtlCopyMemory(AccessToken->Privileges,
301 Privileges,
302 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
303 }
304 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
305 {
306 Status = _SEH2_GetExceptionCode();
307 }
308 _SEH2_END;
309 }
310 else
311 {
312 RtlCopyMemory(AccessToken->Privileges,
313 Privileges,
314 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
315 }
316
317 if (!NT_SUCCESS(Status))
318 goto Quit;
319 }
320
321 /* Update the privilege flags */
322 SepUpdatePrivilegeFlagsToken(AccessToken);
323
324 /* Copy the user and groups */
325 AccessToken->UserAndGroupCount = 1 + GroupCount;
326 AccessToken->UserAndGroups = EndMem;
327 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
328 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
329
330 Status = RtlCopySidAndAttributesArray(1,
331 User,
332 VariableLength,
333 &AccessToken->UserAndGroups[0],
334 EndMem,
335 &EndMem,
336 &VariableLength);
337 if (!NT_SUCCESS(Status))
338 goto Quit;
339
340 Status = RtlCopySidAndAttributesArray(GroupCount,
341 Groups,
342 VariableLength,
343 &AccessToken->UserAndGroups[1],
344 EndMem,
345 &EndMem,
346 &VariableLength);
347 if (!NT_SUCCESS(Status))
348 goto Quit;
349
350 /* Find the token primary group and default owner */
351 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
352 PrimaryGroup,
353 Owner,
354 &PrimaryGroupIndex,
355 &DefaultOwnerIndex);
356 if (!NT_SUCCESS(Status))
357 {
358 DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
359 goto Quit;
360 }
361
362 /*
363 * Now allocate the token's dynamic information area
364 * and set the data. The dynamic part consists of two
365 * contents, the primary group SID and the default DACL
366 * of the token, in this strict order.
367 */
368 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
369 DynamicPartSize,
370 TAG_TOKEN_DYNAMIC);
371 if (AccessToken->DynamicPart == NULL)
372 {
373 Status = STATUS_INSUFFICIENT_RESOURCES;
374 goto Quit;
375 }
376
377 /* Unused memory in the dynamic area */
378 AccessToken->DynamicAvailable = 0;
379
380 /*
381 * Assign the primary group to the token
382 * and put it in the dynamic part as well.
383 */
384 EndMem = (PVOID)AccessToken->DynamicPart;
385 AccessToken->PrimaryGroup = EndMem;
386 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
387 EndMem,
388 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
389 AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
390 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
391
392 /*
393 * We have assigned a primary group and put it in the
394 * dynamic part, now it's time to copy the provided
395 * default DACL (if it's provided to begin with) into
396 * the DACL field of the token and put it at the end
397 * tail of the dynamic part too.
398 */
399 if (DefaultDacl != NULL)
400 {
401 AccessToken->DefaultDacl = EndMem;
402
403 RtlCopyMemory(EndMem,
404 DefaultDacl,
405 DefaultDacl->AclSize);
406 }
407
408 /* Insert the token only if it's not the system token, otherwise return it directly */
409 if (!SystemToken)
410 {
411 Status = ObInsertObject(AccessToken,
412 NULL,
413 DesiredAccess,
414 0,
415 NULL,
416 TokenHandle);
417 if (!NT_SUCCESS(Status))
418 {
419 DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status);
420 }
421 }
422 else
423 {
424 /* Return pointer instead of handle */
425 *TokenHandle = (HANDLE)AccessToken;
426 }
427
428 Quit:
429 if (!NT_SUCCESS(Status))
430 {
431 /* Dereference the token, the delete procedure will clean it up */
432 ObDereferenceObject(AccessToken);
433 }
434
435 return Status;
436 }
437
438 /**
439 * @brief
440 * Duplicates an access token, from an existing valid token.
441 *
442 * @param[in] Token
443 * Access token to duplicate.
444 *
445 * @param[in] ObjectAttributes
446 * Object attributes for the new token.
447 *
448 * @param[in] EffectiveOnly
449 * If set to TRUE, the function removes all the disabled privileges and groups of the token
450 * to duplicate.
451 *
452 * @param[in] TokenType
453 * Type of token.
454 *
455 * @param[in] Level
456 * Security impersonation level of a token.
457 *
458 * @param[in] PreviousMode
459 * The processor request level mode.
460 *
461 * @param[out] NewAccessToken
462 * The duplicated token.
463 *
464 * @return
465 * Returns STATUS_SUCCESS if the token has been duplicated. STATUS_INSUFFICIENT_RESOURCES is returned
466 * if memory pool allocation of the dynamic part of the token for duplication has failed due to the lack
467 * of memory resources. A failure NTSTATUS code is returned otherwise.
468 */
469 NTSTATUS
470 NTAPI
SepDuplicateToken(_In_ PTOKEN Token,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ BOOLEAN EffectiveOnly,_In_ TOKEN_TYPE TokenType,_In_ SECURITY_IMPERSONATION_LEVEL Level,_In_ KPROCESSOR_MODE PreviousMode,_Out_ PTOKEN * NewAccessToken)471 SepDuplicateToken(
472 _In_ PTOKEN Token,
473 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
474 _In_ BOOLEAN EffectiveOnly,
475 _In_ TOKEN_TYPE TokenType,
476 _In_ SECURITY_IMPERSONATION_LEVEL Level,
477 _In_ KPROCESSOR_MODE PreviousMode,
478 _Out_ PTOKEN* NewAccessToken)
479 {
480 NTSTATUS Status;
481 PTOKEN AccessToken;
482 PVOID EndMem;
483 ULONG PrimaryGroupIndex;
484 ULONG VariableLength;
485 ULONG DynamicPartSize, TotalSize;
486 ULONG PrivilegesIndex, GroupsIndex;
487
488 PAGED_CODE();
489
490 /* Compute how much size we need to allocate for the token */
491 VariableLength = Token->VariableLength;
492 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
493
494 /*
495 * Compute how much size we need to allocate
496 * the dynamic part of the newly duplicated
497 * token.
498 */
499 DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
500 DynamicPartSize += RtlLengthSid(Token->PrimaryGroup);
501
502 Status = ObCreateObject(PreviousMode,
503 SeTokenObjectType,
504 ObjectAttributes,
505 PreviousMode,
506 NULL,
507 TotalSize,
508 Token->DynamicCharged,
509 TotalSize,
510 (PVOID*)&AccessToken);
511 if (!NT_SUCCESS(Status))
512 {
513 DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
514 return Status;
515 }
516
517 /* Zero out the buffer and initialize the token */
518 RtlZeroMemory(AccessToken, TotalSize);
519
520 ExAllocateLocallyUniqueId(&AccessToken->TokenId);
521
522 AccessToken->TokenType = TokenType;
523 AccessToken->ImpersonationLevel = Level;
524
525 /* Initialise the lock for the access token */
526 Status = SepCreateTokenLock(AccessToken);
527 if (!NT_SUCCESS(Status))
528 {
529 ObDereferenceObject(AccessToken);
530 return Status;
531 }
532
533 /* Copy the immutable fields */
534 AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier;
535 RtlCopyMemory(AccessToken->TokenSource.SourceName,
536 Token->TokenSource.SourceName,
537 sizeof(Token->TokenSource.SourceName));
538
539 AccessToken->AuthenticationId = Token->AuthenticationId;
540 AccessToken->ParentTokenId = Token->ParentTokenId;
541 AccessToken->ExpirationTime = Token->ExpirationTime;
542 AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
543 AccessToken->DynamicCharged = Token->DynamicCharged;
544
545 /* Lock the source token and copy the mutable fields */
546 SepAcquireTokenLockShared(Token);
547
548 AccessToken->SessionId = Token->SessionId;
549 AccessToken->ModifiedId = Token->ModifiedId;
550
551 AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
552
553 /* Reference the logon session */
554 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
555 if (!NT_SUCCESS(Status))
556 {
557 /* No logon session could be found, bail out */
558 DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
559 /* Set the flag for proper cleanup by the delete procedure */
560 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
561 goto Quit;
562 }
563
564 /* Insert the referenced logon session into the token */
565 Status = SepRmInsertLogonSessionIntoToken(AccessToken);
566 if (!NT_SUCCESS(Status))
567 {
568 /* Failed to insert the logon session into the token, bail out */
569 DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
570 goto Quit;
571 }
572
573 /* Fill in token debug information */
574 #if DBG
575 RtlCopyMemory(AccessToken->ImageFileName,
576 PsGetCurrentProcess()->ImageFileName,
577 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
578
579 AccessToken->ProcessCid = PsGetCurrentProcessId();
580 AccessToken->ThreadCid = PsGetCurrentThreadId();
581 AccessToken->CreateMethod = TOKEN_DUPLICATE_METHOD;
582 #endif
583
584 /* Assign the data that reside in the token's variable information area */
585 AccessToken->VariableLength = VariableLength;
586 EndMem = (PVOID)&AccessToken->VariablePart;
587
588 /* Copy the privileges */
589 AccessToken->PrivilegeCount = 0;
590 AccessToken->Privileges = NULL;
591 if (Token->Privileges && (Token->PrivilegeCount > 0))
592 {
593 ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
594 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
595
596 ASSERT(VariableLength >= PrivilegesLength);
597
598 AccessToken->PrivilegeCount = Token->PrivilegeCount;
599 AccessToken->Privileges = EndMem;
600 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
601 VariableLength -= PrivilegesLength;
602
603 RtlCopyMemory(AccessToken->Privileges,
604 Token->Privileges,
605 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
606 }
607
608 /* Copy the user and groups */
609 AccessToken->UserAndGroupCount = 0;
610 AccessToken->UserAndGroups = NULL;
611 if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
612 {
613 AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
614 AccessToken->UserAndGroups = EndMem;
615 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
616 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
617
618 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
619 Token->UserAndGroups,
620 VariableLength,
621 AccessToken->UserAndGroups,
622 EndMem,
623 &EndMem,
624 &VariableLength);
625 if (!NT_SUCCESS(Status))
626 {
627 DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status);
628 goto Quit;
629 }
630 }
631
632 /* Find the token primary group */
633 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
634 Token->PrimaryGroup,
635 NULL,
636 &PrimaryGroupIndex,
637 NULL);
638 if (!NT_SUCCESS(Status))
639 {
640 DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
641 goto Quit;
642 }
643
644 /* Copy the restricted SIDs */
645 AccessToken->RestrictedSidCount = 0;
646 AccessToken->RestrictedSids = NULL;
647 if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
648 {
649 AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
650 AccessToken->RestrictedSids = EndMem;
651 EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
652 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
653
654 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
655 Token->RestrictedSids,
656 VariableLength,
657 AccessToken->RestrictedSids,
658 EndMem,
659 &EndMem,
660 &VariableLength);
661 if (!NT_SUCCESS(Status))
662 {
663 DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status);
664 goto Quit;
665 }
666 }
667
668 /* Now allocate the token's dynamic information area and set the data */
669 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
670 DynamicPartSize,
671 TAG_TOKEN_DYNAMIC);
672 if (AccessToken->DynamicPart == NULL)
673 {
674 Status = STATUS_INSUFFICIENT_RESOURCES;
675 goto Quit;
676 }
677
678 /* Unused memory in the dynamic area */
679 AccessToken->DynamicAvailable = 0;
680
681 /*
682 * Assign the primary group to the token
683 * and put it in the dynamic part as well.
684 */
685 EndMem = (PVOID)AccessToken->DynamicPart;
686 AccessToken->PrimaryGroup = EndMem;
687 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
688 EndMem,
689 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
690 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
691 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
692
693 /*
694 * The existing token has a default DACL only
695 * if it has an allocated dynamic part.
696 */
697 if (Token->DynamicPart && Token->DefaultDacl)
698 {
699 AccessToken->DefaultDacl = EndMem;
700
701 RtlCopyMemory(EndMem,
702 Token->DefaultDacl,
703 Token->DefaultDacl->AclSize);
704 }
705
706 /*
707 * Filter the token by removing the disabled privileges
708 * and groups if the caller wants to duplicate an access
709 * token as effective only.
710 */
711 if (EffectiveOnly)
712 {
713 /*
714 * Begin querying the groups and search for disabled ones. Do not touch the
715 * user which is at the first position because it cannot be disabled, no
716 * matter what attributes it has.
717 */
718 for (GroupsIndex = 1; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++)
719 {
720 /*
721 * A group is considered disabled if its attributes is either
722 * 0 or SE_GROUP_ENABLED is not included in the attributes flags list.
723 * That is because a certain user and/or group can have several attributes
724 * that bear no influence on whether a user/group is enabled or not
725 * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator
726 * that the group has just been enabled by default). A mandatory
727 * group (that is, the group has SE_GROUP_MANDATORY attribute)
728 * by standards it's always enabled and no one can disable it.
729 */
730 if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 ||
731 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0)
732 {
733 /*
734 * If this group is an administrators group
735 * and the token belongs to such group,
736 * we've to take away TOKEN_HAS_ADMIN_GROUP
737 * for the fact that's not enabled and as
738 * such the token no longer belongs to
739 * this group.
740 */
741 if (RtlEqualSid(SeAliasAdminsSid,
742 &AccessToken->UserAndGroups[GroupsIndex].Sid))
743 {
744 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
745 }
746
747 /*
748 * A group is not enabled, it's time to remove
749 * from the token and update the groups index
750 * accordingly and continue with the next group.
751 */
752 SepRemoveUserGroupToken(AccessToken, GroupsIndex);
753 GroupsIndex--;
754 }
755 }
756
757 /* Begin querying the privileges and search for disabled ones */
758 for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++)
759 {
760 /*
761 * A privilege is considered disabled if its attributes is either
762 * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list.
763 * That is because a certain privilege can have several attributes
764 * that bear no influence on whether a privilege is enabled or not
765 * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator
766 * that the privilege has just been enabled by default).
767 */
768 if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 ||
769 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0)
770 {
771 /*
772 * A privilege is not enabled, therefor it's time
773 * to strip it from the token and continue with the next
774 * privilege. Of course we must also want to update the
775 * privileges index accordingly.
776 */
777 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex);
778 PrivilegesIndex--;
779 }
780 }
781 }
782
783 /* Return the token to the caller */
784 *NewAccessToken = AccessToken;
785 Status = STATUS_SUCCESS;
786
787 Quit:
788 if (!NT_SUCCESS(Status))
789 {
790 /* Dereference the token, the delete procedure will clean it up */
791 ObDereferenceObject(AccessToken);
792 }
793
794 /* Unlock the source token */
795 SepReleaseTokenLock(Token);
796
797 return Status;
798 }
799
800 /**
801 * @brief
802 * Private helper function responsible for creating a restricted access
803 * token, that is, a filtered token from privileges and groups and with
804 * restricted SIDs added into the token on demand by the caller.
805 *
806 * @param[in] Token
807 * An existing and valid access token.
808 *
809 * @param[in] PrivilegesToBeDeleted
810 * A list of privileges to be deleted within the token that's going
811 * to be filtered. This parameter is ignored if the caller wants to disable
812 * all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags
813 * parameter.
814 *
815 * @param[in] SidsToBeDisabled
816 * A list of group SIDs to be disabled within the token. This parameter
817 * can be NULL.
818 *
819 * @param[in] RestrictedSidsIntoToken
820 * A list of restricted SIDs to be added into the token. This parameter
821 * can be NULL.
822 *
823 * @param[in] PrivilegesCount
824 * The privilege count of the privileges list.
825 *
826 * @param[in] RegularGroupsSidCount
827 * The SIDs count of the group SIDs list.
828 *
829 * @param[in] RestrictedSidsCount
830 * The restricted SIDs count of restricted SIDs list.
831 *
832 * @param[in] PrivilegeFlags
833 * Influences how the privileges should be filtered in an access
834 * token. See NtFilterToken syscall for more information.
835 *
836 * @param[in] PreviousMode
837 * Processor level access mode.
838 *
839 * @param[out] FilteredToken
840 * The filtered token, returned to the caller.
841 *
842 * @return
843 * Returns STATUS_SUCCESS if token token filtering has completed successfully.
844 * STATUS_INVALID_PARAMETER is returned if one or more of the parameters
845 * do not meet the conditions imposed by the function. A failure NTSTATUS
846 * code is returned otherwise.
847 *
848 * @remarks
849 * The final outcome of privileges and/or SIDs filtering is not always
850 * deterministic. That is, any privileges or SIDs that aren't present
851 * in the access token are ignored and the function continues with the
852 * next privilege or SID to find for filtering. For a fully deterministic
853 * outcome the caller is responsible for querying the information details
854 * of privileges and SIDs present in the token and then afterwards use
855 * such obtained information to do any kind of filtering to the token.
856 */
857 static
858 NTSTATUS
859 SepPerformTokenFiltering(
860 _In_ PTOKEN Token,
861 _In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted,
862 _In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled,
863 _In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken,
864 _When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount,
865 _When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount,
866 _When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount,
867 _In_ ULONG PrivilegeFlags,
868 _In_ KPROCESSOR_MODE PreviousMode,
869 _Out_ PTOKEN *FilteredToken)
870 {
871 NTSTATUS Status;
872 PTOKEN AccessToken;
873 PVOID EndMem;
874 ULONG DynamicPartSize;
875 ULONG RestrictedSidsLength;
876 ULONG PrivilegesLength;
877 ULONG PrimaryGroupIndex;
878 ULONG RestrictedSidsInList;
879 ULONG RestrictedSidsInToken;
880 ULONG VariableLength, TotalSize;
881 ULONG PrivsInToken, PrivsInList;
882 ULONG GroupsInToken, GroupsInList;
883 BOOLEAN WantPrivilegesDisabled;
884 BOOLEAN FoundPrivilege;
885 BOOLEAN FoundGroup;
886
887 PAGED_CODE();
888
889 /* Ensure that the source token is valid, and lock it */
890 ASSERT(Token);
891 SepAcquireTokenLockShared(Token);
892
893 /* Assume the caller doesn't want privileges disabled */
894 WantPrivilegesDisabled = FALSE;
895
896 /* Assume we haven't found anything */
897 FoundPrivilege = FALSE;
898 FoundGroup = FALSE;
899
900 /*
901 * Take the size that we need for filtered token
902 * allocation based upon the existing access token
903 * we've been given.
904 */
905 VariableLength = Token->VariableLength;
906
907 if (RestrictedSidsIntoToken != NULL)
908 {
909 /*
910 * If the caller provided a list of restricted SIDs
911 * to be added onto the filtered access token then
912 * we must compute the size which is the total space
913 * of the current token and the length of the restricted
914 * SIDs for the filtered token.
915 */
916 RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES);
917 RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken);
918 RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID));
919
920 /*
921 * The variable length of the token is not just
922 * the actual space length of the existing token
923 * but also the sum of the restricted SIDs length.
924 */
925 VariableLength += RestrictedSidsLength;
926 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength;
927 }
928 else
929 {
930 /* Otherwise the size is of the actual current token */
931 TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
932 }
933
934 /*
935 * Compute how much size we need to allocate
936 * the dynamic part of the newly duplicated
937 * token.
938 */
939 DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
940 DynamicPartSize += RtlLengthSid(Token->PrimaryGroup);
941
942 /* Set up a filtered token object */
943 Status = ObCreateObject(PreviousMode,
944 SeTokenObjectType,
945 NULL,
946 PreviousMode,
947 NULL,
948 TotalSize,
949 Token->DynamicCharged,
950 TotalSize,
951 (PVOID*)&AccessToken);
952 if (!NT_SUCCESS(Status))
953 {
954 DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status);
955
956 /* Unlock the source token and bail out */
957 SepReleaseTokenLock(Token);
958 return Status;
959 }
960
961 /* Initialize the token and begin filling stuff to it */
962 RtlZeroMemory(AccessToken, TotalSize);
963
964 /* Set up a lock for the new token */
965 Status = SepCreateTokenLock(AccessToken);
966 if (!NT_SUCCESS(Status))
967 goto Quit;
968
969 /* Allocate new IDs for the token */
970 ExAllocateLocallyUniqueId(&AccessToken->TokenId);
971 ExAllocateLocallyUniqueId(&AccessToken->ModifiedId);
972
973 /* Copy the type and impersonation level from the token */
974 AccessToken->TokenType = Token->TokenType;
975 AccessToken->ImpersonationLevel = Token->ImpersonationLevel;
976
977 /* Copy the immutable fields */
978 AccessToken->TokenSource.SourceIdentifier = Token->TokenSource.SourceIdentifier;
979 RtlCopyMemory(AccessToken->TokenSource.SourceName,
980 Token->TokenSource.SourceName,
981 sizeof(Token->TokenSource.SourceName));
982
983 AccessToken->AuthenticationId = Token->AuthenticationId;
984 AccessToken->ParentTokenId = Token->TokenId;
985 AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
986 AccessToken->DynamicCharged = Token->DynamicCharged;
987
988 AccessToken->ExpirationTime = Token->ExpirationTime;
989
990 /* Copy the mutable fields */
991 AccessToken->SessionId = Token->SessionId;
992 AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
993
994 /* Reference the logon session */
995 Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
996 if (!NT_SUCCESS(Status))
997 {
998 /* We failed, bail out*/
999 DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status);
1000 AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
1001 goto Quit;
1002 }
1003
1004 /* Insert the referenced logon session into the token */
1005 Status = SepRmInsertLogonSessionIntoToken(AccessToken);
1006 if (!NT_SUCCESS(Status))
1007 {
1008 /* Failed to insert the logon session into the token, bail out */
1009 DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status);
1010 goto Quit;
1011 }
1012
1013 /* Fill in token debug information */
1014 #if DBG
1015 RtlCopyMemory(AccessToken->ImageFileName,
1016 PsGetCurrentProcess()->ImageFileName,
1017 min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
1018
1019 AccessToken->ProcessCid = PsGetCurrentProcessId();
1020 AccessToken->ThreadCid = PsGetCurrentThreadId();
1021 AccessToken->CreateMethod = TOKEN_FILTER_METHOD;
1022 #endif
1023
1024 /* Assign the data that reside in the token's variable information area */
1025 AccessToken->VariableLength = VariableLength;
1026 EndMem = (PVOID)&AccessToken->VariablePart;
1027
1028 /* Copy the privileges from the existing token */
1029 AccessToken->PrivilegeCount = 0;
1030 AccessToken->Privileges = NULL;
1031 if (Token->Privileges && (Token->PrivilegeCount > 0))
1032 {
1033 PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
1034 PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
1035
1036 /*
1037 * Ensure that the token can actually hold all
1038 * the privileges from the existing token.
1039 * Otherwise something's seriously wrong and
1040 * we've to guard ourselves.
1041 */
1042 ASSERT(VariableLength >= PrivilegesLength);
1043
1044 AccessToken->PrivilegeCount = Token->PrivilegeCount;
1045 AccessToken->Privileges = EndMem;
1046 EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1047 VariableLength -= PrivilegesLength;
1048
1049 RtlCopyMemory(AccessToken->Privileges,
1050 Token->Privileges,
1051 AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1052 }
1053
1054 /* Copy the user and groups */
1055 AccessToken->UserAndGroupCount = 0;
1056 AccessToken->UserAndGroups = NULL;
1057 if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
1058 {
1059 AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
1060 AccessToken->UserAndGroups = EndMem;
1061 EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1062 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1063
1064 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
1065 Token->UserAndGroups,
1066 VariableLength,
1067 AccessToken->UserAndGroups,
1068 EndMem,
1069 &EndMem,
1070 &VariableLength);
1071 if (!NT_SUCCESS(Status))
1072 {
1073 DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status);
1074 goto Quit;
1075 }
1076 }
1077
1078 /* Copy the restricted SIDs */
1079 AccessToken->RestrictedSidCount = 0;
1080 AccessToken->RestrictedSids = NULL;
1081 if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
1082 {
1083 AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
1084 AccessToken->RestrictedSids = EndMem;
1085 EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
1086 VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
1087
1088 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
1089 Token->RestrictedSids,
1090 VariableLength,
1091 AccessToken->RestrictedSids,
1092 EndMem,
1093 &EndMem,
1094 &VariableLength);
1095 if (!NT_SUCCESS(Status))
1096 {
1097 DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status);
1098 goto Quit;
1099 }
1100 }
1101
1102 /*
1103 * Insert the restricted SIDs into the token on
1104 * the request by the caller.
1105 */
1106 if (RestrictedSidsIntoToken != NULL)
1107 {
1108 for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++)
1109 {
1110 /* Did the caller assign attributes to the restricted SIDs? */
1111 if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0)
1112 {
1113 /* There mustn't be any attributes, bail out */
1114 DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n");
1115 Status = STATUS_INVALID_PARAMETER;
1116 goto Quit;
1117 }
1118 }
1119
1120 /*
1121 * Ensure that the token can hold the restricted SIDs
1122 * (the variable length is calculated at the beginning
1123 * of the routine call).
1124 */
1125 ASSERT(VariableLength >= RestrictedSidsLength);
1126
1127 /*
1128 * Now let's begin inserting the restricted SIDs into the filtered
1129 * access token from the list the caller gave us.
1130 */
1131 AccessToken->RestrictedSidCount = RestrictedSidsCount;
1132 AccessToken->RestrictedSids = EndMem;
1133 EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength);
1134 VariableLength -= RestrictedSidsLength;
1135
1136 RtlCopyMemory(AccessToken->RestrictedSids,
1137 RestrictedSidsIntoToken,
1138 AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
1139
1140 /*
1141 * As we've copied the restricted SIDs into
1142 * the token, we must assign them the following
1143 * combination of attributes SE_GROUP_ENABLED,
1144 * SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY.
1145 * With such attributes we estabilish that restricting
1146 * SIDs into the token are enabled for access checks.
1147 */
1148 for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++)
1149 {
1150 AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY);
1151 }
1152
1153 /*
1154 * As we added restricted SIDs into the token, mark
1155 * it as restricted.
1156 */
1157 AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED;
1158 }
1159
1160 /* Search for the primary group */
1161 Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1162 Token->PrimaryGroup,
1163 NULL,
1164 &PrimaryGroupIndex,
1165 NULL);
1166 if (!NT_SUCCESS(Status))
1167 {
1168 DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status);
1169 goto Quit;
1170 }
1171
1172 /* Now allocate the token's dynamic information area and set the data */
1173 AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1174 DynamicPartSize,
1175 TAG_TOKEN_DYNAMIC);
1176 if (AccessToken->DynamicPart == NULL)
1177 {
1178 Status = STATUS_INSUFFICIENT_RESOURCES;
1179 goto Quit;
1180 }
1181
1182 /* Unused memory in the dynamic area */
1183 AccessToken->DynamicAvailable = 0;
1184
1185 /*
1186 * Assign the primary group to the token
1187 * and put it in the dynamic part as well.
1188 */
1189 EndMem = (PVOID)AccessToken->DynamicPart;
1190 AccessToken->PrimaryGroup = EndMem;
1191 RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
1192 EndMem,
1193 AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
1194 AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
1195 EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
1196
1197 /*
1198 * The existing token has a default DACL only
1199 * if it has an allocated dynamic part.
1200 */
1201 if (Token->DynamicPart && Token->DefaultDacl)
1202 {
1203 AccessToken->DefaultDacl = EndMem;
1204
1205 RtlCopyMemory(EndMem,
1206 Token->DefaultDacl,
1207 Token->DefaultDacl->AclSize);
1208 }
1209
1210 /*
1211 * Now figure out what does the caller
1212 * want with the privileges.
1213 */
1214 if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE)
1215 {
1216 /*
1217 * The caller wants them disabled, cache this request
1218 * for later operations.
1219 */
1220 WantPrivilegesDisabled = TRUE;
1221 }
1222
1223 if (PrivilegeFlags & SANDBOX_INERT)
1224 {
1225 /* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */
1226 AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT;
1227 }
1228
1229 /*
1230 * Now it's time to filter the token's privileges.
1231 * Loop all the privileges in the token.
1232 */
1233 for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++)
1234 {
1235 if (WantPrivilegesDisabled)
1236 {
1237 /*
1238 * We got the acknowledgement that the caller wants
1239 * to disable all the privileges so let's just do it.
1240 * However, as per the general documentation is stated
1241 * that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept
1242 * therefore in that case we must skip this privilege.
1243 */
1244 if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
1245 {
1246 continue;
1247 }
1248 else
1249 {
1250 /*
1251 * The act of disabling privileges actually means
1252 * "deleting" them from the access token entirely.
1253 * First we must disable them so that we can update
1254 * token flags accordingly.
1255 */
1256 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
1257 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
1258
1259 /* Remove the privileges now */
1260 SepRemovePrivilegeToken(AccessToken, PrivsInToken);
1261 PrivsInToken--;
1262 }
1263 }
1264 else
1265 {
1266 if (PrivilegesToBeDeleted != NULL)
1267 {
1268 /* Loop the privileges we've got to delete */
1269 for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++)
1270 {
1271 /* Does this privilege exist in the token? */
1272 if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid,
1273 &PrivilegesToBeDeleted[PrivsInList].Luid))
1274 {
1275 /* Mark that we found it */
1276 FoundPrivilege = TRUE;
1277 break;
1278 }
1279 }
1280
1281 /* Did we find the privilege? */
1282 if (PrivsInList == PrivilegesCount)
1283 {
1284 /* We didn't, continue with next one */
1285 continue;
1286 }
1287 }
1288 }
1289
1290 /*
1291 * If we have found the target privilege in the token
1292 * based on the privileges list given by the caller
1293 * then begin deleting it.
1294 */
1295 if (FoundPrivilege)
1296 {
1297 /* Disable the privilege and update the flags */
1298 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
1299 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
1300
1301 /* Delete the privilege */
1302 SepRemovePrivilegeToken(AccessToken, PrivsInToken);
1303
1304 /*
1305 * Adjust the index and reset the FoundPrivilege indicator
1306 * so that we can continue with the next privilege to delete.
1307 */
1308 PrivsInToken--;
1309 FoundPrivilege = FALSE;
1310 continue;
1311 }
1312 }
1313
1314 /*
1315 * Loop the group SIDs that we want to disable as
1316 * per on the request by the caller.
1317 */
1318 if (SidsToBeDisabled != NULL)
1319 {
1320 for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++)
1321 {
1322 for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++)
1323 {
1324 /* Does this group SID exist in the token? */
1325 if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid,
1326 &SidsToBeDisabled[GroupsInList].Sid))
1327 {
1328 /* Mark that we found it */
1329 FoundGroup = TRUE;
1330 break;
1331 }
1332 }
1333
1334 /* Did we find the group? */
1335 if (GroupsInList == RegularGroupsSidCount)
1336 {
1337 /* We didn't, continue with next one */
1338 continue;
1339 }
1340
1341 /* If we have found the group, disable it */
1342 if (FoundGroup)
1343 {
1344 /*
1345 * If the acess token belongs to the administrators
1346 * group and this is the target group, we must take
1347 * away TOKEN_HAS_ADMIN_GROUP flag from the token.
1348 */
1349 if (RtlEqualSid(SeAliasAdminsSid,
1350 &AccessToken->UserAndGroups[GroupsInToken].Sid))
1351 {
1352 AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
1353 }
1354
1355 /*
1356 * If the target group that we have found it is the
1357 * owner then from now on it no longer is but the user.
1358 * Therefore assign the default owner index as the user.
1359 */
1360 if (AccessToken->DefaultOwnerIndex == GroupsInToken)
1361 {
1362 AccessToken->DefaultOwnerIndex = 0;
1363 }
1364
1365 /*
1366 * The principle of disabling a group SID is by
1367 * taking away SE_GROUP_ENABLED_BY_DEFAULT and
1368 * SE_GROUP_ENABLED attributes and assign
1369 * SE_GROUP_USE_FOR_DENY_ONLY. This renders
1370 * SID a "Deny only" SID.
1371 */
1372 AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
1373 AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY;
1374
1375 /* Adjust the index and continue with the next group */
1376 GroupsInToken--;
1377 FoundGroup = FALSE;
1378 continue;
1379 }
1380 }
1381 }
1382
1383 /* We've finally filtered the token, return it to the caller */
1384 *FilteredToken = AccessToken;
1385 Status = STATUS_SUCCESS;
1386 DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n");
1387
1388 Quit:
1389 if (!NT_SUCCESS(Status))
1390 {
1391 /* Dereference the created token */
1392 ObDereferenceObject(AccessToken);
1393 }
1394
1395 /* Unlock the source token */
1396 SepReleaseTokenLock(Token);
1397
1398 return Status;
1399 }
1400
1401 /* PUBLIC FUNCTIONS ***********************************************************/
1402
1403 /**
1404 * @brief
1405 * Filters an access token from an existing token, making it more restricted
1406 * than the previous one.
1407 *
1408 * @param[in] ExistingToken
1409 * An existing token for filtering.
1410 *
1411 * @param[in] Flags
1412 * Privilege flag options. This parameter argument influences how the token
1413 * is filtered. Such parameter can be 0. See NtFilterToken syscall for
1414 * more information.
1415 *
1416 * @param[in] SidsToDisable
1417 * Array of SIDs to disable. Such parameter can be NULL.
1418 *
1419 * @param[in] PrivilegesToDelete
1420 * Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified
1421 * in the Flags parameter, PrivilegesToDelete is ignored.
1422 *
1423 * @param[in] RestrictedSids
1424 * An array of restricted SIDs for the new filtered token. Such parameter
1425 * can be NULL.
1426 *
1427 * @param[out] FilteredToken
1428 * The newly filtered token, returned to the caller.
1429 *
1430 * @return
1431 * Returns STATUS_SUCCESS if the function has successfully completed its
1432 * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER
1433 * is returned if one or more of the parameter are not valid. A failure NTSTATUS code
1434 * is returned otherwise.
1435 */
1436 NTSTATUS
1437 NTAPI
SeFilterToken(_In_ PACCESS_TOKEN ExistingToken,_In_ ULONG Flags,_In_opt_ PTOKEN_GROUPS SidsToDisable,_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,_In_opt_ PTOKEN_GROUPS RestrictedSids,_Out_ PACCESS_TOKEN * FilteredToken)1438 SeFilterToken(
1439 _In_ PACCESS_TOKEN ExistingToken,
1440 _In_ ULONG Flags,
1441 _In_opt_ PTOKEN_GROUPS SidsToDisable,
1442 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
1443 _In_opt_ PTOKEN_GROUPS RestrictedSids,
1444 _Out_ PACCESS_TOKEN *FilteredToken)
1445 {
1446 NTSTATUS Status;
1447 PTOKEN AccessToken;
1448 ULONG PrivilegesCount = 0;
1449 ULONG SidsCount = 0;
1450 ULONG RestrictedSidsCount = 0;
1451
1452 PAGED_CODE();
1453
1454 /* Begin copying the counters */
1455 if (SidsToDisable != NULL)
1456 {
1457 SidsCount = SidsToDisable->GroupCount;
1458 }
1459
1460 if (PrivilegesToDelete != NULL)
1461 {
1462 PrivilegesCount = PrivilegesToDelete->PrivilegeCount;
1463 }
1464
1465 if (RestrictedSids != NULL)
1466 {
1467 RestrictedSidsCount = RestrictedSids->GroupCount;
1468 }
1469
1470 /* Call the internal API */
1471 Status = SepPerformTokenFiltering(ExistingToken,
1472 PrivilegesToDelete->Privileges,
1473 SidsToDisable->Groups,
1474 RestrictedSids->Groups,
1475 PrivilegesCount,
1476 SidsCount,
1477 RestrictedSidsCount,
1478 Flags,
1479 KernelMode,
1480 &AccessToken);
1481 if (!NT_SUCCESS(Status))
1482 {
1483 DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
1484 return Status;
1485 }
1486
1487 /* Insert the filtered token */
1488 Status = ObInsertObject(AccessToken,
1489 NULL,
1490 0,
1491 0,
1492 NULL,
1493 NULL);
1494 if (!NT_SUCCESS(Status))
1495 {
1496 DPRINT1("SeFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
1497 return Status;
1498 }
1499
1500 /* Return it to the caller */
1501 *FilteredToken = AccessToken;
1502 return Status;
1503 }
1504
1505 /* SYSTEM CALLS ***************************************************************/
1506
1507 /**
1508 * @brief
1509 * Creates an access token.
1510 *
1511 * @param[out] TokenHandle
1512 * The returned created token handle to the caller.
1513 *
1514 * @param[in] DesiredAccess
1515 * The desired access rights for the token that we're creating.
1516 *
1517 * @param[in] ObjectAttributes
1518 * The object attributes for the token object that we're creating.
1519 *
1520 * @param[in] TokenType
1521 * The type of token to assign for the newly created token.
1522 *
1523 * @param[in] AuthenticationId
1524 * Authentication ID that represents the token's identity.
1525 *
1526 * @param[in] ExpirationTime
1527 * Expiration time for the token. If set to -1, the token never expires.
1528 *
1529 * @param[in] TokenUser
1530 * The main user entity for the token to assign.
1531 *
1532 * @param[in] TokenGroups
1533 * Group list of SIDs for the token to assign.
1534 *
1535 * @param[in] TokenPrivileges
1536 * Privileges for the token.
1537 *
1538 * @param[in] TokenOwner
1539 * The main user that owns the newly created token.
1540 *
1541 * @param[in] TokenPrimaryGroup
1542 * The primary group that represents as the main group of the token.
1543 *
1544 * @param[in] TokenDefaultDacl
1545 * Discretionary access control list for the token. This limits on how
1546 * the token can be used, accessed and used by whom.
1547 *
1548 * @param[in] TokenSource
1549 * The source origin of the token who creates it.
1550 *
1551 * @return
1552 * Returns STATUS_SUCCESS if the function has successfully created the token.
1553 * A failure NTSTATUS code is returned otherwise.
1554 */
1555 __kernel_entry
1556 NTSTATUS
1557 NTAPI
NtCreateToken(_Out_ PHANDLE TokenHandle,_In_ ACCESS_MASK DesiredAccess,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ TOKEN_TYPE TokenType,_In_ PLUID AuthenticationId,_In_ PLARGE_INTEGER ExpirationTime,_In_ PTOKEN_USER TokenUser,_In_ PTOKEN_GROUPS TokenGroups,_In_ PTOKEN_PRIVILEGES TokenPrivileges,_In_opt_ PTOKEN_OWNER TokenOwner,_In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,_In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,_In_ PTOKEN_SOURCE TokenSource)1558 NtCreateToken(
1559 _Out_ PHANDLE TokenHandle,
1560 _In_ ACCESS_MASK DesiredAccess,
1561 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1562 _In_ TOKEN_TYPE TokenType,
1563 _In_ PLUID AuthenticationId,
1564 _In_ PLARGE_INTEGER ExpirationTime,
1565 _In_ PTOKEN_USER TokenUser,
1566 _In_ PTOKEN_GROUPS TokenGroups,
1567 _In_ PTOKEN_PRIVILEGES TokenPrivileges,
1568 _In_opt_ PTOKEN_OWNER TokenOwner,
1569 _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
1570 _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
1571 _In_ PTOKEN_SOURCE TokenSource)
1572 {
1573 HANDLE hToken;
1574 KPROCESSOR_MODE PreviousMode;
1575 ULONG PrivilegeCount, GroupCount;
1576 PSID OwnerSid, PrimaryGroupSid;
1577 PACL DefaultDacl;
1578 LARGE_INTEGER LocalExpirationTime = {{0, 0}};
1579 LUID LocalAuthenticationId;
1580 TOKEN_SOURCE LocalTokenSource;
1581 SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
1582 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
1583 PSID_AND_ATTRIBUTES CapturedUser = NULL;
1584 PSID_AND_ATTRIBUTES CapturedGroups = NULL;
1585 PSID CapturedOwnerSid = NULL;
1586 PSID CapturedPrimaryGroupSid = NULL;
1587 PACL CapturedDefaultDacl = NULL;
1588 ULONG PrivilegesLength, UserLength, GroupsLength;
1589 NTSTATUS Status;
1590
1591 PAGED_CODE();
1592
1593 PreviousMode = ExGetPreviousMode();
1594
1595 if (PreviousMode != KernelMode)
1596 {
1597 _SEH2_TRY
1598 {
1599 ProbeForWriteHandle(TokenHandle);
1600
1601 if (ObjectAttributes != NULL)
1602 {
1603 ProbeForRead(ObjectAttributes,
1604 sizeof(OBJECT_ATTRIBUTES),
1605 sizeof(ULONG));
1606 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
1607 }
1608
1609 ProbeForRead(AuthenticationId,
1610 sizeof(LUID),
1611 sizeof(ULONG));
1612 LocalAuthenticationId = *AuthenticationId;
1613
1614 LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
1615
1616 ProbeForRead(TokenUser,
1617 sizeof(TOKEN_USER),
1618 sizeof(ULONG));
1619
1620 ProbeForRead(TokenGroups,
1621 sizeof(TOKEN_GROUPS),
1622 sizeof(ULONG));
1623 GroupCount = TokenGroups->GroupCount;
1624
1625 ProbeForRead(TokenPrivileges,
1626 sizeof(TOKEN_PRIVILEGES),
1627 sizeof(ULONG));
1628 PrivilegeCount = TokenPrivileges->PrivilegeCount;
1629
1630 if (TokenOwner != NULL)
1631 {
1632 ProbeForRead(TokenOwner,
1633 sizeof(TOKEN_OWNER),
1634 sizeof(ULONG));
1635 OwnerSid = TokenOwner->Owner;
1636 }
1637 else
1638 {
1639 OwnerSid = NULL;
1640 }
1641
1642 ProbeForRead(TokenPrimaryGroup,
1643 sizeof(TOKEN_PRIMARY_GROUP),
1644 sizeof(ULONG));
1645 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
1646
1647 if (TokenDefaultDacl != NULL)
1648 {
1649 ProbeForRead(TokenDefaultDacl,
1650 sizeof(TOKEN_DEFAULT_DACL),
1651 sizeof(ULONG));
1652 DefaultDacl = TokenDefaultDacl->DefaultDacl;
1653 }
1654 else
1655 {
1656 DefaultDacl = NULL;
1657 }
1658
1659 ProbeForRead(TokenSource,
1660 sizeof(TOKEN_SOURCE),
1661 sizeof(ULONG));
1662 LocalTokenSource = *TokenSource;
1663 }
1664 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1665 {
1666 /* Return the exception code */
1667 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1668 }
1669 _SEH2_END;
1670 }
1671 else
1672 {
1673 if (ObjectAttributes != NULL)
1674 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
1675 LocalAuthenticationId = *AuthenticationId;
1676 LocalExpirationTime = *ExpirationTime;
1677 GroupCount = TokenGroups->GroupCount;
1678 PrivilegeCount = TokenPrivileges->PrivilegeCount;
1679 OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
1680 PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
1681 DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
1682 LocalTokenSource = *TokenSource;
1683 }
1684
1685 /* Check token type */
1686 if ((TokenType < TokenPrimary) ||
1687 (TokenType > TokenImpersonation))
1688 {
1689 return STATUS_BAD_TOKEN_TYPE;
1690 }
1691
1692 /* Check for token creation privilege */
1693 if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
1694 {
1695 return STATUS_PRIVILEGE_NOT_HELD;
1696 }
1697
1698 /* Capture the user SID and attributes */
1699 Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
1700 1,
1701 PreviousMode,
1702 NULL,
1703 0,
1704 PagedPool,
1705 FALSE,
1706 &CapturedUser,
1707 &UserLength);
1708 if (!NT_SUCCESS(Status))
1709 {
1710 goto Cleanup;
1711 }
1712
1713 /* Capture the groups SID and attributes array */
1714 Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
1715 GroupCount,
1716 PreviousMode,
1717 NULL,
1718 0,
1719 PagedPool,
1720 FALSE,
1721 &CapturedGroups,
1722 &GroupsLength);
1723 if (!NT_SUCCESS(Status))
1724 {
1725 goto Cleanup;
1726 }
1727
1728 /* Capture privileges */
1729 Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
1730 PrivilegeCount,
1731 PreviousMode,
1732 NULL,
1733 0,
1734 PagedPool,
1735 FALSE,
1736 &CapturedPrivileges,
1737 &PrivilegesLength);
1738 if (!NT_SUCCESS(Status))
1739 {
1740 goto Cleanup;
1741 }
1742
1743 /* Capture the token owner SID */
1744 if (TokenOwner != NULL)
1745 {
1746 Status = SepCaptureSid(OwnerSid,
1747 PreviousMode,
1748 PagedPool,
1749 FALSE,
1750 &CapturedOwnerSid);
1751 if (!NT_SUCCESS(Status))
1752 {
1753 goto Cleanup;
1754 }
1755 }
1756
1757 /* Capture the token primary group SID */
1758 Status = SepCaptureSid(PrimaryGroupSid,
1759 PreviousMode,
1760 PagedPool,
1761 FALSE,
1762 &CapturedPrimaryGroupSid);
1763 if (!NT_SUCCESS(Status))
1764 {
1765 goto Cleanup;
1766 }
1767
1768 /* Capture DefaultDacl */
1769 if (DefaultDacl != NULL)
1770 {
1771 Status = SepCaptureAcl(DefaultDacl,
1772 PreviousMode,
1773 NonPagedPool,
1774 FALSE,
1775 &CapturedDefaultDacl);
1776 if (!NT_SUCCESS(Status))
1777 {
1778 goto Cleanup;
1779 }
1780 }
1781
1782 /* Call the internal function */
1783 Status = SepCreateToken(&hToken,
1784 PreviousMode,
1785 DesiredAccess,
1786 ObjectAttributes,
1787 TokenType,
1788 LocalSecurityQos.ImpersonationLevel,
1789 &LocalAuthenticationId,
1790 &LocalExpirationTime,
1791 CapturedUser,
1792 GroupCount,
1793 CapturedGroups,
1794 GroupsLength,
1795 PrivilegeCount,
1796 CapturedPrivileges,
1797 CapturedOwnerSid,
1798 CapturedPrimaryGroupSid,
1799 CapturedDefaultDacl,
1800 &LocalTokenSource,
1801 FALSE);
1802 if (NT_SUCCESS(Status))
1803 {
1804 _SEH2_TRY
1805 {
1806 *TokenHandle = hToken;
1807 }
1808 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1809 {
1810 Status = _SEH2_GetExceptionCode();
1811 }
1812 _SEH2_END;
1813 }
1814
1815 Cleanup:
1816
1817 /* Release what we captured */
1818 SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
1819 SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
1820 SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
1821 SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
1822 SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
1823 SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
1824
1825 return Status;
1826 }
1827
1828 /**
1829 * @brief
1830 * Duplicates a token.
1831 *
1832 * @param[in] ExistingTokenHandle
1833 * An existing token to duplicate.
1834 *
1835 * @param[in] DesiredAccess
1836 * The desired access rights for the new duplicated token.
1837 *
1838 * @param[in] ObjectAttributes
1839 * Object attributes for the new duplicated token.
1840 *
1841 * @param[in] EffectiveOnly
1842 * If set to TRUE, the function removes all the disabled privileges and groups
1843 * of the token to duplicate.
1844 *
1845 * @param[in] TokenType
1846 * Type of token to assign to the duplicated token.
1847 *
1848 * @param[out] NewTokenHandle
1849 * The returned duplicated token handle.
1850 *
1851 * @return
1852 * STATUS_SUCCESS is returned if token duplication has completed successfully.
1853 * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants
1854 * to raise the impersonation level even though the conditions do not permit
1855 * it. A failure NTSTATUS code is returned otherwise.
1856 *
1857 * @remarks
1858 * Some sources claim 4th param is ImpersonationLevel, but on W2K
1859 * this is certainly NOT true, although I can't say for sure that EffectiveOnly
1860 * is correct either. -Gunnar
1861 * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
1862 * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
1863 * wrong in that regard, while MSDN documentation is correct.
1864 */
1865 _Must_inspect_result_
1866 __kernel_entry
1867 NTSTATUS
1868 NTAPI
NtDuplicateToken(_In_ HANDLE ExistingTokenHandle,_In_ ACCESS_MASK DesiredAccess,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_ BOOLEAN EffectiveOnly,_In_ TOKEN_TYPE TokenType,_Out_ PHANDLE NewTokenHandle)1869 NtDuplicateToken(
1870 _In_ HANDLE ExistingTokenHandle,
1871 _In_ ACCESS_MASK DesiredAccess,
1872 _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1873 _In_ BOOLEAN EffectiveOnly,
1874 _In_ TOKEN_TYPE TokenType,
1875 _Out_ PHANDLE NewTokenHandle)
1876 {
1877 KPROCESSOR_MODE PreviousMode;
1878 HANDLE hToken;
1879 PTOKEN Token;
1880 PTOKEN NewToken;
1881 PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService;
1882 BOOLEAN QoSPresent;
1883 OBJECT_HANDLE_INFORMATION HandleInformation;
1884 NTSTATUS Status;
1885
1886 PAGED_CODE();
1887
1888 if (TokenType != TokenImpersonation &&
1889 TokenType != TokenPrimary)
1890 {
1891 return STATUS_INVALID_PARAMETER;
1892 }
1893
1894 PreviousMode = KeGetPreviousMode();
1895
1896 if (PreviousMode != KernelMode)
1897 {
1898 _SEH2_TRY
1899 {
1900 ProbeForWriteHandle(NewTokenHandle);
1901 }
1902 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1903 {
1904 /* Return the exception code */
1905 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1906 }
1907 _SEH2_END;
1908 }
1909
1910 Status = SepCaptureSecurityQualityOfService(ObjectAttributes,
1911 PreviousMode,
1912 PagedPool,
1913 FALSE,
1914 &CapturedSecurityQualityOfService,
1915 &QoSPresent);
1916 if (!NT_SUCCESS(Status))
1917 {
1918 DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status);
1919 return Status;
1920 }
1921
1922 Status = ObReferenceObjectByHandle(ExistingTokenHandle,
1923 TOKEN_DUPLICATE,
1924 SeTokenObjectType,
1925 PreviousMode,
1926 (PVOID*)&Token,
1927 &HandleInformation);
1928 if (!NT_SUCCESS(Status))
1929 {
1930 DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
1931 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1932 PreviousMode,
1933 FALSE);
1934 return Status;
1935 }
1936
1937 /*
1938 * Fail, if the original token is an impersonation token and the caller
1939 * tries to raise the impersonation level of the new token above the
1940 * impersonation level of the original token.
1941 */
1942 if (Token->TokenType == TokenImpersonation)
1943 {
1944 if (QoSPresent &&
1945 CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel)
1946 {
1947 ObDereferenceObject(Token);
1948 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1949 PreviousMode,
1950 FALSE);
1951 return STATUS_BAD_IMPERSONATION_LEVEL;
1952 }
1953 }
1954
1955 /*
1956 * Fail, if a primary token is to be created from an impersonation token
1957 * and and the impersonation level of the impersonation token is below SecurityImpersonation.
1958 */
1959 if (Token->TokenType == TokenImpersonation &&
1960 TokenType == TokenPrimary &&
1961 Token->ImpersonationLevel < SecurityImpersonation)
1962 {
1963 ObDereferenceObject(Token);
1964 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
1965 PreviousMode,
1966 FALSE);
1967 return STATUS_BAD_IMPERSONATION_LEVEL;
1968 }
1969
1970 Status = SepDuplicateToken(Token,
1971 ObjectAttributes,
1972 EffectiveOnly,
1973 TokenType,
1974 (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous),
1975 PreviousMode,
1976 &NewToken);
1977
1978 ObDereferenceObject(Token);
1979
1980 if (NT_SUCCESS(Status))
1981 {
1982 Status = ObInsertObject(NewToken,
1983 NULL,
1984 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess),
1985 0,
1986 NULL,
1987 &hToken);
1988 if (NT_SUCCESS(Status))
1989 {
1990 _SEH2_TRY
1991 {
1992 *NewTokenHandle = hToken;
1993 }
1994 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1995 {
1996 Status = _SEH2_GetExceptionCode();
1997 }
1998 _SEH2_END;
1999 }
2000 }
2001
2002 /* Free the captured structure */
2003 SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
2004 PreviousMode,
2005 FALSE);
2006
2007 return Status;
2008 }
2009
2010 /**
2011 * @brief
2012 * Creates an access token in a restricted form
2013 * from the original existing token, that is, such
2014 * action is called filtering.
2015 *
2016 * @param[in] ExistingTokenHandle
2017 * A handle to an access token which is to be filtered.
2018 *
2019 * @param[in] Flags
2020 * Privilege flag options. This parameter argument influences how the
2021 * token's privileges are filtered. For further details see remarks.
2022 *
2023 * @param[in] SidsToDisable
2024 * Array of SIDs to disable. The action of doing so assigns the
2025 * SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group
2026 * SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT.
2027 * This parameter can be NULL. This can be a UM pointer.
2028 *
2029 * @param[in] PrivilegesToDelete
2030 * Array of privileges to delete. The function will walk within this
2031 * array to determine if the specified privileges do exist in the
2032 * access token. Any missing privileges gets ignored. This parameter
2033 * can be NULL. This can be a UM pointer.
2034 *
2035 * @param[in] RestrictedSids
2036 * An array list of restricted groups SID to be added in the access
2037 * token. A token that is already restricted the newly added restricted
2038 * SIDs are redundant information in addition to the existing restricted
2039 * SIDs in the token. This parameter can be NULL. This can be a UM pointer.
2040 *
2041 * @param[out] NewTokenHandle
2042 * A new handle to the restricted (filtered) access token. This can be a
2043 * UM pointer.
2044 *
2045 * @return
2046 * Returns STATUS_SUCCESS if the routine has successfully filtered the
2047 * access token. STATUS_INVALID_PARAMETER is returned if one or more
2048 * parameters are not valid (see SepPerformTokenFiltering routine call
2049 * for more information). A failure NTSTATUS code is returned otherwise.
2050 *
2051 * @remarks
2052 * The Flags parameter determines the final outcome of how the privileges
2053 * in an access token are filtered. This parameter can take these supported
2054 * values (these can be combined):
2055 *
2056 * 0 -- Filter the token's privileges in the usual way. The function expects
2057 * that the caller MUST PROVIDE a valid array list of privileges to be
2058 * deleted (that is, PrivilegesToDelete MUSTN'T BE NULL).
2059 *
2060 * DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege
2061 * in the new access token. Bear in mind if this flag is specified
2062 * the routine ignores PrivilegesToDelete.
2063 *
2064 * SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token.
2065 *
2066 * LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not
2067 * supported in Windows Server 2003.
2068 *
2069 * WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are
2070 * considered only when evaluating write access onto the token.
2071 * This value is not supported in Windows Server 2003.
2072 */
2073 NTSTATUS
2074 NTAPI
NtFilterToken(_In_ HANDLE ExistingTokenHandle,_In_ ULONG Flags,_In_opt_ PTOKEN_GROUPS SidsToDisable,_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,_In_opt_ PTOKEN_GROUPS RestrictedSids,_Out_ PHANDLE NewTokenHandle)2075 NtFilterToken(
2076 _In_ HANDLE ExistingTokenHandle,
2077 _In_ ULONG Flags,
2078 _In_opt_ PTOKEN_GROUPS SidsToDisable,
2079 _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
2080 _In_opt_ PTOKEN_GROUPS RestrictedSids,
2081 _Out_ PHANDLE NewTokenHandle)
2082 {
2083 PTOKEN Token, FilteredToken;
2084 HANDLE FilteredTokenHandle;
2085 NTSTATUS Status;
2086 KPROCESSOR_MODE PreviousMode;
2087 OBJECT_HANDLE_INFORMATION HandleInfo;
2088 ULONG ResultLength;
2089 ULONG CapturedSidsCount = 0;
2090 ULONG CapturedPrivilegesCount = 0;
2091 ULONG CapturedRestrictedSidsCount = 0;
2092 ULONG ProbeSize = 0;
2093 PSID_AND_ATTRIBUTES CapturedSids = NULL;
2094 PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL;
2095 PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
2096
2097 PAGED_CODE();
2098
2099 PreviousMode = ExGetPreviousMode();
2100
2101 _SEH2_TRY
2102 {
2103 /* Probe SidsToDisable */
2104 if (SidsToDisable != NULL)
2105 {
2106 /* Probe the header */
2107 ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG));
2108
2109 CapturedSidsCount = SidsToDisable->GroupCount;
2110 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]);
2111
2112 ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG));
2113 }
2114
2115 /* Probe PrivilegesToDelete */
2116 if (PrivilegesToDelete != NULL)
2117 {
2118 /* Probe the header */
2119 ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG));
2120
2121 CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount;
2122 ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]);
2123
2124 ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG));
2125 }
2126
2127 /* Probe RestrictedSids */
2128 if (RestrictedSids != NULL)
2129 {
2130 /* Probe the header */
2131 ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG));
2132
2133 CapturedRestrictedSidsCount = RestrictedSids->GroupCount;
2134 ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]);
2135
2136 ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG));
2137 }
2138
2139 /* Probe the handle */
2140 ProbeForWriteHandle(NewTokenHandle);
2141 }
2142 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2143 {
2144 /* Return the exception code */
2145 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2146 }
2147 _SEH2_END;
2148
2149 /* Reference the token */
2150 Status = ObReferenceObjectByHandle(ExistingTokenHandle,
2151 TOKEN_DUPLICATE,
2152 SeTokenObjectType,
2153 PreviousMode,
2154 (PVOID*)&Token,
2155 &HandleInfo);
2156 if (!NT_SUCCESS(Status))
2157 {
2158 DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status);
2159 return Status;
2160 }
2161
2162 /* Capture the group SIDs */
2163 if (SidsToDisable != NULL)
2164 {
2165 Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups,
2166 CapturedSidsCount,
2167 PreviousMode,
2168 NULL,
2169 0,
2170 PagedPool,
2171 TRUE,
2172 &CapturedSids,
2173 &ResultLength);
2174 if (!NT_SUCCESS(Status))
2175 {
2176 DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status);
2177 goto Quit;
2178 }
2179 }
2180
2181 /* Capture the privileges */
2182 if (PrivilegesToDelete != NULL)
2183 {
2184 Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges,
2185 CapturedPrivilegesCount,
2186 PreviousMode,
2187 NULL,
2188 0,
2189 PagedPool,
2190 TRUE,
2191 &CapturedPrivileges,
2192 &ResultLength);
2193 if (!NT_SUCCESS(Status))
2194 {
2195 DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status);
2196 goto Quit;
2197 }
2198 }
2199
2200 /* Capture the restricted SIDs */
2201 if (RestrictedSids != NULL)
2202 {
2203 Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups,
2204 CapturedRestrictedSidsCount,
2205 PreviousMode,
2206 NULL,
2207 0,
2208 PagedPool,
2209 TRUE,
2210 &CapturedRestrictedSids,
2211 &ResultLength);
2212 if (!NT_SUCCESS(Status))
2213 {
2214 DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status);
2215 goto Quit;
2216 }
2217 }
2218
2219 /* Call the internal API */
2220 Status = SepPerformTokenFiltering(Token,
2221 CapturedPrivileges,
2222 CapturedSids,
2223 CapturedRestrictedSids,
2224 CapturedPrivilegesCount,
2225 CapturedSidsCount,
2226 CapturedRestrictedSidsCount,
2227 Flags,
2228 PreviousMode,
2229 &FilteredToken);
2230 if (!NT_SUCCESS(Status))
2231 {
2232 DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
2233 goto Quit;
2234 }
2235
2236 /* Insert the filtered token and retrieve a handle to it */
2237 Status = ObInsertObject(FilteredToken,
2238 NULL,
2239 HandleInfo.GrantedAccess,
2240 0,
2241 NULL,
2242 &FilteredTokenHandle);
2243 if (!NT_SUCCESS(Status))
2244 {
2245 DPRINT1("NtFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
2246 goto Quit;
2247 }
2248
2249 /* And return it to the caller once we're done */
2250 _SEH2_TRY
2251 {
2252 *NewTokenHandle = FilteredTokenHandle;
2253 }
2254 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2255 {
2256 Status = _SEH2_GetExceptionCode();
2257 _SEH2_YIELD(goto Quit);
2258 }
2259 _SEH2_END;
2260
2261 Quit:
2262 /* Dereference the token */
2263 ObDereferenceObject(Token);
2264
2265 /* Release all the captured data */
2266 if (CapturedSids != NULL)
2267 {
2268 SeReleaseSidAndAttributesArray(CapturedSids,
2269 PreviousMode,
2270 TRUE);
2271 }
2272
2273 if (CapturedPrivileges != NULL)
2274 {
2275 SeReleaseLuidAndAttributesArray(CapturedPrivileges,
2276 PreviousMode,
2277 TRUE);
2278 }
2279
2280 if (CapturedRestrictedSids != NULL)
2281 {
2282 SeReleaseSidAndAttributesArray(CapturedRestrictedSids,
2283 PreviousMode,
2284 TRUE);
2285 }
2286
2287 return Status;
2288 }
2289
2290 /* EOF */
2291