runtime: System.DirectoryServices.Protocols.LdapConnection fails to find the new version of the LDAP library on Ubuntu 22.04

Description

Ubuntu 20.04 has LDAP library libldap-2.4 https://packages.ubuntu.com/source/focal/openldap Ubuntu 22.04 has LDAP library libldap-2.5.11 https://packages.ubuntu.com/source/jammy/openldap

When running .NET Core 6 code on Ubuntu 22.04, System.DirectoryServices.Protocols.LdapConnection cannot find the latest version of LDAP an fails.

Reproduction Steps

Install the LDAP libraries on Ubuntu 22.04:

ubuntu@w-ubuntu-2:~$ sudo apt-get install --no-install-recommends -y krb5-user ldap-utils libsasl2-modules-gssapi-mit wget
[sudo] password for ubuntu:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
ldap-utils is already the newest version (2.5.11+dfsg-1~exp1ubuntu3).
libsasl2-modules-gssapi-mit is already the newest version (2.1.27+dfsg2-3ubuntu1).
wget is already the newest version (1.21.2-2ubuntu1).
krb5-user is already the newest version (1.19.2-2).
The following package was automatically installed and is no longer required:
  libfreetype6
Use 'sudo apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
ubuntu@w-ubuntu-2:~$

Steps to reproduce: Run the following code from an ASP.NET Core 6 application on Ubuntu 22.04:

using System.DirectoryServices.Protocols;
var id = new LdapDirectoryIdentifier(servers[i], true, false);

Expected behavior

System.DirectoryServices.Protocols.LdapConnection finds the 2.5.11 version of LDAP library on Ubuntu 22.04 and connects to the LDAP server.

Actual behavior

System.TypeInitializationException
  HResult=0x80131534
  Message=The type initializer for 'Ldap' threw an exception.
  Source=System.DirectoryServices.Protocols
  StackTrace:
   at Interop.Ldap.ldap_initialize(IntPtr& ld, String uri)
   at System.DirectoryServices.Protocols.ConnectionHandle..ctor(String uri)
   at System.DirectoryServices.Protocols.LdapConnection.InternalInitConnectionHandle(String hostname)
   at System.DirectoryServices.Protocols.LdapConnection.Init()
   at System.DirectoryServices.Protocols.LdapConnection..ctor(LdapDirectoryIdentifier identifier, NetworkCredential credential, AuthType authType)
   at TheLdapService.Server.Services.LdapService.GetLdapConnection() in C:\Users\ubuntu\source\repos\TheLdapService\src\TheLdapService.Server\Services\LdapService.cs:line 58
   at TheLdapService.Server.Services.LdapService.Authenticate(String username, String password) in C:\Users\ubuntu\source\repos\TheLdapService\src\TheLdapService.Server\Services\LdapService.cs:line 199
   at TheLdapService.Server.Services.AuthorizationService.<>c__DisplayClass4_0.<Login>b__0() in C:\Users\ubuntu\source\repos\TheLdapService\src\TheLdapService.Server\Services\AuthorizationService.cs:line 39
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__272_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
DllNotFoundException: Unable to load shared library 'libldap-2.4.so.2' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibldap-2.4.so.2: cannot open shared object file: No such file or directory


Regression?

Same code worked under Ubuntu 20.04, which has libldap-2.4, the library System.DirectoryServices.Protocols.LdapConnection is looking for.

Known Workarounds

On Ubuntu 22.04, create a symbolic link from ldap-2.4 to ldap-2.5:

ubuntu@w-ubuntu-2:~$ sudo ln -s /usr/lib/x86_64-linux-gnu/libldap-2.5.so.0 /usr/lib/x86_64-linux-gnu/libldap-2.4.so.2

ubuntu@w-ubuntu-2:/usr/lib/x86_64-linux-gnu$ ll | grep ldap
lrwxrwxrwx  1 root root       42 May 17 15:29 libldap-2.4.so.2 -> /usr/lib/x86_64-linux-gnu/libldap-2.5.so.0
lrwxrwxrwx  1 root root       20 Feb 16 12:15 libldap-2.5.so.0 -> libldap-2.5.so.0.1.6
-rw-r--r--  1 root root   376576 Feb 16 12:15 libldap-2.5.so.0.1.6

Configuration

ubuntu@w-ubuntu-2:~$ dotnet --info
.NET SDK (reflecting any global.json):
 Version:   6.0.300
 Commit:    8473146e7d

Runtime Environment:
 OS Name:     ubuntu
 OS Version:  22.04
 OS Platform: Linux
 RID:         ubuntu.22.04-x64
 Base Path:   /usr/share/dotnet/sdk/6.0.300/

Host (useful for support):
  Version: 6.0.5
  Commit:  70ae3df4a6

.NET SDKs installed:
  6.0.300 [/usr/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.5 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.5 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download

Other information

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 15 (6 by maintainers)

Commits related to this issue

Most upvoted comments

After some quick testing. Looks like this workaround does work for .net 6 alpine based images. Might need to update the link if the libldap version changes. Here is my docker file

FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

#install LDAP dependencies for System.DirectoryServices 
RUN apk update \
    && apk add --upgrade libldap \
    && ln -s libldap.so.2 /usr/lib/libldap-2.4.so.2

Looks like this is what you want in you /usr/lib directory.

/ # ls /usr/lib -la

image

My C# for ref.

private LdapConnection GetConnection(string userName, string password, string domain)
        {
            try
            {
                var ldapConnection = new LdapConnection(
                    new LdapDirectoryIdentifier(ldapServer),
                    new NetworkCredential($"{domain}\\{userName}", password),
                    OperatingSystem.IsLinux() ? AuthType.Basic : AuthType.Negotiate);

                ldapConnection.SessionOptions.ProtocolVersion = 3;
                ldapConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;

                ldapConnection.Bind();

                return ldapConnection;
            }
            catch (LdapException ex)
            {
                logger.LogError(ex, "Authenticate user LDAP error. {message}", ex.Message);
                return null;
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Authenticate user unknown error. {message}", ex.Message);
                return null;
            }
        }

An alternative work around that worked for me is to use AssemblyLoadContext as shown below:

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            AssemblyLoadContext.Default.ResolvingUnmanagedDll += (assembly, s) =>
            {
                if (!s.Equals("libldap-2.4.so.2"))
                {
                    return IntPtr.Zero;
                }
                try
                {
                    return NativeLibrary.Load("libldap-2.4.so.2");
                }
                catch (Exception)
                {
                    return NativeLibrary.Load("libldap-2.5.so.0");
                }
            };
        }

This is expected. The reason why this happens has nothing to do with your code, but instead with the native library installed in ubuntu 22.04. There is no fix for this available yet, just a known workaround that was posted above. Copying it here for reference:

sudo ln -s /usr/lib/x86_64-linux-gnu/libldap-2.5.so.0 /usr/lib/x86_64-linux-gnu/libldap-2.4.so.2

After running that, you should be able to run your app (this is assuming you are running on a x64 machine)

I chatted with @AaronRobinsonMSFT about this last week. There is a way for us to basically hook into an event that gets triggered whenever binding to the native assembly fails, and at that point we can try some other versions to see if we can find one supported which is installed, and that seemed like a good-enough solution for now. Unfortunately, we won’t have enough bandwidth to get that done for 7.0 any longer, so I’m adjusting the milestone accordingly.

For folks hitting this in 7.0, They will be able to use the workarounds provided above (adding symlink or manually downloading the 2.4 version) in order to get unblocked. I tried both and they worked for me in Ubuntu 22.04.

I took a look at this today. Unfortunately, seems like libldap doesn’t provide a major version .so or a version-less .so that we can create our PInvokes against. There is a version-less one but only on the dev package libldap2-dev which doesn’t come installed on the distros. Due to the fact that DllImports require the library name to be a string constant, in order to support multiple versions we would need to create Interop stubs for each version in order for this to work. Alternatively, we could manually use NativeLibrary and implement our own policy for picking the right versions, or providing a native shim. Depending on the solution we go with, this may not make it to 7.0 depending on the complexity. I’ll post an update in the next few days, and will adjust the milestone if necessary.