Tuesday, July 24, 2012

Adding domain user as local admin immediatly after domain join

Here's a way to add a domain user as a local admin immediatly after joining the domain, without rebooting first:
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;

class NativeMethods
{
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_LOGON_NETWORK = 3;
    public const int LOGON32_LOGON_BATCH = 4;
    public const int LOGON32_LOGON_SERVICE = 5;
    public const int LOGON32_LOGON_UNLOCK = 7;
    public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
    public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

    public enum SID_NAME_USE
    {
        SidTypeUser = 1,
        SidTypeGroup,
        SidTypeDomain,
        SidTypeAlias,
        SidTypeWellKnownGroup,
        SidTypeDeletedAccount,
        SidTypeInvalid,
        SidTypeUnknown,
        SidTypeComputer,
    }

    public struct LOCALGROUP_MEMBERS_INFO_0
    {
        public IntPtr PSID;
    }

    [DllImport("kernel32.dll")]
    public extern static bool CloseHandle(IntPtr hToken);

    [DllImport("advapi32.DLL", SetLastError = true)]
    public static extern int LogonUser(
        string lpszUsername,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        out IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool LookupAccountName(
        string lpSystemName,
        string lpAccountName,
        [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
        ref uint cbSid,
        StringBuilder ReferencedDomainName,
        ref uint cchReferencedDomainName,
        out SID_NAME_USE peUse);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool LookupAccountSid(
        string lpSystemName,
        [MarshalAs(UnmanagedType.LPArray)] byte[] lpSid,
        StringBuilder lpName,
        ref uint cchName,
        StringBuilder lpReferencedDomainName,
        ref uint cchReferencedDomainName,
        out SID_NAME_USE peUse);

    [DllImport("netapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int NetLocalGroupAddMembers(
        string servername,
        string groupname,
        uint level,
        ref LOCALGROUP_MEMBERS_INFO_0 buf,
        uint totalentries);
}

public class AddAdminUserHelper
{
    public static void AddAdminUser(string domain, string username, string password)
    {
        // Get built in administrators account name
        StringBuilder adminGroupName = new StringBuilder();
        uint adminGroupNameCapacity = (uint)adminGroupName.Capacity;
        StringBuilder referencedDomainName = new StringBuilder();
        uint referencedDomainNameCapacity = (uint)referencedDomainName.Capacity;
        NativeMethods.SID_NAME_USE eUse;
        byte[] adminGroupSid = new byte[] { 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2 };
        if (!NativeMethods.LookupAccountSid(
            null,
            adminGroupSid,
            adminGroupName,
            ref adminGroupNameCapacity,
            referencedDomainName,
            ref referencedDomainNameCapacity,
            out eUse))
        {
            Console.WriteLine("LookupAccountSid failed with error " + Marshal.GetLastWin32Error());
            return;
        }

        // Get a security token needed to be able to afterwards query for the user's SID
        IntPtr token = IntPtr.Zero;
        if (NativeMethods.LogonUser(
            username,
            domain,
            password,
            NativeMethods.LOGON32_LOGON_NEW_CREDENTIALS,
            0,
            out token) == 0)
        {
            Console.WriteLine("LogonUser failed with error " + Marshal.GetLastWin32Error());
            return;
        }

        // Get user's SID
        byte[] userSid = new byte[1024];
        uint userSidLength = (uint)userSid.Length;
        referencedDomainName = new StringBuilder();
        referencedDomainNameCapacity = (uint)referencedDomainName.Capacity;
        NativeMethods.SID_NAME_USE peUse;
        using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token))
        {
            if (!NativeMethods.LookupAccountName(
                domain,
                username,
                userSid,
                ref userSidLength,
                referencedDomainName,
                ref referencedDomainNameCapacity,
                out peUse))
            {
                Console.WriteLine("LookupAccountName failed with error " + Marshal.GetLastWin32Error());
                return;
            }
        }
        NativeMethods.CloseHandle(token);

        // Add user's SID to local admins group
        IntPtr userSidNative = Marshal.AllocHGlobal(userSid.Length);
        Marshal.Copy(userSid, 0, userSidNative, (int)userSid.Length);
        NativeMethods.LOCALGROUP_MEMBERS_INFO_0 info0;
        info0.PSID = userSidNative;
        int r = NativeMethods.NetLocalGroupAddMembers(
            null,
            adminGroupName.ToString(),
            0,
            ref info0,
            1);
        Marshal.FreeHGlobal(userSidNative);
        if (r != 0)
        {
            Console.WriteLine("NetLocalGroupAddMembers failed by returning " + r);
            return;
        }
    }
}

No comments:

Post a Comment