Change local user rights assignment from powershell

There are lots of “solutions” out there that just shell out to ntrights.exe or secedit or something else not powershell, and say “but powershell calls it so it counts!”  No it doesn’t.

There is no native NET or COM interface to manage local user rights assignment.  You have to use P/Invoke to call the API.  It’s a pain.  But I swiped some code from Roel van Lisdonk (http://www.roelvanlisdonk.nl/?p=1151) and got it working.  Looks like he got the code from Willy Denoyette (see http://www.pinvoke.net/default.aspx/advapi32.lsalookupsids) which I kept intact except for a minor error correction (it was missing a semicolon and wouldn’t compile). See http://www.hightechtalks.com/csharp/lsa-functions-276626.html for what seems to be Willy’s original source.

$LsaWrapper = @'
using System;
using System.Collections.Generic;
using System.Text;

namespace LsaSecurity
 {
     /*
      * LsaWrapper class credit: Willy Denoyette [MVP]
      * 
      * http://www.hightechtalks.com/csharp/lsa-functions-276626.html
      * 
      * Added support for:
      * 
      *      LsaLookupSids
      * 
      * for the purposes of providing a working example.
      * 
      * 
      * 
      */


     using System.Runtime.InteropServices;
     using System.Security;
     using System.Management;
     using System.Runtime.CompilerServices;
     using System.ComponentModel;

     using LSA_HANDLE = IntPtr;

     public class Program
     {
     public static void Main()
     {
         using (LsaWrapper lsaSec = new LsaWrapper())
         {
         string[] accounts = lsaSec.GetUsersWithPrivilege("SeNetworkLogonRight");

         }

     }
     }



    [StructLayout(LayoutKind.Sequential)]
     struct LSA_OBJECT_ATTRIBUTES
     {
     internal int Length;
     internal IntPtr RootDirectory;
     internal IntPtr ObjectName;
     internal int Attributes;
     internal IntPtr SecurityDescriptor;
     internal IntPtr SecurityQualityOfService;
     }
     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
     struct LSA_UNICODE_STRING
     {
     internal ushort Length;
     internal ushort MaximumLength;
     [MarshalAs(UnmanagedType.LPWStr)]
     internal string Buffer;
     }

     sealed class Win32Sec
     {
     [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
     SuppressUnmanagedCodeSecurityAttribute]
     internal static extern uint LsaOpenPolicy(
     LSA_UNICODE_STRING[] SystemName,
     ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
     int AccessMask,
     out IntPtr PolicyHandle
     );

     [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
     SuppressUnmanagedCodeSecurityAttribute]
     internal static extern uint LsaAddAccountRights(
     LSA_HANDLE PolicyHandle,
     IntPtr pSID,
     LSA_UNICODE_STRING[] UserRights,
     int CountOfRights
     );

     [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
         SuppressUnmanagedCodeSecurityAttribute]
     internal static extern uint LsaRemoveAccountRights(
     LSA_HANDLE PolicyHandle,
     IntPtr pSID,
     bool allRights,
     LSA_UNICODE_STRING[] UserRights,
     int CountOfRights
     );

     [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
         SuppressUnmanagedCodeSecurityAttribute]
     internal static extern uint LsaEnumerateAccountsWithUserRight(
         LSA_HANDLE PolicyHandle,
         LSA_UNICODE_STRING[] UserRights,
         out IntPtr EnumerationBuffer,
         out int CountReturned
     );

     [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
     SuppressUnmanagedCodeSecurityAttribute]
     internal static extern uint LsaLookupSids(
         LSA_HANDLE PolicyHandle,
         int count,
         IntPtr buffer,
         out LSA_HANDLE domainList,
         out LSA_HANDLE nameList
     );



    [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
     SuppressUnmanagedCodeSecurityAttribute]
     internal static extern int LsaLookupNames2(
     LSA_HANDLE PolicyHandle,
     uint Flags,
     uint Count,
     LSA_UNICODE_STRING[] Names,
     ref IntPtr ReferencedDomains,
     ref IntPtr Sids
     );



    [DllImport("advapi32")]
     internal static extern int LsaNtStatusToWinError(int NTSTATUS);

     [DllImport("advapi32")]
     internal static extern int LsaClose(IntPtr PolicyHandle);

     [DllImport("advapi32")]
     internal static extern int LsaFreeMemory(IntPtr Buffer);

     }

     public sealed class LsaWrapper : IDisposable
     {
     private bool _writeToConsole = false;

     [StructLayout(LayoutKind.Sequential)]
     struct LSA_TRUST_INFORMATION
     {
         internal LSA_UNICODE_STRING Name;
         internal IntPtr Sid;
     }
     [StructLayout(LayoutKind.Sequential)]
     struct LSA_TRANSLATED_SID2
     {
         internal SidNameUse Use;
         internal IntPtr Sid;
         internal int DomainIndex;
         uint Flags;
     }

     //[StructLayout(LayoutKind.Sequential)]
     //struct LSA_REFERENCED_DOMAIN_LIST
     //{
     //    internal uint Entries;
     //    internal LSA_TRUST_INFORMATION Domains;
     //}
     // Commented by KaushalendraATgmail.com

     [StructLayout(LayoutKind.Sequential)]
     internal struct LSA_REFERENCED_DOMAIN_LIST
     {
         internal uint Entries;
         internal IntPtr Domains;
     }

     [StructLayout(LayoutKind.Sequential)]
     struct LSA_ENUMERATION_INFORMATION
     {
         internal LSA_HANDLE PSid;
     }

     [StructLayout(LayoutKind.Sequential)]
     struct LSA_SID
     {
         internal uint Sid;
     }

     [StructLayout(LayoutKind.Sequential)]
     struct LSA_TRANSLATED_NAME
     {
         internal SidNameUse Use;
         internal LSA_UNICODE_STRING Name;
         internal int DomainIndex;
     }

     enum SidNameUse : int
     {
         User = 1,
         Group = 2,
         Domain = 3,
         Alias = 4,
         KnownGroup = 5,
         DeletedAccount = 6,
         Invalid = 7,
         Unknown = 8,
         Computer = 9
     }

     enum Access : int
     {
         POLICY_READ = 0x20006,
         POLICY_ALL_ACCESS = 0x00F0FFF,
         POLICY_EXECUTE = 0X20801,
         POLICY_WRITE = 0X207F8
     }
     const uint STATUS_ACCESS_DENIED = 0xc0000022;
     const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a;
     const uint STATUS_NO_MEMORY = 0xc0000017;

     IntPtr lsaHandle;

     public LsaWrapper()
         : this(null)
     { }
     // // local system if systemName is null
     public LsaWrapper(string systemName)
     {
         LSA_OBJECT_ATTRIBUTES lsaAttr;
         lsaAttr.RootDirectory = IntPtr.Zero;
         lsaAttr.ObjectName = IntPtr.Zero;
         lsaAttr.Attributes = 0;
         lsaAttr.SecurityDescriptor = IntPtr.Zero;
         lsaAttr.SecurityQualityOfService = IntPtr.Zero;
         lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES));
         lsaHandle = IntPtr.Zero;
         LSA_UNICODE_STRING[] system = null;
         if (systemName != null)
         {
         system = new LSA_UNICODE_STRING[1];
         system[0] = InitLsaString(systemName);
         }

         uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr, (int)Access.POLICY_ALL_ACCESS, out lsaHandle);
         if (ret == 0)
         return;
         if (ret == STATUS_ACCESS_DENIED)
         {
         throw new UnauthorizedAccessException();
         }
         if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY))
         {
         throw new OutOfMemoryException();
         }
         throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
     }

     public string[] GetUsersWithPrivilege(string privilege)
     {
         LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1];
         privileges[0] = InitLsaString(privilege);

         IntPtr buffer;
         int count;
         uint ret =
         Win32Sec.LsaEnumerateAccountsWithUserRight(lsaHandle, privileges, out buffer, out count);

         if (ret != 0)
         {
         if (ret == STATUS_ACCESS_DENIED)
         {
             throw new UnauthorizedAccessException();
         }

         if (ret == STATUS_INSUFFICIENT_RESOURCES || ret == STATUS_NO_MEMORY)
         {
             throw new OutOfMemoryException();
         }

         throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
         }

         LSA_ENUMERATION_INFORMATION[] lsaInfo = new LSA_ENUMERATION_INFORMATION[count];
         for (int i = 0, elemOffs = (int)buffer; i < count; i++)
         {
         lsaInfo[i] = (LSA_ENUMERATION_INFORMATION)Marshal.PtrToStructure((IntPtr)elemOffs, typeof(LSA_ENUMERATION_INFORMATION));
         elemOffs += Marshal.SizeOf(typeof(LSA_ENUMERATION_INFORMATION));
         }

         LSA_HANDLE domains;
         LSA_HANDLE names;
         ret = Win32Sec.LsaLookupSids(lsaHandle, lsaInfo.Length, buffer, out domains, out names);

         if (ret != 0)
         {
         if (ret == STATUS_ACCESS_DENIED)
         {
             throw new UnauthorizedAccessException();
         }

         if (ret == STATUS_INSUFFICIENT_RESOURCES || ret == STATUS_NO_MEMORY)
         {
             throw new OutOfMemoryException();
         }

         throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
         }

         /*string[] retNames = new string[count];

         LSA_TRANSLATED_NAME[] lsaNames = new LSA_TRANSLATED_NAME[count];
         for (int i = 0, elemOffs = (int)names; i < count; i++)
         {
         lsaNames[i] = (LSA_TRANSLATED_NAME)Marshal.PtrToStructure((LSA_HANDLE)elemOffs, typeof(LSA_TRANSLATED_NAME));
         elemOffs += Marshal.SizeOf(typeof(LSA_TRANSLATED_NAME));

         LSA_UNICODE_STRING name = lsaNames[i].Name;
         retNames[i] = name.Buffer.Substring(0, name.Length / 2);
         }*/

         // Following code also fetches Domains and associates domains and usernames
         string[] retNames = new string[count];
         List currentDomain = new List();
         int domainCount = 0;

         LSA_TRANSLATED_NAME[] lsaNames = new LSA_TRANSLATED_NAME[count];
         for (int i = 0, elemOffs = (int)names; i < count; i++)
         {
         lsaNames[i] = (LSA_TRANSLATED_NAME)Marshal.PtrToStructure((LSA_HANDLE)elemOffs, typeof(LSA_TRANSLATED_NAME));
         elemOffs += Marshal.SizeOf(typeof(LSA_TRANSLATED_NAME));

         LSA_UNICODE_STRING name = lsaNames[i].Name;
         retNames[i] = name.Buffer.Substring(0, name.Length / 2);

         if (!currentDomain.Contains(lsaNames[i].DomainIndex))
         {
             domainCount = domainCount + 1;
             currentDomain.Add(lsaNames[i].DomainIndex);
         }

         }

         string[] domainPtrNames = new string[count];

         LSA_REFERENCED_DOMAIN_LIST[] lsaDomainNames = new LSA_REFERENCED_DOMAIN_LIST[count];

         for (int i = 0, elemOffs = (int)domains; i < count; i++)
         {
         lsaDomainNames[i] = (LSA_REFERENCED_DOMAIN_LIST)Marshal.PtrToStructure((LSA_HANDLE)elemOffs, typeof(LSA_REFERENCED_DOMAIN_LIST));
         elemOffs += Marshal.SizeOf(typeof(LSA_REFERENCED_DOMAIN_LIST));
         }

         LSA_TRUST_INFORMATION[] lsaDomainName = new LSA_TRUST_INFORMATION[count];
         string[] domainNames = new string[domainCount];

         for (int i = 0, elemOffs = (int)lsaDomainNames[i].Domains; i < domainCount; i++)
         {
         lsaDomainName[i] = (LSA_TRUST_INFORMATION)Marshal.PtrToStructure((LSA_HANDLE)elemOffs, typeof(LSA_TRUST_INFORMATION));
         elemOffs += Marshal.SizeOf(typeof(LSA_TRUST_INFORMATION));



        LSA_UNICODE_STRING tempDomain = lsaDomainName[i].Name;
         //if(tempDomain.Buffer != null)
         //{
             domainNames[i] = tempDomain.Buffer.Substring(0, tempDomain.Length / 2);
         //}
         }

         string[] domainUserName = new string[count];

         for (int i = 0; i  0x7ffe)
         throw new ArgumentException("String too long");
         LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING();
         lus.Buffer = s;
         lus.Length = (ushort)(s.Length * sizeof(char));
         lus.MaximumLength = (ushort)(lus.Length + sizeof(char));

         // If unicode issues then do this instead of previous two line
         //lus.Length = (ushort)(s.Length * 2); // Unicode char is 2 bytes
         //lus.MaximumLength = (ushort)(lus.Length + 2)



        return lus;
     }

     public bool WriteToConsole
     {
         set { this._writeToConsole = value; }
     }
     }
    public class LsaWrapperCaller
    {
        public static void AddPrivileges(string account, string privilege)
        {
            using (LsaWrapper lsaWrapper = new LsaWrapper())
            {
                lsaWrapper.AddPrivileges(account, privilege);
            }
        }
        public static void RemovePrivileges(string account, string privilege)
        {
            using (LsaWrapper lsaWrapper = new LsaWrapper())
            {
                lsaWrapper.RemovePrivileges(account, privilege);
            }
        }
    }
 }
'@

Add-Type $LsaWrapper

$account = "computername\name" # Will default to local computer if you do not specify computername.  Can also accept domain name there.  Name is a user name or group name.
$right = "SeServiceLogonRight" # see http://msdn.microsoft.com/en-us/library/windows/desktop/bb545671(v=vs.85).aspx for a list.
[LsaSecurity.LsaWrapperCaller]::RemovePrivileges($account,$right);

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s