11

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

1
  • 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
    – mistika
    Commented Jun 5, 2013 at 14:40

4 Answers 4

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()

4
  • Doesn't work unfortunately. It returns nothing. According to this (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
    Commented Jan 27, 2010 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. Commented Jan 27, 2010 at 15:11
  • Thanks! This one works perfectly. I used TokenLogonSid instead of TokenGroups, but that only works for Windows Vista and 7.
    – Jos
    Commented Jan 27, 2010 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: stackoverflow.com/questions/6066650)
    – Rory
    Commented May 21, 2011 at 13:35
1

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

5
  • 1
    It is not the Logon Session ID SilverSkin asked about Commented Jan 27, 2010 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
    Commented Jan 27, 2010 at 11:07
  • The token is indeed what I need, but I have to convert to a usable SID.
    – Jos
    Commented Jan 27, 2010 at 11:51
  • What do you need it for? Impersonation and other things should be available through the token.
    – Kalle
    Commented Jan 27, 2010 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
    Commented Jan 27, 2010 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();
        }
    }

}
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!

Not the answer you're looking for? Browse other questions tagged or ask your own question.