How to authenticate with Active Directory through LDAP through TLS?

I have an authentication application that can successfully authenticate with Active Directory through LDAP on the test server, but the production application will need to do this through TLS - the domain controller closes any connection that does not initiate through TLS.

I installed the LDAP browser in Eclipse, and I can really bind myself to myself using TLS, but I can’t figure out for my whole life how to get my application to use TLS.

ldap.xml

<bean id="ldapAuthenticationProvider" class="my.project.package.OverrideActiveDirectoryLdapAuthenticationProvider"> <!-- this works to authenticate by binding as the user in question --> <constructor-arg value="test.server"/> <constructor-arg value="ldap://192.168.0.2:389"/> <!-- this doesn't work, because the server requires a TLS connection --> <!-- <constructor-arg value="production.server"/> --> <!-- <constructor-arg value="ldaps://192.168.0.3:389"/> --> <property name="convertSubErrorCodesToExceptions" value="true"/> </bean> 

OverrideActiveDirectoryLdapAuthenticationProvider - An override class that extends a copy of the Spring class ActiveDirectoryLdapAuthenticationProvider , which is for some reason designated as final . My reasons for overriding are related to setting how the credentials / permissions are populated on the user object (we will either use the group memberships of the corresponding groups to create user rights, or we will read from the AD user object field). In this, I only redefine the loadUserAuthorities() method, but I suspect that I may also need to override the bindAsUser() method or, possibly, the doAuthentication() method.

XML and one override class are just two places where authentication is controlled by my application, and not let Spring do the work. I read several places to enable TLS; I need to extend the DefaultTlsDirContextAuthenticationStrategy class, but where to connect it? Is there a namespace solution? Do I need to do anything at all (i.e. refuse to use Spring ActiveDirectoryLdapAuthenticationProvider and use LdapAuthenticationProvider )?

Any help is appreciated.

+4
source share
1 answer

Okay, so after about a day and a half of working on it, I realized.

My initial approach was to extend the Spring class ActiveDirectoryLdapAuthenticationProvider and override its loadUserAuthorities() method to set up a way to create authenticated user rights. For non-obvious reasons, the ActiveDirectoryLdapAuthenticationProvider class ActiveDirectoryLdapAuthenticationProvider designated as final , so of course I cannot extend it.

Fortunately, open source provides hacking (and the superclass of the class is not final ), so I just copied all its contents, removed the final notation and adjusted the links to packages and classes accordingly. I have not edited the code in this class, except to add a very noticeable comment saying that it does not edit it. Then I extended this class to OverrideActiveDirectoryLdapAuthenticationProvider , which was also mentioned in my ldap.xml file, and it added an override method for loadUserAuthorities . All of this worked great with a simple LDAP binding to an unencrypted session (on an isolated virtual server).

The real network environment requires that all LDAP requests start with TLS handshaking, however the requested server is not a PDC - its name is "sub.domain.tld", but the user is correctly authenticated against 'domain.tld. In addition, the username must be added with the name "NT_DOMAIN \" to bind. All this required configuration work, and, unfortunately, I did not find or did not find anything.

So, here are the ridiculously simple changes, all of which are related to additional overrides in OverrideActiveDirectoryLdapAuthenticationProvider :

 @Override protected DirContext bindAsUser(String username, String password) { final String bindUrl = url; //super reference Hashtable<String,String> env = new Hashtable<String,String>(); env.put(Context.SECURITY_AUTHENTICATION, "simple"); //String bindPrincipal = createBindPrincipal(username); String bindPrincipal = "NT_DOMAIN\\" + username; //the bindPrincipal() method builds the principal name incorrectly env.put(Context.SECURITY_PRINCIPAL, bindPrincipal); env.put(Context.PROVIDER_URL, bindUrl); env.put(Context.SECURITY_CREDENTIALS, password); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxtFactory"); //and finally, this simple addition env.put(Context.SECURITY_PROTOCOL, "tls"); //. . . try/catch portion left alone } 

That is, everything I did with this method changed the way the bindPrincipal string was bindPrincipal , and I added the key / value to the hash table.

I did not need to remove the subdomain from the domain parameter passed to my class, because it was passed ldap.xml ; I just changed the parameter there to <constructor-arg value="domain.tld"/>

Then I changed the searchForUser() method to OverrideActiveDirectoryLdapAuthenticationProvider :

 @Override protected DirContextOperations searchForUser(DirContext ctx, String username) throws NamingException { SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); //this doesn't work, and I'm not sure exactly what the value of the parameter {0} is //String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))"; String searchFilter = "(&(objectClass=user)(userPrincipalName=" + username + "@domain.tld))"; final String bindPrincipal = createBindPrincipal(username); String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal); return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter, new Object[]{bindPrincipal}); 

The last change was in the createBindPrincipal() method to build the string correctly (for my purposes):

 @Override String createBindPrincipal(String username) { if (domain == null || username.toLowerCase().endsWith(domain)) { return username; } return "NT_DOMAIN\\" + username; } 

And with the above changes, which still need to be cleared of all my tests and bells and whistles - I was able to connect and authenticate as I am against Active Directory on the network, capture all the fields of user objects that I wanted, define group membership, etc.

Oh, and apparently TLS does not require "ldaps: //", so my ldap.xml just has ldap://192.168.0.3:389 .


TL; DR :

To enable TLS, copy the Spring class ActiveDirectoryLdapAuthenticationProvider , remove the final notation, extend it in the user class and override bindAsUser() by adding env.put(Context.SECURITY_PROTOCOL, "tls"); to the hash table of the environment. What is it.

To better control the binding username, domain, and LDAP query sequence, use the appropriate methods. In my case, I could not determine what the value {0} , so I completely deleted it and inserted the passed username string instead.

Hope someone out there finds this helpful.

+6
source

Source: https://habr.com/ru/post/1480987/


All Articles