-
Enhancement
-
Resolution: Fixed
-
P4
-
8, 9, 11
-
b20
-
x86_64
-
windows_7
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8251344 | 11.0.9 | Christoph Langer | P4 | Resolved | Fixed | b06 |
JDK-8240475 | 11.0.8-oracle | Robert Mckenna | P4 | Resolved | Fixed | b01 |
JDK-8252535 | openjdk8u272 | Zhengyu Gu | P4 | Resolved | Fixed | b06 |
JDK-8251269 | 8-pool | Zhengyu Gu | P4 | Closed | Duplicate | |
JDK-8238543 | 8u261 | Robert Mckenna | P4 | Resolved | Fixed | b01 |
JDK-8242275 | 8u251 | Robert Mckenna | P4 | Resolved | Fixed | master |
JDK-8240600 | 8u241 | Robert Mckenna | P4 | Closed | Fixed | b33 |
JDK-8246970 | emb-8u261 | Robert Mckenna | P4 | Resolved | Fixed | team |
JDK-8271031 | openjdk7u | Dmitry Cherepanov | P4 | Resolved | Fixed | master |
Currently, the Oracle JNDI LDAP provider takes a host name from ldap://example.com/... as-is and tries to connect to. This is deemed to fail for several reasons:
1. This might be a domain and not a host name
2. Kerberos and Digest MD5 always require a FQDN to operate on otherwise authentication will fail
3. Using host names is not reliable in a big enrivonment because servers are commissioned and decommissioned at any time without subject to prior notice. User shall rely on domain names only.
The propopal is to provide a context property for a factory which maps this domain name to a host name at its discretion (implementation detail). It should cover a domain name coming from ldap:///dc=example,dc=com (as a replacement for ServiceLocator), from a referral where you do not have control over the URL returned from the server or simply an initial context where you can simply say ldap://example.com whithout knowing which servers handle this domain.
The proposed solution hooks into the spots where a host name is passed before being connected with LdapClient, perform custom resolution and result one or more possible targets. If nothing is returned, use as-is.
In detail:
One provides an implementation of javax.naming.spi.ObjectFactory which will receive the following arguments to getObjectInstance:
Object obj:
Either a string passed as domainName:port OR Reference object with the value domainName:port and the type DomainPort
Name name: null
Context nameCtx: null
Hashtable environment: the environment passed to the initial or referral context to piggy back properties to the (custom) implementation.
Return value: String -- one host name OR String[] -- multiple host names (as fallback)
This object factory could be either used from the NamingManager or the ResourceManager. There are two possible spots where this will be called. Right before LdapClient#getInstance is called and/or LdapCtxFactory#getUsingURL. It shall be done place to avoid duplicate resolution.
Oracle can provide one default implementation which does the same as the ServiceLocator now. More sophisticated approaches are to be implemented by the caller.
Using merely host name canonicalization (A and PTR record) is deemed to fail with Active Directory.
This RFE solves previously reported bug 9089870.
JUSTIFICATION :
Microsoft takes a very sophisticated approach on not to rely on host names because servers can be provisioned and decommissioned any time. Instead, they heavily rely on DNS domain names and DNS SRV records at runtime. I.e., an initial or referral URL does not contain a host name, but only a DNS domain name. While you can connect to the service with this name, you cannot easily authenticate against it with Kerberos/Digest MD5 because one cannot bind the same SPN ldap/<dnsDomainName>@<REALM>, e.g., ldap/example.com@EXAMPLE.COM to more than one account. If you try authenticate anyway, you will receive a "Server not found in Kerberos database (7)" error. Therefore, one has to perform a DNS SRV query (_ldap._tcp.<dnsDomainName>) to test whether this name is a host name or a DNS domain name served by one or more machines. If it turns out to be a DNS domain name, you have to select one target host from the query response (according to RFC 2782, construct a special SPN ldap/<targetHost>/<dnsDomainName>@<REALM> or a regular one ldap/<targetHost>@ <REALM>, obtain a service ticket for and connect to that target host. If it is a regular host name, which is not the usual case with Active Directory, Oracle's internal implementation will behave correctly.
The referral follow implementation cannot be made to work because there is no way to tell the internal classes to perform this DNS SRV query and pass the appropriate server name(s) for the SPN to the SaslClient. It is deemed to fail. Note, that host name canocalization might sound reasonable within the SaslClient, but this is deemed to fail too for two reasons: First, the SaslClient will receive an arbitrary IP address without knowing whether the LDAP client socket will use the same one. You will have a service ticket issued for another host and your authentication will fail. Second, most Kerberos implementations rely on reverse DNS records, but Microsoft's SSPI Kerberos provider does not care about reverse DNS, it does not canonicalize host names by default and there is no guarantee, that reverse DNS is set up properly. Using throw will not make it any better because the referral URL returned by ReferralException.getReferralInfo() cannot be changed with the calculated values from DNS. ReferralException.getReferralContext() will unconditionally reuse that value. The only way (theoretically) to achieve this is to construct an InitialDirContext with the new URL manually and work with it appropriately.
My writeup: http://tomcatspnegoad.sourceforge.net/referral-handling
A terrible and non-maintainable workaround to intercept all URLs manually (initial or referral) reconstruct an InititialDirContext and perform operations. This adds signifact amount of boilerplate code, additionally there is not LdapURL class which makes this handling easy in Java, one needs to write this first.
How would a simple implementation for Active Directory look like if Oracle would add the support for:
domainName: passed domain name via LDAP URL
site: passed via Hashtable environment
1. Split DomainPort
2. if (site != null)
perform 3. with _ldap._tcp.<site>._sites.<domainName> (or _gc... if port is 3268 or 3269)
3. Perform DNS SRV query based on port IF site-based list is empty:
if (port in (3268, 3269)
lookup _gc._tcp.<domainName>
else
lookup _ldap._tcp.<domainName>
4. Return list of hostnames
See here for more details: https://technet.microsoft.com/en-us/library/cc759550%28v=ws.10%29.aspx
A complex implementation would even issue an LDAP ping to domainName to autodiscover the client site and some more stuff: https://msdn.microsoft.com/en-us/library/cc223811.aspx
The very same mechanism is implemented in Microsoft's .NET equivalent to perform the lookups described as above. ldap://example.com just works.
If someone does not use Active Directory but simply another directory server and has proper DNS SRV records, Oracle's default implemenation would work ideally here.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
ObjectFactory implementation is called and lets the custom code resolve the domain name that the initial or referral context can successfully authenticate without knowning the FQDN.
ACTUAL -
The authentication simply fails with Server not found in Kerberos database.
CUSTOMER SUBMITTED WORKAROUND :
A cheap version is to patch com.sun.jndi.ldap.LdapCtx with:
====
Index: com/sun/jndi/ldap/LdapCtx.java
===================================================================
--- com/sun/jndi/ldap/LdapCtx.java (revision 284)
+++ com/sun/jndi/ldap/LdapCtx.java (working copy)
@@ -2695,6 +2695,13 @@
ldapVersion = (ver != null) ? Integer.parseInt(ver) :
DEFAULT_LDAP_VERSION;
+ String[] servers = ServiceLocator.getLdapService(hostname, envprops);
+
+ if (servers != null) {
+ String selectedServer = servers[0];
+ hostname = selectedServer.substring(0, selectedServer.lastIndexOf('.'));
+ }
+
clnt = LdapClient.getInstance(
usePool, // Whether to use connection pooling
====
and prepend it to the bootclasspath: -Xbootclasspath/p:
- backported by
-
JDK-8238543 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Resolved
-
JDK-8240475 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Resolved
-
JDK-8242275 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Resolved
-
JDK-8246970 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Resolved
-
JDK-8251344 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Resolved
-
JDK-8252535 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Resolved
-
JDK-8271031 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Resolved
-
JDK-8240600 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Closed
-
JDK-8251269 Add capability to custom resolve host/domain names within the default JNDI LDAP provider
- Closed
- csr for
-
JDK-8192975 Add an SPI to allow custom DNS resolution of LDAP endpoints
- Closed
- relates to
-
JDK-8275535 Retrying a failed authentication on multiple LDAP servers can lead to users blocked
- Resolved
-
JDK-8214440 ldap over a TLS connection negotiate failed with "javax.net.ssl.SSLPeerUnverifiedException: hostname of the server '' does not match the hostname in the server's certificate"
- Closed
-
JDK-8161361 Cannot authenticate via Kerberos or Digest MD5 to an Active Directory LDAP server when a referral is followed or created with throw
- Open
-
JDK-8337792 javax.naming.NamingException: Could not resolve a valid ldap host when using LDAP connection in JDK11
- Resolved
-
JDK-8151678 com/sun/jndi/ldap/LdapTimeoutTest.java failed due to timeout on DeadServerNoTimeoutTest is incorrect
- Closed
-
JDK-8240523 JCK Test Case api/modulegraph/index.html#ModuleGraphTest failed in CI
- Closed