#pragma alloc_text(INIT, SepInitializeTokenImplementation)
#endif
+#include <ntlsa.h>
+
+typedef struct _TOKEN_AUDIT_POLICY_INFORMATION
+{
+ ULONG PolicyCount;
+ struct
+ {
+ ULONG Category;
+ UCHAR Value;
+ } Policies[1];
+} TOKEN_AUDIT_POLICY_INFORMATION, *PTOKEN_AUDIT_POLICY_INFORMATION;
+
/* GLOBALS ********************************************************************/
POBJECT_TYPE SeTokenObjectType = NULL;
TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}};
LUID SeSystemAuthenticationId = SYSTEM_LUID;
+LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID;
static GENERIC_MAPPING SepTokenMapping = {
TOKEN_READ,
ICI_SQ_SAME( 0, 0, 0),
/* TokenUser */
- ICI_SQ_SAME( sizeof(TOKEN_USER), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
+ ICI_SQ_SAME( sizeof(TOKEN_USER), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
/* TokenGroups */
- ICI_SQ_SAME( sizeof(TOKEN_GROUPS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
+ ICI_SQ_SAME( sizeof(TOKEN_GROUPS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
/* TokenPrivileges */
- ICI_SQ_SAME( sizeof(TOKEN_PRIVILEGES), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
+ ICI_SQ_SAME( sizeof(TOKEN_PRIVILEGES), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
/* TokenOwner */
ICI_SQ_SAME( sizeof(TOKEN_OWNER), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
/* TokenPrimaryGroup */
/* TokenDefaultDacl */
ICI_SQ_SAME( sizeof(TOKEN_DEFAULT_DACL), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
/* TokenSource */
- ICI_SQ_SAME( sizeof(TOKEN_SOURCE), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
+ ICI_SQ_SAME( sizeof(TOKEN_SOURCE), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
/* TokenType */
ICI_SQ_SAME( sizeof(TOKEN_TYPE), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
/* TokenImpersonationLevel */
ICI_SQ_SAME( sizeof(SECURITY_IMPERSONATION_LEVEL), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
/* TokenStatistics */
- ICI_SQ_SAME( sizeof(TOKEN_STATISTICS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ),
+ ICI_SQ_SAME( sizeof(TOKEN_STATISTICS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
/* TokenRestrictedSids */
ICI_SQ_SAME( sizeof(TOKEN_GROUPS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
/* TokenSessionId */
/* TokenGroupsAndPrivileges */
ICI_SQ_SAME( sizeof(TOKEN_GROUPS_AND_PRIVILEGES), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
/* TokenSessionReference */
- ICI_SQ_SAME( /* FIXME */0, sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
+ ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
/* TokenSandBoxInert */
ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
/* TokenAuditPolicy */
- ICI_SQ_SAME( /* FIXME */0, sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
+ ICI_SQ_SAME( /* FIXME */0, sizeof(ULONG), ICIF_QUERY | ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
/* TokenOrigin */
- ICI_SQ_SAME( sizeof(TOKEN_ORIGIN), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
+ ICI_SQ_SAME( sizeof(TOKEN_ORIGIN), sizeof(ULONG), ICIF_QUERY | ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
};
/* FUNCTIONS *****************************************************************/
}
/* FIXME: Check if every privilege that is present in either token is also present in the other one */
+ DPRINT1("FIXME: Pretending tokens are equal!\n");
+ IsEqual = TRUE;
}
*Equal = IsEqual;
return STATUS_SUCCESS;
}
+static
+VOID
+SepUpdateSinglePrivilegeFlagToken(
+ _Inout_ PTOKEN Token,
+ _In_ ULONG Index)
+{
+ ULONG TokenFlag;
+ NT_ASSERT(Index < Token->PrivilegeCount);
+
+ /* The high part of all values we are interested in is 0 */
+ if (Token->Privileges[Index].Luid.HighPart != 0)
+ {
+ return;
+ }
+
+ /* Check for certain privileges to update flags */
+ if (Token->Privileges[Index].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
+ {
+ TokenFlag = TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ }
+ else if (Token->Privileges[Index].Luid.LowPart == SE_BACKUP_PRIVILEGE)
+ {
+ TokenFlag = TOKEN_HAS_BACKUP_PRIVILEGE;
+ }
+ else if (Token->Privileges[Index].Luid.LowPart == SE_RESTORE_PRIVILEGE)
+ {
+ TokenFlag = TOKEN_HAS_RESTORE_PRIVILEGE;
+ }
+ else if (Token->Privileges[Index].Luid.LowPart == SE_IMPERSONATE_PRIVILEGE)
+ {
+ TokenFlag = TOKEN_HAS_IMPERSONATE_PRIVILEGE;
+ }
+ else
+ {
+ /* Nothing to do */
+ return;
+ }
+
+ /* Check if the specified privilege is enabled */
+ if (Token->Privileges[Index].Attributes & SE_PRIVILEGE_ENABLED)
+ {
+ /* It is enabled, so set the flag */
+ Token->TokenFlags |= TokenFlag;
+ }
+ else
+ {
+ /* Is is disabled, so remove the flag */
+ Token->TokenFlags &= ~TokenFlag;
+ }
+}
+
+static
+VOID
+SepUpdatePrivilegeFlagsToken(
+ _Inout_ PTOKEN Token)
+{
+ ULONG i;
+
+ /* Loop all privileges */
+ for (i = 0; i < Token->PrivilegeCount; i++)
+ {
+ /* Updates the flags dor this privilege */
+ SepUpdateSinglePrivilegeFlagToken(Token, i);
+ }
+}
+
+static
+VOID
+SepRemovePrivilegeToken(
+ _Inout_ PTOKEN Token,
+ _In_ ULONG Index)
+{
+ ULONG MoveCount;
+ NT_ASSERT(Index < Token->PrivilegeCount);
+
+ /* Calculate the number of trailing privileges */
+ MoveCount = Token->PrivilegeCount - Index - 1;
+ if (MoveCount != 0)
+ {
+ /* Move them one location ahead */
+ RtlMoveMemory(&Token->Privileges[Index],
+ &Token->Privileges[Index + 1],
+ MoveCount * sizeof(LUID_AND_ATTRIBUTES));
+ }
+
+ /* Update privilege count */
+ Token->PrivilegeCount--;
+}
+
VOID
NTAPI
SepFreeProxyData(PVOID ProxyData)
PAGED_CODE();
if (NewToken->TokenType != TokenPrimary) return(STATUS_BAD_TOKEN_TYPE);
- if (NewToken->TokenInUse) return(STATUS_TOKEN_ALREADY_IN_USE);
+ if (NewToken->TokenInUse)
+ {
+ BOOLEAN IsEqual;
+ NTSTATUS Status;
+
+ /* Maybe we're trying to set the same token */
+ OldToken = PsReferencePrimaryToken(Process);
+ if (OldToken == NewToken)
+ {
+ /* So it's a nop. */
+ *OldTokenP = OldToken;
+ return STATUS_SUCCESS;
+ }
+
+ Status = SepCompareTokens(OldToken, NewToken, &IsEqual);
+ if (!NT_SUCCESS(Status))
+ {
+ *OldTokenP = NULL;
+ PsDereferencePrimaryToken(OldToken);
+ return Status;
+ }
+
+ if (!IsEqual)
+ {
+ *OldTokenP = NULL;
+ PsDereferencePrimaryToken(OldToken);
+ return STATUS_TOKEN_ALREADY_IN_USE;
+ }
+ /* Silently return STATUS_SUCCESS but do not set the new token,
+ * as it's already in use elsewhere. */
+ *OldTokenP = OldToken;
+ return STATUS_SUCCESS;
+ }
/* Mark new token in use */
NewToken->TokenInUse = 1;
}
}
- /* Loop all privileges */
- for (i = 0; i < PrivilegeCount; i++)
- {
- /* For optimization, check for change notify and impersonate rights */
- if (((RtlEqualLuid(&Privileges[i].Luid, &SeChangeNotifyPrivilege)) &&
- (Privileges[i].Attributes & SE_PRIVILEGE_ENABLED)))
- {
- /* Remember token has traverse */
- TokenFlags |= TOKEN_HAS_TRAVERSE_PRIVILEGE;
- }
- }
-
Status = ZwAllocateLocallyUniqueId(&TokenId);
if (!NT_SUCCESS(Status))
return Status;
(PVOID*)&AccessToken);
if (!NT_SUCCESS(Status))
{
- DPRINT1("ObCreateObject() failed (Status %lx)\n");
+ DPRINT1("ObCreateObject() failed (Status %lx)\n", Status);
return Status;
}
for (i = 0; i < GroupCount; i++)
uLength += RtlLengthSid(Groups[i].Sid);
+ // FIXME: should use the object itself
AccessToken->UserAndGroups = ExAllocatePoolWithTag(PagedPool,
uLength,
TAG_TOKEN_USERS);
if (!NT_SUCCESS(Status))
goto done;
+ // FIXME: should use the object itself
uLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
+ if (uLength == 0) uLength = sizeof(PVOID);
AccessToken->Privileges = ExAllocatePoolWithTag(PagedPool,
uLength,
TAG_TOKEN_PRIVILAGES);
if (!NT_SUCCESS(Status))
goto done;
- AccessToken->DefaultDacl = ExAllocatePoolWithTag(PagedPool,
- DefaultDacl->AclSize,
- TAG_TOKEN_ACL);
- if (AccessToken->DefaultDacl == NULL)
+ /* Update privilege flags */
+ SepUpdatePrivilegeFlagsToken(AccessToken);
+
+ if (DefaultDacl != NULL)
{
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto done;
- }
+ // FIXME: should use the object itself
+ AccessToken->DefaultDacl = ExAllocatePoolWithTag(PagedPool,
+ DefaultDacl->AclSize,
+ TAG_TOKEN_ACL);
+ if (AccessToken->DefaultDacl == NULL)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto done;
+ }
- RtlCopyMemory(AccessToken->DefaultDacl,
- DefaultDacl,
- DefaultDacl->AclSize);
+ RtlCopyMemory(AccessToken->DefaultDacl,
+ DefaultDacl,
+ DefaultDacl->AclSize);
+ }
+ else
+ {
+ AccessToken->DefaultDacl = NULL;
+ }
if (!SystemToken)
{
{
if (AccessToken)
{
- if (AccessToken->UserAndGroups)
- ExFreePoolWithTag(AccessToken->UserAndGroups, TAG_TOKEN_USERS);
-
- if (AccessToken->Privileges)
- ExFreePoolWithTag(AccessToken->Privileges, TAG_TOKEN_PRIVILAGES);
-
- if (AccessToken->DefaultDacl)
- ExFreePoolWithTag(AccessToken->DefaultDacl, TAG_TOKEN_ACL);
-
+ /* Dereference the token, the delete procedure will clean up */
ObDereferenceObject(AccessToken);
}
}
PTOKEN Token;
ULONG RequiredLength;
KPROCESSOR_MODE PreviousMode;
- NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS Status;
PAGED_CODE();
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
}
_SEH2_END;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
}
_SEH2_END;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
}
_SEH2_END;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
}
_SEH2_END;
break;
}
+ case TokenSessionReference:
+ {
+ ULONG SessionReference;
+
+ _SEH2_TRY
+ {
+ /* Buffer size was already verified, no need to check here again */
+ SessionReference = *(PULONG)TokenInformation;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
+ }
+ _SEH2_END;
+
+ if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
+ {
+ Status = STATUS_PRIVILEGE_NOT_HELD;
+ goto Cleanup;
+ }
+
+ /* Check if it is 0 */
+ if (SessionReference == 0)
+ {
+ /* Atomically set the flag in the token */
+ RtlInterlockedSetBits(&Token->TokenFlags,
+ TOKEN_SESSION_NOT_REFERENCED);
+ }
+
+ break;
+
+ }
+
+
+ case TokenAuditPolicy:
+ {
+ PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation =
+ (PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation;
+ SEP_AUDIT_POLICY AuditPolicy;
+ ULONG i;
+
+ _SEH2_TRY
+ {
+ ProbeForRead(PolicyInformation,
+ FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION,
+ Policies[PolicyInformation->PolicyCount]),
+ sizeof(ULONG));
+
+ /* Loop all policies in the structure */
+ for (i = 0; i < PolicyInformation->PolicyCount; i++)
+ {
+ /* Set the corresponding bits in the packed structure */
+ switch (PolicyInformation->Policies[i].Category)
+ {
+ case AuditCategorySystem:
+ AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryLogon:
+ AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryObjectAccess:
+ AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryPrivilegeUse:
+ AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryDetailedTracking:
+ AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryPolicyChange:
+ AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryAccountManagement:
+ AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryDirectoryServiceAccess:
+ AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value;
+ break;
+
+ case AuditCategoryAccountLogon:
+ AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value;
+ break;
+ }
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
+ }
+ _SEH2_END;
+
+ if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
+ PreviousMode))
+ {
+ Status = STATUS_PRIVILEGE_NOT_HELD;
+ break;
+ }
+
+ /* Lock the token */
+ SepAcquireTokenLockExclusive(Token);
+
+ /* Set the new audit policy */
+ Token->AuditPolicy = AuditPolicy;
+
+ /* Unlock the token */
+ SepReleaseTokenLock(Token);
+
+ break;
+ }
+
+ case TokenOrigin:
+ {
+ TOKEN_ORIGIN TokenOrigin;
+
+ _SEH2_TRY
+ {
+ /* Copy the token origin */
+ TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
+ }
+ _SEH2_END;
+
+ /* Check for TCB privilege */
+ if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
+ {
+ Status = STATUS_PRIVILEGE_NOT_HELD;
+ break;
+ }
+
+ /* Lock the token */
+ SepAcquireTokenLockExclusive(Token);
+
+ /* Check if there is no token origin set yet */
+ if ((Token->OriginatingLogonSession.LowPart == 0) &&
+ (Token->OriginatingLogonSession.HighPart == 0))
+ {
+ /* Set the token origin */
+ Token->OriginatingLogonSession =
+ TokenOrigin.OriginatingLogonSession;
+ }
+
+ /* Unlock the token */
+ SepReleaseTokenLock(Token);
+
+ break;
+ }
+
default:
{
- Status = STATUS_NOT_IMPLEMENTED;
+ DPRINT1("Invalid TokenInformationClass: 0x%lx\n",
+ TokenInformationClass);
+ Status = STATUS_INVALID_INFO_CLASS;
break;
}
}
-
+Cleanup:
ObDereferenceObject(Token);
}
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status);
+ }
+
return Status;
}
return(STATUS_NOT_IMPLEMENTED);
}
+
+static
+ULONG
+SepAdjustPrivileges(
+ _Inout_ PTOKEN Token,
+ _In_ BOOLEAN DisableAllPrivileges,
+ _In_opt_ PLUID_AND_ATTRIBUTES NewState,
+ _In_ ULONG NewStateCount,
+ _Out_opt_ PTOKEN_PRIVILEGES PreviousState,
+ _In_ BOOLEAN ApplyChanges,
+ _Out_ PULONG ChangedPrivileges)
+{
+ ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes;
+
+ /* Count the found privileges and those that need to be changed */
+ PrivilegeCount = 0;
+ ChangeCount = 0;
+
+ /* Loop all privileges in the token */
+ for (i = 0; i < Token->PrivilegeCount; i++)
+ {
+ /* Shall all of them be disabled? */
+ if (DisableAllPrivileges)
+ {
+ /* The new attributes are the old ones, but disabled */
+ NewAttributes = Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
+ }
+ else
+ {
+ /* Otherwise loop all provided privileges */
+ for (j = 0; j < NewStateCount; j++)
+ {
+ /* Check if this is the LUID we are looking for */
+ if (RtlEqualLuid(&Token->Privileges[i].Luid, &NewState[j].Luid))
+ {
+ DPRINT("Found privilege\n");
+
+ /* Copy SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED */
+ NewAttributes = NewState[j].Attributes;
+ NewAttributes &= (SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED);
+ NewAttributes |= Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Check if we didn't find the privilege */
+ if (j == NewStateCount)
+ {
+ /* Continue with the token's next privilege */
+ continue;
+ }
+ }
+
+ /* We found a privilege, count it */
+ PrivilegeCount++;
+
+ /* Does the privilege need to be changed? */
+ if (Token->Privileges[i].Attributes != NewAttributes)
+ {
+ /* Does the caller want the old privileges? */
+ if (PreviousState != NULL)
+ {
+ /* Copy the old privilege */
+ PreviousState->Privileges[ChangeCount] = Token->Privileges[i];
+ }
+
+ /* Does the caller want to apply the changes? */
+ if (ApplyChanges)
+ {
+ /* Shall we remove the privilege? */
+ if (NewAttributes & SE_PRIVILEGE_REMOVED)
+ {
+ /* Set the token as disabled and update flags for it */
+ Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
+ SepUpdateSinglePrivilegeFlagToken(Token, i);
+
+ /* Remove the privilege */
+ SepRemovePrivilegeToken(Token, i);
+
+ /* Fix the running index */
+ i--;
+
+ /* Continue with next */
+ continue;
+ }
+
+ /* Set the new attributes and update flags */
+ Token->Privileges[i].Attributes = NewAttributes;
+ SepUpdateSinglePrivilegeFlagToken(Token, i);
+ }
+
+ /* Increment the change count */
+ ChangeCount++;
+ }
+ }
+
+ /* Set the number of saved privileges */
+ if (PreviousState != NULL)
+ PreviousState->PrivilegeCount = ChangeCount;
+
+ /* Return the number of changed privileges */
+ *ChangedPrivileges = ChangeCount;
+
+ /* Check if we missed some */
+ if (!DisableAllPrivileges && (PrivilegeCount < NewStateCount))
+ {
+ return STATUS_NOT_ALL_ASSIGNED;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
/*
* @implemented
*/
-NTSTATUS NTAPI
-NtAdjustPrivilegesToken(IN HANDLE TokenHandle,
- IN BOOLEAN DisableAllPrivileges,
- IN PTOKEN_PRIVILEGES NewState,
- IN ULONG BufferLength,
- OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL,
- OUT PULONG ReturnLength OPTIONAL)
+_Must_inspect_result_
+__kernel_entry
+NTSTATUS
+NTAPI
+NtAdjustPrivilegesToken(
+ _In_ HANDLE TokenHandle,
+ _In_ BOOLEAN DisableAllPrivileges,
+ _In_opt_ PTOKEN_PRIVILEGES NewState,
+ _In_ ULONG BufferLength,
+ _Out_writes_bytes_to_opt_(BufferLength,*ReturnLength)
+ PTOKEN_PRIVILEGES PreviousState,
+ _When_(PreviousState!=NULL, _Out_) PULONG ReturnLength)
{
PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
KPROCESSOR_MODE PreviousMode;
ULONG CapturedLength = 0;
ULONG NewStateSize = 0;
ULONG ChangeCount;
+ ULONG RequiredLength;
PTOKEN Token;
- ULONG i;
- ULONG j;
NTSTATUS Status;
-
PAGED_CODE();
- DPRINT ("NtAdjustPrivilegesToken() called\n");
+ DPRINT("NtAdjustPrivilegesToken() called\n");
/* Fail, if we do not disable all privileges but NewState is NULL */
if (DisableAllPrivileges == FALSE && NewState == NULL)
/* Probe NewState */
if (DisableAllPrivileges == FALSE)
{
- ProbeForRead(NewState,
- sizeof(TOKEN_PRIVILEGES),
- sizeof(ULONG));
+ /* First probe the header */
+ ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG));
CapturedCount = NewState->PrivilegeCount;
- NewStateSize = (ULONG)sizeof(TOKEN_PRIVILEGES) +
- ((CapturedCount - ANYSIZE_ARRAY) * (ULONG)sizeof(LUID_AND_ATTRIBUTES));
+ NewStateSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedCount]);
- ProbeForRead(NewState,
- NewStateSize,
- sizeof(ULONG));
+ ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
}
/* Probe PreviousState and ReturnLength */
if (PreviousState != NULL)
{
- ProbeForWrite(PreviousState,
- BufferLength,
- sizeof(ULONG));
-
- ProbeForWrite(ReturnLength,
- sizeof(ULONG),
- sizeof(ULONG));
+ ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
+ ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
}
else
{
+ /* This is kernel mode, we trust the caller */
if (DisableAllPrivileges == FALSE)
CapturedCount = NewState->PrivilegeCount;
}
+ /* Do we need to capture the new state? */
if (DisableAllPrivileges == FALSE)
{
_SEH2_TRY
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
NULL);
if (!NT_SUCCESS(Status))
{
- DPRINT1 ("Failed to reference token (Status %lx)\n", Status);
+ DPRINT1("Failed to reference token (Status %lx)\n", Status);
/* Release the captured privileges */
if (CapturedPrivileges != NULL)
return Status;
}
- /* Count the privileges that need to be changed */
- ChangeCount = 0;
- for (i = 0; i < Token->PrivilegeCount; i++)
- {
- if (DisableAllPrivileges)
- {
- if (Token->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED)
- {
- DPRINT("Privilege enabled\n");
+ /* Lock the token */
+ ExEnterCriticalRegionAndAcquireResourceExclusive(Token->TokenLock);
- ChangeCount++;
- }
- }
- else
- {
- for (j = 0; j < CapturedCount; j++)
- {
- if (Token->Privileges[i].Luid.LowPart == CapturedPrivileges[j].Luid.LowPart &&
- Token->Privileges[i].Luid.HighPart == CapturedPrivileges[j].Luid.HighPart)
- {
- DPRINT("Found privilege\n");
-
- if ((Token->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED) !=
- (CapturedPrivileges[j].Attributes & SE_PRIVILEGE_ENABLED))
- {
- DPRINT("Attributes differ\n");
- DPRINT("Current attributes %lx New attributes %lx\n",
- Token->Privileges[i].Attributes,
- CapturedPrivileges[j].Attributes);
+ /* Count the privileges that need to be changed, do not apply them yet */
+ Status = SepAdjustPrivileges(Token,
+ DisableAllPrivileges,
+ CapturedPrivileges,
+ CapturedCount,
+ NULL,
+ FALSE,
+ &ChangeCount);
- ChangeCount++;
- }
- }
- }
- }
- }
-
- /*
- * Return the required buffer size and
- * check if the available buffer is large enough
- */
+ /* Check if the caller asked for the previous state */
if (PreviousState != NULL)
{
- ULONG RequiredLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
- ((ChangeCount - ANYSIZE_ARRAY) * (ULONG)sizeof(LUID_AND_ATTRIBUTES));
+ /* Calculate the required length */
+ RequiredLength = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[ChangeCount]);
/* Try to return the required buffer length */
_SEH2_TRY
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- /* Dereference the token */
- ObDereferenceObject(Token);
-
- /* Release the captured privileges */
- if (CapturedPrivileges != NULL)
- SeReleaseLuidAndAttributesArray(CapturedPrivileges,
- PreviousMode,
- TRUE);
-
- /* Return the exception code */
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ /* Do cleanup and return the exception code */
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
}
_SEH2_END;
/* Fail, if the buffer length is smaller than the required length */
if (BufferLength < RequiredLength)
{
- /* Dereference the token */
- ObDereferenceObject(Token);
-
- /* Release the captured privileges */
- if (CapturedPrivileges != NULL)
- SeReleaseLuidAndAttributesArray(CapturedPrivileges,
- PreviousMode,
- TRUE);
-
- return STATUS_BUFFER_TOO_SMALL;
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto Cleanup;
}
}
- /* Change the privilege attributes */
- ChangeCount = 0;
+ /* Now enter SEH, since we might return the old privileges */
_SEH2_TRY
{
- for (i = 0; i < Token->PrivilegeCount; i++)
- {
- if (DisableAllPrivileges == TRUE)
- {
- if (Token->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED)
- {
- DPRINT("Privilege enabled\n");
-
- /* Save the current privilege */
- if (PreviousState != NULL)
- {
- PreviousState->Privileges[ChangeCount].Luid = Token->Privileges[i].Luid;
- PreviousState->Privileges[ChangeCount].Attributes = Token->Privileges[i].Attributes;
- }
-
- /* Disable the current privlege */
- Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
-
- ChangeCount++;
- }
- }
- else
- {
- for (j = 0; j < CapturedCount; j++)
- {
- if (Token->Privileges[i].Luid.LowPart == CapturedPrivileges[j].Luid.LowPart &&
- Token->Privileges[i].Luid.HighPart == CapturedPrivileges[j].Luid.HighPart)
- {
- DPRINT("Found privilege\n");
-
- /* Check whether the attributes differ */
- if ((Token->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED) !=
- (CapturedPrivileges[j].Attributes & SE_PRIVILEGE_ENABLED))
- {
- DPRINT("Attributes differ\n");
- DPRINT("Current attributes %lx New attributes %lx\n",
- Token->Privileges[i].Attributes,
- CapturedPrivileges[j].Attributes);
-
- /* Save the current privilege */
- if (PreviousState != NULL)
- {
- PreviousState->Privileges[ChangeCount].Luid = Token->Privileges[i].Luid;
- PreviousState->Privileges[ChangeCount].Attributes = Token->Privileges[i].Attributes;
- }
-
- /* Update the current privlege */
- Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
- Token->Privileges[i].Attributes |=
- (CapturedPrivileges[j].Attributes & SE_PRIVILEGE_ENABLED);
- DPRINT("New attributes %lx\n",
- Token->Privileges[i].Attributes);
-
- ChangeCount++;
- }
- }
- }
- }
- }
-
- /* Set the number of saved privileges */
- if (PreviousState != NULL)
- PreviousState->PrivilegeCount = ChangeCount;
+ /* This time apply the changes */
+ Status = SepAdjustPrivileges(Token,
+ DisableAllPrivileges,
+ CapturedPrivileges,
+ CapturedCount,
+ PreviousState,
+ TRUE,
+ &ChangeCount);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- /* Dereference the token */
- ObDereferenceObject(Token);
-
- /* Release the captured privileges */
- if (CapturedPrivileges != NULL)
- SeReleaseLuidAndAttributesArray(CapturedPrivileges,
- PreviousMode,
- TRUE);
-
- /* Return the exception code */
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ /* Do cleanup and return the exception code */
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
}
_SEH2_END;
- /* Set the status */
- Status = (ChangeCount < CapturedCount) ? STATUS_NOT_ALL_ASSIGNED : STATUS_SUCCESS;
-
- /* Dereference the token */
- ObDereferenceObject (Token);
+Cleanup:
+ /* Unlock and dereference the token */
+ ExReleaseResourceAndLeaveCriticalRegion(Token->TokenLock);
+ ObDereferenceObject(Token);
/* Release the captured privileges */
if (CapturedPrivileges != NULL)
TRUE);
DPRINT ("NtAdjustPrivilegesToken() done\n");
-
return Status;
}
NTSTATUS
NTAPI
-NtCreateToken(OUT PHANDLE TokenHandle,
- IN ACCESS_MASK DesiredAccess,
- IN 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 PTOKEN_OWNER TokenOwner,
- IN PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
- IN PTOKEN_DEFAULT_DACL TokenDefaultDacl,
- IN PTOKEN_SOURCE TokenSource)
+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)
{
HANDLE hToken;
KPROCESSOR_MODE PreviousMode;
- ULONG nTokenPrivileges = 0;
+ ULONG PrivilegeCount, GroupCount;
+ PSID OwnerSid, PrimaryGroupSid;
+ PACL DefaultDacl;
LARGE_INTEGER LocalExpirationTime = {{0, 0}};
+ LUID LocalAuthenticationId;
+ TOKEN_SOURCE LocalTokenSource;
+ SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
+ PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
+ PSID_AND_ATTRIBUTES CapturedUser = NULL;
+ PSID_AND_ATTRIBUTES CapturedGroups = NULL;
+ PSID CapturedOwnerSid = NULL;
+ PSID CapturedPrimaryGroupSid = NULL;
+ PACL CapturedDefaultDacl = NULL;
+ ULONG PrivilegesLength, UserLength, GroupsLength;
NTSTATUS Status;
PAGED_CODE();
_SEH2_TRY
{
ProbeForWriteHandle(TokenHandle);
+
+ if (ObjectAttributes != NULL)
+ {
+ ProbeForRead(ObjectAttributes,
+ sizeof(OBJECT_ATTRIBUTES),
+ sizeof(ULONG));
+ LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
+ }
+
ProbeForRead(AuthenticationId,
sizeof(LUID),
sizeof(ULONG));
+ LocalAuthenticationId = *AuthenticationId;
+
LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
+
ProbeForRead(TokenUser,
sizeof(TOKEN_USER),
sizeof(ULONG));
+
ProbeForRead(TokenGroups,
sizeof(TOKEN_GROUPS),
sizeof(ULONG));
+ GroupCount = TokenGroups->GroupCount;
+
ProbeForRead(TokenPrivileges,
sizeof(TOKEN_PRIVILEGES),
sizeof(ULONG));
- ProbeForRead(TokenOwner,
- sizeof(TOKEN_OWNER),
- sizeof(ULONG));
+ PrivilegeCount = TokenPrivileges->PrivilegeCount;
+
+ if (TokenOwner != NULL)
+ {
+ ProbeForRead(TokenOwner,
+ sizeof(TOKEN_OWNER),
+ sizeof(ULONG));
+ OwnerSid = TokenOwner->Owner;
+ }
+ else
+ {
+ OwnerSid = NULL;
+ }
+
ProbeForRead(TokenPrimaryGroup,
sizeof(TOKEN_PRIMARY_GROUP),
sizeof(ULONG));
- ProbeForRead(TokenDefaultDacl,
- sizeof(TOKEN_DEFAULT_DACL),
- sizeof(ULONG));
+ PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
+
+ if (TokenDefaultDacl != NULL)
+ {
+ ProbeForRead(TokenDefaultDacl,
+ sizeof(TOKEN_DEFAULT_DACL),
+ sizeof(ULONG));
+ DefaultDacl = TokenDefaultDacl->DefaultDacl;
+ }
+ else
+ {
+ DefaultDacl = NULL;
+ }
+
ProbeForRead(TokenSource,
sizeof(TOKEN_SOURCE),
sizeof(ULONG));
- nTokenPrivileges = TokenPrivileges->PrivilegeCount;
+ LocalTokenSource = *TokenSource;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
- _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ return _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
- nTokenPrivileges = TokenPrivileges->PrivilegeCount;
+ if (ObjectAttributes != NULL)
+ LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
+ LocalAuthenticationId = *AuthenticationId;
LocalExpirationTime = *ExpirationTime;
+ GroupCount = TokenGroups->GroupCount;
+ PrivilegeCount = TokenPrivileges->PrivilegeCount;
+ OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
+ PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
+ DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
+ LocalTokenSource = *TokenSource;
+ }
+
+ /* Check token type */
+ if ((TokenType < TokenPrimary) ||
+ (TokenType > TokenImpersonation))
+ {
+ return STATUS_BAD_TOKEN_TYPE;
+ }
+
+ /* Capture the user SID and attributes */
+ Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
+ 1,
+ PreviousMode,
+ NULL,
+ 0,
+ PagedPool,
+ FALSE,
+ &CapturedUser,
+ &UserLength);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ /* Capture the groups SID and attributes array */
+ Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
+ GroupCount,
+ PreviousMode,
+ NULL,
+ 0,
+ PagedPool,
+ FALSE,
+ &CapturedGroups,
+ &GroupsLength);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
}
+ /* Capture privileges */
+ Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
+ PrivilegeCount,
+ PreviousMode,
+ NULL,
+ 0,
+ PagedPool,
+ FALSE,
+ &CapturedPrivileges,
+ &PrivilegesLength);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ /* Capture the token owner SID */
+ if (TokenOwner != NULL)
+ {
+ Status = SepCaptureSid(OwnerSid,
+ PreviousMode,
+ PagedPool,
+ FALSE,
+ &CapturedOwnerSid);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+ }
+
+ /* Capture the token primary group SID */
+ Status = SepCaptureSid(PrimaryGroupSid,
+ PreviousMode,
+ PagedPool,
+ FALSE,
+ &CapturedPrimaryGroupSid);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ /* Capture DefaultDacl */
+ if (DefaultDacl != NULL)
+ {
+ Status = SepCaptureAcl(DefaultDacl,
+ PreviousMode,
+ NonPagedPool,
+ FALSE,
+ &CapturedDefaultDacl);
+ }
+
+ /* Call the internal function */
Status = SepCreateToken(&hToken,
PreviousMode,
DesiredAccess,
ObjectAttributes,
TokenType,
- ((PSECURITY_QUALITY_OF_SERVICE)(ObjectAttributes->SecurityQualityOfService))->ImpersonationLevel,
- AuthenticationId,
+ LocalSecurityQos.ImpersonationLevel,
+ &LocalAuthenticationId,
&LocalExpirationTime,
- &TokenUser->User,
- TokenGroups->GroupCount,
- TokenGroups->Groups,
+ CapturedUser,
+ GroupCount,
+ CapturedGroups,
0, // FIXME: Should capture
- nTokenPrivileges,
- TokenPrivileges->Privileges,
- TokenOwner->Owner,
- TokenPrimaryGroup->PrimaryGroup,
- TokenDefaultDacl->DefaultDacl,
- TokenSource,
+ PrivilegeCount,
+ CapturedPrivileges,
+ CapturedOwnerSid,
+ CapturedPrimaryGroupSid,
+ CapturedDefaultDacl,
+ &LocalTokenSource,
FALSE);
if (NT_SUCCESS(Status))
{
_SEH2_END;
}
+Cleanup:
+
+ /* Release what we captured */
+ SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
+ SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
+ SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
+ SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
+ SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
+ SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
+
return Status;
}
PACL Dacl = NULL;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
+ BOOLEAN RestoreImpersonation = FALSE;
PAGED_CODE();
if (OpenAsSelf)
{
- PsDisableImpersonation(PsGetCurrentThread(), &ImpersonationState);
+ RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(),
+ &ImpersonationState);
}
if (CopyOnOpen)
if (Dacl) ExFreePoolWithTag(Dacl, TAG_TOKEN_ACL);
- if (OpenAsSelf)
+ if (RestoreImpersonation)
{
PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState);
}