PRB: Privileges Are Unexpectedly Removed from the Access Token (326256)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • the operating system: Microsoft Windows Server 2003

This article was previously published under Q326256

SYMPTOMS

Processes that are running on Windows Server 2003 can unexpectedly cause to be removed privileges that are granted by an access token that they opened. As a result, the process and any processes that it spawns may receive the error 1314 ERROR_PRIVILEGE_NOT_HELD when they try to execute any privileged operations that require the removed privileges (for example, shutting down the system).

Other processes that are spawned by the user account are not affected.

CAUSE

This problem occurs only when a process calls the AdjustTokenPrivileges() function with the DisableAllPrivileges parameter set to FALSE (0) and the NewState parameter pointing to a buffer that was not initialized.

If the memory to which NewState points has a bit set at a location that corresponds to the SE_PRIVILEGE_REMOVED bit in the Attributes field of any LUID_AND_ATTRIBUTES structure, the corresponding privilege is permanently removed from the token.

RESOLUTION

To prevent this problem, always zero the memory buffer that is pointed to by NewState, and then fill it out with the correct count of privileges to be modified, and with one initialized LUID_AND_ATTRIBUTES structure for each privilege that is to be modified.

MORE INFORMATION

To understand when privileges may be unintentionally removed, you must understand the format of the NewState parameter and the operation of AdjustTokenPrivileges in response to DisableAllPrivileges and NewState.

Because the remainder of this article discusses AdjustTokenPrivileges and the format of its parameters in detail, the declarations and definitions are provided here for your reference:
BOOL AdjustTokenPrivileges(
  HANDLE TokenHandle, 
  BOOL DisableAllPrivileges, 
  PTOKEN_PRIVILEGES NewState, 
  DWORD BufferLength, 
  PTOKEN_PRIVILEGES PreviousState, 
  PDWORD ReturnLength
);
typedef struct _TOKEN_PRIVILEGES {
  DWORD PrivilegeCount;
  LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;

typedef struct _LUID_AND_ATTRIBUTES {
  LUID Luid;
  DWORD Attributes;
} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;
				
The NewState parameter points to a caller-supplied buffer that holds a TOKEN_PRIVILEGES structure. This structure contains a count and an array of LUID_AND_ATTRIBUTES structures, each of which specifies a privilege and its attributes. The Attributes field is a bit mask of flags that can be combined (these flags are defined in the Winnt.h file):
SE_PRIVILEGE_ENABLED
SE_PRIVILEGE_ENABLED_BY_DEFAULT
SE_PRIVILEGE_USED_FOR_ACCESS
				
If the DisableAllPrivileges parameter is FALSE, AdjustTokenPrivileges uses the array of LUID_AND_ATTRIBUTES pointed to by NewState to modify the state of each corresponding privilege in the access token. The state is adjusted depending on the value specified in the Attributes field. For example, if the SE_PRIVILEGE_ENABLED attribute is set for a privilege, AdjustTokenPrivileges enables that privilege in the access token. Otherwise, it disables the privilege.

New Windows Server 2003 Feature

Windows Server 2003 provides a new way for processes to remove privileges from an access token permanently, as though they had never been granted. This feature allows a process at startup, or any other time, to remove a privilege from an access token that it opened. This capability was added so that processes can comply with the principle of least privilege.

For example, a process that impersonates users should voluntarily remove any privileges that it does not need or that impersonated users should not be able to acquire under any circumstances. This was implemented by adding a new attribute to the Attributes field:
#define SE_PRIVILEGE_REMOVED            (0X00000004L)
				
If SE_PRIVILEGE_REMOVED is set in the attribute for a privilege, AdjustTokenPrivileges will permanently remove the corresponding privilege from the privileges in the access token. Additionally, SE_PRIVILEGE_REMOVED supercedes SE_PRIVILEGE_ENABLED; if both are present in the Attributes field for a privilege, the privilege will be removed from the token.

After a privilege is removed from the access token, attempts to re-enable it result in the warning 1300 STATUS_NOT_ALL_ASSIGNED, as though the privilege had never existed. Privilege checks for removed privileges result in the error code 1314 STATUS_PRIVILEGE_NOT_HELD, similar to disabled or missing privileges.

Therefore, if a process passes an uninitialized buffer for the NewState parameter, and if the memory has the bit set at a location that corresponds to the SE_PRIVILEGE_REMOVED bit in the Attributes field of any LUID_AND_ATTRIBUTES structure, the corresponding privilege will be removed.

REFERENCES

For additional information about SE_PRIVILEGE_REMOVED, see the Platform Software Development Kit documentation.

Modification Type:MinorLast Reviewed:10/30/2003
Keywords:kbAPI kbKernBase kbprb kbSecurity KB326256