-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Description
Using LdapSessionOptions.StartTransportLayerSecurity() fails on .NET 6 Preview 6 on Linux.
using System;
using System.Net;
using System.Text;
using System.IO;
using System.DirectoryServices.Protocols;
var identifier = new LdapDirectoryIdentifier("localhost", 389);
var cred = new NetworkCredential("cn=admin,dc=example,dc=org", "password");
using var conn = new LdapConnection(identifier, cred);
conn.SessionOptions.ProtocolVersion = 3;
conn.SessionOptions.StartTransportLayerSecurity(null); // Exception thrown here
conn.Bind();fails with
Unhandled exception. System.DirectoryServices.Protocols.LdapException: The LDAP server is unavailable.
at System.DirectoryServices.Protocols.LdapSessionOptions.StartTransportLayerSecurity(DirectoryControlCollection controls) in System.DirectoryServices.Protocols.dll:token 0x60002c1+0x361
at <Program>$.<Main>$(String[] args) in /src/ldapStartTlsTest/Program.cs:line 15
Configuration
.NET 6 Preview 6, Linux x86-64
Regression?
This worked in .NET 6 Preview 5.
Other information
When stepping through the problem it fails on the interop call to ldap_start_tls_s in LdapSessionOptions.cs:608
I think there are two problems: First, LdapPal.StartTls and Interop.Ldap.ldap_start_tls have the wrong method signatures. I think that's an easy fix.
The second is more complex. OpenLDAP's ldap_start_tls needs to be called after the URI is configured for the LDAP handle. But in main, the URI isn't configured before SessionOptions.StartTransportLayerSecurity() is called by clients. This is because OpenLDAP requires the scheme to be "fully initialized." The parameters needed to determine the URI scheme are LdapDirectoryIdentifier.Connectionless, Servers and PortNumber, and SessionOptions.SecureSocketLayer. The SSL option is the problematic one, since it can't be set until after the LdapConnection is finished being constructed.
Here are a couple of options:
- When
SessionOptions.StartTransportLayerSecurity()is called, store a_startTlsbool on theLdapConnectionand defer callingldap_start_tlsuntil theConnect()phase. - During
Init(), Initialize the LDAP handle with theldap://scheme (orcldap://sinceLdapDirectoryIdentifier.Connectionlessis known at that time). Then duringConnect(), check ifSecureSocketsLayeris set, and then rebuild the URI.- This means we'd have to build the URI list twice if
SecureSocketsLayeris set. - If there are no other pre-bind operations that can take place using S.DS.P, then this might be an OK option.
- This means we'd have to build the URI list twice if
I'm working on a PR that implements the first option, but I don't know how to write an automated test for it. I've tested manually, and viewed the output in Wireshark to confirm that it works. We can't just pass if it binds successfully with that option set, since the credentials would still fall through to the server. Maybe there's a way to restrict an LDAP server only to use StartTLS and not plain text, or to attempt to bind to a random TCP socket and detect whether the StartTLS operation was sent to it?