Why might LsaAddAccountRights return STATUS_INVALID_PARAMETER?
Asked Answered
S

6

8

Here's some C# source code which 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 variable values at runtime are as follows:

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

When caught, the message within the Win32Exception is: "The parameter is incorrect"

The code is running on Windows Web Server 2008. I can verify that the account does exist, and this code works fine on another server... I'm not sure if this could have been caused by Windows 2008 SP2. I'm thinking that I've forgotten to install something, but I can't think what...

The code is from: http://weblogs.asp.net/avnerk/archive/2007/05/10/granting-user-rights-in-c.aspx

Shaunna answered 18/7, 2009 at 16:12 Comment(0)
S
2

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

Shaunna answered 18/7, 2009 at 19:16 Comment(0)
S
10

Following the provided link through to the code at 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 (an LSA_TRANSLATED_SID2 struct) contains a pointer that points at memory that is freed by the call to Win32Sec.LsaFreeMemory. Using the pointer after the memory is freed is bad practice and will have unpredictable results -- it might even "work".

Tweaking the code at the link by using the SecurityIdentifier class (.Net 2 and above) along a little cleanup of unneeded code avoids the 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;
      }
   }
}
Salted answered 22/1, 2013 at 22:29 Comment(2)
'it might even "work"' describes my experience. That code appeared to only have issues with built-in groups, everything else returned. I'd get "Security ID structure invalid" but then I got this error (87). I don't work often with the Win32 API but Win32Sec.LsaFreeMemory did catch my eye. Although, I figured "whomever wrote this probably knows best".Bennybenoit
Also, suggested edit queue is full? Link to original code is dead, here's a web archive version: web.archive.org/web/20151218044501/http://www.hightechtalks.com/…Bennybenoit
S
2

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

Shaunna answered 18/7, 2009 at 19:16 Comment(0)
L
2

lts.Sid is freed before returning in GetSIDInformation. Moving the codes of GetSIDInformation out. It worked fine 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));
    }
Latent answered 26/4, 2013 at 3:4 Comment(0)
C
1

I came across the same error when calling LsaAddAccountRights and I found out I was using sizeof(char) instead of sizeof(wchar) when initializing LSA_UNICODE_STRING.

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

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

Should be something like:

 lus.Length = (ushort)(s.Length * UnicodeEncoding.CharSize);
 lus.MaximumLength = (ushort)(lus.Length + UnicodeEncoding.CharSize);
Cornelius answered 8/4, 2011 at 7:30 Comment(0)
L
1

I was able to get this working on one box but then on another box it failed with the error you received:

System.ComponentModel.Win32Exception: The parameter is incorrect

I discovered that the root cause of this issue for me had to do with architecture of the process that was running the code. I was running a msbuild 32-bit process which worked fine, but when I used the 64-bit msbuild.exe to run this it failed with this error.

I hope that helps!

Regards, Brandon

Leatherworker answered 27/9, 2011 at 20:0 Comment(0)
S
0

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

Sparkman answered 19/4, 2012 at 22:46 Comment(1)
Downgrading is not a real option. Going forward with a fix is better than going backwards.Redundant

© 2022 - 2024 — McMap. All rights reserved.