2011-08-22 25 views
1

我在調用CertFreeCertificateContext方法時運行下面的代碼時遇到了訪問衝突異常。使用PInvoke在回調期間調用CertFreeCertificateContext以訪問ldap功能

我想這是因爲pServerCert參數在LdapServerCertDelegate上被Mashalled的方式,但一直無法找到解決方案。

using System; 
using System.Runtime.InteropServices; 

namespace ldaptest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      new LdapAuthenticationProvider().AuthenticateUser("a.qas", "a", "administrator", "test123"); 
     } 
    } 

    public class LdapAuthenticationProvider 
    { 
     public void AuthenticateUser(string server, string domain, string username, string password) 
     { 
      IntPtr ld = ldap_sslinit(server, LDAP_SSL_PORT, 1); 
      if (IntPtr.Zero == ld) throw new Exception("ldap_sslinit"); 

      var version = new IntPtr(LDAP_VERSION3); 
      var ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, version); 
      if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_PROTOCOL_VERSION 0x{0:X}", ret)); 

      var ldapOn = new IntPtr(LDAP_OPT_ON); 
      ret = ldap_set_option(ld, LDAP_OPT_SSL, ldapOn); 
      if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_SSL 0x{0:X}", ret)); 

      // note the necessity to convert the delegate to a function pointer 
      var callback = new LdapServerCertDelegate(AcceptAnySslCertificate); 
      IntPtr pFn = Marshal.GetFunctionPointerForDelegate(callback); 
      ret = ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, pFn); 
      if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_SERVER_CERTIFICATE 0x{0:X}", ret)); 

      var tv = new l_timeval(); 
      ret = ldap_connect(ld, ref tv); 
      if (ret != LDAP_SUCCESS) throw new Exception(string.Format("ldap_connect 0x{0:X}", ret)); 

      string login = string.Format(@"{0}\{1}", domain, username); 
      ret = ldap_bind_s(ld, login, password, LDAP_AUTH_SIMPLE); // triggers the callback 
      if (ret != LDAP_SUCCESS) throw new Exception(string.Format("ldap_bind_s 0x{0:X}", ret)); 

      ldap_unbind_s(ld); 
      Console.WriteLine("Success"); 
      Console.Read(); 
     } 

     private delegate bool LdapServerCertDelegate(IntPtr connection, IntPtr pServerCert); 

     private bool AcceptAnySslCertificate(IntPtr connection, IntPtr pServerCert) 
     { 
      CertFreeCertificateContext(pServerCert); // << System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. 
      return true; 
     } 

     #region crypt32.dll functions 

     [DllImport("crypt32.dll", CharSet = CharSet.Unicode)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     private static extern bool CertFreeCertificateContext(IntPtr pCertContext); 

     #endregion 

     #region Winldap.h definitions 

     private const int LDAP_PORT = 389; 
     private const uint LDAP_SSL_PORT = 636; 
     private const int LDAP_VERSION3 = 3; 
     private const int LDAP_OPT_PROTOCOL_VERSION = 17; 
     private const int LDAP_OPT_SSL = 10; 
     private const int LDAP_OPT_ON = 1; 
     private const int LDAP_AUTH_SIMPLE = 128; 
     private const int LDAP_OPT_SERVER_CERTIFICATE = 129; 
     private const uint LDAP_SUCCESS = 0; 

     [StructLayoutAttribute(LayoutKind.Sequential)] 
     private struct l_timeval 
     { 
      private int tv_sec; 
      private int tv_usec; 
     } 

     #endregion 

     #region wldap32.dll functions 

     /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_sslinit.asp?frame=true 
     [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_sslinitW", 
      SetLastError = true, CharSet = CharSet.Unicode)] 
     private static extern IntPtr ldap_sslinit(string hostName, uint portNumber, int secure); 

     [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW", 
      SetLastError = true, CharSet = CharSet.Unicode)] 
     private static extern uint ldap_set_option([In] IntPtr ldapHandle, int option, IntPtr invalue); 

     [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_unbind_s", 
      SetLastError = true, CharSet = CharSet.Unicode)] 
     private static extern uint ldap_unbind_s([In] IntPtr ldapHandle); 

     [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_connect", 
      SetLastError = true, CharSet = CharSet.Unicode)] 
     private static extern uint ldap_connect([In] IntPtr ld, [In] ref l_timeval timeout); 

     [DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_bind_sW", 
      SetLastError = true, CharSet = CharSet.Unicode)] 
     private static extern uint ldap_bind_s([In] IntPtr ld, string dn, string cred, uint method); 

     #endregion 
    } 
} 

回答

1

最好的結果是避免PInvoke和使用C++/CLI。

+0

我希望看到你如何P /調用此,如果你在乎更新你的答案。 – jp2code

+1

上面的代碼使用p/invoke,但我從來沒有解決訪問衝突,而是在C++/CLI中實現了LdapAuthenticationProvider :: AuthenticateUser,然後從C#程序集中調用它,結果證明這樣更容易。 – dice