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

LdapConnection does not handle passwords containing UTF-8 characters on Linux #82601

Open
t00 opened this issue Feb 24, 2023 · 13 comments
Open
Labels
area-System.DirectoryServices help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@t00
Copy link

t00 commented Feb 24, 2023

Description

When using LdapConnection with a password containing point symbol (£) on Linux, the server consistently responds with Invalid Credentials error code.

When browsing the code, SessionOptions.ProtocolVersion does not seem to affect the way password is marshalled to ldap_sasl_bind function, LdapPal.StringToPtr function is always used (StringToHGlobalAnsi, StringToAnsiString) meaning the pound character will be converted to ANSI codepage or dropped.

This issue leaves out a lot of users using non-latin characters without access to LDAP authentication through dotnet, running on Linux.

Reproduction Steps

var ldapInfo = new LdapDirectoryIdentifier("1.2.3.4", 389);
var credental = new NetworkCredential("rich", "1337£", "test-ad")

using (var ldapConnection = new LdapConnection(ldapInfo, credential))
{
   ldapConnection.AuthType = AuthType.Basic;
   ldapConnection.SessionOptions.ProtocolVersion = 3;
   ldapConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
   ldapConnection.Bind();
}

Expected behavior

If ProtocolVersion is set to 3, marshalling should be done using UTF8, as described on https://www.ibm.com/docs/en/zos/2.1.0?topic=routines-ldap-sasl-bind-ldap-sasl-bind-s as well as parameter LDAP_OPT_UTF8_IO should be set.

Actual behavior

Invalid Credentials response is returned.

Regression?

Functionality work on my automated tests in dotnet 5, not sure what have changed internally in dotnet.

Known Workarounds

Do not use special characters in a password?

Configuration

dotnet 6 LTS, latest Manjaro

Other information

add label: area-System.DirectoryServices

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Feb 24, 2023
@stephentoub
Copy link
Member

Same as #76426?
cc: @joperezr

@joperezr
Copy link
Member

It does seem very similar, except that in the one that was closed it was never confirmed if ProtocolVersion was set to 3.0 or not, and on this one it does seem like the right version is set. It is interesting that @t00 mentions that this worked fine on 5.0 and has regressed since then, I wouldn't expect that since IIRC we didn't fully support sasl_bind in 5.0, but I may be misremembering. @t00 can you confirm which versions of the S.DS.Protocols package work for you and which ones don't?

@ghost
Copy link

ghost commented Feb 24, 2023

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

Issue Details

Description

When using LdapConnection with a password containing point symbol (£) on Linux, the server consistently responds with Invalid Credentials error code.

When browsing the code, SessionOptions.ProtocolVersion does not seem to affect the way password is marshalled to ldap_sasl_bind function, LdapPal.StringToPtr function is always used (StringToHGlobalAnsi, StringToAnsiString) meaning the pound character will be converted to ANSI codepage or dropped.

This issue leaves out a lot of users using non-latin characters without access to LDAP authentication through dotnet, running on Linux.

Reproduction Steps

var ldapInfo = new LdapDirectoryIdentifier("1.2.3.4", 389);
var credental = new NetworkCredential("rich", "1337£", "test-ad")

using (var ldapConnection = new LdapConnection(ldapInfo, credential))
{
   ldapConnection.AuthType = AuthType.Basic;
   ldapConnection.SessionOptions.ProtocolVersion = 3;
   ldapConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
   ldapConnection.Bind();
}

Expected behavior

If ProtocolVersion is set to 3, marshalling should be done using UTF8, as described on https://www.ibm.com/docs/en/zos/2.1.0?topic=routines-ldap-sasl-bind-ldap-sasl-bind-s as well as parameter LDAP_OPT_UTF8_IO should be set.

Actual behavior

Invalid Credentials response is returned.

Regression?

Functionality work on my automated tests in dotnet 5, not sure what have changed internally in dotnet.

Known Workarounds

Do not use special characters in a password?

Configuration

dotnet 6 LTS, latest Manjaro

Other information

add label: area-System.DirectoryServices

Author: t00
Assignees: -
Labels:

area-System.DirectoryServices, untriaged

Milestone: -

@t00
Copy link
Author

t00 commented Feb 24, 2023

I cannot check version 2 on dotnet 5, but on dotnet 6 version 3 does not work on Linux with the same AD but works on Windows just fine.
I will do more checks over the weekend with a simple password to rule out non-unicode character issues.

@joperezr
Copy link
Member

That would be great, thanks so much for checking. As an FYI, for this particular library (and for this particular APIs that you are calling) we have different implementations for Windows and Linux, so what would work best to get the information that we need is if we can have a repro in the same OS (so in this case Linux) and perhaps even against the same AD and same credentials. Essentially, if we can get that info, as well as which version of a NuGet package you are using when it breaks and which version you are using when it works would get us most of what we need. 😄

@t00
Copy link
Author

t00 commented Feb 25, 2023

I prepared a test project which have shown that dotnet 5, 6 and 7 behave the same way which contradicts what I had mentioned before - sorry for confusion. It might have been working fine with some other libraries on the system but it would be impossible to reproduce again so please assume dotnet 5 did not work with unicode as well.

What indeed continuously does not work is unicode password on both AD and ApacheDS I am using for tests. Also interestingly ApacheDS refuses working with ProtocolVersion 2 while AD works if the credentials are not unicode.

Here is test code which I ran:

        [Test, Combinatorial]
        public void Test1(
            [Values(true, false)] bool isActiveDirectory,
            [Values(true, false)] bool isUnicode,
            [Values(true, false)] bool isVersion3
        )
        {
            var username = isUnicode ? "wróżka" : "fairy";
            var password = isUnicode ? "wążMa£1000" : "snakeHas1000GBP";
            if (!isActiveDirectory)
            {
                username = $"uid={username},ou=users,dc=my-development,dc=com";
            }

            var port = isActiveDirectory ? 389 : 10389;
            var domain = isActiveDirectory ? "my-ad" : "";
            var hostIdentifier = new LdapDirectoryIdentifier("1.2.3.4", port);
            using var searchConnection = new LdapConnection(hostIdentifier, new NetworkCredential(username, password, domain));
            searchConnection.AuthType = AuthType.Basic;
            searchConnection.Timeout = TimeSpan.FromSeconds(5);
            searchConnection.SessionOptions.ProtocolVersion = isVersion3 ? 3 : 2;
            searchConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
            try
            {
                searchConnection.Bind();
            }
            catch (Exception e)
            {
                Assert.Fail($"{Environment.Version}: Exception {e.GetType()} thrown: {e.Message}");
            }
        }

Below are results on Linux:

TestLdapDotNet (24 tests) Failed: 15 tests failed
  TestLdapDotNet (24 tests) Failed: 15 tests failed
    Tests (net5.0) (8 tests) Failed: One or more child tests had errors: 5 tests failed
      Test1 (8 tests) Failed: One or more child tests had errors: 5 tests failed
        Test1(False,False,False) Failed: 5.0.17: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,False,True) Success
        Test1(False,True,False) Failed: 5.0.17: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,True,True) Failed: 5.0.17: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,False,False) Success
        Test1(True,False,True) Success
        Test1(True,True,False) Failed: 5.0.17: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,True,True) Failed: 5.0.17: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
    Tests (net7.0) (8 tests) Failed: One or more child tests had errors: 5 tests failed
      Test1 (8 tests) Failed: One or more child tests had errors: 5 tests failed
        Test1(False,False,False) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,False,True) Success
        Test1(False,True,False) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,True,True) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,False,False) Success
        Test1(True,False,True) Success
        Test1(True,True,False) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,True,True) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
    Tests (net6.0) (8 tests) Failed: One or more child tests had errors: 5 tests failed
      Test1 (8 tests) Failed: One or more child tests had errors: 5 tests failed
        Test1(False,False,False) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,False,True) Success
        Test1(False,True,False) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,True,True) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,False,False) Success
        Test1(True,False,True) Success
        Test1(True,True,False) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,True,True) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.

And here are same test results on Windows:

Test	Duration	Traits	Error Message
Project: TestLdapDotNet (net5.0) Failed (8)	396 ms		
Namespace: TestLdapDotNet Failed (8)	396 ms		
Class: Tests Failed (8)	396 ms		
Test Group: TestLdapDotNet.Tests.Test1 Failed (8)	396 ms		
Test1(False,False,False) Failed	39 ms		5.0.16: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
Test1(False,False,True) Passed	44 ms		
Test1(False,True,False) Failed	44 ms		5.0.16: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
Test1(False,True,True) Passed	42 ms		
Test1(True,False,False) Passed	44 ms		
Test1(True,False,True) Passed	40 ms		
Test1(True,True,False) Failed	88 ms		5.0.16: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
Test1(True,True,True) Passed	55 ms		
Project: TestLdapDotNet (net6.0) Failed (8)	460 ms		
Namespace: TestLdapDotNet Failed (8)	460 ms		
Class: Tests Failed (8)	460 ms		
Test Group: TestLdapDotNet.Tests.Test1 Failed (8)	460 ms		
Test1(False,False,False) Failed	42 ms		6.0.13: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
Test1(False,False,True) Passed	44 ms		
Test1(False,True,False) Failed	48 ms		6.0.13: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
Test1(False,True,True) Passed	42 ms		
Test1(True,False,False) Passed	41 ms		
Test1(True,False,True) Passed	46 ms		
Test1(True,True,False) Failed	136 ms		6.0.13: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
Test1(True,True,True) Passed	61 ms		
Project: TestLdapDotNet (net7.0) Failed (8)	393 ms		
Namespace: TestLdapDotNet Failed (8)	393 ms		
Class: Tests Failed (8)	393 ms		
Test Group: TestLdapDotNet.Tests.Test1 Failed (8)	393 ms		
Test1(False,False,False) Failed	44 ms		7.0.2: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
Test1(False,False,True) Passed	39 ms		
Test1(False,True,False) Failed	46 ms		7.0.2: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
Test1(False,True,True) Passed	39 ms		
Test1(True,False,False) Passed	38 ms		
Test1(True,False,True) Passed	34 ms		
Test1(True,True,False) Failed	89 ms		7.0.2: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
Test1(True,True,True) Passed	64 ms		

Clearly unicode tests are passing on Windows and only ProtocolVersion 2 expectedly fails on a few occassions.

@t00
Copy link
Author

t00 commented Feb 25, 2023

Sorry forgot to mention, using DirectoryServices.Protocols version 6.0.1. After upgrading to the latest 7.0.0 it seems dotnet 5 is not supported anymore but the remaining tests on Linux fail on exactly same tests:

TestLdapDotNet (24 tests) Failed: 18 tests failed
  TestLdapDotNet (24 tests) Failed: 18 tests failed
    Tests (net5.0) (8 tests) Failed: One or more child tests had errors: 8 tests failed
      Test1 (8 tests) Failed: One or more child tests had errors: 8 tests failed
        Test1(False,False,False) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
        Test1(False,False,True) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
        Test1(False,True,False) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
        Test1(False,True,True) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
        Test1(True,False,False) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
        Test1(True,False,True) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
        Test1(True,True,False) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
        Test1(True,True,True) Failed: System.PlatformNotSupportedException : System.DirectoryServices.Protocols is not supported on this platform.
    Tests (net7.0) (8 tests) Failed: One or more child tests had errors: 5 tests failed
      Test1 (8 tests) Failed: One or more child tests had errors: 5 tests failed
        Test1(False,False,False) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,False,True) Success
        Test1(False,True,False) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,True,True) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,False,False) Success
        Test1(True,False,True) Success
        Test1(True,True,False) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,True,True) Failed: 7.0.2: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
    Tests (net6.0) (8 tests) Failed: One or more child tests had errors: 5 tests failed
      Test1 (8 tests) Failed: One or more child tests had errors: 5 tests failed
        Test1(False,False,False) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,False,True) Success
        Test1(False,True,False) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.DirectoryOperationException thrown: A protocol error occurred.
        Test1(False,True,True) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,False,False) Success
        Test1(True,False,True) Success
        Test1(True,True,False) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.
        Test1(True,True,True) Failed: 6.0.14: Exception System.DirectoryServices.Protocols.LdapException thrown: The supplied credential is invalid.

@t00
Copy link
Author

t00 commented Mar 6, 2023

@joperezr Do you have any updates on how the issue is progressing on your end?

@buyaa-n buyaa-n added this to the Future milestone Apr 10, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Apr 10, 2023
@Menci77
Copy link

Menci77 commented Apr 25, 2023

Hello, I have a similar problem but on WINDOWS. I'm not sure if it is linked to this problem. I can open a new issue for this if needed.

We are using DirectoryServices.Protocols version 7.0.0.
_ldapConnection.AuthType = AuthType.Basic; _ldapConnection.SessionOptions.ProtocolVersion = 3;
And when the user's password contains accented characters (éáöüó..) the auth fails with 'Invalid Credentials'.

@t00
Copy link
Author

t00 commented Nov 7, 2023

@joperezr Will this issue be fixed in upcoming .NET 8? It might be a niche use case to use LdapConnection in dotnet on Linux, but still this is a serious bug in an integral part of the released framework :(

@joperezr
Copy link
Member

joperezr commented Nov 8, 2023

Thanks for the ping. Unfortunately we didn’t get a a chance to fix this issue during .NET 8 release. @dotnet/area-system-directoryservices is this something that we think we can get to for .NET 9? Alternatively, @t00 it would be awesome if you happen to be interested in helping us and contributing a PR with a fix 😃

@buyaa-n buyaa-n added the help wanted [up-for-grabs] Good issue for external contributors label Nov 9, 2023
@buyaa-n
Copy link
Contributor

buyaa-n commented Nov 9, 2023

https://github.com/orgs/dotnet/teams/area-system-directoryservices is this something that we think we can get to for .NET 9?

Not sure if we can handle it in .NET 9, most likely will rely on community contribution.

@PavPav
Copy link

PavPav commented Jan 12, 2024

Seems to be already fixed by #76127
Seems to be included into DirectoryServices.Protocols version 8.0.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.DirectoryServices help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

No branches or pull requests

7 participants