Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #41618 Correct marshalling of SortKey objects on Linux #65548

Merged
merged 8 commits into from
Mar 22, 2022
Merged

Fix #41618 Correct marshalling of SortKey objects on Linux #65548

merged 8 commits into from
Mar 22, 2022

Conversation

lscorcia
Copy link
Contributor

@lscorcia lscorcia commented Feb 18, 2022

The SortKey objects are marshalled to pointers and then passed to native functions. Under Windows, the native LDAP functions use Unicode strings, while under Linux they use ANSI - this breaks the use of sort controls under Linux. This PR ensures the correct StructLayout CharSet for each platform.

Fixes #41618 .

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Feb 18, 2022
@ghost
Copy link

ghost commented Feb 18, 2022

Tagging subscribers to this area: @dotnet/area-system-directoryservices, @jay98014
See info in area-owners.md if you want to be subscribed.

Issue Details

The SortKey objects are marshalled to pointers and then passed to native functions. Under Windows, the native LDAP functions use Unicode strings, while under Linux they use ANSI - this breaks the use of sort controls under Linux. This PR ensures the correct StructLayout CharSet for each platform.

Author: lscorcia
Assignees: -
Labels:

area-System.DirectoryServices, community-contribution

Milestone: -

@dnfadmin
Copy link

dnfadmin commented Feb 18, 2022

CLA assistant check
All CLA requirements met.

@lscorcia
Copy link
Contributor Author

Hello @joperezr , what do you think about this?

@joperezr
Copy link
Member

Thanks a lot for the contribution @lscorcia, I'm very sorry that it took a bit for me to take a look at this.

@joperezr joperezr self-assigned this Feb 28, 2022
@joperezr
Copy link
Member

@lscorcia would it be possible to also add a functional test which performs a search and uses the sort control here: https://github.com/dotnet/runtime/blob/main/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs ? Those tests don't run in our builds, but we run them manually every now and then against a real LdapServer so it would be good to get protection against this to ensure it works as expected in all platforms.

@joperezr
Copy link
Member

The changes LGTM, but I'm adding @bartonjs to take a look first because he is much more familiar with PInvoke marshalling in general, but also because SortKey is a public type and even though I believe that changing the StructLayout isn't a breaking change, I want to make sure we double check.

@joperezr
Copy link
Member

joperezr commented Mar 1, 2022

I see, yeah that makes sense, I think it is fine to create an internal struct (that uses the appropriate charset depending on the platform) which is then used for PInvoking is better and we should leave SortKey as is, and just use it to copy the values into that internal struct. @lscorcia would you be able to adjust this PR to do what is suggested?

@lscorcia
Copy link
Contributor Author

lscorcia commented Mar 1, 2022

Thanks for the suggestions! @bartonjs notes make sense and I'd like to give them a shot, it may not look good but I should be able to do it. I also wrote the additional test you mentioned, but despite a few hours spent on it I still have not been able to run it. I always have matching issues between CoreCLR, the libraries and the test assembly. I will spend some more time on it tomorrow.

@lscorcia
Copy link
Contributor Author

lscorcia commented Mar 2, 2022

So, I spent way too much time trying to run the DirectoryServicesProtocolsTests and I have never been able to run them successfully under Linux. I am now wondering if it is a problem of my machine, or if they don't currently pass under Linux.

This is what I did:

sudo docker run -p 1389:1389 -e ROOT_USER_DN='cn=admin,dc=example,dc=com' -e BASE_DN='dc=example,dc=com' -e ROOT_PASSWORD=password  -d openidentityplatform/opendj

export LDAP_TEST_SERVER_INDEX=0
git clean -xdf
./build.sh clr+libs -rc Release
cd src/libraries/System.DirectoryServices.Protocols/tests
pushd ../src && dotnet build && popd && dotnet build /t:test /p:XUnitOptions="-class System.DirectoryServices.Protocols.Tests.DirectoryServicesProtocolsTests"

When compilation ends, all existing tests fail with the following error:

System.DirectoryServices.Protocols.Tests.DirectoryServicesProtocolsTests.TestAddAndModifyAttribute [FAIL]
  /home/luca/dotnet-runtime/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs(1168,0): at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
  /home/luca/dotnet-runtime/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs(1025,0): at System.DirectoryServices.Protocols.LdapConnection.Bind()
  /home/luca/dotnet-runtime/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs(685,0): at System.DirectoryServices.Protocols.Tests.DirectoryServicesProtocolsTests.GetConnection()
  /home/luca/dotnet-runtime/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs(124,0): at System.DirectoryServices.Protocols.Tests.DirectoryServicesProtocolsTests.TestAddAndModifyAttribute()
  System.DirectoryServices.Protocols.LdapException : An encoding error occurred.

I attached a debugger to the existing test, but at a glance nothing seems off. The error is triggered here, before any network interaction:

return Interop.Ldap.ldap_sasl_bind(ld, who, Interop.LDAP_SASL_SIMPLE, passwordBerval, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

I verified the parameters and they are correct, ld is not IntPtr.Zero, who contains the bind username and the password berVal is assigned the correct byte array. The mechanism is LDAP_SASL_SIMPLE (null) and the rest of the params are IntPtr.Zero.

I also tried rebasing my code on top of the current main as some things about marshalling have changed but the output is just the same. Can anyone confirm that those tests are actually passing as of today?

@joperezr
Copy link
Member

joperezr commented Mar 2, 2022

@lscorcia you are right, I'm able to repro the issue. It also seems to repro with other setups as well, as I am seeing this happening with the slapd openldap server as well. Let's open an issue to get that fixed and it is fine if you don't add a functional test on this PR for now, we can add this later as part of the fix of that issue.

@joperezr
Copy link
Member

joperezr commented Mar 2, 2022

I'm still testing, but is looking like what broke this was #64279 changes cc: @jkoritzinsky

@jkoritzinsky
Copy link
Member

Yes, some of the marshalling changes in #64279 affect S.DS.Protocols. Runtime marshalling is disabled in that assembly, so defaults for non-generated P/Invokes are changed. However, I don't think there should be many changes that would affect this particular change in a way that isn't a clear MarshalDirectiveException.

@joperezr
Copy link
Member

joperezr commented Mar 3, 2022

@lscorcia the fix for the encoding issue is now checked in. Could you please rebase your PR and try writing your test again? I have validated that the tests pass after following the steps you outlined above.

@lscorcia
Copy link
Contributor Author

lscorcia commented Mar 3, 2022

I added the new test which now passes, but I still have to introduce the non-public struct that was suggested above. Shouldn't take too long, will take a look at it tomorrow. Meanwhile, any idea about how to name it?

@lscorcia
Copy link
Contributor Author

lscorcia commented Mar 4, 2022

@joperezr I pushed the updated version containing the suggested changes. Test failures seem unrelated.

@lscorcia
Copy link
Contributor Author

lscorcia commented Mar 8, 2022

Rebased again to trigger a new test run since the previous one failed due to issues unrelated to this PR.

@lscorcia
Copy link
Contributor Author

lscorcia commented Mar 8, 2022

Test failures seem unrelated to this PR.

@joperezr
Copy link
Member

joperezr commented Mar 9, 2022

I'm sorry that I haven't looked at this yet @lscorcia. I will look at this today.

Copy link
Member

@joperezr joperezr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left three minor things to fix, but other than that looks good. @bartonjs do you mind taking one last look?

@joperezr
Copy link
Member

@lscorcia thank you so much for addressing all of the comments. One last thing, I'm trying to run your new test against one of the containers we have in our Ldap.Configuration.xml (the slapd with ssl one) and I'm getting an error:

[xUnit.net 00:00:01.12]     System.DirectoryServices.Protocols.Tests.DirectoryServicesProtocolsTests.TestSortedSearch [FAIL]
  Failed System.DirectoryServices.Protocols.Tests.DirectoryServicesProtocolsTests.TestSortedSearch [194 ms]
  Error Message:
   System.DirectoryServices.Protocols.DirectoryOperationException : The server does not support the control. The control is critical. critical extension is not recognized
  Stack Trace:
     at System.DirectoryServices.Protocols.LdapConnection.ConstructResponseAsync(Int32 messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut, Boolean sync) in /home/joepr25/runtime/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs:line 1576
   at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout) in /home/joepr25/runtime/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs:line 273
   at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request) in /home/joepr25/runtime/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs:line 216
   at System.DirectoryServices.Protocols.Tests.DirectoryServicesProtocolsTests.TestSortedSearch() in /home/joepr25/runtime/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs:line 571

Which server did you try your test against?

@joperezr
Copy link
Member

Seems like OpenDJ server works just fine. It would be great if we can add a Skip Attribute to your test that ensures SortKey is supported on the target server by looking into the LdapConfiguration index that was set.

@lscorcia
Copy link
Contributor Author

Yes, I tested it with OpenDJ. In order to use server side sorting with slapd one would have to enable the corresponding overlay (https://www.systutorials.com/docs/linux/man/5-slapo-sssvlv/), and even then in its default schema the uid attribute is not sortable I think. This is a bit overkill for a simple test, so it's probably better to just skip the test if the control is not supported.

What I plan to do: add a new element named SupportsServerSideSort to LDAP.Configuration.xml, expose it as a property in LdapConfiguration.cs and define a new IsServerSideSupported property to DirectoryServicesTests.cs. This in turn would be used to conditionally disable the test. Both Active Directory and OpenDJ should support it so I would set the new element to True for those two configs.

Is this plan ok for you?

@joperezr
Copy link
Member

That sounds perfect. Yeah I wasn't suggesting that you fixed the slapd server so it supported sorting, the only thing I want is that if folks want to manually run the tests against a real server using our Ldap.Configuration.xml provided ones, that they won't see a test failure if they pick slapd one. I'm totally fine with them getting a test skipped if on slapd, and then have it running if they are on opendj

Copy link
Member

@joperezr joperezr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for fixing this @lscorcia!

@joperezr
Copy link
Member

Merging since failures are unrelated.

@joperezr joperezr merged commit b6c6304 into dotnet:main Mar 22, 2022
@lscorcia lscorcia deleted the issue-41618 branch March 22, 2022 16:55
@lscorcia
Copy link
Contributor Author

Thanks for your help and support! 🍻

radekdoulik pushed a commit to radekdoulik/runtime that referenced this pull request Mar 30, 2022
@ghost ghost locked as resolved and limited conversation to collaborators May 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.DirectoryServices community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

System.DirectoryServices.Protocols - Cannot use SortControl with custom attribute on linux
5 participants