12

How does one retrieve the Windows Logon SID in C# .net? (not the user SID, but the unique new one for each session)

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Jos
  • 123
  • 1
  • 1
  • 5
  • 1
    In Windwos environment SID stands for Security Identifier, not Session Id. To obtain Session Id use `System.Diagnostics.Process.GetCurrentProcess().SessionId` For more details see [my answer here](http://stackoverflow.com/a/16942663/725903) – mistika Jun 05 '13 at 14:40

4 Answers4

8

I'm afraid you have to resort to using P/Invoke. There's an example how to do it at pinvoke.net (please see the bottom of the page):

Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenSessionId , TokenInformation , TokenInfLength , out TokenInfLength );

Please note that I changed the example by altering just one line, I replaced TOKEN_INFORMATION_CLASS.TokenUser with TOKEN_INFORMATION_CLASS.TokenSessionId which is exactly what you need.

Hope this helps.

Update: Here's the working (at least on my machine) code:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace LinqTest
{
    public class ClsLookupAccountName
    {
        public const uint SE_GROUP_LOGON_ID = 0xC0000000; // from winnt.h
        public const int TokenGroups = 2; // from TOKEN_INFORMATION_CLASS

        enum TOKEN_INFORMATION_CLASS
        {
            TokenUser = 1,
            TokenGroups,
            TokenPrivileges,
            TokenOwner,
            TokenPrimaryGroup,
            TokenDefaultDacl,
            TokenSource,
            TokenType,
            TokenImpersonationLevel,
            TokenStatistics,
            TokenRestrictedSids,
            TokenSessionId,
            TokenGroupsAndPrivileges,
            TokenSessionReference,
            TokenSandBoxInert,
            TokenAuditPolicy,
            TokenOrigin
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SID_AND_ATTRIBUTES
        {
            public IntPtr Sid;
            public uint Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct TOKEN_GROUPS
        {
            public int GroupCount;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
            public SID_AND_ATTRIBUTES[] Groups;
        };

        // Using IntPtr for pSID instead of Byte[]
        [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid);

        [DllImport("kernel32.dll")]
        static extern IntPtr LocalFree(IntPtr hMem);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool GetTokenInformation(
            IntPtr TokenHandle,
            TOKEN_INFORMATION_CLASS TokenInformationClass,
            IntPtr TokenInformation,
            int TokenInformationLength,
            out int ReturnLength);

        public static string GetLogonId()
        {
            int TokenInfLength = 0;
            // first call gets lenght of TokenInformation
            bool Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, IntPtr.Zero, TokenInfLength, out TokenInfLength);
            IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength);
            Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, TokenInformation, TokenInfLength, out TokenInfLength);

            if (!Result)
            {
                Marshal.FreeHGlobal(TokenInformation);
                return string.Empty;
            }

            string retVal = string.Empty;
            TOKEN_GROUPS groups = (TOKEN_GROUPS)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_GROUPS));
            int sidAndAttrSize = Marshal.SizeOf(new SID_AND_ATTRIBUTES());
            for (int i = 0; i < groups.GroupCount; i++)
            {
                SID_AND_ATTRIBUTES sidAndAttributes = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(
                    new IntPtr(TokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size), typeof(SID_AND_ATTRIBUTES));
                if ((sidAndAttributes.Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
                {
                    IntPtr pstr = IntPtr.Zero;
                    ConvertSidToStringSid(sidAndAttributes.Sid, out pstr);
                    retVal = Marshal.PtrToStringAuto(pstr);
                    LocalFree(pstr);
                    break;
                }
            }

            Marshal.FreeHGlobal(TokenInformation);
            return retVal;
        }
    }
}

N.B. I tested it on my x64 machine, so please pay close attention on TokenInformation.ToInt64() piece of code, maybe you should replace it with TokenInformation.ToInt32()

Igor Korkhov
  • 8,283
  • 1
  • 26
  • 31
  • Doesn't work unfortunately. It returns nothing. According to this (http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx), it only works on terminal server, so it might not be the session id I need. – Jos Jan 27 '10 at 11:52
  • I added another version of the code, it works on my machine. Please feel free to ask me if something is not clear. – Igor Korkhov Jan 27 '10 at 15:11
  • Thanks! This one works perfectly. I used TokenLogonSid instead of TokenGroups, but that only works for Windows Vista and 7. – Jos Jan 27 '10 at 16:15
  • This is very helpful, thanks. Why are you using `TokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size` instead of `TokenInformation.ToInt64() + i * sidAndAttrSize + 4`, given that TOKEN_GROUPS.GroupCount is a DWORD? Is this intentional? (fyi my related question here: http://stackoverflow.com/questions/6066650) – Rory May 21 '11 at 13:35
1

System.Security.Principal.WindowsIdentity.GetCurrent().User.AccountDomainSid - might do the trick?

Kalle
  • 2,282
  • 1
  • 24
  • 30
  • 1
    It is *not* the Logon Session ID SilverSkin asked about – Igor Korkhov Jan 27 '10 at 10:58
  • 1
    I think the user SID remains unchanged between sessions, but for each login a token is created, it should be possible to access this via ...GetCurrent().User.Token ? – Kalle Jan 27 '10 at 11:07
  • The token is indeed what I need, but I have to convert to a usable SID. – Jos Jan 27 '10 at 11:51
  • What do you need it for? Impersonation and other things should be available through the token. – Kalle Jan 27 '10 at 12:44
  • I have an external application that creates a named pipe with the Logon UID in it. Without it, I can't determine the name of the pipe. – Jos Jan 27 '10 at 13:12
1

I know this is an old post. Just ran into this problem as I had to get the ICA session ID and the RDP session ID to have a program collect the correct variables for each type of remote connection. The current session ID is located in Regedit HKEY_CURRENT_USER\Remote*. As I could not find any alternatives to WTS, I am posting my solution here.

    // Prints out ICA or RDP session ID of current user 

using System;
using Microsoft.Win32;

namespace ViaRegedit
{
    class Program03
    {
        static void Main(string[] args)
        {
            // Obtain an instance of RegistryKey for the CurrentUser registry 
            RegistryKey rkCurrentUser = Registry.CurrentUser;
            // Obtain the test key (read-only) and display it.
            RegistryKey rkTest = rkCurrentUser.OpenSubKey("Remote");
            foreach (string valueName in rkTest.GetSubKeyNames())
            {
                //Getting path to RDP/Citrix session ID
                string RDPICApath = "";
                if (rkTest.OpenSubKey(valueName) != null && rkTest.OpenSubKey(valueName) != null) { RDPICApath = rkTest.OpenSubKey(valueName).ToString(); }
                Console.WriteLine("Getting CurrentUser ICA-RDP path from string = " + RDPICApath);

                //Seperating RDPICApath to get session number
                string RDPICAnumber = RDPICApath.Substring(RDPICApath.LastIndexOf('\\') + 1);
                Console.WriteLine("Current User RDPICAnumber = " + RDPICAnumber);
            }
            rkTest.Close();
            rkCurrentUser.Close();
            Console.ReadLine();
        }
    }

}
BrettKennard
  • 171
  • 1
  • 4
1

I just spent a long time getting the SID using TOKEN_USER and so forth, then discovered a shortcut in C#. You still need to get the Process Handle (e.g. https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.handle?view=netcore-3.1) and then the token with P/invoke:

OpenProcessToken(hProcess, TOKEN_READ, out IntPtr hToken))

But once you have the token, you don't need to do any of the nasty GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser... stuff you just use:

var winId = System.Security.Principal.WindowsIdentity(hToken);

... and BOOM you can get all the info you want (inc SID) for the user out of winId.

Don't forget to CloseHandle(hToken) and on hProcess afterwards!

Jon R
  • 836
  • 11
  • 9