Why does LsaAddAccountRights return STATUS_INVALID_PARAMETER?

Here is some C # source code that implements an unmanaged DLL (advapi32).

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(); } int error = Win32Sec.LsaNtStatusToWinError((int)ret); throw new Win32Exception(error); } 

The values ​​of the variables at runtime are as follows:

 privilege: "SeServiceLogonRight" account: "named" ret: 3221225485 (STATUS_INVALID_PARAMETER) error: 87 

When a message is detected in a Win32Exception: "Invalid parameter"

The code works in Windows Web Server 2008. I can verify that the account exists, and this code works fine on another server ... I'm not sure if this could be caused by Windows 2000 SP2. I think I forgot to install something, but I can’t imagine that ...

Code: http://weblogs.asp.net/avnerk/archive/2007/05/10/granting-user-rights-in-c.aspx

+6
c # advapi32
source share
6 answers

I couldn't get this to work, so instead I used the source code of the CodeProject project, LSA Functions - Privileges and Impersonation that works beautifully.

+2
source share

Following the provided link to the code http://www.hightechtalks.com/csharp/lsa-functions-276626.html

  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; } 

lts (LSA_TRANSLATED_SID2 structure) contains a pointer that points to memory that is freed up by a call to Win32Sec.LsaFreeMemory. Using a pointer after freeing memory is bad practice and will have unpredictable results - it may even β€œwork”.

Fine-tuning the code by reference using the SecurityIdentifier class (.Net 2 and higher) with a slight cleaning of unnecessary code avoids a memory problem.

 using System; namespace Willys.LsaSecurity { using System.ComponentModel; using System.Runtime.InteropServices; using System.Security; using System.Security.Principal; 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")] internal static extern int LsaNtStatusToWinError(int NTSTATUS); [DllImport("advapi32")] internal static extern int LsaClose(IntPtr PolicyHandle); } sealed class Sid : IDisposable { public IntPtr pSid = IntPtr.Zero; public SecurityIdentifier sid = null; public Sid(string account) { sid = (SecurityIdentifier) (new NTAccount(account)).Translate(typeof(SecurityIdentifier)); Byte[] buffer = new Byte[sid.BinaryLength]; sid.GetBinaryForm(buffer, 0); pSid = Marshal.AllocHGlobal(sid.BinaryLength); Marshal.Copy(buffer, 0, pSid, sid.BinaryLength); } public void Dispose() { if (pSid != IntPtr.Zero) { Marshal.FreeHGlobal(pSid); pSid = IntPtr.Zero; } GC.SuppressFinalize(this); } ~Sid() { Dispose(); } } public sealed class LsaWrapper : IDisposable { 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) { uint ret = 0; using (Sid sid = new Sid(account)) { LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; privileges[0] = InitLsaString(privilege); ret = Win32Sec.LsaAddAccountRights(lsaHandle, sid.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 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; } } } 
+6
source share

lts.Sid ​​is freed before returning to GetSIDInformation. Moving GetSIDInformation codes. It worked great for .Net 4.5.

  public void AddPrivileges(string account, string privilege) { 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 ret1 = Win32Sec.LsaLookupNames2(lsaHandle, 0, 1, names, ref tdom, ref tsids); if (ret1 != 0) throw new Win32Exception(Win32Sec.LsaNtStatusToWinError(ret1)); lts = (LSA_TRANSLATED_SID2)Marshal.PtrToStructure(tsids, typeof(LSA_TRANSLATED_SID2)); IntPtr pSid = lts.Sid; //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); Win32Sec.LsaFreeMemory(tsids); Win32Sec.LsaFreeMemory(tdom); 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)); } 
+2
source share

I managed to get this work to work with one box, but then in another box it failed with the error you received:

System.ComponentModel.Win32Exception: parameter is invalid

I found that the main reason for this problem for me is related to the architecture of the process that executes the code. I ran the 32-bit msbuild process, which worked fine, but when I used the 64-bit msbuild.exe file to execute it, it could not execute this error.

I hope this helps!

Regards, Brandon

+1
source share

I encountered the same error when calling LsaAddAccountRights, and I found out that when initializing LSA_UNICODE_STRING I used sizeof (char) instead of sizeof (wchar).

I checked the code http://www.codeproject.com/KB/cs/lsadotnet.aspx and found a similar problem:

 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; } 

It should be something like:

  lus.Length = (ushort)(s.Length * UnicodeEncoding.CharSize); lus.MaximumLength = (ushort)(lus.Length + UnicodeEncoding.CharSize); 
0
source share

I found that this problem is related to .NET 4.0. Upgrade your project to .NET 3.5 and it will work.

0
source share

All Articles