Skip to main content

More Active Directory integration in Java

Posted by kohsuke on June 12, 2008 at 6:55 PM PDT

I blogged earlier about the sorry state of Active Directory integration in Java and how I implemented zero-configuration Active Directory support in Hudson by talking to Microsoft COM API.

That was great for those of you who run Hudson on Windows, but since then, I discovered that many folks wanted to run Hudson on a Unix machine but still authenticate against Active Directory. So I needed to implement another approach that works on a Unix machine. It took a bit of research and experiments, but in the end I was able to make this work — now the Active Directory plugin for Hudson works also on Unix, and all it asks you to configure is just one thing, that is the domain name to authenticate with.

Since people still seem to be rather clueless and waste a lot of time by trying to configure general-purpose LDAP or JNDI authentication mechanism to talk to Active Directory, here's how your program can talk to Active Directory from domain name:

Step 1: Locate LDAP server

First, use DNS SRV record to find where the LDAP servers are. You can do this by querying SRV records for _ldap._tcp.DOMAINNAME. You can see more about this mechanism in MSDN. See this JavaSE document for more about how to query DNS through JNDI.

This allows users to avoid hard-coding LDAP server name, and so they won't need to update your config as domain controllers come and go. SRV records also return information about fallback servers and round-robin mechanism (like MX records), and your program can do the right thing.

Step 2: Authenticate

Once you figure out which port of which server the LDAP service is running, it's time to talk to LDAP, again through JNDI. In LDAP, the way you verify that the given password is correct is by trying to login as that user with the password. Unfortunately, many generic LDAP connector (like this one) does a rather round-about way of doing this — first they bind and locate the record of the user being authenticated, to figure out its "canonical name". Then they bind again, this time with the canonical name of the user plus the given password. If that binds successfully, then the password was correct. If not, the password was wrong. This is necessary because the "user name" to connect to LDAP is different from what you might think.

This is very bad idea with Active Directory, since it doesn't support anonymous bind by default. That is, the program needs to have at least one workable user/password pair to do the "figure out the canonical name" step. This forces you to write down a password in a configuration file. Very bad idea.

But you can do better with Active Directory LDAP service. With AD, you can skip everything and just try to connect with the user name and password that the user entered. The trick is to supply the fully qualified user name (like "kohsuke@my.active.directory.domain.com") as the user name, instead of a canonical name (like "CN=Kohsuke Kawaguchi,DC=sun,DC=com"). So by writing code specifically against AD, your user won't have to give you user name and password.

Once you bind to Active Directory through LDAP, you can query all the user/group membership information and more to your hearts content. See Active Directory schema documentation for what attributes are available. It's also useful to use some kind of LDAP browser to check your sanity and do trouble-shooting.

Conclusion

See the source code for more details. For those of you who are suffering because you need to configure Active Directory authentication through LDAP authenticator, please demand to your vendors that they write AD authenticator.

The next one up is to Integrated Windows Authentication. If we can do this nicely in Java (and I know we can — it's just a matter of a bit of additional effort), the Java webapps would work very nicely in Active Directory environment.

Related Topics >>

Comments

Look at IOPLEX Software's "Jespa": http://www.ioplex.com/jespa.html Regarding some of the comments: Jespa includes a JAAS LoginModule, HTTP SSO Filter and much more. Inegrated Windows Authentication is a simple term that just means "whatever Windows security support provider (SSP) applies to this application". This is not limited to Kerberos. IWA means Kerberos, NTLM or SPNEGO to negotiate Kerberos or NTLM (in practice it doesn't really "negotiate" much at all but that's a different story ...). Note that NTLM and Kerberos are not mutually exclusive - NTLM is required if two authenticating parties do both have accounts in the domain or if the client does not have access to the domain to acquire a Kerberos ticket. But when the above two conditions are true, Kerberos is favored because it can cache the "ticket" on the client and the server doesn't need to communicate with the domain which reduces load on the DCs. And Kerberos supports delegation whereas NTLM does not. But for most things NTLM works just fine. In some ways NTLM is actually better than Kerberos. PS: Your blog software is busted. If I "Preview", the text box is empty and the post is lost. I'm using FF 3.0.5 on Linux.

Just to add ldap authentication without SSL is not safe and ...

Just to add ldap authentication without SSL is not safe and anyone can view user credential because ldap client transfer usernamae and password during ldap bind operation so I have also included ldap using SSL also. See Ldap authentication in Java using Spring with Example for more details.

Here's another open source SPNEGO library

Here's another open source library, http://spnego.sourceforge.net, that implements Integrated Windows Authentication (SSO). The project has lots of working examples as well as instructions for Tomcat, JBoss and Glassfish. The library is implemented as an HTTP Servlet Filter that uses Kerberos/SPNEGO tokens.

Nice! I could already use AD authentication but I was trying to retrieve the groups unsuccessfully... You solved it for me Thanks!

Integrated Windows Authentication is essentially just a Kerberos authentication over SPNEGO (plus some configuration parameters pre-defined.) So much of the hard work is already done in JavaSE.

The only hard part is that those security APIs tend to be not reusable, so we just need to figure out that details.

How do you know we can do integrated windows authentication in Java? Is there an API you had in mind?

Integrated AD

Have you talked with Jamie, Pat and the OpenSSO folks about this? I believe they have a fair amount of AD experience. - eduard/o

Unfortunately, the real hard one is finding an implementation that integrates with JAAS; I've found most of my application servers and other components want JAAS, and I've yet to see any Kerberos-based AD integration suite that plays nicely with JAAS. Using LDAP seems like a hack since it really is a directory service, not an authentication one. What about using the real authentication provided by AD's implementation of Kerberos? I'd love to hear your thoughts on that.

Really, really looking forward to the Integrated Windows Authentication article! It would make every Java web application at a company with AD more user friendly. I looked into it a couple of times, but still not sure how to do it. It would definitely make Hudson and JIRA better. I don't think simply requiring the end user to enter their AD credentials is good enough.

mbs3 -- I'm not a security expert by any stretch of imagination, so please take my comments as such.

But personally, I don't like JAAS because it's not really composable nor programmable. It's good that one can do declarative security but IMO that alone is not enough.

The "real authentication" provided by AD is not username/password authentication. It's the password-less single sign-on through Integrated Windows Authentication. So IMHO whether the username/password is sent to LDAP or Kerberos doesn't really seem to make a difference to me.