8

这是一些实现非托管 DLL (advapi32) 的 C# 源代码。

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

4

6 回答 6

10

按照提供的链接到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;
      }
   }
}
于 2013-01-22T22:29:03.177 回答
2

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

于 2009-07-18T19:16:24.027 回答
2

lts.Sid​​ 在返回 GetSIDInformation 之前被释放。将 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));
    }
于 2013-04-26T03:04:56.583 回答
1

我在调用 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);
于 2011-04-08T07:30:44.430 回答
1

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

System.ComponentModel.Win32Exception:参数不正确

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

我希望这会有所帮助!

问候,布兰登

于 2011-09-27T20:00:59.657 回答
0

我发现这个问题与 .NET 4.0 有关。将您的项目降级到 .NET 3.5 即可。

于 2012-04-19T22:46:01.587 回答