Supersedes https://dev.azure.com/dnceng/internal/_git/dotnet-runtime/pullrequest/17428 for release/6.0 branch.
Cherry picked from !17575
[DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)]
public static extern int ldap_set_option_referral([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref LdapReferralCallback outValue);
+ // Note that ldap_start_tls_s has a different signature across Windows LDAP and OpenLDAP
[DllImport(Libraries.OpenLdap, EntryPoint = "ldap_start_tls_s", CharSet = CharSet.Ansi)]
- public static extern int ldap_start_tls(ConnectionHandle ldapHandle, ref int ServerReturnValue, ref IntPtr Message, IntPtr ServerControls, IntPtr ClientControls);
+ public static extern int ldap_start_tls(ConnectionHandle ldapHandle, IntPtr serverControls, IntPtr clientControls);
[DllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_result", CharSet = CharSet.Ansi)]
public static extern int ldap_parse_result([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref int serverError, ref IntPtr dn, ref IntPtr message, ref IntPtr referral, ref IntPtr control, byte freeIt);
<Compile Include="System\DirectoryServices\Protocols\Interop\BerPal.Linux.cs" />
<Compile Include="System\DirectoryServices\Protocols\ldap\LdapConnection.Linux.cs" />
<Compile Include="System\DirectoryServices\Protocols\ldap\LdapSessionOptions.Linux.cs" />
+ <Compile Include="System\DirectoryServices\Protocols\ldap\LocalAppContextSwitches.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\SafeHandles.Linux.cs" />
+ <Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
+ <Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)Interop\Linux\OpenLdap\Interop.Ldap.cs">
<Link>Common\Interop\Linux\OpenLdap\Interop.Ldap.cs</Link>
</Compile>
}
}
- internal static int StartTls(ConnectionHandle ldapHandle, ref int ServerReturnValue, ref IntPtr Message, IntPtr ServerControls, IntPtr ClientControls) => Interop.Ldap.ldap_start_tls(ldapHandle, ref ServerReturnValue, ref Message, ServerControls, ClientControls);
+ internal static int StartTls(ConnectionHandle ldapHandle, ref int serverReturnValue, ref IntPtr message, IntPtr serverControls, IntPtr clientControls)
+ {
+ // Windows and Linux have different signatures for ldap_start_tls_s.
+ // On Linux, we don't have a serverReturnValue or the message/result parameter.
+ //
+ // So in the PAL here, just emulate.
+
+ int error = Interop.Ldap.ldap_start_tls(ldapHandle, serverControls, clientControls);
+
+ // On Windows, serverReturnValue only has meaning if the result code is LDAP_OTHER.
+ // If OpenLDAP returns that, we don't have a better code, so assign that through.
+ // If we get any other error, assign serverReturnValue to 0 since it shouldn't be read.
+ if (error == (int)ResultCode.Other)
+ {
+ serverReturnValue = error;
+ }
+ else
+ {
+ serverReturnValue = 0;
+ }
+
+ // We don't have a referrer/message/result value, so just set it to NULL.
+ message = IntPtr.Zero;
+ return error;
+ }
// openldap doesn't have a ldap_stop_tls function. Returning true as no-op for Linux.
internal static byte StopTls(ConnectionHandle ldapHandle) => 1;
_needDispose = true;
}
+ internal ConnectionHandle(string uri)
+ :base(true)
+ {
+ Interop.Ldap.ldap_initialize(out handle, uri);
+ _needDispose = true;
+ }
+
internal ConnectionHandle(IntPtr value, bool disposeHandle) : base(true)
{
_needDispose = disposeHandle;
throw new NullReferenceException();
}
- _ldapHandle = new ConnectionHandle();
+ _ldapHandle = new ConnectionHandle($"ldap://{hostname}:{((LdapDirectoryIdentifier)_directoryIdentifier).PortNumber}");
}
private int InternalConnectToServer()
private int InternalBind(NetworkCredential tempCredential, SEC_WINNT_AUTH_IDENTITY_EX cred, BindMethod method)
{
int error;
- if (tempCredential == null && (AuthType == AuthType.External || AuthType == AuthType.Kerberos))
+
+ if (LocalAppContextSwitches.UseBasicAuthFallback)
{
- error = BindSasl();
+ if (tempCredential == null && (AuthType == AuthType.External || AuthType == AuthType.Kerberos))
+ {
+ error = BindSasl();
+ }
+ else
+ {
+ error = LdapPal.BindToDirectory(_ldapHandle, cred.user, cred.password);
+ }
}
else
{
- error = LdapPal.BindToDirectory(_ldapHandle, cred.user, cred.password);
+ 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;
response.ResponseName = "1.3.6.1.4.1.1466.20037";
throw new TlsOperationException(response);
}
- else if (LdapErrorMappings.IsLdapError(error))
+
+ if (LdapErrorMappings.IsLdapError(error))
{
string errorMessage = LdapErrorMappings.MapResultCode(error);
throw new LdapException(error, errorMessage);
}
+
+ throw new LdapException(error);
}
}
finally
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+ internal static partial class LocalAppContextSwitches
+ {
+ private static int s_useBasicAuthFallback;
+
+ public static bool UseBasicAuthFallback
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => GetCachedSwitchValue("System.DirectoryServices.Protocols.UseBasicAuthFallback", ref s_useBasicAuthFallback);
+ }
+ }
+}