Get User SID From Logon ID (Windows XP and Up)

Posted by Dave Ruske on Stack Overflow See other posts from Stack Overflow or by Dave Ruske
Published on 2010-04-28T07:01:03Z Indexed on 2010/04/28 7:03 UTC
Read the original article Hit count: 473

Filed under:
|
|
|
|

I have a Windows service that needs to access registry hives under HKEY_USERS when users log on, either locally or via Terminal Server. I'm using a WMI query on win32_logonsession to receive events when users log on, and one of the properties I get from that query is a LogonId. To figure out which registry hive I need to access, now, I need the users's SID, which is used as a registry key name beneath HKEY_USERS.

In most cases, I can get this by doing a RelatedObjectQuery like so (in C#):

RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );

where "logonID" is the logon session ID from the session query. Running the RelatedObjectQuery will generally give me a SID property that contains exactly what I need.

There are two issues I have with this. First and most importantly, the RelatedObjectQuery will not return any results for a domain user that logs in with cached credentials, disconnected from the domain. Second, I'm not pleased with the performance of this RelatedObjectQuery --- it can take up to several seconds to execute.

Here's a quick and dirty command line program I threw together to experiment with the queries. Rather than setting up to receive events, this just enumerates the users on the local machine:

using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace EnumUsersTest
{
    class Program
    {
        static void Main( string[] args )
        {
            ManagementScope scope = new ManagementScope( "\\\\.\\root\\cimv2" );

            string queryString = "select * from win32_logonsession";                          // for all sessions
            //string queryString = "select * from win32_logonsession where logontype = 2";     // for local interactive sessions only

            ManagementObjectSearcher sessionQuery = new ManagementObjectSearcher( scope, new SelectQuery( queryString ) );
            ManagementObjectCollection logonSessions = sessionQuery.Get();
            foreach ( ManagementObject logonSession in logonSessions )
            {
                string logonID = logonSession["LogonId"].ToString();
                Console.WriteLine( "=== {0}, type {1} ===", logonID, logonSession["LogonType"].ToString() );
                RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );
                ManagementObjectSearcher userQuery = new ManagementObjectSearcher( scope, relatedQuery );
                ManagementObjectCollection users = userQuery.Get();
                foreach ( ManagementObject user in users )
                {
                    PrintProperties( user.Properties );
                }
            }

            Console.WriteLine( "\nDone! Press a key to exit..." );
            Console.ReadKey( true );
        }


        private static void PrintProperty( PropertyData pd )
        {
            string value = "null";
            string valueType = "n/a";
            if ( null == pd.Value ) value = "null";
            if ( pd.Value != null )
            {
                value = pd.Value.ToString();
                valueType = pd.Value.GetType().ToString();
            }

            Console.WriteLine( "  \"{0}\" = ({1}) \"{2}\"", pd.Name, valueType, value );
        }


        private static void PrintProperties( PropertyDataCollection properties )
        {
            foreach ( PropertyData pd in properties )
            {
                PrintProperty( pd );
            }
        }
    }
}

So... is there way to quickly and reliably obtain the user SID given the information I retrieve from WMI, or should I be looking at using something like SENS instead?

© Stack Overflow or respective owner

Related posts about wmi

Related posts about sid