runtime: LDAP referral chasing cannot be disabled on Linux
I have an LDAP server running on a separate machine.
I can run the following code successfully when using the dotnet 5.0.100 SDK on Windows 10, and I can perform the corresponding query successfully using ldapsearch on Ubuntu 20.04.1 LTS. However, running the following code on Ubuntu 20.04.1 LTS or in a Docker container built with the dotnet 5.0 SDK image that also has libldap-common installed fails as described below.
I am attempting to use System.DirectoryServices.Protocol to perform an LDAP query that I was able to perform successfully previously when I was using the Novell library. Specifically, I am creating the connection and binding using
_ldapConnection = new LdapConnection(
new LdapDirectoryIdentifier(host, port, false, false),
new NetworkCredential(userName: "CN=Administrator,CN=Users,DC=inc,DC=company,DC=com", password: "mypassword"),
AuthType.Basic
);
_ldapConnection.Bind();
And then doing the following query
string searchBase = "DC=inc,DC=company,DC=com";
string filter = "(&(objectClass=person)(objectClass=user))";
var search = new SearchRequest(searchBase, filter, SearchScope.Subtree, null);
var response = (System.DirectoryServices.Protocols.SearchResponse)_ldapConnection.SendRequest(search, System.TimeSpan.FromMinutes(1));
However, doing so results in the following error
Unhandled exception. System.DirectoryServices.Protocols.DirectoryOperationException: An unspecified operation error occurred.
at System.DirectoryServices.Protocols.LdapConnection.ConstructResponseAsync(Int32 messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut, Boolean sync)
at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)
I unpacked every property in the exception itself, and in the corresponding response attached to the exception. This is what I found:
Data: System.Collections.ListDictionaryInternal
Help Link:
HResult: -2146233088
InnerException:
Message: An unspecified operation error occurred.
Source: System.DirectoryServices.Protocols
StackTrace: at System.DirectoryServices.Protocols.LdapConnection.ConstructResponseAsync(Int32 messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut, Boolean sync)
at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)
at Keep_LdapCore.LdapService.LogUsers() in /workspaces/Keep-LdapCore/LdapService.cs:line 35
TargetSite: Void MoveNext()
Response.Controls: System.DirectoryServices.Protocols.DirectoryControl[]
Response.Error Message: Referral:
ldap://ForestDnsZones.inc.company.com/DC=ForestDnsZones,DC=inc,DC=company,DC=com
ldap://DomainDnsZones.inc.company.com/DC=DomainDnsZones,DC=inc,DC=company,DC=com
ldap://inc.company.com/CN=Configuration,DC=inc,DC=company,DC=com
Response.MatchedDN:
Response.Referral: System.Uri[]
Response.RequestId:
Response.Result Code: -1
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 18 (11 by maintainers)
Hi, I am also running into this issue and I am able to reproduce with ldapsearch. I am using Microsoft Active Directory as the LDAP server (which has the behaviour described).
I have changed the query so it returns one match and one property only so the ldapsearch output is shorter.
On any other DN other than the base DN, the result looks like this:
But on the root DN, the result looks like this:
The difference between the two is that referrals are returned. They can be also seen in the first comment in this issue after the line
Response.Error Message: Referral:in the exception details,ForestDnsZonesandDomainDnsZonesare usually Active Directory specific.By default, ldapsearch does not follow referrals, the option
-Cenables referral following:So I can get the operation error using ldapsearch. (The option is in fact obsolete, see here, it’s not documented)
Trying to understand what is happening, I turned on debug logging for ldapsearch (
-d 1), indicated a SASL mechanism so it’s easier to see in the debug log (-Y GSSAPI), redirected the debug log tostdoutand filtered the output withgrep -B 10 -A 5 ldap_pvt_connect. I get the following output:OpenLDAP seems to follow referrals unauthenticated by default and Active Directory seems to accept the anonymous bind done in this case but gives an error on the first request done on the connection (the errors are further in the output but mixed together).
Checking in the OpenLDAP documentation, referrals can be turned off or a rebind callback can be given and neither of the two options work on
LdapConnectionbecause of differences between Windows and OpenLDAP.For the first way, referrals are turned off with LDAP_OPT_OFF (and on with LDAP_OPT_ON) with the option
LDAP_OPT_REFERRALS(see here for the definition). It is is used inLdapConnection.SessionOptions.ReferralChasingwhich is a property with the enum typeReferralChasingOptionswhich matches the values in Windows (see here) but there are extra values that OpenLDAP does not support. In fact, as LDAP_OPT_OFF is defined as((void *) 0)in OpenLDAP, the define itself must be passed toldap_set_optionas the “pointer” to the option, not a pointer to the value like in Windows, so setting the property always turns on referrals. (The OpenLDAP code only checks for zero / LDAP_OPT_OFF for boolean options (see example here), so LDAP_OPT_ON can be any non-zero pointer)For the second way, callbacks are set with
LdapConnection.SessionOptions.ReferralCallbackand this property contains three delegates which match the ones in the structureLDAP_REFERRAL_CALLBACK(see here) and the structure is passed toldap_set_optionwithLDAP_OPT_REFERRAL_CALLBACKto set them on a LDAP connection object. But OpenLDAP does not support this structure and option, it only has one callback which is set withldap_set_rebind_procand is for rebinds only, there is no multiple callbacks, so setting the property causes a LDAP exception when checking the error code ofldap_set_option.Yup, there is a command called ldapsearch which is includded when you install the
ldap-utilspackage. This one uses libldap underneath in a very similar fashion as we do except that it won’t depend on a previous bind since you will pass credentials directly into the command, whereas with us we get a handle that is bounded and then perform the search on it.In this case I don’t think that libldap is the problem, @NadimNadimNadimNadimNadim’s search has a correct filter and query, so assuming that the starting DN exists on the directory then query should in theory work. In fact, the search on their code looks very similar to the ones we use in our tests:
https://github.com/dotnet/runtime/blob/db3649e67ca6b56eb0e604f7c2e3a2479b02d950/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs#L549-L551
That is why I’m thinking that the problem here is that the bind was unsuccessful, and why I asked to try with a different username to see if that solves the issue.
It varies (something we are trying to do better at) but you can always ping the folks listed at https://github.com/dotnet/runtime/blob/master/docs/area-owners.md
In this case I happen to know that it’s @joperezr for Linux stuff - hopefully he can help here.