GetTokenInformation(TokenRestrictedDeviceGroups) returns ERROR_INVALID_PARAMETER (87)

2026-10-16T11:48:18.32+00:00

Problem

On Windows x64, I am investigating the supported user-mode contract for GetTokenInformation with TokenRestrictedDeviceGroups (TOKEN_INFORMATION_CLASS value 38).

A primary access token is opened with TOKEN_QUERY. Calls to GetTokenInformation return FALSE, with GetLastError() captured immediately as ERROR_INVALID_PARAMETER (87).

Attempts already made

TokenInformation = NULL, TokenInformationLength = 0.

A non-null, 8-byte-aligned, 24-byte TOKEN_GROUPS buffer.

Both approaches returned error 87.

The Win32 documentation describes TokenRestrictedDeviceGroups as returning TOKEN_GROUPS, while native-interface documentation identifies this information class as reserved for system use.

I do not assume that error 87 means the collection is empty, that the class is unsupported, or that the token has no restricted device groups.

Questions

Is TokenRestrictedDeviceGroups supported for user-mode GetTokenInformation calls? Which Windows versions, token types, access rights, and buffer requirements apply?

What is the documented meaning of ERROR_INVALID_PARAMETER (87) for this information class? Can it indicate an unsupported or inapplicable information class?

Is there a supported Windows API that provides a complete, lossless readback of restricted-device-group SIDs and their attributes from a primary token?

Is there a documented mechanism to prove exact preservation of these SIDs and attributes across three primary tokens: an original caller token, a dedicated carrier token, and a worker process token?

How can an application positively prove that a token has an empty restricted-device-group collection, rather than inferring emptiness from an error or an access-check-equivalence result?

If class 38 is not supported for this purpose, what Microsoft-supported mechanism provides equivalent verification guarantees?

Security requirement

The application must preserve exact token identity and security attributes while performing narrowly scoped privilege removal. It must reject unverified token state rather than treating an unsupported query or generic token equivalence as proof of exact restricted-device-group preservation.

I am looking for a documented and supported solution, including the appropriate validation and failure-handling contract.

Thank you.

Windows development | Windows API - Win32

1 answer

Sort by: Most helpful
  1. AI answer

    2026-10-16T11:57:21.7033333+00:00

    The documented public contract is insufficient to treat a class-38 failure as emptiness or to prove lossless restricted-device-group preservation across distinct tokens; the application must reject that state or obtain a class-specific support statement from Microsoft.

    Status of TokenRestrictedDeviceGroups

    Microsoft’s two API surfaces conflict:

    • The Win32 TOKEN_INFORMATION_CLASS documentation says TokenRestrictedDeviceGroups returns a TOKEN_GROUPS containing the token’s restricted device groups and associates the enumeration with GetTokenInformation.^1^
    • The WDK/native enumeration says the same class is reserved for system use.^2^

    The available documentation does not resolve:

    • Supported Windows versions or builds for this particular query
    • Applicable token types or token states
    • Whether a NULL sizing call is supported for this class
    • The representation of an empty result
    • Class-specific failure conditions

    The general GetTokenInformation contract requires TOKEN_QUERY for every class except TokenSource, which requires TOKEN_QUERY_SOURCE. It also says that if the output pointer is NULL, its length must be zero—but that requirement does not guarantee that every information class supports a NULL sizing probe.^3^

    Therefore, TOKEN_QUERY is the documented general access right, but there is no documented class-38 contract establishing additional rights, token prerequisites, versions, or reliable buffer behavior.

    Meaning of error 87

    ERROR_INVALID_PARAMETER means only “The parameter is incorrect.” Microsoft notes that system error codes are generic and must be interpreted in the context of the API that returned them.^4^

    No cited GetTokenInformation documentation defines error 87 specifically for class 38. Consequently, it does not establish any of these conditions:

    • The collection is empty
    • The token has no restricted device groups
    • The buffer is too small
    • The token type is inapplicable
    • Class 38 is unsupported

    At the native interface, invalid or unavailable information classes can produce STATUS_INVALID_INFO_CLASS, while insufficient storage is separately represented by STATUS_BUFFER_TOO_SMALL with the required length returned.^5^ That does not document how every native status is surfaced through the Win32 class-38 call, so error 87 cannot safely be reverse-mapped to one specific cause.

    Buffer implications

    A fixed 24-byte buffer is not a general TOKEN_GROUPS buffer. TOKEN_GROUPS contains a count followed by a variable-length array of SID_AND_ATTRIBUTES; each entry contains a SID pointer and attributes.^6^ A successful self-contained result may require storage for the structure, all array entries, alignment/padding, and SID bodies.

    The native NtQueryInformationToken documentation requires 32-bit alignment for its output structures.^5^ That alignment statement is documented for the native function and should not be treated as an additional class-specific Win32 guarantee.

    Because the initial zero-length call already returns error 87 without a documented required length, allocating a larger speculative buffer cannot establish support or emptiness.

    No documented lossless substitute in these APIs

    The documented alternatives do not provide the requested proof:

    • TokenRestrictedSids reports the restricting SID list of a restricted token; it is not documented as the restricted device-group list.^2^
    • TOKEN_ACCESS_INFORMATION contains information needed for access checks, including SID hashes, but is not documented as a lossless serialization of restricted device-group SIDs and attributes.^7^
    • NtCompareTokens proves equivalence with respect to AccessCheck; it does not promise byte-for-byte or attribute-for-attribute token identity.^8^
    • CompareObjectHandles proves that two handles refer to the same kernel object, not that two distinct token objects have identical contents.^9^

    The documentation supplied here identifies no public API that provides an independent, complete class-38 readback or exact comparison guarantee across three distinct token objects. That is not proof that no specialized Microsoft mechanism exists; it means the public contracts cited here do not establish one.

    Supported fail-closed design

    For narrowly scoped privilege removal:

    1. Create one private primary-token duplicate rather than modifying the caller’s shared token. DuplicateTokenEx supports creation of a primary token.^10^
    2. Open/request the duplicate with at least TOKEN_QUERY, TOKEN_DUPLICATE, TOKEN_ADJUST_PRIVILEGES, and the process-creation rights required later.
    3. Snapshot TokenPrivileges using the normal size-query/allocation/retrieval sequence.
    4. Remove only the intended privileges with AdjustTokenPrivileges.
    5. Require:
      • a nonzero function result;
      • GetLastError() == ERROR_SUCCESS, not ERROR_NOT_ALL_ASSIGNED;
      • a successful post-operation TokenPrivileges query;
      • exact expected before/after privilege differences.

    Privilege removal is irreversible for that token, and AdjustTokenPrivileges cannot add privileges.^11^

    1. Launch the worker with that resulting primary token. CreateProcessAsUser requires the token handle to have TOKEN_QUERY, TOKEN_DUPLICATE, and TOKEN_ASSIGN_PRIMARY.^12^
    2. Treat the privilege comparison as proving only the intended privilege mutation—not preservation of every token field.
    3. If exact restricted-device-group preservation is mandatory, abort whenever class 38 cannot be successfully and unambiguously queried. Do not substitute error 87, NtCompareTokens, access-check tests, or matching ordinary/restricted SID lists.

    For a production contract requiring class-38 verification, open a Microsoft support request with the exact Windows build, architecture, token provenance, minimal reproduction, returned ReturnLength, and all three token-creation transitions. Request an authoritative statement covering class-38 availability, empty-result representation, buffer contract, and preservation semantics.


    References

    1. TOKEN_INFORMATION_CLASS enumeration (winnt.h)
    2. TOKEN_INFORMATION_CLASS enumeration (ntifs.h)
    3. GetTokenInformation function (securitybaseapi.h)
    4. System Error Codes (0-499)
    5. NtQueryInformationToken function (ntifs.h)
    6. TOKEN_GROUPS structure (winnt.h)
    7. TOKEN_ACCESS_INFORMATION structure (winnt.h)
    8. NtCompareTokens function
    9. CompareObjectHandles function (handleapi.h)
    10. DuplicateTokenEx function (securitybaseapi.h)
    11. AdjustTokenPrivileges function (securitybaseapi.h)
    12. CreateProcessAsUserW function (processthreadsapi.h)
    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.