summaryrefslogtreecommitdiffstats
path: root/private/ntos/se/tokenadj.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/se/tokenadj.c1447
1 files changed, 1447 insertions, 0 deletions
diff --git a/private/ntos/se/tokenadj.c b/private/ntos/se/tokenadj.c
new file mode 100644
index 000000000..fb89fa5e9
--- /dev/null
+++ b/private/ntos/se/tokenadj.c
@@ -0,0 +1,1447 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tokenadj.c
+
+Abstract:
+
+ This module implements the services that perform individual adjustments
+ on token objects.
+
+Author:
+
+ Jim Kelly (JimK) 15-June-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "sep.h"
+#include "seopaque.h"
+#include "tokenp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtAdjustPrivilegesToken)
+#pragma alloc_text(PAGE,NtAdjustGroupsToken)
+#pragma alloc_text(PAGE,SepAdjustPrivileges)
+#pragma alloc_text(PAGE,SepAdjustGroups)
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Token Object Routines & Methods //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+NtAdjustPrivilegesToken (
+ IN HANDLE TokenHandle,
+ IN BOOLEAN DisableAllPrivileges,
+ IN PTOKEN_PRIVILEGES NewState OPTIONAL,
+ IN ULONG BufferLength OPTIONAL,
+ IN PTOKEN_PRIVILEGES PreviousState OPTIONAL,
+ OUT PULONG ReturnLength
+ )
+
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to disable or enable privileges in the
+ specified token. The absence of some of the privileges listed to
+ be changed won't effect the successful modification of the
+ privileges that are in the token. The previous enabled/disabled
+ state of changed privileges may optionally be capture (for
+ resetting later).
+
+ TOKEN_ADJUST_PRIVILEGES access is required to enable or disable
+ privileges in a token.
+
+
+Arguments:
+
+ TokenHandle - Provides a handle to the token to operate on.
+
+ DisableAllPrivileges - This boolean parameter may be
+ used to disable all privileges assigned to the token. If
+ this parameter is specified as TRUE, then the NewState parameter is
+ ignored.
+
+ NewState - This (optional) parameter points to a TOKEN_PRIVILEGES
+ data structure containing the privileges whose states are to
+ be adjusted (disabled or enabled). Only the Enabled flag of
+ the attributes associated with each privilege is used. It
+ provides the new value that is to be assigned to the
+ privilege in the token.
+
+ BufferLength - This optional parameter indicates the length (in
+ bytes) of the PreviousState buffer. This value must be
+ provided if the PreviousState parameter is provided.
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any privileges actually changed by this
+ request. This information is formated as a TOKEN_PRIVILEGES
+ data structure which may be passed as the NewState parameter
+ in a subsequent call to this routine to restore the original
+ state of those privilges. TOKEN_QUERY access is needed to
+ use this parameter.
+
+ If this buffer does not contain enough space to receive the
+ complete list of modified privileges, then no privilege
+ states are changed and STATUS_BUFFER_TOO_SMALL is returned.
+ In this case, the ReturnLength OUT parameter will
+ contain the actual number of bytes needed to hold the
+ information.
+
+ ReturnLength - Indicates the actual number of bytes needed to
+ contain the previous privilege state information.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+Return Value:
+
+ STATUS_SUCCESS - The service successfully completed the requested
+ operation.
+
+ STATUS_NOT_ALL_ASSIGNED - This NT_SUCCESS severity return status
+ indicates that not all the specified privileges are currently
+ assigned to the caller. All specified privileges that are
+ currently assigned have been successfully adjusted.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the optional buffer provided
+ to receive the previous states of changed privileges wasn't
+ large enough to receive that information. No changes to
+ privilege states has been made. The number of bytes needed
+ to hold the state change information is returned via the
+ ReturnLength parameter.
+
+ STATUS_INVALID_PARAMETER - Indicates neither the DisableAllPrivileges
+ parameter was specified as true, nor was an explicit NewState
+ provided.
+
+--*/
+
+{
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ PTOKEN Token;
+
+ ACCESS_MASK DesiredAccess;
+
+ ULONG CapturedPrivilegeCount;
+ PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
+ ULONG CapturedPrivilegesLength;
+
+ ULONG LocalReturnLength;
+ ULONG ChangeCount;
+ BOOLEAN ChangesMade;
+
+ ULONG ParameterLength;
+
+ PAGED_CODE();
+
+ //
+ // The semantics of the PreviousState parameter leads to a two-pass
+ // approach to adjusting privileges. The first pass simply checks
+ // to see which privileges will change and counts them. This allows
+ // the amount of space needed to be calculated and returned. If
+ // the caller's PreviousState return buffer is not large enough, then
+ // an error is returned without making any modifications. Otherwise,
+ // a second pass is made to actually make the changes.
+ //
+ //
+
+ if (!DisableAllPrivileges && !ARGUMENT_PRESENT(NewState)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Get previous processor mode and probe parameters if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ try {
+
+ //
+ // Make sure we can see all of the new state
+ //
+
+ if (!DisableAllPrivileges) {
+
+ ProbeForRead(
+ NewState,
+ sizeof(TOKEN_PRIVILEGES),
+ sizeof(ULONG)
+ );
+
+ CapturedPrivilegeCount = NewState->PrivilegeCount;
+ ParameterLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
+ ( (CapturedPrivilegeCount - ANYSIZE_ARRAY) *
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) );
+
+ ProbeForRead(
+ NewState,
+ ParameterLength,
+ sizeof(ULONG)
+ );
+
+ }
+
+
+ //
+ // Check the PreviousState buffer for writeability
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ ProbeForWrite(
+ PreviousState,
+ BufferLength,
+ sizeof(ULONG)
+ );
+
+ ProbeForWriteUlong(ReturnLength);
+ }
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ if (!DisableAllPrivileges) {
+
+ CapturedPrivilegeCount = NewState->PrivilegeCount;
+ }
+ }
+
+
+
+ //
+ // Capture NewState if passed.
+ //
+
+ if (!DisableAllPrivileges) {
+
+ try {
+
+
+ Status = SeCaptureLuidAndAttributesArray(
+ (NewState->Privileges),
+ CapturedPrivilegeCount,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedPrivileges,
+ &CapturedPrivilegesLength
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+
+ }
+
+ }
+
+
+ //
+ // Reference the token object and validate the caller's right
+ // to adjust the privileges.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ DesiredAccess = (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY);
+ } else {
+ DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
+ }
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ DesiredAccess, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenWriteLock( Token );
+
+ //
+ // First pass through the privileges list - just count the changes
+ //
+
+
+ Status = SepAdjustPrivileges(
+ Token,
+ FALSE, // Don't make changes this pass
+ DisableAllPrivileges,
+ CapturedPrivilegeCount,
+ CapturedPrivileges,
+ PreviousState,
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ try {
+
+ (*ReturnLength) = LocalReturnLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return GetExceptionCode();
+ }
+
+ }
+
+
+ //
+ // Make sure there is enough room to return any requested
+ // information.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ if (LocalReturnLength > BufferLength) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ //
+ // Second pass through the privileges list - Make the changes.
+ //
+ // Note that the internal routine attempts to write the previous
+ // state directly to the caller's buffer - and so may get an exception.
+ //
+
+ try {
+
+ Status = SepAdjustPrivileges(
+ Token,
+ TRUE, // Make the changes this pass
+ DisableAllPrivileges,
+ CapturedPrivilegeCount,
+ CapturedPrivileges,
+ PreviousState,
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->PrivilegeCount = ChangeCount;
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenWriteLock( Token, TRUE );
+ ObDereferenceObject( Token );
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+ return GetExceptionCode();
+
+ }
+
+
+ SepReleaseTokenWriteLock( Token, ChangesMade );
+ ObDereferenceObject( Token );
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NtAdjustGroupsToken (
+ IN HANDLE TokenHandle,
+ IN BOOLEAN ResetToDefault,
+ IN PTOKEN_GROUPS NewState OPTIONAL,
+ IN ULONG BufferLength OPTIONAL,
+ IN PTOKEN_GROUPS PreviousState OPTIONAL,
+ OUT PULONG ReturnLength
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to disable or enable groups in the specified
+ token. The absence of some of the groups listed to be changed
+ won't effect the successful modification of the groups that are in
+ the token. The previous enabled/disabled state of changed groups
+ may optionally be capture (for resetting later).
+
+ TOKEN_ADJUST_GROUPS access is required to enable or disable groups
+ in a token
+
+ Note that mandatory groups can not be disabled. An attempt
+ disable any mandatory groups will cause the call to fail, leaving
+ the state of all groups unchanged.
+
+
+Arguments:
+
+ TokenHandle - Provides a handle to the token to operate on.
+
+ ResetToDefault - The parameter indicates whether all the groups
+ in the token are to be reset to their default enabled/disabled
+ state.
+
+ NewState - This parameter points to a TOKEN_GROUPS data structure
+ containing the groups whose states are to be adjusted
+ (disabled or enabled). Only the Enabled flag of the
+ attributes associated with each group is used. It provides
+ the new value that is to be assigned to the group in the
+ token. If the ResetToDefault argument is specified as TRUE,
+ then this argument is ignored. Otherwise, it must be passed.
+
+ BufferLength - This optional parameter indicates the length (in
+ bytes) of the PreviousState buffer. This value must be
+ provided if the PreviousState parameter is provided.
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any groups actually changed by this
+ request. This information is formated as a TOKEN_GROUPS data
+ structure which may be passed as the NewState parameter in a
+ subsequent call to NtAdjustGroups to restore the original state
+ of those groups. TOKEN_QUERY access is needed to use this
+ parameter.
+
+ If this buffer does not contain enough space to receive the
+ complete list of modified groups, then no group states are
+ changed and STATUS_BUFFER_TOO_SMALL is returned. In this
+ case, the ReturnLength return parameter will contain the
+ actual number of bytes needed to hold the information.
+
+ ReturnLength - Indicates the actual number of bytes needed to
+ contain the previous group state information.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The service successfully completed the requested
+ operation.
+
+ STATUS_NOT_ALL_ASSIGNED - This NT_SUCCESS severity return status
+ indicates that not all the specified groups are currently
+ assigned to the caller. All specified groups that are
+ currently assigned have been successfully adjusted.
+
+ STATUS_CANT_DISABLE_MANDATORY - Indicates an attempt was made to
+ disable a mandatory group. The states of all groups remains
+ unchanged.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the optional buffer provided
+ to receive the previous states of changed group wasn't large
+ enough to receive that information. No changes to group
+ states has been made. The number of bytes needed to hold the
+ state change information is returned via the ReturnLength
+ parameter.
+
+ STATUS_INVALID_PARAMETER - Indicates neither the ResetToDefault
+ parameter was specified as true, nor was an explicit NewState
+ provided.
+
+--*/
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ PTOKEN Token;
+
+ ACCESS_MASK DesiredAccess;
+
+ ULONG CapturedGroupCount;
+ PSID_AND_ATTRIBUTES CapturedGroups = NULL;
+ ULONG CapturedGroupsLength;
+
+ ULONG LocalReturnLength;
+ ULONG ChangeCount;
+ BOOLEAN ChangesMade;
+ PSID SidBuffer;
+
+ PAGED_CODE();
+
+ //
+ // The semantics of the PreviousState parameter and the
+ // STATUS_CANT_DISABLE_MANDATORY completion status leads to a two-pass
+ // approach to adjusting groups. The first pass simply checks
+ // to see which groups will change and counts them. This allows
+ // the amount of space needed to be calculated and returned. If
+ // the caller's PreviousState return buffer is not large enough, or
+ // one of the specified groups is a mandatory group, then an error
+ // is returned without making any modifications. Otherwise, a second
+ // pass is made to actually make the changes.
+ //
+
+ if (!ResetToDefault && !ARGUMENT_PRESENT(NewState)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Get previous processor mode and probe parameters if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ try {
+
+ if (!ResetToDefault) {
+ ProbeForRead(
+ NewState,
+ sizeof(TOKEN_GROUPS),
+ sizeof(ULONG)
+ );
+ }
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ ProbeForRead(
+ PreviousState,
+ BufferLength,
+ sizeof(ULONG)
+ );
+
+ //
+ // This parameter is only used if PreviousState
+ // is present
+ //
+
+ ProbeForWriteUlong(ReturnLength);
+
+ }
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+ }
+
+ //
+ // Capture NewState.
+ //
+
+ if (!ResetToDefault) {
+
+ try {
+
+ CapturedGroupCount = NewState->GroupCount;
+ Status = SeCaptureSidAndAttributesArray(
+ &(NewState->Groups[0]),
+ CapturedGroupCount,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedGroups,
+ &CapturedGroupsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ } // endtry
+ } // endif !ResetToDefault
+
+
+ //
+ // Reference the token object and validate the caller's right
+ // to adjust the groups.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ DesiredAccess = (TOKEN_ADJUST_GROUPS | TOKEN_QUERY);
+ } else {
+ DesiredAccess = TOKEN_ADJUST_GROUPS;
+ }
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ DesiredAccess, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
+ }
+
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenWriteLock( Token );
+
+ //
+ // First pass through the groups list.
+ //
+ // This pass is always necessary for groups to make sure the caller
+ // isn't trying to do anything illegal to mandatory groups.
+ //
+
+ Status = SepAdjustGroups(
+ Token,
+ FALSE, // Don't make changes this pass
+ ResetToDefault,
+ CapturedGroupCount,
+ CapturedGroups,
+ PreviousState,
+ NULL, // Not returning SIDs this call
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ try {
+
+ (*ReturnLength) = LocalReturnLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray(
+ CapturedGroups,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return GetExceptionCode();
+ }
+ }
+
+ //
+ // Make sure we didn't encounter an error
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray(
+ CapturedGroups,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return Status;
+
+ }
+
+ //
+ // Make sure there is enough room to return requested information.
+ // Also go on to calculate where the SID values go.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ if (LocalReturnLength > BufferLength) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray(
+ CapturedGroups,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Calculate where the SIDs can be placed in the PreviousState
+ // buffer.
+ //
+
+ SidBuffer = (PSID)(LongAlign(
+ (ULONG)PreviousState + (ULONG)sizeof(TOKEN_GROUPS) +
+ (ChangeCount * (ULONG)sizeof(SID_AND_ATTRIBUTES)) -
+ (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES))
+ ) );
+
+ }
+
+ //
+ // Second pass through the groups list.
+ //
+
+ try {
+
+ Status = SepAdjustGroups(
+ Token,
+ TRUE, // Make changes in this pass
+ ResetToDefault,
+ CapturedGroupCount,
+ CapturedGroups,
+ PreviousState,
+ SidBuffer,
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->GroupCount = ChangeCount;
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ //SepFreeToken( Token, TRUE );
+ SepReleaseTokenWriteLock( Token, TRUE );
+ ObDereferenceObject( Token );
+ SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
+ return GetExceptionCode();
+
+ }
+
+ //SepFreeToken( Token, ChangesMade );
+ SepReleaseTokenWriteLock( Token, ChangesMade );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+SepAdjustPrivileges(
+ IN PTOKEN Token,
+ IN BOOLEAN MakeChanges,
+ IN BOOLEAN DisableAllPrivileges,
+ IN ULONG PrivilegeCount OPTIONAL,
+ IN PLUID_AND_ATTRIBUTES NewState OPTIONAL,
+ OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL,
+ OUT PULONG ReturnLength,
+ OUT PULONG ChangeCount,
+ OUT PBOOLEAN ChangesMade
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to walk the privileges array in a token as a
+ result of a request to adjust privileges.
+
+ If the MakeChanges parameter is FALSE, this routine simply determines
+ what changes are needed and how much space is necessary to save the
+ current state of changed privileges.
+
+ If the MakeChanges parameter is TRUE, this routine will not only
+ calculate the space necessary to save the current state, but will
+ actually make the changes.
+
+ This routine makes the following assumptions:
+
+ 1) The token is locked for exclusive access.
+
+ 2) The PrivilegeCount and NewState parameters (if passed) are captured
+ and accesses to them will not result in access violations.
+
+ 4) Any access violations encountered may leave the request
+ partially completed. It is the calling routine's responsibility
+ to catch exceptions.
+
+ 5) The calling routine is responsible for inrementing the token's
+ ModifiedId field.
+
+Arguments:
+
+ Token - Pointer to the token to act upon.
+
+ MakeChanges - A boolean value indicating whether the changes should
+ actually be made, or just evaluated. A value of TRUE indicates
+ the changes should be made.
+
+ DisableAllPrivilegs - A boolean value indicating whether all privileges
+ are to be disabled, or only select, specified privileges. A value
+ of TRUE indicates all privileges are to be disabled.
+
+ PrivilegeCount - This parameter is required only if the NewState parameter
+ is used. In that case, this parameter indicates how many entries are
+ in the NewState parameter.
+
+ NewState - This parameter is ignored if the DisableAllPrivileges
+ argument is TRUE. If the DisableAllPrivileges argument is FALSE,
+ then this parameter must be provided and specifies the new state
+ to set privileges to (enabled or disabled).
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any privileges actually changed by this
+ request. This information is formated as a TOKEN_PRIVILEGES data
+ structure which may be passed as the NewState parameter in a
+ subsequent call to NtAdjustPrivileges to restore the original state
+ of those privileges. It is the caller's responsibility to make
+ sure this buffer is large enough to receive all the state
+ information.
+
+ ReturnLength - Points to a buffer to receive the number of bytes needed
+ to retrieve the previous state information of changed privileges.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+ ChangeCount - Points to a ULONG to receive the number of privileges
+ which were adjusted (or would be adjusted, if changes are made).
+
+ ChangesMade - Points to a boolean flag which is to receive an indication
+ as to whether any changes were made as a result of this call. This
+ is expected to be used to decide whether or not to increment the
+ token's ModifiedId field.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed sccessfully.
+
+ STATUS_NOT_ALL_ASSIGNED - Indicates not all the specified adjustments
+ have been made (or could be made, if update wasn't requested).
+
+--*/
+{
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+
+ ULONG OldIndex;
+ ULONG NewIndex;
+ BOOLEAN Found;
+ ULONG MatchCount = 0;
+
+ LUID_AND_ATTRIBUTES CurrentPrivilege;
+
+ PAGED_CODE();
+
+ //
+ // Walk through the privileges array to determine which need to be
+ // adjusted.
+ //
+
+ OldIndex = 0;
+ (*ChangeCount) = 0;
+
+ while (OldIndex < Token->PrivilegeCount) {
+
+ CurrentPrivilege = (Token->Privileges)[OldIndex];
+
+ if (DisableAllPrivileges) {
+
+ if (SepTokenPrivilegeAttributes(Token,OldIndex) &
+ SE_PRIVILEGE_ENABLED ) {
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->Privileges[(*ChangeCount)] =
+ CurrentPrivilege;
+ }
+
+ SepTokenPrivilegeAttributes(Token,OldIndex) &=
+ ~SE_PRIVILEGE_ENABLED;
+
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+ } // endif privilege enabled
+
+ } else {
+
+ //
+ // Selective adjustments - this is a little trickier
+ // Compare the current privilege to each of those in
+ // the NewState array. If a match is found, then adjust
+ // the current privilege appropriately.
+ //
+
+ NewIndex = 0;
+ Found = FALSE;
+
+ while ( (NewIndex < PrivilegeCount) && !Found) {
+
+ //
+ // Look for a comparison
+ //
+
+ if (RtlEqualLuid(&CurrentPrivilege.Luid,&NewState[NewIndex].Luid)) {
+
+ Found = TRUE;
+ MatchCount += 1;
+
+ if ( (SepArrayPrivilegeAttributes( NewState, NewIndex ) &
+ SE_PRIVILEGE_ENABLED)
+ !=
+ (SepTokenPrivilegeAttributes(Token,OldIndex) &
+ SE_PRIVILEGE_ENABLED) ) {
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->Privileges[(*ChangeCount)] =
+ CurrentPrivilege;
+ }
+
+ SepTokenPrivilegeAttributes(Token,OldIndex) &=
+ ~(SepTokenPrivilegeAttributes(Token,OldIndex)
+ & SE_PRIVILEGE_ENABLED);
+ SepTokenPrivilegeAttributes(Token,OldIndex) |=
+ (SepArrayPrivilegeAttributes(NewState,NewIndex)
+ & SE_PRIVILEGE_ENABLED);
+
+ //
+ // if this is SeChangeNotifyPrivilege, then
+ // change its corresponding bit in TokenFlags
+ //
+
+ if (RtlEqualLuid(&CurrentPrivilege.Luid,
+ &SeChangeNotifyPrivilege)) {
+ Token->TokenFlags ^= TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ }
+
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+
+ } // endif change needed
+
+ } // endif found
+
+ NewIndex += 1;
+
+ } // endwhile searching NewState
+
+ } // endelse
+
+ OldIndex += 1;
+
+ } // endwhile privileges in token
+
+ //
+ // If we disabled all privileges, then clear the TokenFlags flag
+ // corresponding to the SeChangeNotifyPrivilege privilege.
+ //
+
+
+ if (DisableAllPrivileges) {
+ Token->TokenFlags &= ~TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ }
+
+ //
+ // Set completion status appropriately if some not assigned
+ //
+
+ if (!DisableAllPrivileges) {
+
+ if (MatchCount < PrivilegeCount) {
+ CompletionStatus = STATUS_NOT_ALL_ASSIGNED;
+ }
+ }
+
+ //
+ // Indicate whether changes were made
+ //
+
+ if ((*ChangeCount) > 0 && MakeChanges) {
+ (*ChangesMade) = TRUE;
+ } else {
+ (*ChangesMade) = FALSE;
+ }
+
+ //
+ // Calculate the space needed to return previous state information
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ (*ReturnLength) = (ULONG)sizeof(TOKEN_PRIVILEGES) +
+ ((*ChangeCount) * (ULONG)sizeof(LUID_AND_ATTRIBUTES)) -
+ (ANYSIZE_ARRAY * (ULONG)sizeof(LUID_AND_ATTRIBUTES));
+ }
+
+ return CompletionStatus;
+}
+
+NTSTATUS
+SepAdjustGroups(
+ IN PTOKEN Token,
+ IN BOOLEAN MakeChanges,
+ IN BOOLEAN ResetToDefault,
+ IN ULONG GroupCount,
+ IN PSID_AND_ATTRIBUTES NewState OPTIONAL,
+ OUT PTOKEN_GROUPS PreviousState OPTIONAL,
+ OUT PSID SidBuffer OPTIONAL,
+ OUT PULONG ReturnLength,
+ OUT PULONG ChangeCount,
+ OUT PBOOLEAN ChangesMade
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to walk the groups array in a token as a
+ result of a request to adjust groups.
+
+ If the MakeChanges parameter is FALSE, this routine simply determines
+ what changes are needed and how much space is necessary to save the
+ current state of changed groups.
+
+ If the MakeChanges parameter is TRUE, this routine will not only
+ calculate the space necessary to save the current state, but will
+ actually make the changes.
+
+ This routine makes the following assumptions:
+
+ 1) The token is locked for exclusive access.
+
+ 2) The NewState parameter is captured and accesses
+ to it will not result in access violations.
+
+ 4) Any access violations encountered may leave the request
+ partially completed. It is the calling routine's responsibility
+ to catch exceptions.
+
+ 5) The calling routine is responsible for inrementing the token's
+ ModifiedId field.
+
+Arguments:
+
+ Token - Pointer to the token to act upon.
+
+ MakeChanges - A boolean value indicating whether the changes should
+ actually be made, or just evaluated. A value of TRUE indicates
+ the changes should be made.
+
+ ResetToDefault - Indicates that the groups are to be reset to their
+ default enabled/disabled state.
+
+ GroupCount - This parameter is required only if the NewState parameter
+ is used. In that case, this parameter indicates how many entries are
+ in the NewState parameter.
+
+ NewState - This parameter points to a SID_AND_ATTRIBUTES array
+ containing the groups whose states are to be adjusted
+ (disabled or enabled). Only the Enabled flag of the
+ attributes associated with each group is used. It provides
+ the new value that is to be assigned to the group in the
+ token. If the ResetToDefault argument is specified as TRUE,
+ then this argument is ignored. Otherwise, it must be passed.
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any groups actually changed by this
+ request. This information is formated as a TOKEN_GROUPS data
+ structure which may be passed as the NewState parameter in a
+ subsequent call to NtAdjustGroups to restore the original state
+ of those groups. It is the caller's responsibility to make
+ sure this buffer is large enough to receive all the state
+ information.
+
+ SidBuffer - Pointer to buffer to receive the SID values corresponding
+ to the groups returned in the PreviousState argument.
+
+ ReturnLength - Points to a buffer to receive the number of bytes needed
+ to retrieve the previous state information of changed privileges.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+ ChangeCount - Points to a ULONG to receive the number of groups
+ which were adjusted (or would be adjusted, if changes are made).
+
+ ChangesMade - Points to a boolean flag which is to receive an indication
+ as to whether any changes were made as a result of this call. This
+ is expected to be used to decide whether or not to increment the
+ token's ModifiedId field.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed sccessfully.
+
+ STATUS_NOT_ALL_ASSIGNED - Indicates not all the specified adjustments
+ have been made (or could be made, if update wasn't requested).
+
+ STATUS_CANT_DISABLE_MANDATORY - Not all adjustments were made (or could
+ be made, if update not requested) because an attempt was made to
+ disable a mandatory group. The state of the groups is left
+ in an underterministic state if update was requested.
+
+
+--*/
+{
+
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+
+ ULONG OldIndex;
+ ULONG NewIndex;
+ ULONG SidLength;
+ ULONG LocalReturnLength = 0;
+ PSID NextSid;
+ BOOLEAN Found;
+ ULONG MatchCount = 0;
+ BOOLEAN EnableGroup;
+ BOOLEAN DisableGroup;
+ ULONG TokenGroupAttributes;
+
+ SID_AND_ATTRIBUTES CurrentGroup;
+
+ PAGED_CODE();
+
+ //
+ // NextSid is used to copy group SID values if asked for previous state.
+ //
+
+ NextSid = SidBuffer;
+
+
+ //
+ // Walk through the groups array to determine which need to be
+ // adjusted.
+ //
+
+ OldIndex = 1; // Don't evaluate the 0th entry (user ID)
+ (*ChangeCount) = 0;
+
+ while (OldIndex < Token->UserAndGroupCount) {
+
+ CurrentGroup = Token->UserAndGroups[OldIndex];
+
+ if (ResetToDefault) {
+
+ TokenGroupAttributes = SepTokenGroupAttributes(Token,OldIndex);
+
+ //
+ // If the group is enabled by default and currently disabled,
+ // then we must enable it.
+ //
+
+ EnableGroup = (BOOLEAN)( (TokenGroupAttributes & SE_GROUP_ENABLED_BY_DEFAULT)
+ && !(TokenGroupAttributes & SE_GROUP_ENABLED));
+
+ //
+ // If the group is disabled by default and currently enabled,
+ // then we must disable it.
+ //
+
+ DisableGroup = (BOOLEAN)( !(TokenGroupAttributes & SE_GROUP_ENABLED_BY_DEFAULT)
+ && (TokenGroupAttributes & SE_GROUP_ENABLED));
+
+ //
+ // Blow up if it's a mandatory group that is not both
+ // enabled by default and enabled (SepCreateToken should
+ // make sure that this never happens).
+ //
+
+ ASSERT(!(TokenGroupAttributes & SE_GROUP_MANDATORY)
+ || (TokenGroupAttributes & (SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED)
+ == (SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED)));
+
+ if ( EnableGroup || DisableGroup ) {
+
+ SidLength = SeLengthSid( CurrentGroup.Sid );
+ SidLength = (ULONG)LongAlign(SidLength);
+ LocalReturnLength += SidLength;
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ (*(PreviousState)).Groups[(*ChangeCount)].Attributes =
+ CurrentGroup.Attributes;
+
+ (*(PreviousState)).Groups[(*ChangeCount)].Sid =
+ NextSid;
+
+ RtlCopySid( SidLength, NextSid, CurrentGroup.Sid );
+ NextSid = (PSID)((ULONG)NextSid + SidLength);
+ }
+
+ if (EnableGroup) {
+ SepTokenGroupAttributes(Token,OldIndex) |= SE_GROUP_ENABLED;
+ } else {
+ SepTokenGroupAttributes(Token,OldIndex) &= ~SE_GROUP_ENABLED;
+ }
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+ } // endif group enabled
+
+ } else {
+
+ //
+ // Selective adjustments - this is a little trickier
+ // Compare the current group to each of those in
+ // the NewState array. If a match is found, then adjust
+ // the current group appropriately.
+ //
+
+ NewIndex = 0;
+ Found = FALSE;
+
+ while ( (NewIndex < GroupCount) && !Found) {
+
+ //
+ // Look for a comparison
+ //
+
+ if (RtlEqualSid(
+ CurrentGroup.Sid,
+ NewState[NewIndex].Sid
+ ) ) {
+
+ Found = TRUE;
+ MatchCount += 1;
+
+
+ //
+ // See if it needs to be changed
+ //
+
+ if ( (SepArrayGroupAttributes( NewState, NewIndex ) &
+ SE_GROUP_ENABLED ) !=
+ (SepTokenGroupAttributes(Token,OldIndex) &
+ SE_GROUP_ENABLED ) ) {
+
+ //
+ // Make sure group is not mandatory
+ //
+
+ if (SepTokenGroupAttributes(Token,OldIndex) &
+ SE_GROUP_MANDATORY ) {
+ return STATUS_CANT_DISABLE_MANDATORY;
+ }
+
+ SidLength = SeLengthSid( CurrentGroup.Sid );
+ SidLength = (ULONG)LongAlign(SidLength);
+ LocalReturnLength += SidLength;
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->Groups[(*ChangeCount)].Attributes =
+ CurrentGroup.Attributes;
+
+ PreviousState->Groups[(*ChangeCount)].Sid =
+ NextSid;
+
+ RtlCopySid( SidLength, NextSid, CurrentGroup.Sid );
+
+ NextSid = (PSID)((ULONG)NextSid + SidLength);
+ }
+
+ SepTokenGroupAttributes(Token,OldIndex) &=
+ ~(SepTokenGroupAttributes(Token,OldIndex)
+ & SE_GROUP_ENABLED);
+ SepTokenGroupAttributes(Token,OldIndex) |=
+ (SepArrayGroupAttributes(NewState,NewIndex)
+ & SE_GROUP_ENABLED);
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+
+ } // endif change needed
+
+ } // endif found
+
+ NewIndex += 1;
+
+ } // endwhile searching NewState
+
+ } // endelse
+
+ OldIndex += 1;
+
+ } // endwhile more groups in token
+
+ //
+ // Set completion status appropriately if some not assigned
+ //
+
+ if (!ResetToDefault) {
+
+ if (MatchCount < GroupCount) {
+ CompletionStatus = STATUS_NOT_ALL_ASSIGNED;
+ }
+ }
+
+ //
+ // Indicate whether changes were made
+ //
+
+ if ((*ChangeCount) > 0 && MakeChanges) {
+ (*ChangesMade) = TRUE;
+ } else {
+ (*ChangesMade) = FALSE;
+ }
+
+ //
+ // Calculate the space needed to return previous state information
+ // (The SID lengths have already been added up in LocalReturnLength).
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ (*ReturnLength) = LocalReturnLength +
+ (ULONG)sizeof(TOKEN_GROUPS) +
+ ((*ChangeCount) * (ULONG)sizeof(SID_AND_ATTRIBUTES)) -
+ (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES));
+ }
+
+ return CompletionStatus;
+}