Skip to content

Commit a327ddd

Browse files
authored
[release/9.0-staging] Add support for LDAPTLS_CACERTDIR \ TrustedCertificateDirectory (#112531)
* Add support for LDAPTLS_CACERTDIR \ TrustedCertificateDirectory (#111877) * Add CompatibilitySuppressions.xml * Remove unwanted test changes that were ported from v10
1 parent 109f957 commit a327ddd

File tree

10 files changed

+254
-26
lines changed

10 files changed

+254
-26
lines changed

src/libraries/Common/src/Interop/Interop.Ldap.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ internal enum LdapOption
157157
LDAP_OPT_ROOTDSE_CACHE = 0x9a, // Not Supported in Linux
158158
LDAP_OPT_DEBUG_LEVEL = 0x5001,
159159
LDAP_OPT_URI = 0x5006, // Not Supported in Windows
160+
LDAP_OPT_X_TLS_CACERTDIR = 0x6003, // Not Supported in Windows
161+
LDAP_OPT_X_TLS_NEWCTX = 0x600F, // Not Supported in Windows
160162
LDAP_OPT_X_SASL_REALM = 0x6101,
161163
LDAP_OPT_X_SASL_AUTHCID = 0x6102,
162164
LDAP_OPT_X_SASL_AUTHZID = 0x6103

src/libraries/Common/tests/System/DirectoryServices/LDAP.Configuration.xml

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Configuration>
22
<CommentThatAllowsDoubleHyphens>
3-
To enable the tests marked with [ConditionalFact(nameof(IsLdapConfigurationExist))], you need to setup an LDAP server and provide the needed server info here.
3+
To enable the tests marked with [ConditionalFact(nameof(IsLdapConfigurationExist))], you need to setup an LDAP server as described below and set the environment variable LDAP_TEST_SERVER_INDEX to the appropriate offset into the XML section found at the end of this file.
44

55
To ship, we should test on both an Active Directory LDAP server, and at least one other server, as behaviors are a little different. However for local testing, it is easiest to connect to an OpenDJ LDAP server in a docker container (eg., in WSL2).
66

@@ -11,7 +11,7 @@ OPENDJ SERVER
1111

1212
test it with this command - it should return some results in WSL2
1313

14-
ldapsearch -h localhost -p 1389 -D 'cn=admin,dc=example,dc=com' -x -w password
14+
ldapsearch -H ldap://localhost:1389 -D 'cn=admin,dc=example,dc=com' -x -w password
1515

1616
this command views the status
1717

@@ -24,16 +24,16 @@ SLAPD OPENLDAP SERVER
2424

2525
and to test and view status
2626

27-
ldapsearch -h localhost -p 390 -D 'cn=admin,dc=example,dc=com' -x -w password
27+
ldapsearch -H ldap://localhost:390 -D 'cn=admin,dc=example,dc=com' -x -w password
2828

2929
docker exec -it slapd01 slapcat
3030

3131
SLAPD OPENLDAP SERVER WITH TLS
3232
==============================
3333

34-
The osixia/openldap container image automatically creates a TLS lisener with a self-signed certificate. This can be used to test TLS.
34+
The osixia/openldap container image automatically creates a TLS listener with a self-signed certificate. This can be used to test TLS.
3535

36-
Start the container, with TLS on port 1636, without client certificate verification:
36+
Start the container, with TLS on port 1636, but without client certificate verification:
3737

3838
docker run --publish 1389:389 --publish 1636:636 --name ldap --hostname ldap.local --detach --rm --env LDAP_TLS_VERIFY_CLIENT=never --env LDAP_ADMIN_PASSWORD=password osixia/openldap --loglevel debug
3939

@@ -56,6 +56,8 @@ To test and view the status:
5656

5757
ldapsearch -H ldaps://ldap.local:1636 -b dc=example,dc=org -x -D cn=admin,dc=example,dc=org -w password
5858

59+
use '-d 1' or '-d 2' for debugging.
60+
5961
ACTIVE DIRECTORY
6062
================
6163

@@ -65,7 +67,7 @@ When running against Active Directory from a Windows client, you should not see
6567

6668
If you are running your AD server as a VM on the same machine that you are running WSL2, you must execute this command on the host to bridge the two Hyper-V networks so that it is visible from WSL2:
6769

68-
Get-NetIPInterface | where {$_.InterfaceAlias -eq 'vEthernet (WSL)' -or $_.InterfaceAlias -eq 'vEthernet (Default Switch)'} | Set-NetIPInterface -Forwarding Enabled
70+
Get-NetIPInterface | where {$_.InterfaceAlias -eq 'vEthernet (WSL)' -or $_.InterfaceAlias -eq 'vEthernet (Default Switch)'} | Set-NetIPInterface -Forwarding Enabled
6971

7072
The WSL2 VM should now be able to see the AD VM by IP address. To make it visible by host name, it's probably easiest to just add it to /etc/hosts.
7173

@@ -82,7 +84,7 @@ Note:
8284
</CommentThatAllowsDoubleHyphens>
8385

8486
<!-- To choose a connection, set an environment variable LDAP_TEST_SERVER_INDEX
85-
to the zero-based index, eg., 0, 1, or 2
87+
to the zero-based index, eg., 0, 1, 2, or 3.
8688
If you don't set LDAP_TEST_SERVER_INDEX then tests that require a server
8789
will skip.
8890
-->
@@ -105,15 +107,6 @@ Note:
105107
<AuthenticationTypes>ServerBind,None</AuthenticationTypes>
106108
<SupportsServerSideSort>False</SupportsServerSideSort>
107109
</Connection>
108-
<Connection Name="ACTIVE DIRECTORY SERVER">
109-
<ServerName>danmose-ldap.danmose-domain.com</ServerName>
110-
<SearchDN>DC=danmose-domain,DC=com</SearchDN>
111-
<Port>389</Port>
112-
<User>danmose-domain\Administrator</User>
113-
<Password>%TESTPASSWORD%</Password>
114-
<AuthenticationTypes>ServerBind,None</AuthenticationTypes>
115-
<SupportsServerSideSort>True</SupportsServerSideSort>
116-
</Connection>
117110
<Connection Name="SLAPD OPENLDAP SERVER TLS">
118111
<ServerName>ldap.local</ServerName>
119112
<SearchDN>DC=example,DC=org</SearchDN>
@@ -124,5 +117,14 @@ Note:
124117
<UseTls>true</UseTls>
125118
<SupportsServerSideSort>False</SupportsServerSideSort>
126119
</Connection>
120+
<Connection Name="ACTIVE DIRECTORY SERVER">
121+
<ServerName>danmose-ldap.danmose-domain.com</ServerName>
122+
<SearchDN>DC=danmose-domain,DC=com</SearchDN>
123+
<Port>389</Port>
124+
<User>danmose-domain\Administrator</User>
125+
<Password>%TESTPASSWORD%</Password>
126+
<AuthenticationTypes>ServerBind,None</AuthenticationTypes>
127+
<SupportsServerSideSort>True</SupportsServerSideSort>
128+
</Connection>
127129

128130
</Configuration>

src/libraries/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,8 @@ public partial class LdapSessionOptions
382382
internal LdapSessionOptions() { }
383383
public bool AutoReconnect { get { throw null; } set { } }
384384
public string DomainName { get { throw null; } set { } }
385+
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")]
386+
public string TrustedCertificatesDirectory { get { throw null; } set { } }
385387
public string HostName { get { throw null; } set { } }
386388
public bool HostReachable { get { throw null; } }
387389
public System.DirectoryServices.Protocols.LocatorFlags LocatorFlag { get { throw null; } set { } }
@@ -402,6 +404,8 @@ internal LdapSessionOptions() { }
402404
public bool Signing { get { throw null; } set { } }
403405
public System.DirectoryServices.Protocols.SecurityPackageContextConnectionInformation SslInformation { get { throw null; } }
404406
public int SspiFlag { get { throw null; } set { } }
407+
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")]
408+
public void StartNewTlsSessionContext() { }
405409
public bool TcpKeepAlive { get { throw null; } set { } }
406410
public System.DirectoryServices.Protocols.VerifyServerCertificateCallback VerifyServerCertificate { get { throw null; } set { } }
407411
public void FastConcurrentBind() { }
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
3+
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
4+
<Suppression>
5+
<DiagnosticId>CP0002</DiagnosticId>
6+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.get_TrustedCertificatesDirectory</Target>
7+
<Left>lib/net8.0/System.DirectoryServices.Protocols.dll</Left>
8+
<Right>lib/net8.0/System.DirectoryServices.Protocols.dll</Right>
9+
<IsBaselineSuppression>true</IsBaselineSuppression>
10+
</Suppression>
11+
<Suppression>
12+
<DiagnosticId>CP0002</DiagnosticId>
13+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.set_TrustedCertificatesDirectory(System.String)</Target>
14+
<Left>lib/net8.0/System.DirectoryServices.Protocols.dll</Left>
15+
<Right>lib/net8.0/System.DirectoryServices.Protocols.dll</Right>
16+
<IsBaselineSuppression>true</IsBaselineSuppression>
17+
</Suppression>
18+
<Suppression>
19+
<DiagnosticId>CP0002</DiagnosticId>
20+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.StartNewTlsSessionContext</Target>
21+
<Left>lib/net8.0/System.DirectoryServices.Protocols.dll</Left>
22+
<Right>lib/net8.0/System.DirectoryServices.Protocols.dll</Right>
23+
<IsBaselineSuppression>true</IsBaselineSuppression>
24+
</Suppression>
25+
<Suppression>
26+
<DiagnosticId>CP0002</DiagnosticId>
27+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.get_TrustedCertificatesDirectory</Target>
28+
<Left>lib/net9.0/System.DirectoryServices.Protocols.dll</Left>
29+
<Right>lib/net9.0/System.DirectoryServices.Protocols.dll</Right>
30+
<IsBaselineSuppression>true</IsBaselineSuppression>
31+
</Suppression>
32+
<Suppression>
33+
<DiagnosticId>CP0002</DiagnosticId>
34+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.set_TrustedCertificatesDirectory(System.String)</Target>
35+
<Left>lib/net9.0/System.DirectoryServices.Protocols.dll</Left>
36+
<Right>lib/net9.0/System.DirectoryServices.Protocols.dll</Right>
37+
<IsBaselineSuppression>true</IsBaselineSuppression>
38+
</Suppression>
39+
<Suppression>
40+
<DiagnosticId>CP0002</DiagnosticId>
41+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.StartNewTlsSessionContext</Target>
42+
<Left>lib/net9.0/System.DirectoryServices.Protocols.dll</Left>
43+
<Right>lib/net9.0/System.DirectoryServices.Protocols.dll</Right>
44+
<IsBaselineSuppression>true</IsBaselineSuppression>
45+
</Suppression>
46+
<Suppression>
47+
<DiagnosticId>CP0002</DiagnosticId>
48+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.get_TrustedCertificatesDirectory</Target>
49+
<Left>lib/netstandard2.0/System.DirectoryServices.Protocols.dll</Left>
50+
<Right>lib/netstandard2.0/System.DirectoryServices.Protocols.dll</Right>
51+
<IsBaselineSuppression>true</IsBaselineSuppression>
52+
</Suppression>
53+
<Suppression>
54+
<DiagnosticId>CP0002</DiagnosticId>
55+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.set_TrustedCertificatesDirectory(System.String)</Target>
56+
<Left>lib/netstandard2.0/System.DirectoryServices.Protocols.dll</Left>
57+
<Right>lib/netstandard2.0/System.DirectoryServices.Protocols.dll</Right>
58+
<IsBaselineSuppression>true</IsBaselineSuppression>
59+
</Suppression>
60+
<Suppression>
61+
<DiagnosticId>CP0002</DiagnosticId>
62+
<Target>M:System.DirectoryServices.Protocols.LdapSessionOptions.StartNewTlsSessionContext</Target>
63+
<Left>lib/netstandard2.0/System.DirectoryServices.Protocols.dll</Left>
64+
<Right>lib/netstandard2.0/System.DirectoryServices.Protocols.dll</Right>
65+
<IsBaselineSuppression>true</IsBaselineSuppression>
66+
</Suppression>
67+
</Suppressions>

src/libraries/System.DirectoryServices.Protocols/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,4 +426,7 @@
426426
<data name="ReferralChasingOptionsNotSupported" xml:space="preserve">
427427
<value>Only ReferralChasingOptions.None and ReferralChasingOptions.All are supported on Linux.</value>
428428
</data>
429+
<data name="DirectoryNotFound" xml:space="preserve">
430+
<value>The directory '{0}' does not exist.</value>
431+
</data>
429432
</root>

src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -955,13 +955,13 @@ private unsafe Interop.BOOL ProcessClientCertificate(IntPtr ldapHandle, IntPtr C
955955

956956
private void Connect()
957957
{
958-
//Ccurrently ldap does not accept more than one certificate.
958+
// Currently ldap does not accept more than one certificate.
959959
if (ClientCertificates.Count > 1)
960960
{
961961
throw new InvalidOperationException(SR.InvalidClientCertificates);
962962
}
963963

964-
// Set the certificate callback routine here if user adds the certifcate to the certificate collection.
964+
// Set the certificate callback routine here if user adds the certificate to the certificate collection.
965965
if (ClientCertificates.Count != 0)
966966
{
967967
int certError = LdapPal.SetClientCertOption(_ldapHandle, LdapOption.LDAP_OPT_CLIENT_CERTIFICATE, _clientCertificateRoutine);

src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.ComponentModel;
5+
using System.IO;
6+
using System.Runtime.Versioning;
57

68
namespace System.DirectoryServices.Protocols
79
{
@@ -11,6 +13,34 @@ public partial class LdapSessionOptions
1113

1214
private bool _secureSocketLayer;
1315

16+
/// <summary>
17+
/// Specifies the path of the directory containing CA certificates in the PEM format.
18+
/// Multiple directories may be specified by separating with a semi-colon.
19+
/// </summary>
20+
/// <remarks>
21+
/// The certificate files are looked up by the CA subject name hash value where that hash can be
22+
/// obtained by using, for example, <code>openssl x509 -hash -noout -in CA.crt</code>.
23+
/// It is a common practice to have the certificate file be a symbolic link to the actual certificate file
24+
/// which can be done by using <code>openssl rehash .</code> or <code>c_rehash .</code> in the directory
25+
/// containing the certificate files.
26+
/// </remarks>
27+
/// <exception cref="DirectoryNotFoundException">The directory not exist.</exception>
28+
[UnsupportedOSPlatform("windows")]
29+
public string TrustedCertificatesDirectory
30+
{
31+
get => GetStringValueHelper(LdapOption.LDAP_OPT_X_TLS_CACERTDIR, releasePtr: true);
32+
33+
set
34+
{
35+
if (!Directory.Exists(value))
36+
{
37+
throw new DirectoryNotFoundException(SR.Format(SR.DirectoryNotFound, value));
38+
}
39+
40+
SetStringOptionHelper(LdapOption.LDAP_OPT_X_TLS_CACERTDIR, value);
41+
}
42+
}
43+
1444
public bool SecureSocketLayer
1545
{
1646
get
@@ -52,6 +82,16 @@ public ReferralChasingOptions ReferralChasing
5282
}
5383
}
5484

85+
/// <summary>
86+
/// Create a new TLS library context.
87+
/// Calling this is necessary after setting TLS-based options, such as <c>TrustedCertificatesDirectory</c>.
88+
/// </summary>
89+
[UnsupportedOSPlatform("windows")]
90+
public void StartNewTlsSessionContext()
91+
{
92+
SetIntValueHelper(LdapOption.LDAP_OPT_X_TLS_NEWCTX, 0);
93+
}
94+
5595
private bool GetBoolValueHelper(LdapOption option)
5696
{
5797
if (_connection._disposed) throw new ObjectDisposedException(GetType().Name);
@@ -71,5 +111,14 @@ private void SetBoolValueHelper(LdapOption option, bool value)
71111

72112
ErrorChecking.CheckAndSetLdapError(error);
73113
}
114+
115+
private void SetStringOptionHelper(LdapOption option, string value)
116+
{
117+
if (_connection._disposed) throw new ObjectDisposedException(GetType().Name);
118+
119+
int error = LdapPal.SetStringOption(_connection._ldapHandle, option, value);
120+
121+
ErrorChecking.CheckAndSetLdapError(error);
122+
}
74123
}
75124
}

src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ public partial class LdapSessionOptions
1010
{
1111
private static void PALCertFreeCRLContext(IntPtr certPtr) => Interop.Ldap.CertFreeCRLContext(certPtr);
1212

13+
[UnsupportedOSPlatform("windows")]
14+
public string TrustedCertificatesDirectory
15+
{
16+
get => throw new PlatformNotSupportedException();
17+
set => throw new PlatformNotSupportedException();
18+
}
19+
1320
public bool SecureSocketLayer
1421
{
1522
get
@@ -24,6 +31,9 @@ public bool SecureSocketLayer
2431
}
2532
}
2633

34+
[UnsupportedOSPlatform("windows")]
35+
public void StartNewTlsSessionContext() => throw new PlatformNotSupportedException();
36+
2737
public int ProtocolVersion
2838
{
2939
get => GetIntValueHelper(LdapOption.LDAP_OPT_VERSION);

0 commit comments

Comments
 (0)