How does one retrieve the Windows Logon SID in C# .net? (not the user SID, but the unique new one for each session)
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();
}
}
}
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://docs.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!
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()
System.Security.Principal.WindowsIdentity.GetCurrent().User.AccountDomainSid - might do the trick?