[NTOS:SE] Fix locking in SepDuplicateToken() and SepPerformTokenFiltering() (#4523)

Shared locking must be used on the source token as it is accessed for
reading only. This fixes in particular the kmtest SeTokenFiltering that
would hang otherwise on a (wrong) exclusive locking.

- SepPerformTokenFiltering(): Always shared-lock the source token.
  Its callers (NtFilterToken and SeFilterToken) just need to sanitize and
  put the parameters in correct form before calling this helper function.

- Sync comments in NtFilterToken() with SeFilterToken().
This commit is contained in:
Hermès Bélusca-Maïto 2022-05-22 18:11:18 +02:00
parent b33911b93d
commit 3370652dfc
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0

View file

@ -1059,7 +1059,7 @@ SepDuplicateToken(
AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession; AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
/* Lock the source token and copy the mutable fields */ /* Lock the source token and copy the mutable fields */
SepAcquireTokenLockExclusive(Token); SepAcquireTokenLockShared(Token);
AccessToken->SessionId = Token->SessionId; AccessToken->SessionId = Token->SessionId;
RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId); RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId);
@ -1286,23 +1286,20 @@ SepDuplicateToken(
Token->DefaultDacl->AclSize); Token->DefaultDacl->AclSize);
} }
/* Unlock the source token */ /* Return the token to the caller */
SepReleaseTokenLock(Token);
/* Return the token */
*NewAccessToken = AccessToken; *NewAccessToken = AccessToken;
Status = STATUS_SUCCESS; Status = STATUS_SUCCESS;
Quit: Quit:
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
/* Unlock the source token */
SepReleaseTokenLock(Token);
/* Dereference the token, the delete procedure will clean it up */ /* Dereference the token, the delete procedure will clean it up */
ObDereferenceObject(AccessToken); ObDereferenceObject(AccessToken);
} }
/* Unlock the source token */
SepReleaseTokenLock(Token);
return Status; return Status;
} }
@ -2086,8 +2083,8 @@ SepPerformTokenFiltering(
_In_ KPROCESSOR_MODE PreviousMode, _In_ KPROCESSOR_MODE PreviousMode,
_Out_ PTOKEN *FilteredToken) _Out_ PTOKEN *FilteredToken)
{ {
PTOKEN AccessToken;
NTSTATUS Status; NTSTATUS Status;
PTOKEN AccessToken;
PVOID EndMem; PVOID EndMem;
ULONG RestrictedSidsLength; ULONG RestrictedSidsLength;
ULONG PrivilegesLength; ULONG PrivilegesLength;
@ -2100,10 +2097,12 @@ SepPerformTokenFiltering(
BOOLEAN WantPrivilegesDisabled; BOOLEAN WantPrivilegesDisabled;
BOOLEAN FoundPrivilege; BOOLEAN FoundPrivilege;
BOOLEAN FoundGroup; BOOLEAN FoundGroup;
PAGED_CODE(); PAGED_CODE();
/* Ensure that the token we get is not garbage */ /* Ensure that the source token is valid, and lock it */
ASSERT(Token); ASSERT(Token);
SepAcquireTokenLockShared(Token);
/* Assume the caller doesn't want privileges disabled */ /* Assume the caller doesn't want privileges disabled */
WantPrivilegesDisabled = FALSE; WantPrivilegesDisabled = FALSE;
@ -2159,6 +2158,9 @@ SepPerformTokenFiltering(
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status); DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status);
/* Unlock the source token and bail out */
SepReleaseTokenLock(Token);
return Status; return Status;
} }
@ -2168,10 +2170,7 @@ SepPerformTokenFiltering(
/* Set up a lock for the new token */ /* Set up a lock for the new token */
Status = SepCreateTokenLock(AccessToken); Status = SepCreateTokenLock(AccessToken);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ goto Quit;
ObDereferenceObject(AccessToken);
return Status;
}
/* Allocate new IDs for the token */ /* Allocate new IDs for the token */
ExAllocateLocallyUniqueId(&AccessToken->TokenId); ExAllocateLocallyUniqueId(&AccessToken->TokenId);
@ -2576,7 +2575,7 @@ SepPerformTokenFiltering(
AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED; AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED;
} }
/* We've finally filtered the token, give it to the caller */ /* We've finally filtered the token, return it to the caller */
*FilteredToken = AccessToken; *FilteredToken = AccessToken;
Status = STATUS_SUCCESS; Status = STATUS_SUCCESS;
DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n"); DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n");
@ -2584,10 +2583,13 @@ SepPerformTokenFiltering(
Quit: Quit:
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
/* Dereference the token */ /* Dereference the created token */
ObDereferenceObject(AccessToken); ObDereferenceObject(AccessToken);
} }
/* Unlock the source token */
SepReleaseTokenLock(Token);
return Status; return Status;
} }
@ -2903,12 +2905,6 @@ SepCreateSystemAnonymousLogonTokenNoEveryone(VOID)
* operations and that the access token has been filtered. STATUS_INVALID_PARAMETER * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER
* is returned if one or more of the parameter are not valid. A failure NTSTATUS code * is returned if one or more of the parameter are not valid. A failure NTSTATUS code
* is returned otherwise. * is returned otherwise.
*
* @remarks
* WARNING -- The caller IS RESPONSIBLE for locking the existing access token
* before attempting to do any kind of filtering operation into
* the token. The lock MUST BE RELEASED after this kernel routine
* has finished doing its work.
*/ */
NTSTATUS NTSTATUS
NTAPI NTAPI
@ -2925,6 +2921,7 @@ SeFilterToken(
ULONG PrivilegesCount = 0; ULONG PrivilegesCount = 0;
ULONG SidsCount = 0; ULONG SidsCount = 0;
ULONG RestrictedSidsCount = 0; ULONG RestrictedSidsCount = 0;
PAGED_CODE(); PAGED_CODE();
/* Begin copying the counters */ /* Begin copying the counters */
@ -2969,11 +2966,11 @@ SeFilterToken(
NULL); NULL);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
DPRINT1("SeFilterToken(): Failed to insert the token (Status 0x%lx)\n", Status); DPRINT1("SeFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
return Status; return Status;
} }
/* Give it to the caller */ /* Return it to the caller */
*FilteredToken = AccessToken; *FilteredToken = AccessToken;
return Status; return Status;
} }
@ -6661,7 +6658,7 @@ NtFilterToken(
} }
_SEH2_END; _SEH2_END;
/* Reference the token and do the job */ /* Reference the token */
Status = ObReferenceObjectByHandle(ExistingTokenHandle, Status = ObReferenceObjectByHandle(ExistingTokenHandle,
TOKEN_DUPLICATE, TOKEN_DUPLICATE,
SeTokenObjectType, SeTokenObjectType,
@ -6674,9 +6671,6 @@ NtFilterToken(
return Status; return Status;
} }
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Capture the group SIDs */ /* Capture the group SIDs */
if (SidsToDisable != NULL) if (SidsToDisable != NULL)
{ {
@ -6734,7 +6728,7 @@ NtFilterToken(
} }
} }
/* Call the internal API so that it can filter the token for us */ /* Call the internal API */
Status = SepPerformTokenFiltering(Token, Status = SepPerformTokenFiltering(Token,
CapturedPrivileges, CapturedPrivileges,
CapturedSids, CapturedSids,
@ -6751,7 +6745,7 @@ NtFilterToken(
goto Quit; goto Quit;
} }
/* We got our filtered token, insert it to the handle */ /* Insert the filtered token and retrieve a handle to it */
Status = ObInsertObject(FilteredToken, Status = ObInsertObject(FilteredToken,
NULL, NULL,
HandleInfo.GrantedAccess, HandleInfo.GrantedAccess,
@ -6760,11 +6754,11 @@ NtFilterToken(
&FilteredTokenHandle); &FilteredTokenHandle);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
DPRINT1("NtFilterToken(): Failed to insert the filtered token object into the handle (Status 0x%lx)\n", Status); DPRINT1("NtFilterToken(): Failed to insert the filtered token (Status 0x%lx)\n", Status);
goto Quit; goto Quit;
} }
/* And give it to the caller once we're done */ /* And return it to the caller once we're done */
_SEH2_TRY _SEH2_TRY
{ {
*NewTokenHandle = FilteredTokenHandle; *NewTokenHandle = FilteredTokenHandle;
@ -6777,17 +6771,15 @@ NtFilterToken(
_SEH2_END; _SEH2_END;
Quit: Quit:
/* Unlock and dereference the token */ /* Dereference the token */
SepReleaseTokenLock(Token);
ObDereferenceObject(Token); ObDereferenceObject(Token);
/* Release all the stuff we've captured */ /* Release all the captured data */
if (CapturedSids != NULL) if (CapturedSids != NULL)
{ {
SeReleaseSidAndAttributesArray(CapturedSids, SeReleaseSidAndAttributesArray(CapturedSids,
PreviousMode, PreviousMode,
TRUE); TRUE);
CapturedSids = NULL;
} }
if (CapturedPrivileges != NULL) if (CapturedPrivileges != NULL)
@ -6795,7 +6787,6 @@ Quit:
SeReleaseLuidAndAttributesArray(CapturedPrivileges, SeReleaseLuidAndAttributesArray(CapturedPrivileges,
PreviousMode, PreviousMode,
TRUE); TRUE);
CapturedPrivileges = NULL;
} }
if (CapturedRestrictedSids != NULL) if (CapturedRestrictedSids != NULL)
@ -6803,7 +6794,6 @@ Quit:
SeReleaseSidAndAttributesArray(CapturedRestrictedSids, SeReleaseSidAndAttributesArray(CapturedRestrictedSids,
PreviousMode, PreviousMode,
TRUE); TRUE);
CapturedRestrictedSids = NULL;
} }
return Status; return Status;