为什么LsaAddAccountRights会返回STATUS_INVALID_PARAMETER?

时间:2009-07-18 16:12:35

标签: c# advapi32

这是一些实现非托管DLL的C#源代码(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);
}

运行时的变量值如下:

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

捕获时,Win32Exception中的消息是:“参数不正确”

代码在Windows Web Server 2008上运行。我可以验证该帐户是否存在,并且此代码在另一台服务器上运行良好...我不确定这是否可能是由Windows 2008 SP2引起的。我在想我已经忘了安装一些东西,但我想不出来......

代码来自:http://weblogs.asp.net/avnerk/archive/2007/05/10/granting-user-rights-in-c.aspx

6 个答案:

答案 0 :(得分:8)

按照提供的链接访问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结构)包含一个指针,该指针指向通过调用 Win32Sec.LsaFreeMemory 释放的内存。在释放内存后使用指针是不好的做法,并且会产生不可预测的结果 - 它甚至可能“起作用”。

使用SecurityIdentifier类(.Net 2及更高版本)调整链接上的代码以及对不需要的代码进行一些清理可以避免内存问题。

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

答案 1 :(得分:2)

我无法让它工作,所以我使用了CodeProject项目中的源代码,LSA Functions - Privileges and Impersonation效果很好。

答案 2 :(得分:2)

在GetSIDInformation中返回之前释放lts.Sid​​。 移出GetSIDInformation的代码。它适用于.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));
    }

答案 3 :(得分:1)

我能够在一个盒子上工作,但是在另一个盒子上它因为你收到错误而失败了:

System.ComponentModel.Win32Exception:参数不正确

我发现这个问题的根本原因与运行代码的进程的体系结构有关。我正在运行一个msbuild 32位进程工作正常,但是当我使用64位msbuild.exe运行它时,它失败并出现此错误。

我希望有所帮助!

此致 布兰登

答案 4 :(得分:0)

我在调用LsaAddAccountRights时遇到了同样的错误,我发现在初始化LSA_UNICODE_STRING时我使用的是sizeof(char)而不是sizeof(wchar)。

我检查了http://www.codeproject.com/KB/cs/lsadotnet.aspx处的代码,发现了类似的问题:

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

应该是这样的:

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

答案 5 :(得分:0)

我发现这个问题与.NET 4.0有关。将项目降级到.NET 3.5,它将起作用。