Skip to content

[API Proposal]: Add properties to WindowsIdentity to get info about process user #81784

Description

@mconnew

Background and motivation

In CoreWCF, we need to obtain a SecurityIdentifier for the process logon SID to apply permissions to a shared memory object to prevent other processes running under the same user from writing to the shared memory. This requires two steps, first getting the process token, and then getting the Token Information for the TokenGroup info for the process token. The first step can be done with managed API's today, but it's cumbersome and undocumented. I can get a process token handle which is usable for passing to the Win32 GetTokenInformation api by calling WindowsIdentity.GetCurrent(TokenAccessLevels.Query).AccessToken. The problem is if you are running on a thread where impersonation is being used, WindowsIdentity.GetCurrent().AccessToken returns the thread token handle which represents the impersonated user. This can't be used to get the token information needed to retrieve the process logon SID. You have to un-impersonate by passing SafeAccessTokenHandle.InvalidHandle to WindowsIdentity.RunImpersonated:

WindowsIdentity.RunImpersonated(SafeAccessTokenHandle.InvalidHandle, () =>
            {
                var processIdentity = WindowsIdentity.GetCurrent(TokenAccessLevels.Query);
                SafeAccessTokenHandle processToken = processIdentity.AccessToken; 
                // Use the processToken in calls to GetTokenInformation
            }

As I mentioned, passing SafeAccessTokenHandle.InvalidHandle is not documented. This is a side effect of implementation of other methods and properties which require reverting to the process user before calling some win32 api's to get various pieces of information. This is because a WindowsIdentity instance that is being accessed may represent a different user than the current thread identity, so internally it unimpersonates by passing an invalid handle before calling the win32 apis. I had to discover this "feature" by reading the code when I was looking for a way to achieve what I wanted through existing managed apis.

Once I have the WindowsIdentity for the process logon user, I need to get the SID. This is returned by the GetTokenInformation win32 api which is used by WindowsIdeneity.Groups, but it explicitly filters out SIDs with the attribute SE_GROUP_LOGON_ID here.

API Proposal

namespace System.Security.Principal;

public class WindowsIdentity
{
    public static WindowsIdentity GetCurrentProcess();
    public static WindowsIdentity GetCurrentProcess(System.Security.Principal.TokenAccessLevels desiredAccess);
    public System.Security.Principal.SecurityIdentifier? LogonSession { get; }
}

API Usage

// Retrieving the Process Logon SID
SecurityIdentifier processLogonSid = WindowsIdentity.GetCurrentProcess(TokenAccessLevels.Query).LogonSession;
// Use the logonSid as part of a dacl eg for a named pipe
DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, capacity);
int accessRights = UnsafeNativeMethods.GENERIC_WRITE | UnsafeNativeMethods.GENERIC_READ;
dacl.AddAccess(AccessControlType.Allow, processLogonSid, accessRights, InheritanceFlags.None, PropagationFlags.None);

Alternative Designs

Getting the process identity could also be added to the Process class. This might be more generally useful as WindowsIdentity is Windows only, and the Process class I believe works on Linux too. To keep it cross platform, the property could be declared as IIdentity so that on Linux it could return a GenericIdentity which can provide the user name. This is how NegotiateStream handles the RemoteIdentity property in a cross platform way.

namespace System.Diagnostics;

public class Process
{
    public System.Security.Principal.IIdentity? Identity { get; }
}

You would still need the LogonSession property on WindowsIdentity to to get the logon session SID. You would then retrieve the logon side like this:

WindowsIdentity processIdentity = Process.GetCurrentProcess().Identity as WindowsIdentity;
SecurityIdentifier processLogonSid = processIdentity.LogonSession;

This is slightly less capable in one way as you can't specify the TokenAccessLevels, but there is a value of MaximumAllowed which should prevent any exceptions being thrown requesting access beyond what is allowed. In another way, it's more flexible as you can query other processes besides your own.

Risks

No risks as far as I'm aware. All new functionality would be lazily executed so there is no need for any performance regression. No change in behavior for existing apis.

Metadata

Metadata

Assignees

Labels

api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Securityneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions