Skip to content

Commit

Permalink
Fix #41618 Correct marshalling of SortKey objects on Linux (#65548)
Browse files Browse the repository at this point in the history
  • Loading branch information
lscorcia authored Mar 22, 2022
1 parent 96ef47b commit b6c6304
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Note:
<User>cn=admin,dc=example,dc=com</User>
<Password>password</Password>
<AuthenticationTypes>ServerBind,None</AuthenticationTypes>
<SupportsServerSideSort>True</SupportsServerSideSort>
</Connection>
<Connection Name="SLAPD OPENLDAP SERVER">
<ServerName>localhost</ServerName>
Expand All @@ -102,6 +103,7 @@ Note:
<User>cn=admin,dc=example,dc=com</User>
<Password>password</Password>
<AuthenticationTypes>ServerBind,None</AuthenticationTypes>
<SupportsServerSideSort>False</SupportsServerSideSort>
</Connection>
<Connection Name="ACTIVE DIRECTORY SERVER">
<ServerName>danmose-ldap.danmose-domain.com</ServerName>
Expand All @@ -110,6 +112,7 @@ Note:
<User>danmose-domain\Administrator</User>
<Password>%TESTPASSWORD%</Password>
<AuthenticationTypes>ServerBind,None</AuthenticationTypes>
<SupportsServerSideSort>True</SupportsServerSideSort>
</Connection>
<Connection Name="SLAPD OPENLDAP SERVER TLS">
<ServerName>ldap.local</ServerName>
Expand All @@ -119,6 +122,7 @@ Note:
<Password>password</Password>
<AuthenticationTypes>ServerBind,None</AuthenticationTypes>
<UseTls>true</UseTls>
<SupportsServerSideSort>False</SupportsServerSideSort>
</Connection>

</Configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace System.DirectoryServices.Tests
{
internal class LdapConfiguration
{
private LdapConfiguration(string serverName, string searchDn, string userName, string password, string port, AuthenticationTypes at, bool useTls)
private LdapConfiguration(string serverName, string searchDn, string userName, string password, string port, AuthenticationTypes at, bool useTls, bool supportsServerSideSort)
{
ServerName = serverName;
SearchDn = searchDn;
Expand All @@ -19,6 +19,7 @@ private LdapConfiguration(string serverName, string searchDn, string userName, s
Port = port;
AuthenticationTypes = at;
UseTls = useTls;
SupportsServerSideSort = supportsServerSideSort;
}

private static LdapConfiguration s_ldapConfiguration = GetConfiguration("LDAP.Configuration.xml");
Expand All @@ -32,6 +33,7 @@ private LdapConfiguration(string serverName, string searchDn, string userName, s
internal string SearchDn { get; set; }
internal AuthenticationTypes AuthenticationTypes { get; set; }
internal bool UseTls { get; set; }
internal bool SupportsServerSideSort { get; set; }
internal string LdapPath => string.IsNullOrEmpty(Port) ? $"LDAP://{ServerName}/{SearchDn}" : $"LDAP://{ServerName}:{Port}/{SearchDn}";
internal string RootDSEPath => string.IsNullOrEmpty(Port) ? $"LDAP://{ServerName}/rootDSE" : $"LDAP://{ServerName}:{Port}/rootDSE";
internal string UserNameWithNoDomain
Expand Down Expand Up @@ -107,6 +109,7 @@ internal static LdapConfiguration GetConfiguration(string configFile)
string password = "";
AuthenticationTypes at = AuthenticationTypes.None;
bool useTls = false;
bool supportsServerSideSort = false;

XElement child = connection.Element("ServerName");
if (child != null)
Expand Down Expand Up @@ -141,6 +144,12 @@ internal static LdapConfiguration GetConfiguration(string configFile)
useTls = bool.Parse(child.Value);
}

child = connection.Element("SupportsServerSideSort");
if (child != null)
{
supportsServerSideSort = bool.Parse(child.Value);
}

child = connection.Element("AuthenticationTypes");
if (child != null)
{
Expand Down Expand Up @@ -170,7 +179,7 @@ internal static LdapConfiguration GetConfiguration(string configFile)
at |= AuthenticationTypes.Signing;
}

ldapConfig = new LdapConfiguration(serverName, searchDn, user, password, port, at, useTls);
ldapConfig = new LdapConfiguration(serverName, searchDn, user, password, port, at, useTls, supportsServerSideSort);
}
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<Compile Include="System\DirectoryServices\Protocols\ldap\LdapPartialResultsProcessor.cs" />
<Compile Include="System\DirectoryServices\Protocols\ldap\LdapSessionOptions.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\SafeHandles.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\SortKeyInterop.cs" />
<Compile Include="$(CommonPath)DisableRuntimeMarshalling.cs"
Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">
<Link>Common\DisableRuntimeMarshalling.cs</Link>
Expand All @@ -58,6 +59,7 @@
<Compile Include="System\DirectoryServices\Protocols\common\QuotaControl.Windows.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\LdapPal.Windows.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\BerPal.Windows.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\SortKeyInterop.Windows.cs" />
<Compile Include="System\DirectoryServices\Protocols\ldap\LdapConnection.Windows.cs" />
<Compile Include="System\DirectoryServices\Protocols\ldap\LdapSessionOptions.Windows.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\SafeHandles.Windows.cs" />
Expand All @@ -76,6 +78,7 @@
<Compile Include="System\DirectoryServices\Protocols\common\QuotaControl.Linux.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\LdapPal.Linux.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\BerPal.Linux.cs" />
<Compile Include="System\DirectoryServices\Protocols\Interop\SortKeyInterop.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" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Runtime.Versioning;

namespace System.DirectoryServices.Protocols
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal partial struct SortKeyInterop
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Runtime.Versioning;

namespace System.DirectoryServices.Protocols
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal partial struct SortKeyInterop
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Runtime.Versioning;

namespace System.DirectoryServices.Protocols
{
// Declared as partial in order to be able to set the different StructLayout
// attributes in the Windows and Linux specific files.
// This is a layout-controlled struct, do not alter property ordering.
internal partial struct SortKeyInterop
{
public SortKeyInterop(SortKey sortKey)
{
if (sortKey == null)
throw new ArgumentNullException(nameof(sortKey));

AttributeName = sortKey.AttributeName;
MatchingRule = sortKey.MatchingRule;
ReverseOrder = sortKey.ReverseOrder;
}

internal string AttributeName { get; set; }

internal string MatchingRule { get; set; }

internal bool ReverseOrder { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -703,9 +703,15 @@ public SortKey[] SortKeys

public override byte[] GetValue()
{
SortKeyInterop[] nativeSortKeys = new SortKeyInterop[_keys.Length];
for (int i = 0; i < _keys.Length; ++i)
{
nativeSortKeys[i] = new SortKeyInterop(_keys[i]);
}

IntPtr control = IntPtr.Zero;
int structSize = Marshal.SizeOf(typeof(SortKey));
int keyCount = _keys.Length;
int structSize = Marshal.SizeOf(typeof(SortKeyInterop));
int keyCount = nativeSortKeys.Length;
IntPtr memHandle = Utility.AllocHGlobalIntPtrArray(keyCount + 1);

try
Expand All @@ -716,7 +722,7 @@ public override byte[] GetValue()
for (i = 0; i < keyCount; i++)
{
sortPtr = Marshal.AllocHGlobal(structSize);
Marshal.StructureToPtr(_keys[i], sortPtr, false);
Marshal.StructureToPtr(nativeSortKeys[i], sortPtr, false);
tempPtr = (IntPtr)((long)memHandle + IntPtr.Size * i);
Marshal.WriteIntPtr(tempPtr, sortPtr);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public partial class DirectoryServicesProtocolsTests
internal static bool IsLdapConfigurationExist => LdapConfiguration.Configuration != null;
internal static bool IsActiveDirectoryServer => IsLdapConfigurationExist && LdapConfiguration.Configuration.IsActiveDirectoryServer;

internal static bool IsServerSideSortSupported => IsLdapConfigurationExist && LdapConfiguration.Configuration.SupportsServerSideSort;

[ConditionalFact(nameof(IsLdapConfigurationExist))]
public void TestInvalidFilter()
{
Expand Down Expand Up @@ -533,6 +535,58 @@ public void TestPageRequests()
}
}

[ConditionalFact(nameof(IsServerSideSortSupported))]
public void TestSortedSearch()
{
using (LdapConnection connection = GetConnection())
{
string ouName = "ProtocolsGroup10";
string dn = "ou=" + ouName;

try
{
for (int i=0; i<10; i++)
{
DeleteEntry(connection, "ou=ProtocolsSubGroup10." + i + "," + dn);
}
DeleteEntry(connection, dn);

AddOrganizationalUnit(connection, dn);
SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.SearchDn, ouName);
Assert.NotNull(sre);

for (int i=0; i<10; i++)
{
AddOrganizationalUnit(connection, "ou=ProtocolsSubGroup10." + i + "," + dn);
}

string filter = "(objectClass=*)";
SearchRequest searchRequest = new SearchRequest(
dn + "," + LdapConfiguration.Configuration.SearchDn,
filter,
SearchScope.Subtree,
null);

var sortRequestControl = new SortRequestControl("ou", true);
searchRequest.Controls.Add(sortRequestControl);

SearchResponse searchResponse = (SearchResponse) connection.SendRequest(searchRequest);
Assert.Equal(1, searchResponse.Controls.Length);
Assert.True(searchResponse.Controls[0] is SortResponseControl);
Assert.True(searchResponse.Entries.Count > 0);
Assert.Equal("ou=ProtocolsSubGroup10.9," + dn + "," + LdapConfiguration.Configuration.SearchDn, searchResponse.Entries[0].DistinguishedName);
}
finally
{
for (int i=0; i<20; i++)
{
DeleteEntry(connection, "ou=ProtocolsSubGroup10." + i + "," + dn);
}
DeleteEntry(connection, dn);
}
}
}

private void DeleteAttribute(LdapConnection connection, string entryDn, string attributeName)
{
string dn = entryDn + "," + LdapConfiguration.Configuration.SearchDn;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ public void Ctor_SortKeys(bool critical)

control.IsCritical = critical;
var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ?
// WLDAP formatted ASN.1
new byte[] { 48, 132, 0, 0, 0, 43, 48, 132, 0, 0, 0, 17, 4, 5,110,
97, 109, 101, 49, 128, 5, 114, 117, 108, 101, 49, 129,
1, 255, 48, 132, 0, 0, 0, 14, 4, 5, 110, 97, 109, 101,
50, 128, 5, 114, 117, 108, 101, 50} :
new byte[] { 48, 19, 48, 9, 4, 1, 110, 128, 1, 114, 129, 1, 255, 48, 6, 4, 1, 110, 128, 1, 114 };
50, 128, 5, 114, 117, 108, 101, 50 } :
// OpenLdap formatted ASN.1
new byte[] { 48, 35, 48, 17, 4, 5, 110, 97, 109, 101, 49, 128, 5,
114, 117, 108, 101, 49, 129, 1, 255, 48, 14, 4, 5, 110, 97, 109,
101, 50, 128, 5, 114, 117, 108, 101, 50 };
Assert.Equal(expected, control.GetValue());
}

Expand Down

0 comments on commit b6c6304

Please sign in to comment.