Click here to Skip to main content
15,885,141 members
Articles / Programming Languages / C#

Using the Local Security Authority to Enumerate User Sessions in .NET

Rate me:
Please Sign up or sign in to vote.
4.17/5 (6 votes)
28 Mar 20073 min read 72.6K   1.2K   30  
An article on enumerating Windows user logon sessions in .NET
using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace LsaEnumUsersExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            PopulateListbox();
        }


        public void PopulateListbox()
        {
            System.Security.Principal.WindowsIdentity currentUser = System.Security.Principal.WindowsIdentity.GetCurrent();

            DateTime systime = new DateTime(1601, 1, 1, 0, 0, 0, 0); //win32 systemdate

            UInt64 count;
            IntPtr luidPtr = IntPtr.Zero;
            LsaEnumerateLogonSessions(out count, out luidPtr);  //gets an array of pointers to LUIDs

            IntPtr iter = luidPtr;      //set the pointer to the start of the array

            for (ulong i = 0; i < count; i++)   //for each pointer in the array
            {
                IntPtr sessionData;

                LsaGetLogonSessionData(iter, out sessionData);
                SECURITY_LOGON_SESSION_DATA data = (SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionData, typeof(SECURITY_LOGON_SESSION_DATA));

                //if we have a valid logon
                if (data.PSiD != IntPtr.Zero)
                {
                    //get the security identifier for further use
                    System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(data.PSiD);
               
                    //extract some useful information from the session data struct
                    string username = Marshal.PtrToStringUni(data.Username.buffer).Trim();          //get the account username
                    string domain =  Marshal.PtrToStringUni(data.LoginDomain.buffer).Trim();        //domain for this account  
                    string authpackage = Marshal.PtrToStringUni(data.AuthenticationPackage.buffer).Trim();    //authentication package
                
                    SECURITY_LOGON_TYPE secType = (SECURITY_LOGON_TYPE)data.LogonType;
                    DateTime time = systime.AddTicks((long)data.LoginTime);                              //get the datetime the session was logged in

                    listBox1.Items.Add("User: " + username + " *** Domain: " + domain + " *** Login Type: (" + data.LogonType + ") " + secType.ToString() +" *** Login Time: "+time.ToLocalTime().ToString());
   
                }
                iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(LUID)));  //move the pointer forward
                LsaFreeReturnBuffer(sessionData);   //free the SECURITY_LOGON_SESSION_DATA memory in the struct
            }
            LsaFreeReturnBuffer(luidPtr);   //free the array of LUIDs
        }


        /************************************************************************/
        /* The following Interop code should be placed in a sealed internal NativeMethod class
         * but has been left here to simplify the example.
        /************************************************************************/

        [DllImport("secur32.dll", SetLastError = false)]
        private static extern uint LsaFreeReturnBuffer(IntPtr buffer);

        [DllImport("Secur32.dll", SetLastError = false)]
        private static extern uint LsaEnumerateLogonSessions(out UInt64 LogonSessionCount, out IntPtr LogonSessionList);

        [DllImport("Secur32.dll", SetLastError = false)]
        private static extern uint LsaGetLogonSessionData(IntPtr luid, out IntPtr ppLogonSessionData);

        [StructLayout(LayoutKind.Sequential)]
        private struct LSA_UNICODE_STRING
        {
            public UInt16 Length;
            public UInt16 MaximumLength;
            public IntPtr buffer;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct LUID
        {
            public UInt32 LowPart;
            public UInt32 HighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct SECURITY_LOGON_SESSION_DATA
        {
            public UInt32 Size;
            public LUID LoginID;
            public LSA_UNICODE_STRING Username;
            public LSA_UNICODE_STRING LoginDomain;
            public LSA_UNICODE_STRING AuthenticationPackage;
            public UInt32 LogonType;
            public UInt32 Session;
            public IntPtr PSiD;
            public UInt64 LoginTime;
            public LSA_UNICODE_STRING LogonServer;
            public LSA_UNICODE_STRING DnsDomainName;
            public LSA_UNICODE_STRING Upn;
        }

        private enum SECURITY_LOGON_TYPE : uint
        {
            Interactive = 2,    //The security principal is logging on interactively. 
            Network,            //The security principal is logging using a network. 
            Batch,              //The logon is for a batch process. 
            Service,            //The logon is for a service account. 
            Proxy,              //Not supported. 
            Unlock,             //The logon is an attempt to unlock a workstation.
            NetworkCleartext,   //The logon is a network logon with cleartext credentials.
            NewCredentials,     // Allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identity but uses different credentials for other network connections.
            RemoteInteractive,  // A terminal server session that is both remote and interactive.
            CachedInteractive, // Attempt to use the cached credentials without going out across the network.
            CachedRemoteInteractive, // Same as RemoteInteractive, except used internally for auditing purposes.
            CachedUnlock          // The logon is an attempt to unlock a workstation.
        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Australia Australia
Started my programming career working in C++ and Domino before heading out to start my own company with some old University buddies.

Spend most of my time managing staff, but when able to I focus on database architecture and .NET development.

Comments and Discussions