7

For an application I what to check whether a particular user has log on as a service right? How to do this programmatically? I checked in the Internet and could not find out some good resource

Thanks

Upul

Upul Bandara
  • 5,973
  • 4
  • 37
  • 60

4 Answers4

16

I checked all previous answers and many other in the related posts, spent 3 days digging into Win32 API, but didn't get the reliable answer to this question (some thrown exeptions, some didn't compile even with attempts to fix them, some worked only with local accounts). Finally I've found acceptable answer for me. As a base, I used the answer of Wieger1983 on technet forum. I introduced some changes to it:

  • slight adaptation to pure C# (originally it was declared as powershell extention)
  • adding stability to it (it doesn't return error code 2 now in case user has no rights. Explanation was provided by @David Woodwart in this post: LsaEnumerateAccountRights always returns "File not found").
  • preventing memory leaks in case of any unpredictable exceptions. CloseHandle is called now in the finally block.

Now it works both with local and Active directory accounts and works fine if user has no rights. I hope it will save time to somebody like me =).

WARNING: If you will be using this code on x64 architecture, please mind comment of EricRRichards below. I can't verify now if it should be applied in every case.

//
// This code has been adapted from http://www.codeproject.com/KB/cs/lsadotnet.aspx
// The rights enumeration code came from http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.interop/2004-11/0394.html
//
// Windows Security via .NET is covered on by Pluralsight:http://alt.pluralsight.com/wiki/default.aspx/Keith.GuideBook/HomePage.html
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

namespace LSA
{
    //
    // Provides methods the local security authority which controls user rights. Managed via secpol.msc normally.
    //
    public class LocalSecurityAuthorityController
    {
        private const int Access = (int)(
                                             LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN |
                                             LSA_AccessPolicy.POLICY_CREATE_ACCOUNT |
                                             LSA_AccessPolicy.POLICY_CREATE_PRIVILEGE |
                                             LSA_AccessPolicy.POLICY_CREATE_SECRET |
                                             LSA_AccessPolicy.POLICY_GET_PRIVATE_INFORMATION |
                                             LSA_AccessPolicy.POLICY_LOOKUP_NAMES |
                                             LSA_AccessPolicy.POLICY_NOTIFICATION |
                                             LSA_AccessPolicy.POLICY_SERVER_ADMIN |
                                             LSA_AccessPolicy.POLICY_SET_AUDIT_REQUIREMENTS |
                                             LSA_AccessPolicy.POLICY_SET_DEFAULT_QUOTA_LIMITS |
                                             LSA_AccessPolicy.POLICY_TRUST_ADMIN |
                                             LSA_AccessPolicy.POLICY_VIEW_AUDIT_INFORMATION |
                                             LSA_AccessPolicy.POLICY_VIEW_LOCAL_INFORMATION
                                         );

        [DllImport("advapi32.dll", PreserveSig = true)]
        private static extern UInt32 LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, Int32 DesiredAccess, out IntPtr PolicyHandle);

        [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
        private static extern uint LsaAddAccountRights(IntPtr PolicyHandle, IntPtr AccountSid, LSA_UNICODE_STRING[] UserRights, int CountOfRights);

        [DllImport("advapi32")]
        public static extern void FreeSid(IntPtr pSid);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
        private static extern bool LookupAccountName(string lpSystemName, string lpAccountName, IntPtr psid, ref int cbsid, StringBuilder domainName, ref int cbdomainLength, ref int use);

        [DllImport("advapi32.dll")]
        private static extern bool IsValidSid(IntPtr pSid);

        [DllImport("advapi32.dll")]
        private static extern int LsaClose(IntPtr ObjectHandle);

        [DllImport("kernel32.dll")]
        private static extern int GetLastError();

        [DllImport("advapi32.dll")]
        private static extern int LsaNtStatusToWinError(uint status);

        [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
        private static extern uint LsaEnumerateAccountRights(IntPtr PolicyHandle, IntPtr AccountSid, out IntPtr UserRightsPtr, out int CountOfRights);

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

        [StructLayout(LayoutKind.Sequential)]
        private struct LSA_OBJECT_ATTRIBUTES
        {
            public int Length;
            public IntPtr RootDirectory;
            public LSA_UNICODE_STRING ObjectName;
            public UInt32 Attributes;
            public IntPtr SecurityDescriptor;
            public IntPtr SecurityQualityOfService;
        }

        [Flags]
        private enum LSA_AccessPolicy : long
        {
            POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L,
            POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L,
            POLICY_GET_PRIVATE_INFORMATION = 0x00000004L,
            POLICY_TRUST_ADMIN = 0x00000008L,
            POLICY_CREATE_ACCOUNT = 0x00000010L,
            POLICY_CREATE_SECRET = 0x00000020L,
            POLICY_CREATE_PRIVILEGE = 0x00000040L,
            POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L,
            POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L,
            POLICY_AUDIT_LOG_ADMIN = 0x00000200L,
            POLICY_SERVER_ADMIN = 0x00000400L,
            POLICY_LOOKUP_NAMES = 0x00000800L,
            POLICY_NOTIFICATION = 0x00001000L
        }

        // Returns the Local Security Authority rights granted to the account
        public IList<string> GetRights(string accountName)
        {
            IList<string> rights = new List<string>();
            string errorMessage = string.Empty;

            long winErrorCode = 0;
            IntPtr sid = IntPtr.Zero;
            int sidSize = 0;
            StringBuilder domainName = new StringBuilder();
            int nameSize = 0;
            int accountType = 0;

            LookupAccountName(string.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType);

            domainName = new StringBuilder(nameSize);
            sid = Marshal.AllocHGlobal(sidSize);

            if (!LookupAccountName(string.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType))
            {
                winErrorCode = GetLastError();
                errorMessage = ("LookupAccountName failed: " + winErrorCode);
                throw new Win32Exception((int)winErrorCode, errorMessage);
            }
            else
            {
                LSA_UNICODE_STRING systemName = new LSA_UNICODE_STRING();

                IntPtr policyHandle = IntPtr.Zero;
                IntPtr userRightsPtr = IntPtr.Zero;
                int countOfRights = 0;

                LSA_OBJECT_ATTRIBUTES objectAttributes = CreateLSAObject();

                uint policyStatus = LsaOpenPolicy(ref systemName, ref objectAttributes, Access, out policyHandle);
                winErrorCode = LsaNtStatusToWinError(policyStatus);

                if (winErrorCode != 0)
                {
                    errorMessage = string.Format("OpenPolicy failed: {0}.", winErrorCode);
                    throw new Win32Exception((int)winErrorCode, errorMessage);
                }
                else
                {
                    try
                    {
                        uint result = LsaEnumerateAccountRights(policyHandle, sid, out userRightsPtr, out countOfRights);
                        winErrorCode = LsaNtStatusToWinError(result);
                        if (winErrorCode != 0)
                        {
                            if (winErrorCode == 2)
                            {
                                return new List<string>();
                            }
                            errorMessage = string.Format("LsaEnumerateAccountRights failed: {0}", winErrorCode);
                            throw new Win32Exception((int)winErrorCode, errorMessage);
                        }

                        Int32 ptr = userRightsPtr.ToInt32();
                        LSA_UNICODE_STRING userRight;

                        for (int i = 0; i < countOfRights; i++)
                        {
                            userRight = (LSA_UNICODE_STRING)Marshal.PtrToStructure(new IntPtr(ptr), typeof(LSA_UNICODE_STRING));
                            string userRightStr = Marshal.PtrToStringAuto(userRight.Buffer);
                            rights.Add(userRightStr);
                            ptr += Marshal.SizeOf(userRight);
                        }
                    }
                    finally
                    {
                        LsaClose(policyHandle);
                    }
                }
                FreeSid(sid);
            }
            return rights;
        }

        // Adds a privilege to an account
        public void SetRight(string accountName, string privilegeName)
        {
            long winErrorCode = 0;
            string errorMessage = string.Empty;

            IntPtr sid = IntPtr.Zero;
            int sidSize = 0;
            StringBuilder domainName = new StringBuilder();
            int nameSize = 0;
            int accountType = 0;

            LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType);

            domainName = new StringBuilder(nameSize);
            sid = Marshal.AllocHGlobal(sidSize);

            if (!LookupAccountName(string.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType))
            {
                winErrorCode = GetLastError();
                errorMessage = string.Format("LookupAccountName failed: {0}", winErrorCode);
                throw new Win32Exception((int)winErrorCode, errorMessage);
            }
            else
            {
                LSA_UNICODE_STRING systemName = new LSA_UNICODE_STRING();
                IntPtr policyHandle = IntPtr.Zero;
                LSA_OBJECT_ATTRIBUTES objectAttributes = CreateLSAObject();

                uint resultPolicy = LsaOpenPolicy(ref systemName, ref objectAttributes, Access, out policyHandle);
                winErrorCode = LsaNtStatusToWinError(resultPolicy);

                if (winErrorCode != 0)
                {
                    errorMessage = string.Format("OpenPolicy failed: {0} ", winErrorCode);
                    throw new Win32Exception((int)winErrorCode, errorMessage);
                }
                else
                {
                    try
                    {
                        LSA_UNICODE_STRING[] userRights = new LSA_UNICODE_STRING[1];
                        userRights[0] = new LSA_UNICODE_STRING();
                        userRights[0].Buffer = Marshal.StringToHGlobalUni(privilegeName);
                        userRights[0].Length = (UInt16)(privilegeName.Length * UnicodeEncoding.CharSize);
                        userRights[0].MaximumLength = (UInt16)((privilegeName.Length + 1) * UnicodeEncoding.CharSize);

                        uint res = LsaAddAccountRights(policyHandle, sid, userRights, 1);
                        winErrorCode = LsaNtStatusToWinError(res);
                        if (winErrorCode != 0)
                        {
                            errorMessage = string.Format("LsaAddAccountRights failed: {0}", winErrorCode);
                            throw new Win32Exception((int)winErrorCode, errorMessage);
                        }
                    }
                    finally
                    {
                        LsaClose(policyHandle);
                    }
                }
                FreeSid(sid);
            }
        }

        private static LSA_OBJECT_ATTRIBUTES CreateLSAObject()
        {
            LSA_OBJECT_ATTRIBUTES newInstance = new LSA_OBJECT_ATTRIBUTES();

            newInstance.Length = 0;
            newInstance.RootDirectory = IntPtr.Zero;
            newInstance.Attributes = 0;
            newInstance.SecurityDescriptor = IntPtr.Zero;
            newInstance.SecurityQualityOfService = IntPtr.Zero;

            return newInstance;
        }
    }

    // Local security rights managed by the Local Security Authority
    public class LocalSecurityAuthorityRights
    {
        // Log on as a service right
        public const string LogonAsService = "SeServiceLogonRight";
        // Log on as a batch job right
        public const string LogonAsBatchJob = "SeBatchLogonRight";
        // Interactive log on right
        public const string InteractiveLogon = "SeInteractiveLogonRight";
        // Network log on right
        public const string NetworkLogon = "SeNetworkLogonRight";
        // Generate security audit logs right
        public const string GenerateSecurityAudits = "SeAuditPrivilege";
    }

    /* added wrapper for PowerShell */

    public class LSAWrapper
    {
        public static IList<string> GetRights(string accountName)
        {
            return new LocalSecurityAuthorityController().GetRights(accountName);
        }

        public static void SetRight(string accountName, string privilegeName)
        {
            new LocalSecurityAuthorityController().SetRight(accountName, privilegeName);
        }
    }
}

Finally it is easy enough to check the rights:

var rights = LSAWrapper.GetRights("myMachineName\\TestUser");
if (rights.Contains(LocalSecurityAuthorityRights.LogonAsService))
{
    Console.WriteLine("User has right to logon as a service");
}
else
{
    Console.WriteLine("User doesn't have right to logon as a service");
}

UPDATE: Check also @robertburke answer - he seem to came up with good improvements regarding x64 support and memory management.

Sasha
  • 8,537
  • 4
  • 49
  • 76
  • 1
    This can have an overflow error on the line: Int32 ptr = userRightsPtr.ToInt32(); on x64 systems. Using: long ptr = userRightsPtr.ToInt64(); instead seems to work fine on x64. – EricRRichards Aug 09 '16 at 21:21
  • @EricRRichards, thanks for pointing this. I'm considering to edit my answer. Can you confirm it still works on x86 as well with ToInt64() conversion? I'm afraid that it might return 4 neighbouring bytes and lead to unexpected result and I can't verify that now. – Sasha Aug 10 '16 at 10:34
  • Unfortunately I don't have a 32-bit machine to check against at the moment – EricRRichards Aug 10 '16 at 11:47
  • 1
    You should not cast the pointer to either `int` or `long` instead use unsafe code and pointer arithmetic: void* ptr = userRightsPtr.ToPointer(); ptr = (byte*)ptr + Marshal.SizeOf(userRight); – LunicLynx Sep 07 '16 at 07:57
  • This still does return error code 2 in the case that the user has no rights. I added an `if (winErrorCode ==2){return null;}` statement to remedy that, although I don't know if there is another case of error code 2 where it would be useful to throw an exception. – Jessie Westlake Jan 29 '20 at 18:50
  • 1
    I added another answer to expand upon this excellent solution. My answer shows how to change a section of this so it is portable from 32-bit to 64-bit without changing code, or using unsafe code. – robertburke Apr 16 '20 at 04:32
  • Thank you @robertburke for improvements - I've added note to check your answer. It would be nice though to tell more precisely where LsaFreeMemory should go – Sasha Apr 17 '20 at 10:02
  • I updated my answer with the call to LsaFreeMemory. – robertburke Apr 17 '20 at 22:24
  • 1
    Additionally, to test on a 64-bit system, just change the project type from 64-bit to 32-bit. Projects are usually AnyCPU and often have "Prefer 32 bit" checked, which will cause an EXE project to run as 32-bit. To see if you are a 64-bit or 32-bit process: Console.WriteLine(Environment.Is64BitProcess); – robertburke Apr 17 '20 at 22:31
1

Do you have a token for that account? If so you can call GetTokenInformation with TokenInformationClass == TokenPrivileges. That will return the list of privileges associated with the token and whether or not they have been activated.

If the account isn't logged in and thus you can't get their token, it becomes much harder. You can't query for the privileges allowed for the account let alone the privileges that any of the account's groups bring into the token. You would need to logon the account to retrieve a token and then follow the previous advice.

Avilo
  • 1,204
  • 7
  • 7
1

An edit to Sasha's solution:

For 32/64 bit portability, you don't need to have different code or use unsafe code. Rather use:

                LSA_UNICODE_STRING userRight;

                for (int i = 0; i < countOfRights; i++)
                {
                    var indexPointer = IntPtr.Add(userRightsPtr, i * Marshal.SizeOf(typeof(LSA_UNICODE_STRING)));
                    userRight = (LSA_UNICODE_STRING)Marshal.PtrToStructure(indexPointer, typeof(LSA_UNICODE_STRING));
                    string userRightStr = Marshal.PtrToStringAuto(userRight.Buffer);
                    rights.Add(userRightStr);
                }

                LsaFreeMemory(userRightsPtr);

Additionally, use LsaFreeMemory to free userRightsPtr when done with it. See doc for LsaEnumerateAccountRights. This states that you must free the user rights pointer returned by LsaEnumerateAccountRights using LsaFreeMemory .

    [DllImport("advapi32.dll")]
    private static extern int LsaFreeMemory(IntPtr ObjectHandle);

Also, for built-in machine account, just pass the user name without domain, example: IIS_IUSRS

robertburke
  • 511
  • 1
  • 5
  • 11
0

Have a look at using

WindowsIdentity.GetCurrent Method and WindowsPrincipal Class

Adriaan Stander
  • 162,879
  • 31
  • 289
  • 284