1523 lines
46 KiB
C
1523 lines
46 KiB
C
|
/*++
|
|||
|
|
|||
|
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 "pch.h"
|
|||
|
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
|
|||
|
#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,
|
|||
|
OUT 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 = 0;
|
|||
|
PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
|
|||
|
ULONG CapturedPrivilegesLength = 0;
|
|||
|
|
|||
|
ULONG LocalReturnLength = 0;
|
|||
|
ULONG ChangeCount = 0;
|
|||
|
BOOLEAN ChangesMade = FALSE;
|
|||
|
|
|||
|
ULONG ParameterLength = 0;
|
|||
|
|
|||
|
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) {
|
|||
|
|
|||
|
ProbeForReadSmallStructure(
|
|||
|
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
|
|||
|
SeTokenObjectType, // 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,
|
|||
|
OUT 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 = 0;
|
|||
|
PSID_AND_ATTRIBUTES CapturedGroups = NULL;
|
|||
|
ULONG CapturedGroupsLength = 0;
|
|||
|
|
|||
|
ULONG LocalReturnLength;
|
|||
|
ULONG ChangeCount;
|
|||
|
BOOLEAN ChangesMade;
|
|||
|
PSID SidBuffer = NULL;
|
|||
|
|
|||
|
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) {
|
|||
|
ProbeForReadSmallStructure(
|
|||
|
NewState,
|
|||
|
sizeof(TOKEN_GROUPS),
|
|||
|
sizeof(ULONG)
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(PreviousState)) {
|
|||
|
|
|||
|
ProbeForWrite(
|
|||
|
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
|
|||
|
SeTokenObjectType, // 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)(LongAlignPtr(
|
|||
|
(PCHAR)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 );
|
|||
|
if (ARGUMENT_PRESENT(CapturedGroups)) {
|
|||
|
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;
|
|||
|
(*ChangesMade) = FALSE;
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
//
|
|||
|
// Check if the caller wants the privilege removed. We give
|
|||
|
// SE_PRIVILEGE_REMOVED a preferance over any other flags.
|
|||
|
//
|
|||
|
|
|||
|
if ( (SepArrayPrivilegeAttributes( NewState, NewIndex ) &
|
|||
|
SE_PRIVILEGE_REMOVED) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Change, if necessary. There is no need to save the
|
|||
|
// previous state. This is a one way journey.
|
|||
|
//
|
|||
|
|
|||
|
if (MakeChanges) {
|
|||
|
|
|||
|
//
|
|||
|
// if this is one of the recorded privileges, then
|
|||
|
// delete its corresponding bit in TokenFlags
|
|||
|
//
|
|||
|
|
|||
|
if (RtlEqualLuid(&CurrentPrivilege.Luid,
|
|||
|
&SeChangeNotifyPrivilege)) {
|
|||
|
Token->TokenFlags &= ~TOKEN_HAS_TRAVERSE_PRIVILEGE;
|
|||
|
} else if (RtlEqualLuid(&CurrentPrivilege.Luid,
|
|||
|
&SeBackupPrivilege)) {
|
|||
|
Token->TokenFlags &= ~TOKEN_HAS_BACKUP_PRIVILEGE;
|
|||
|
} else if (RtlEqualLuid(&CurrentPrivilege.Luid,
|
|||
|
&SeRestorePrivilege)) {
|
|||
|
Token->TokenFlags &= ~TOKEN_HAS_RESTORE_PRIVILEGE;
|
|||
|
} else if (RtlEqualLuid( &CurrentPrivilege.Luid,
|
|||
|
&SeImpersonatePrivilege)) {
|
|||
|
Token->TokenFlags &= ~TOKEN_HAS_IMPERSONATE_PRIVILEGE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Swap this privilege with the last one.
|
|||
|
//
|
|||
|
|
|||
|
if (OldIndex + 1 != Token->PrivilegeCount) {
|
|||
|
LUID_AND_ATTRIBUTES TempLuidAttr;
|
|||
|
TempLuidAttr = Token->Privileges[OldIndex];
|
|||
|
Token->Privileges[OldIndex] = Token->Privileges[Token->PrivilegeCount-1];
|
|||
|
Token->Privileges[Token->PrivilegeCount-1] = TempLuidAttr;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We just lost a privilege. Make note of it.
|
|||
|
//
|
|||
|
|
|||
|
Token->PrivilegeCount--;
|
|||
|
OldIndex--;
|
|||
|
(*ChangesMade) = TRUE;
|
|||
|
|
|||
|
|
|||
|
} //endif make changes
|
|||
|
|
|||
|
//
|
|||
|
// Note: Do NOT increment the number of changes
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Check if there is a state change from/to enabled to/from
|
|||
|
// disabled
|
|||
|
//
|
|||
|
|
|||
|
} else 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
|
|||
|
// Note that Backup and Restore privileges do not
|
|||
|
// care about Enabled/Disabled state.
|
|||
|
//
|
|||
|
|
|||
|
if (RtlEqualLuid(&CurrentPrivilege.Luid,
|
|||
|
&SeChangeNotifyPrivilege)) {
|
|||
|
Token->TokenFlags ^= TOKEN_HAS_TRAVERSE_PRIVILEGE;
|
|||
|
} else if ( RtlEqualLuid( &CurrentPrivilege.Luid,
|
|||
|
&SeImpersonatePrivilege)) {
|
|||
|
Token->TokenFlags ^= TOKEN_HAS_IMPERSONATE_PRIVILEGE ;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} //endif make changes
|
|||
|
|
|||
|
//
|
|||
|
// increment the number of changes
|
|||
|
//
|
|||
|
|
|||
|
(*ChangeCount) += 1;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} // 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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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));
|
|||
|
|
|||
|
if ( EnableGroup || DisableGroup ) {
|
|||
|
|
|||
|
SidLength = SeLengthSid( CurrentGroup.Sid );
|
|||
|
SidLength = (ULONG)LongAlignSize(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_PTR)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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure group is not deny-only
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (SepTokenGroupAttributes(Token,OldIndex) &
|
|||
|
SE_GROUP_USE_FOR_DENY_ONLY ) {
|
|||
|
return STATUS_CANT_ENABLE_DENY_ONLY;
|
|||
|
}
|
|||
|
|
|||
|
SidLength = SeLengthSid( CurrentGroup.Sid );
|
|||
|
SidLength = (ULONG)LongAlignSize(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_PTR)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;
|
|||
|
}
|