runtime: LdapSessionOptions.VerifyServerCertificate is not supported in non-Windows and error message is not helpful.

Description

LdapConnection fails to bind on Linux when running .NET 6.0.0-rc.2.21480.5 version of System.DirectoryServices.Protocols package and throws

Unhandled exception. System.DirectoryServices.Protocols.LdapException: The feature is not supported. 
   at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
   at System.DirectoryServices.Protocols.LdapConnection.Bind()

The same code works when using switching to version 5.0.0 of System.DirectoryServices.Protocols or running under windows

Reproduction Steps

Run the following code under Linux

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="System.DirectoryServices.Protocols" Version="6.0.0-rc.2.21480.5" />
    </ItemGroup>
</Project>
using System.DirectoryServices.Protocols;
using System.Net;

var di = new LdapDirectoryIdentifier(server: "ad.almirex.com", 389);
var connection = new LdapConnection(di, new NetworkCredential("username","password","almirex.dc"));
connection.Bind();
Console.WriteLine("Hello, World!");

If the package version is switched to 5.0.0, the above code works.

Expected behavior

The code works

Actual behavior

The code throws

Regression?

This worked in .NET 5 version of the package.

Known Workarounds

None

Configuration

Tested with .NET 6.0.100-rc.2.21505.57 SDK on WSL2 Ubuntu

Other information

No response

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 1
  • Comments: 45 (25 by maintainers)

Commits related to this issue

Most upvoted comments

Yes, we have several issues for trying to surface the error messages better, we can use this issue to track the work of making that better for .NET 7

Interestingly trying to do this from within the process via Environment.SetEnvironmentVariable(“LDAPTLS_REQCERT”,“never”); does not work.

This is most likely because .NET sets environment vars for a process in a separate layer to the actual process environment block. I came across this issue before when trying to set KRB5_TRACE=/dev/stdout (or any of the other GSSAPI env vars) to debug some stuff and the C layer was unable to see these env vars.

To set an env var at runtime for use by a called “PInvoke” function you need to essentially call setenv in libc like

using System;
using System.Runtime.InteropServices;

namespace Native
{
    public class Libc
    {
        [DllImport("libc")]
        public static extern void setenv(string name, string value);

        [DllImport("libc")]
        public static extern void unsetenv(string name);
    }
}

You would then do Native.Libc.setenv("LDAPTLS_REQCERT", "never"); to set the env var.

@akamud We’ve found out that this issue was related to the wrong configurated LDAP server & missing certificates. Also, I found that specifying FQDN as server address in the LdapDirectoryIdentifier not working on the Linux platform, probably because of this. Also, there’s the VerifyServerCertificate callback in which you must validate your LDAP certificate, but this functionality not working on the Linux (because, on the Linux, we are basically only passing through the Bind to the underlying native library), and I’m not sure about Mac platform.

Here’s the code sample of the class that I wrote to manage LDAP connections (I changed the code a bit so as not to violate the police company I work with):

string address = "some.ldap.server.com"; // You can specify port number here and then extract it from this string
int port = IsSsl ? 636 : 389;           
var directoryIdentifier = new LdapDirectoryIdentifier(address, port);
_ldapConnection = new LdapConnection(directoryIdentifier, credential, AuthType.Basic);
_ldapConnection.SessionOptions.ReferralChasing = _referralChasingOptions; // All
_ldapConnection.SessionOptions.ProtocolVersion = LDAP3ProtocolVersion; // 3
_ldapConnection.SessionOptions.SecureSocketLayer = IsSsl;
_ldapConnection.Timeout = Timeout;
_ldapConnection.AutoBind = AutoBind; 
// When using LDAPS in Linux we are basically only passing through the Bind to the underlying native library (openldap), which does perform a certificate validation.
// If the certificate fails to validate successfully, we will get an exception during call to Bind() which should have more info saying that the certificate was invalid or
// that the connection with the server couldn't be established.
// In case of Win NT systems we can use the built-in verification functionality provided in the X509Cerificate2 class
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
    _ldapConnection.SessionOptions.VerifyServerCertificate += 
        (ldapConnection, certificate) => new X509Certificate2(certificate).Verify();
}   

In the case of Mac, maybe you must verify the certificate yourself too (like on Win), so try to do some experiments with this sample.

@macsux I tried Your solution but got the same error as always:

Unhandled exception. System.DirectoryServices.Protocols.LdapException: The LDAP server is unavailable.
   at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
   at System.DirectoryServices.Protocols.LdapConnection.Bind()
   at <Program>$.<Main>$(String[] args) in Program.cs:line 25

I use the same server address string as for Windows: var di = new LdapDirectoryIdentifier(server: "10.6.1.162", 636, true, false); Am I using wrong connection string? Maybe external connection to this IP address through 636 port is blocked, I’ll try to contact my DevOps.

This code is confirmed to work https://github.com/NMica/NMica.Security/blob/3868ceab4880058fbca4b66d651a169d04757d1d/src/NMica.AspNetCore.Authentication.Spnego/Ldap/LdapRolesClaimsTransformation.cs#L204

On Thu., Mar. 17, 2022, 1:40 p.m. parrssee, @.***> wrote:

It works but if you’re looking to skip ssl validation you have to do it via env var rather then the callback event. See my earlier comments

Quite the contrary, I don’t need to skip this validation and because of this I don’t know how to connect over 636 port

— Reply to this email directly, view it on GitHub https://github.com/dotnet/runtime/issues/60972#issuecomment-1071136088, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAINFWDKZ2X7GY7L4T3LU33VANU7RANCNFSM5G5DSI3A . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

Also, I’m getting a CA1416 warning on connection.SessionOptions.SecureSocketLayer = options.UseSsl; telling me this API is windows only. Is this a left over that forgot to be removed after LDAPS supported was added for Linux?