如何强制 LDAP 在 .NET 中使用辅助本地 IP 地址?

发布于 2024-12-10 18:35:49 字数 748 浏览 0 评论 0原文

我需要访问防火墙后面的远程 LDAP 服务器(使用 C#/.NET)进行用户身份验证。

远程站点的防火墙设置为允许特定的 IP 地址,但它不是服务器上的主 IP 地址,即默认情况下,到远程 LDAP 服务器的连接将使用主 IP。

如何强制 LDAP 在 .NET 中使用辅助 IP 地址?


我专门使用 System.DirectoryServices.DirectoryEntrySystem.DirectoryServices.AccountManagement.PrincipalContext 类,但我没有看到明显的方法来控制本地端点。

这就是我使用 TcpClient 绑定到本地 IP 地址的方式:

using System.Net;
using System.Net.Sockets;

IPEndPoint localEndpoint = ...get relevant local ip address that needs to connect
TcpClient tcp = new TcpClient( localEndpoint );
...do stuff with tcp client

注意:在这种情况下无法更改服务器的主 IP 地址。

PS:当我使用“绑定”一词时" 这里表示绑定到本地端点,LDAP 使用“绑定”一词来连接/验证目录。

I need to access a remote LDAP server behind a firewall (using C#/.NET) for user authentication.

The firewall at the remote site is set to allow a specific IP address, but it is not the primary IP Address on the server i.e. by default, the connection to the remote LDAP server would use the primary IP.

How do you force LDAP to use a secondary IP Address in .NET?


I am specifically using the System.DirectoryServices.DirectoryEntry and System.DirectoryServices.AccountManagement.PrincipalContext classes, but there isn't obvious way I could see to control the local end point.

This is how I would bind to a local IP address using a TcpClient:

using System.Net;
using System.Net.Sockets;

IPEndPoint localEndpoint = ...get relevant local ip address that needs to connect
TcpClient tcp = new TcpClient( localEndpoint );
...do stuff with tcp client

NB: The primary IP address of the server cannot be changed in this instance.

PS: While I use the word "bind" here to mean binding to a local end point, LDAP uses the word "bind" for connecting/authenticating to the directory.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

许一世地老天荒 2024-12-17 18:35:49

您必须 PInvoke wldap32.dll 中的 ldap_* 函数。它看起来像 中的 LDAP_OPT_SOCKET_BIND_ADDRESSES 选项会话选项将让您控制要使用的本地端点。 System.DirectoryServices.Protocols 是此 API 的托管版本,但我在 LdapSessionOptions

这对我有用:

class LDAPConnection : IDisposable
{
    public static bool IsValidCredentials(string domain, string localAddress, 
                       string usernameDomain, string username, SecureString password)
    {
        try
        {
            using (LDAPConnection ldapConnection = 
                       new LDAPConnection(domain, LDAP_PORT, localAddress))
            {
                ldapConnection.Bind(usernameDomain, username, password);
                return true;
            }
        }
        catch
        {
            return false;
        }
    }

    protected IntPtr _ld;
    protected List<IntPtr> _stringPointers;

    public LDAPConnection(string hostname, uint port, params string[] localAddresses)
    {
        _stringPointers = new List<IntPtr>();

        _ld = LdapInit(hostname, port);
        LdapSetOption(_ld, LDAP_OPT_VERSION, LDAP_VERSION3);

        if (localAddresses != null && localAddresses.Length > 0)
        {
            string addr = string.Join(" ", localAddresses);
            IntPtr pStr = LdapSetOption(_ld, LDAP_OPT_SOCKET_BIND_ADDRESSES, addr);
            _stringPointers.Add(pStr);
        }
    }

    public void Bind(string domain, string username, SecureString password)
    {
        LdapBind(_ld, domain, username, password);
    }

    public void Dispose()
    {
        if (_ld != NULL) ldap_unbind_s(_ld);
        foreach (IntPtr pString in _stringPointers)
        {
            Marshal.FreeHGlobal(pString);
        }
    }

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint LdapGetLastError();

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    protected static extern IntPtr ldap_init(string HostName, uint PortNumber);

    //caller must call ldap_unbind or ldap_unbind_s on the return value
    public static IntPtr LdapInit(string hostname, uint port)
    {
        IntPtr ld = ldap_init(hostname, port);
        if (ld == NULL)
        {
            throw new Exception("LDAP Error: " + LdapGetLastError());
        }
        return ld;
    }

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint ldap_unbind_s(IntPtr ld);

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint ldap_set_option(IntPtr ld, uint option, ref IntPtr invalue);

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint ldap_set_option(IntPtr ld, uint option, ref uint invalue);

    //caller must free IntPtr after calling ldap_unbind_s
    public static IntPtr LdapSetOption(IntPtr ld, uint option, string invalue)
    {
        IntPtr pString = Marshal.StringToHGlobalUni(invalue);
        bool exception = true;

        try
        {
            uint errorCode = ldap_set_option(ld, option, ref pString);
            if (errorCode != LDAP_SUCCESS)
            {
                throw new Exception("LDAP Error: " + errorCode);
            }
            exception = false;
            return pString;
        }
        finally
        {
            if (exception && pString != NULL)
            {
                Marshal.FreeHGlobal(pString);
            }
        }
    }

    public static void LdapSetOption(IntPtr ld, uint option, uint invalue)
    {
        uint errorCode = ldap_set_option(ld, option, ref invalue);
        if (errorCode != LDAP_SUCCESS)
        {
            throw new Exception("LDAP Error: " + errorCode);
        }
    }

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    protected static extern uint ldap_bind_s(IntPtr ld, IntPtr dn, IntPtr cred, uint method);

    public static void LdapBind(IntPtr ld, string domain, 
                                string username, SecureString password)
    {
        IntPtr cred = SEC_WINNT_AUTH_IDENTITY.GetUnicode(username, password, domain);
        try
        {
            uint errorCode = ldap_bind_s(ld, NULL, cred, LDAP_AUTH_NEGOTIATE);
            if (errorCode != LDAP_SUCCESS)
            {
                throw new Exception("LDAP Error: " + errorCode);
            }
        }
        finally
        {
            if (cred != NULL) SEC_WINNT_AUTH_IDENTITY.Free(cred);
        }
    }

    public const uint LDAP_PORT = 389;
    public const uint LDAP_VERSION3 = 3;
    public const uint LDAP_SUCCESS = 0;
    public const uint LDAP_OPT_VERSION = 0x11;
    public const uint LDAP_OPT_SOCKET_BIND_ADDRESSES = 0x44;
    public const uint LDAP_AUTH_NEGOTIATE = 0x486;
    public static readonly IntPtr NULL = IntPtr.Zero;
    public const uint SEC_WINNT_AUTH_IDENTITY_ANSI = 1;
    public const uint SEC_WINNT_AUTH_IDENTITY_UNICODE = 2;

    [StructLayout(LayoutKind.Sequential)]
    public struct SEC_WINNT_AUTH_IDENTITY
    {
        public IntPtr User;
        public int UserLength;
        public IntPtr Domain;
        public int DomainLength;
        public IntPtr Password;
        public int PasswordLength;
        public uint Flags;

        public static IntPtr GetUnicode(string username, 
                                        SecureString password, string domain)
        {
            SEC_WINNT_AUTH_IDENTITY swai = new SEC_WINNT_AUTH_IDENTITY();
            bool exception = true;
            try
            {
                swai.User = Marshal.StringToHGlobalUni(username);
                swai.UserLength = username.Length;
                swai.Domain = Marshal.StringToHGlobalUni(domain);
                swai.DomainLength = domain.Length;
                swai.Password = Marshal.SecureStringToGlobalAllocUnicode(password);
                swai.PasswordLength = password.Length;
                swai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

                IntPtr pSwai = Marshal.AllocHGlobal(Marshal.SizeOf(swai));
                try
                {
                    Marshal.StructureToPtr(swai, pSwai, false);
                    exception = false;
                    return pSwai;
                }
                finally
                {
                    if (exception && pSwai != NULL)
                    {
                        Marshal.FreeHGlobal(pSwai);
                    }
                }
            }
            finally
            {
                if (exception)
                {
                    if (swai.User != NULL) Marshal.FreeHGlobal(swai.User);
                    if (swai.Domain != NULL) Marshal.FreeHGlobal(swai.Domain);
                    if (swai.Password != NULL)
                    {
                        Marshal.ZeroFreeGlobalAllocUnicode(swai.Password);
                    }
                }
            }
        }

        public static void Free(IntPtr pSwai)
        {
            SEC_WINNT_AUTH_IDENTITY swai = 
                (SEC_WINNT_AUTH_IDENTITY)Marshal.PtrToStructure(
                    pSwai, typeof(SEC_WINNT_AUTH_IDENTITY));
            if (swai.Flags == SEC_WINNT_AUTH_IDENTITY_ANSI)
            {
                Marshal.ZeroFreeGlobalAllocAnsi(swai.Password);
            }
            else
            {
                Marshal.ZeroFreeGlobalAllocUnicode(swai.Password);
            }
            Marshal.FreeHGlobal(swai.Domain);
            Marshal.FreeHGlobal(swai.User);
            Marshal.FreeHGlobal(pSwai);
        }
    }

示例用法:

LDAPConnection.IsValidCredentials("leaf.domain.com", "10.0.0.1", "leaf", 
    "myusername", password);

You'll have to PInvoke the ldap_* functions in wldap32.dll. It looks like the LDAP_OPT_SOCKET_BIND_ADDRESSES option in Session Options will let you control which local endpoint to use. The System.DirectoryServices.Protocols is the managed version of this API, but I don't see a corresponding property in LdapSessionOptions.

This works for me:

class LDAPConnection : IDisposable
{
    public static bool IsValidCredentials(string domain, string localAddress, 
                       string usernameDomain, string username, SecureString password)
    {
        try
        {
            using (LDAPConnection ldapConnection = 
                       new LDAPConnection(domain, LDAP_PORT, localAddress))
            {
                ldapConnection.Bind(usernameDomain, username, password);
                return true;
            }
        }
        catch
        {
            return false;
        }
    }

    protected IntPtr _ld;
    protected List<IntPtr> _stringPointers;

    public LDAPConnection(string hostname, uint port, params string[] localAddresses)
    {
        _stringPointers = new List<IntPtr>();

        _ld = LdapInit(hostname, port);
        LdapSetOption(_ld, LDAP_OPT_VERSION, LDAP_VERSION3);

        if (localAddresses != null && localAddresses.Length > 0)
        {
            string addr = string.Join(" ", localAddresses);
            IntPtr pStr = LdapSetOption(_ld, LDAP_OPT_SOCKET_BIND_ADDRESSES, addr);
            _stringPointers.Add(pStr);
        }
    }

    public void Bind(string domain, string username, SecureString password)
    {
        LdapBind(_ld, domain, username, password);
    }

    public void Dispose()
    {
        if (_ld != NULL) ldap_unbind_s(_ld);
        foreach (IntPtr pString in _stringPointers)
        {
            Marshal.FreeHGlobal(pString);
        }
    }

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint LdapGetLastError();

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    protected static extern IntPtr ldap_init(string HostName, uint PortNumber);

    //caller must call ldap_unbind or ldap_unbind_s on the return value
    public static IntPtr LdapInit(string hostname, uint port)
    {
        IntPtr ld = ldap_init(hostname, port);
        if (ld == NULL)
        {
            throw new Exception("LDAP Error: " + LdapGetLastError());
        }
        return ld;
    }

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint ldap_unbind_s(IntPtr ld);

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint ldap_set_option(IntPtr ld, uint option, ref IntPtr invalue);

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint ldap_set_option(IntPtr ld, uint option, ref uint invalue);

    //caller must free IntPtr after calling ldap_unbind_s
    public static IntPtr LdapSetOption(IntPtr ld, uint option, string invalue)
    {
        IntPtr pString = Marshal.StringToHGlobalUni(invalue);
        bool exception = true;

        try
        {
            uint errorCode = ldap_set_option(ld, option, ref pString);
            if (errorCode != LDAP_SUCCESS)
            {
                throw new Exception("LDAP Error: " + errorCode);
            }
            exception = false;
            return pString;
        }
        finally
        {
            if (exception && pString != NULL)
            {
                Marshal.FreeHGlobal(pString);
            }
        }
    }

    public static void LdapSetOption(IntPtr ld, uint option, uint invalue)
    {
        uint errorCode = ldap_set_option(ld, option, ref invalue);
        if (errorCode != LDAP_SUCCESS)
        {
            throw new Exception("LDAP Error: " + errorCode);
        }
    }

    [DllImport("wldap32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    protected static extern uint ldap_bind_s(IntPtr ld, IntPtr dn, IntPtr cred, uint method);

    public static void LdapBind(IntPtr ld, string domain, 
                                string username, SecureString password)
    {
        IntPtr cred = SEC_WINNT_AUTH_IDENTITY.GetUnicode(username, password, domain);
        try
        {
            uint errorCode = ldap_bind_s(ld, NULL, cred, LDAP_AUTH_NEGOTIATE);
            if (errorCode != LDAP_SUCCESS)
            {
                throw new Exception("LDAP Error: " + errorCode);
            }
        }
        finally
        {
            if (cred != NULL) SEC_WINNT_AUTH_IDENTITY.Free(cred);
        }
    }

    public const uint LDAP_PORT = 389;
    public const uint LDAP_VERSION3 = 3;
    public const uint LDAP_SUCCESS = 0;
    public const uint LDAP_OPT_VERSION = 0x11;
    public const uint LDAP_OPT_SOCKET_BIND_ADDRESSES = 0x44;
    public const uint LDAP_AUTH_NEGOTIATE = 0x486;
    public static readonly IntPtr NULL = IntPtr.Zero;
    public const uint SEC_WINNT_AUTH_IDENTITY_ANSI = 1;
    public const uint SEC_WINNT_AUTH_IDENTITY_UNICODE = 2;

    [StructLayout(LayoutKind.Sequential)]
    public struct SEC_WINNT_AUTH_IDENTITY
    {
        public IntPtr User;
        public int UserLength;
        public IntPtr Domain;
        public int DomainLength;
        public IntPtr Password;
        public int PasswordLength;
        public uint Flags;

        public static IntPtr GetUnicode(string username, 
                                        SecureString password, string domain)
        {
            SEC_WINNT_AUTH_IDENTITY swai = new SEC_WINNT_AUTH_IDENTITY();
            bool exception = true;
            try
            {
                swai.User = Marshal.StringToHGlobalUni(username);
                swai.UserLength = username.Length;
                swai.Domain = Marshal.StringToHGlobalUni(domain);
                swai.DomainLength = domain.Length;
                swai.Password = Marshal.SecureStringToGlobalAllocUnicode(password);
                swai.PasswordLength = password.Length;
                swai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

                IntPtr pSwai = Marshal.AllocHGlobal(Marshal.SizeOf(swai));
                try
                {
                    Marshal.StructureToPtr(swai, pSwai, false);
                    exception = false;
                    return pSwai;
                }
                finally
                {
                    if (exception && pSwai != NULL)
                    {
                        Marshal.FreeHGlobal(pSwai);
                    }
                }
            }
            finally
            {
                if (exception)
                {
                    if (swai.User != NULL) Marshal.FreeHGlobal(swai.User);
                    if (swai.Domain != NULL) Marshal.FreeHGlobal(swai.Domain);
                    if (swai.Password != NULL)
                    {
                        Marshal.ZeroFreeGlobalAllocUnicode(swai.Password);
                    }
                }
            }
        }

        public static void Free(IntPtr pSwai)
        {
            SEC_WINNT_AUTH_IDENTITY swai = 
                (SEC_WINNT_AUTH_IDENTITY)Marshal.PtrToStructure(
                    pSwai, typeof(SEC_WINNT_AUTH_IDENTITY));
            if (swai.Flags == SEC_WINNT_AUTH_IDENTITY_ANSI)
            {
                Marshal.ZeroFreeGlobalAllocAnsi(swai.Password);
            }
            else
            {
                Marshal.ZeroFreeGlobalAllocUnicode(swai.Password);
            }
            Marshal.FreeHGlobal(swai.Domain);
            Marshal.FreeHGlobal(swai.User);
            Marshal.FreeHGlobal(pSwai);
        }
    }

Example usage:

LDAPConnection.IsValidCredentials("leaf.domain.com", "10.0.0.1", "leaf", 
    "myusername", password);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文