10 Comments

If you want to grant “Log on as a service” rights to a user account, using PowerShell you can use the secedit.exe tool, using a *.inf security template file.

 

[Script]

secedit /import /db secedit.sdb /cfg "C:\Temp\MySecurityTemplate.inf" /silent >nul

gpupdate /force

 

[MySecurityTemplate.inf]

[Unicode]
Unicode=yes
[Version]
signature="$CHICAGO$"
Revision=1
[Registry Values]
[Profile Description]
Description=Set log on as a service etc
[Privilege Rights]
SeServiceLogonRight = *S-1-5-21-3051157669-1303869509-1073059025-1019

 

You can generate the “C:\Temp\MySecurityTemplate.inf” by following the steps on http://msdn.microsoft.com/en-us/library/ms933182(WinEmbedded.5).aspx

 

[But in some cases, this was not what I wanted]

An other solution, is to P/Invoke the LSA functions in the advapi32.dl with C#.

Willy created a LSA wrapper for C#: http://www.developmentnow.com/g/36_2005_3_0_0_223925/LSA-functions.htm

I used this wrapper class to grant “Log on as a service” rights to an user account, using PowerShell

After building the assembly containing the LSA wrapper class, you can grant access with this PowerShell script:

 

[Test.ps1]

# [General]
"Set execution policy to [Unrestricted]"
Set-ExecutionPolicy Unrestricted

"Load assemblies"
$assemblyLsa = "C:\Temp\MyLsaWrapper.dll"
[System.Reflection.Assembly]::LoadFrom($assemblyLsa)

“Grant Log on as a service rights to the account MyComputer\User1”

[MyLsaWrapper.LsaWrapperCaller]::("MyComputer\User1", "SeServiceLogonRight")

 

 

[LsaWrapper in C#]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace MyLsaWrapper
{
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Management;
    using System.Runtime.CompilerServices;
    using System.ComponentModel;

    using LSA_HANDLE = IntPtr;

    [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 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);

    }
    /// <summary>
    /// This class is used to grant "Log on as a service", "Log on as a batchjob", "Log on localy" etc.
    /// to a user.
    /// </summary>
    public sealed class LsaWrapper : IDisposable
    {
        [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;
        }

        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 void AddPrivileges(string account, string privilege)
        {
            IntPtr pSid = GetSIDInformation(account);
            LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1];
            privileges[0] = InitLsaString(privilege);
            uint ret = Win32Sec.LsaAddAccountRights(lsaHandle, pSid, privileges, 1);
            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 void Dispose()
        {
            if (lsaHandle != IntPtr.Zero)
            {
                Win32Sec.LsaClose(lsaHandle);
                lsaHandle = IntPtr.Zero;
            }
            GC.SuppressFinalize(this);
        }
        ~LsaWrapper()
        {
            Dispose();
        }
        // helper functions

        IntPtr GetSIDInformation(string account)
        {
            LSA_UNICODE_STRING[] names = new LSA_UNICODE_STRING[1];
            LSA_TRANSLATED_SID2 lts;
            IntPtr tsids = IntPtr.Zero;
            IntPtr tdom = IntPtr.Zero;
            names[0] = InitLsaString(account);
            lts.Sid = IntPtr.Zero;
            Console.WriteLine("String account: {0}", names[0].Length);
            int ret = Win32Sec.LsaLookupNames2(lsaHandle, 0, 1, names, ref tdom, ref tsids);
            if (ret != 0)
                throw new Win32Exception(Win32Sec.LsaNtStatusToWinError(ret));
            lts = (LSA_TRANSLATED_SID2)Marshal.PtrToStructure(tsids,
            typeof(LSA_TRANSLATED_SID2));
            Win32Sec.LsaFreeMemory(tsids);
            Win32Sec.LsaFreeMemory(tdom);
            return lts.Sid;
        }

        static LSA_UNICODE_STRING InitLsaString(string s)
        {
            // Unicode strings max. 32KB
            if (s.Length > 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));
            return lus;
        }
    }
    public class LsaWrapperCaller
    {
        public static void AddPrivileges(string account, string privilege)
        {
            using (LsaWrapper lsaWrapper = new LsaWrapper())
            {
                lsaWrapper.AddPrivileges(account, privilege);
            }
        }
    }
}

10 Replies to “How to grant “Log on as a service” rights to an user account, using PowerShell”

  1. Van Lisdonk,

    This is of high interest to me…
    How would one go about using this?
    Does the C# code need to be compiled?

  2. After building the assembly containing the LSA wrapper class, you can grant access with this PowerShell script:

    So how do you build the assembly?

  3. You should use Microsoft Visual Studio to compile the code in an assembly. This will produce an assembly => MyLsaWrapper.dll, which can be used in powershell

  4. This doesn’t seem to work.

    I’ve created the DLL LsaWrapper.dll (size: 7,680 bytes).

    Here is my wrapper for the wrapper 🙂

    Run like so:
    Set-UserRightsAssignment.ps1 -Username bob -CreateATokenObject

    Script Wrapper:
    param (
    [String] $Username = $(throw “Parameter -Username [System.String] is required.”),
    [String] $Domain = $env:COMPUTERNAME,
    [Switch] $CreateATokenObject,
    [Switch] $ReplaceAProcessLevelToken,
    [Switch] $LockPagesInMemory,
    [Switch] $AdjustMemoryQuotasForAProcess,
    [Switch] $AddWorkstationsToDomain,
    [Switch] $ActAsPartOfTheOperatingSystem,
    [Switch] $ManageAuditingAndSecurityLog,
    [Switch] $TakeOwnershipOfFilesOrOtherObjects,
    [Switch] $LoadAndUnloadDeviceDrivers,
    [Switch] $ProfileSystemPerformance,
    [Switch] $ChangeTheSystemTime,
    [Switch] $ProfileSingleProcess,
    [Switch] $IncreaseSchedulingPriority,
    [Switch] $CreateAPagefile,
    [Switch] $CreatePermanentSharedObjects,
    [Switch] $BackUpFilesAndDirectories,
    [Switch] $RestoreFilesAndDirectories,
    [Switch] $ShutDownTheSystem,
    [Switch] $GenerateSecurityAudits,
    [Switch] $ModifyFirmwareEnvironmentValues,
    [Switch] $BypassTraverseChecking,
    [Switch] $ForceShutdownFromARemoteSystem,
    [Switch] $All
    )

    function Get-ScriptDirectory {
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    Split-Path $Invocation.MyCommand.Path
    }

    $scriptHome = [IO.DirectoryInfo] (Get-ScriptDirectory)

    $assemblyFilename = “LsaWrapper.dll”
    $assemblyLsa = [IO.FileInfo] (Join-Path $scriptHome $assemblyFilename)

    if (!$assemblyLsa.Exists)
    {
    Write-Error “Cannot find ‘$assemblyFilename’ in ‘$scriptHome’.”
    Exit 1
    }

    [System.Reflection.Assembly]::LoadFrom($assemblyLsa.Fullname)

    trap [System.Security.Principal.IdentityNotMappedException] {
    Write-Error “Account ‘$Domain\$Username’ cannot be found.”
    Exit 1
    }

    $sid = ([System.Security.Principal.NTAccount] “$Domain\$Username”).Translate([System.Security.Principal.SecurityIdentifier])

    if ($CreateATokenObject -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeCreateTokenPrivilege”)
    }
    if ($ReplaceAProcessLevelToken -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeAssignPrimaryTokenPrivilege”)
    }
    if ($LockPagesInMemory -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeLockMemoryPrivilege”)
    }
    if ($AdjustMemoryQuotasForAProcess -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeIncreaseQuotaPrivilege”)
    }
    if ($AddWorkstationsToDomain -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeMachineAccountPrivilege”)
    }
    if ($ActAsPartOfTheOperatingSystem -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeTcbPrivilege”)
    }
    if ($ManageAuditingAndSecurityLog -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeSecurityPrivilege”)
    }
    if ($TakeOwnershipOfFilesOrOtherObjects -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeTakeOwnershipPrivilege”)
    }
    if ($LoadAndUnloadDeviceDrivers -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeLoadDriverPrivilege”)
    }
    if ($ProfileSystemPerformance -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeSystemProfilePrivilege”)
    }
    if ($ChangeTheSystemTime -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeSystemtimePrivilege”)
    }
    if ($ProfileSingleProcess -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeProfileSingleProcessPrivilege”)
    }
    if ($IncreaseSchedulingPriority -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeIncreaseBasePriorityPrivilege”)
    }
    if ($CreateAPagefile -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeCreatePagefilePrivilege”)
    }
    if ($CreatePermanentSharedObjects -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeCreatePermanentPrivilege”)
    }
    if ($BackUpFilesAndDirectories -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeBackupPrivilege”)
    }
    if ($RestoreFilesAndDirectories -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeRestorePrivilege”)
    }
    if ($ShutDownTheSystem -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeShutdownPrivilege”)
    }
    if ($GenerateSecurityAudits -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeAuditPrivilege”)
    }
    if ($ModifyFirmwareEnvironmentValues -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeSystemEnvironmentPrivilege”)
    }
    if ($BypassTraverseChecking -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeChangeNotifyPrivilege”)
    }
    if ($ForceShutdownFromARemoteSystem -or $All)
    {
    [MyLsaWrapper.LsaWrapperCaller]::(“$Domain\$Username”, “SeRemoteShutdownPrivilege”)
    }

  5. From “GetSIDInformation”:
    lts = LSA_TRANSLATED_SID2)Marshal.PtrToStructure(tsids, typeof(LSA_TRANSLATED_SID2));
    Win32Sec.LsaFreeMemory(tsids);

    That is a problem: lts contains a IntPtr (Sid) that points at memory that belongs to tsids and is freed right after the pointer is copied to lts. Accessing this memory may or may not “work” but should never be done. A locally owned copy needs to be made prior to freeing tsids.

  6. Your code helped me a lot! I used it within a C# service project in the ProjectInstaller.OnAfterInstall() and it worked right away.

    Thank you
    rok

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Posts