diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/se/tokenadj.c | 1447 |
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; +} |