Secure LDAP does not work using the FQDN of the domain for GCs?

I have been running into this issue a couple of times. You have a forest with multiple domains and you cannot use LDAPs if you are using the FQDN of the domain in your LDAP connection string to connect to a global catalog. Here is a simple scenario:

(Note that this is also valid when it is another tree within the same forest)

The root domain is contoso.com therefore the name of the forest is contoso.com (it is actually important for later).

Why is LDAPs sometimes working using the FQDN of the domain for GC?

This is the connection window of ldp.exe. But what I am talking about is valid with any tool. As soon as you query the port 3269 which is the LDAPs for the global catalog partition.

If it works, there are only two possibilities:

  1. You have only one domain in your forest.
  2. You are using the FQDN of the root domain in the connection string.

When you are using the ADSI APIs, what you type is not what you get. You're fooled by the DsGetDcName function of the DCLocator. When you type contoso.com on a Windows application using the default API, you are not performing a DNS resolution for the A record of contoso.com. Instead your system realizes that contoso.com is the name of a domain, and instead of doing a DNS resolution of the simple domain name (the <same as parent folder> record  you can see in your DNS management console) it will try to localize the closest domain controller using the SRV records.

Just to be clear, what I called the <same as parent folder> record is this one: 

So in this specific case the DNS query that your machine will send is:

  • Record: _gc._tcp.MySite._sites.contoso.com Type: SRV

In this case your machine is sitting in the Active Directory site called MySite. If you haven't configured your Sites and Services correctly, this record might not exist and then you fallback to any GC available with the following DNS query:

  • Record: _gc._tcp.contoso.com Type: SRV

So the ADSI call for the LDAPs connection is actually sent to the FQDN of the DC returned by the queries above. And this is good! Because by default, the subject name of the certificate used by the domain controller just has its own FQDN. So you cannot establish an LDAPs connection with a DC using something else than the actual FQDN (try with the IP address for example, it will fail).

Why is it not working for me?

If you are trying the following in your LDP:

It will not work anymore.

ld = ldap_sslinit("child.contoso.com", 3269, 1);Error 0 = ldap_set_option(hLdap, LDAP_OPT_PROTOCOL_VERSION, 3);Error 81 = ldap_connect(hLdap, NULL);Server error: <empty>Error <0x51>: Fail to connect to child.contoso.com.

If you enable the CAPI2 logs on the client (see this if you are not familiar with this logging) you will see the following eventid 30:

Why that? Let's look at what is happening on the network... You are asking for a global catalog for child.contoso.com... The global catalog is a forest service. So all the _gc SRV records are registered only on the <forest name> zone. In other words, every global catalog is registering its _gc in the root domain zone (you can look at all the records here: https://technet.microsoft.com/en-us/library/cc778029(v=ws.10).aspx you'll see GC records are only recorded in the DnsForestName no matter in what domain the domain controller is). So everytime you query a GC, you should use the DnsForestName. Let's look at the DNS queries:

  1. Record: _gc._tcp.MySite._sites.child.contoso.com Type: SRV > Fail
  2. Record: _gc._tcp.child.contoso.com Type: SRV > Fail
  3. Record: child.contoso.com Type: A > Success! But it ends up using the <same as parent folder> record, so any domain controller of the domain (by default) and more importantly, the LDAPs connection is created using the FQDN of the domain and not the FQDN of the DC (since none was returned by the DNS for the <same as parent> A record).

How can I query a GC using LDAPs from a specific domain?

Well this request does not make sense. GC is a forest wide service therefore every GC (potentially) has the same information. If your application wants a GC from a specific domain, it means it is probably using data which are not in the the partial attribute set (aka PAS). 

What if my application wants to use LDAPs for GC but does not use the DCLocator?

Deal with those good old apps that claims to be Active Directory "compatible" (whatever it means) because they have a connection string setting somewhere that accepts an LDAP path. For those, you have several options. I'll discuss two of them quickly:

  1. Make them use the FQDN of the forest... Dirty I know because you are losing the "resiliency" feature in case the GC is not available. But think about it... If the app does not use the DCLocator, you actually do not have resiliency to start with...
  2. You can add a SAN to the domain controller certificate to accept connections on both the FQDN of the domain and the FQDN of the domain controller.

Bonus option... The logic of the DCLocator has been around for 15 years. Ask the vendor to use it (using ADSI object for example or by calling the API and functions mentioned earlier in this post).