get machine SID (including primary domain controller)
Asked Answered
K

2

6

I need to get machine SID (not computer account's SID) in C#. Computer is specified be host name, it is't necessarily local computer and it can be domain computer or workgroup computer. I using this helper class to call LookupAccountName API function:

    private static class Helper
    {
        internal enum SID_NAME_USE
        {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool LookupAccountName(
            string systemName,
            string accountName,
            byte[] sid,
            ref int sidLen,
            System.Text.StringBuilder domainName,
            ref int domainNameLen,
            out SID_NAME_USE peUse);

        public static SecurityIdentifier LookupAccountName(
            string systemName,
            string accountName,
            out string strDomainName,
            out SID_NAME_USE accountType)
        {
            const int ERROR_INSUFFICIENT_BUFFER = 122;

            int lSidSize = 0;
            int lDomainNameSize = 0;

            //First get the required buffer sizes for SID and domain name.
            LookupAccountName(systemName,
                              accountName,
                              null,
                              ref lSidSize,
                              null,
                              ref lDomainNameSize,
                              out accountType);

            if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
            {
                //Allocate the buffers with actual sizes that are required
                //for SID and domain name.
                byte[] sid = new byte[lSidSize];
                var sbDomainName = new System.Text.StringBuilder(lDomainNameSize);

                if (LookupAccountName(systemName,
                                      accountName,
                                      sid,
                                      ref lSidSize,
                                      sbDomainName,
                                      ref lDomainNameSize,
                                      out accountType))
                {
                    strDomainName = sbDomainName.ToString();
                    return new SecurityIdentifier(sid, 0);
                }
            }

            throw new Win32Exception();
        }
    }

and using it like this:

Helper.SID_NAME_USE accountType;
string refDomain;
SecurityIdentifier sid = Helper.LookupAccountName("falcon.mydomain.local", "falcon", out refDomain, out accountType);  //Domain computer

SecurityIdentifier sid = Helper.LookupAccountName("rat", "rat", out refDomain, out accountType);  //Workgroup computer

My only problem is, that this is not working if computer is primary domain controller (i need to obtain domain SID in that case).

Karnak answered 12/2, 2010 at 19:49 Comment(0)
H
3

It appears that for most computers you do the following:

LookupAccountName("", "ComputerName", ...); ConvertSidToStringSid(...)

But for domain controllers you have to add a dollar sign to the computer name parameter, and then delete the last segment in the returned SID.

Hearsh answered 3/11, 2010 at 23:50 Comment(0)
S
0

You can receive the machine SID (matching the output of psgetsid) via PolicyAccountDomainInformation:

using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

internal class Program
{
    private static void Main(string[] args)
    {
        unsafe
        {
            var lsaOA = new LSA_OBJECT_ATTRIBUTES() { Length = 24 };
            var result = LsaOpenPolicy(IntPtr.Zero, lsaOA, 1 /* FILE_READ_DATA */, out var handle);
            if (result != 0)
            {
                throw new Win32Exception(result);
            }

            using (handle)
            {
                result = LsaQueryInformationPolicy(handle, 5 /* PolicyAccountDomainInformation */, out var policyInfo);
                if (result != 0)
                {
                    throw new Win32Exception(result);
                }

                using (policyInfo)
                {
                    var domInfo = (POLICY_ACCOUNT_DOMAIN_INFO*)policyInfo.DangerousGetHandle();
                    var sid = new SecurityIdentifier(domInfo->DomainSid);
                    Console.WriteLine(sid);
                }
            }
        }
    }

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern unsafe int LsaOpenPolicy(IntPtr SystemName, in LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out LsaCloseSafeHandle PolicyHandle);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern unsafe int LsaQueryInformationPolicy(LsaCloseSafeHandle PolicyHandle, int InformationClass, out LsaFreeSafeHandle Buffer);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern int LsaClose(IntPtr ObjectHandle);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern int LsaFreeMemory(IntPtr Buffer);

    internal partial struct LSA_OBJECT_ATTRIBUTES
    {
        internal uint Length;
        internal IntPtr RootDirectory;
        internal IntPtr ObjectName;
        internal uint Attributes;
        internal IntPtr SecurityDescriptor;
        internal IntPtr SecurityQualityOfService;
    }

    internal partial struct POLICY_ACCOUNT_DOMAIN_INFO
    {
        internal LSA_UNICODE_STRING DomainName;
        internal IntPtr DomainSid;
    }

    internal partial struct LSA_UNICODE_STRING
    {
        internal ushort Length;
        internal ushort MaximumLength;
        internal IntPtr Buffer;
    }

    internal class LsaCloseSafeHandle : SafeHandleMinusOneIsInvalid
    {
        public LsaCloseSafeHandle() : base(true) { }
        protected override bool ReleaseHandle() => LsaClose(this.handle) == 0;
    }

    internal class LsaFreeSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public LsaFreeSafeHandle() : base(true) { }
        protected override bool ReleaseHandle() => LsaFreeMemory(this.handle) == 0;
    }
}

If you want the computer account sid, use LookupAccountName with MachineName$ (again matching psgetsid):

static void Main(string[] args)
{
    try
    {
        var sid = new byte[1024];
        var cbSid = sid.Length;
        int cbReferencedDomainName = 0;
        var domainName = new StringBuilder(1024);
        uint domainNameLen = (uint)domainName.Capacity;
        if (!LookupAccountName(null, Environment.MachineName + "$", sid, ref cbSid, domainName, ref domainNameLen, out int peUse))
        {
            throw new Win32Exception();
        }

        Console.WriteLine(new System.Security.Principal.SecurityIdentifier(sid, 0));
    }
    catch (Win32Exception ex) when (ex.NativeErrorCode == 1332 /* ERROR_NONE_MAPPED */)
    {
        Console.Error.WriteLine("Not domain joined");
    }
}

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static extern bool LookupAccountName(string machineName, string accountName, byte[] sid,
                            ref int sidLen, [Out] StringBuilder domainName, ref uint domainNameLen, out int peUse);
Sapiential answered 19/6 at 3:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.