2009-07-18 4 views
6

Voici un code source C# qui implémente une DLL non gérée (advapi32).Pourquoi LsaAddAccountRights peut-il retourner STATUS_INVALID_PARAMETER?

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

Les valeurs des variables lors de l'exécution sont les suivantes:

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

Quand on est pris, le message dans le Win32Exception est: "Le paramètre est incorrect"

Le code est en cours d'exécution sur Windows Web Server 2008. Je peux vérifier que le compte existe et que ce code fonctionne correctement sur un autre serveur ... Je ne sais pas si cela a pu être causé par Windows 2008 SP2. Je pense que je l'ai oublié d'installer quelque chose, mais je ne peux pas penser à ce que ...

Le code est de: http://weblogs.asp.net/avnerk/archive/2007/05/10/granting-user-rights-in-c.aspx

Répondre

0

J'ai rencontré la même erreur lors de l'appel de LsaAddAccountRights et j'ai découvert que j'utilisais sizeof (char) au lieu de sizeof (wchar) lors de l'initialisation de LSA_UNICODE_STRING.

j'ai vérifié le code à http://www.codeproject.com/KB/cs/lsadotnet.aspx et trouvé problème similaire:

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

Devrait être quelque chose comme:

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

J'ai pu obtenir ce travail sur une case mais sur une autre boîte, il Échec avec l'erreur que vous avez reçue:

System.ComponentModel.Win32Exception: le paramètre est incorrect

J'ai découvert que la cause première de ce problème pour moi était liée à l'architecture du processus qui exécutait le code. Je courais un processus de msbuild de 32 bits qui a fonctionné très bien, mais quand j'ai utilisé le msbuild.exe de 64 bits pour exécuter ceci il a échoué avec cette erreur.

J'espère que cela aide!

Cordialement, Brandon

0

Je trouve ce problème est lié à .NET 4.0. Rétrogradez votre projet vers .NET 3.5 et cela fonctionnera.

+0

La rétrogradation n'est pas une option réelle. Aller de l'avant avec un correctif est mieux que de revenir en arrière. – Snowy

6

En suivant le lien fourni à travers le 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 (e struct LSA_TRANSLATED_SID2) contient un pointeur qui pointe sur la mémoire qui est libéré par l'appel à Win32Sec.LsaFreeMemory. L'utilisation du pointeur après que la mémoire soit libérée est une mauvaise pratique et aura des résultats imprévisibles - il pourrait même "fonctionner". Le fait de peaufiner le code sur le lien en utilisant la classe SecurityIdentifier (.Net 2 et plus) le long d'un petit nettoyage de code inutile évite le problème de mémoire.

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

lts.Sid ​​est libéré avant de retourner dans GetSIDInformation. Déplacement des codes de GetSIDInformation. Cela a bien fonctionné pour .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)); 
    } 
Questions connexes