| File: System\DirectoryServices\Protocols\ldap\LdapConnection.Linux.cs | Web Access |
| Project: src\runtime\src\libraries\System.DirectoryServices.Protocols\src\System.DirectoryServices.Protocols.csproj (System.DirectoryServices.Protocols) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; using System.Text; namespace System.DirectoryServices.Protocols { public partial class LdapConnection { // Linux doesn't support setting FQDN so we mark the flag as if it is already set so we don't make a call to set it again. private bool _setFQDNDone = true; private void InternalInitConnectionHandle() { if ((LdapDirectoryIdentifier)_directoryIdentifier == null) { throw new NullReferenceException(); } _ldapHandle = new ConnectionHandle(); } private int InternalConnectToServer() { // In Linux you don't have to call Connect after calling init. You // directly call bind. However, we set the URI for the connection // here instead of during initialization because we need access to // the SessionOptions property to properly define it, which is not // available during init. Debug.Assert(!_ldapHandle.IsInvalid); string scheme; LdapDirectoryIdentifier directoryIdentifier = (LdapDirectoryIdentifier)_directoryIdentifier; if (directoryIdentifier.Connectionless) { scheme = "cldap://"; } else if (SessionOptions.SecureSocketLayer) { scheme = "ldaps://"; } else { scheme = "ldap://"; } string uris = null; string[] servers = directoryIdentifier.Servers; if (servers != null && servers.Length != 0) { StringBuilder temp = new StringBuilder(200); for (int i = 0; i < servers.Length; i++) { if (i != 0) { temp.Append(' '); } temp.Append(scheme); temp.Append(servers[i]); if (!servers[i].Contains(':')) { temp.Append(':'); temp.Append(directoryIdentifier.PortNumber); } } if (temp.Length != 0) { uris = temp.ToString(); } } else { uris = $"{scheme}:{directoryIdentifier.PortNumber}"; } int result = LdapPal.SetStringOption(_ldapHandle, LdapOption.LDAP_OPT_URI, uris); if (result == 0) { // Set the network timeout option to honor the Timeout property var timeout = new LDAP_TIMEVAL() { tv_sec = (int)(_connectionTimeOut.Ticks / TimeSpan.TicksPerSecond), tv_usec = (int)((_connectionTimeOut.Ticks % TimeSpan.TicksPerSecond) / TimeSpan.TicksPerMicrosecond) // Convert 100ns ticks to microseconds }; int timeoutResult = LdapPal.SetTimevalOption(_ldapHandle, LdapOption.LDAP_OPT_NETWORK_TIMEOUT, ref timeout); ErrorChecking.CheckAndSetLdapError(timeoutResult); } return result; } private int InternalBind(NetworkCredential tempCredential, SEC_WINNT_AUTH_IDENTITY_EX cred, BindMethod method) { int error; if (LocalAppContextSwitches.UseBasicAuthFallback) { if (tempCredential == null && (AuthType == AuthType.External || AuthType == AuthType.Kerberos)) { error = BindSasl(); } else { error = LdapPal.BindToDirectory(_ldapHandle, cred.user, cred.password); } } else { if (method == BindMethod.LDAP_AUTH_NEGOTIATE) { if (tempCredential == null) { error = BindSasl(); } else { // Explicit credentials were provided. If we call ldap_bind_s it will // return LDAP_NOT_SUPPORTED, so just skip the P/Invoke. error = (int)LdapError.NotSupported; } } else { // Basic and Anonymous are handled elsewhere. Debug.Assert(AuthType != AuthType.Anonymous && AuthType != AuthType.Basic); error = (int)LdapError.AuthUnknown; } } return error; } private int BindSasl() { SaslDefaultCredentials defaults = GetSaslDefaults(); IntPtr ptrToDefaults = Marshal.AllocHGlobal(Marshal.SizeOf(defaults)); Marshal.StructureToPtr(defaults, ptrToDefaults, false); try { // Bump up the protocol version because ldap_sasl_interactive_bind requires LDAP V3 else it returns LDAP_NOT_SUPPORTED and this ends up throwing LdapException: The feature is not supported. SessionOptions.ProtocolVersion = 3; return Interop.Ldap.ldap_sasl_interactive_bind(_ldapHandle, null, Interop.KerberosDefaultMechanism, IntPtr.Zero, IntPtr.Zero, Interop.LDAP_SASL_QUIET, LdapPal.SaslInteractionProcedure, ptrToDefaults); } finally { GC.KeepAlive(defaults); //Making sure we keep it in scope as we will still use ptrToDefaults Marshal.FreeHGlobal(ptrToDefaults); } } private SaslDefaultCredentials GetSaslDefaults() { var defaults = new SaslDefaultCredentials { mech = Interop.KerberosDefaultMechanism }; IntPtr outValue = IntPtr.Zero; int error = Interop.Ldap.ldap_get_option_ptr(_ldapHandle, LdapOption.LDAP_OPT_X_SASL_REALM, ref outValue); if (error == 0 && outValue != IntPtr.Zero) { defaults.realm = Marshal.PtrToStringAnsi(outValue); } error = Interop.Ldap.ldap_get_option_ptr(_ldapHandle, LdapOption.LDAP_OPT_X_SASL_AUTHCID, ref outValue); if (error == 0 && outValue != IntPtr.Zero) { defaults.authcid = Marshal.PtrToStringAnsi(outValue); } error = Interop.Ldap.ldap_get_option_ptr(_ldapHandle, LdapOption.LDAP_OPT_X_SASL_AUTHZID, ref outValue); if (error == 0 && outValue != IntPtr.Zero) { defaults.authzid = Marshal.PtrToStringAnsi(outValue); } return defaults; } } }