All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy Maven / Gradle / Ivy

/*
 * Copyright 2005-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.ldap.core.support;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Hashtable;

import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;

import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.core.DirContextProxy;
import org.springframework.ldap.support.LdapUtils;

/**
 * Abstract superclass for {@link DirContextAuthenticationStrategy} implementations that
 * apply TLS security to the connections. The supported TLS behavior differs between
 * servers. E.g., some servers expect the TLS connection be shut down gracefully before
 * the actual target context is closed, whereas other servers do not support that. The
 * shutdownTlsGracefully property controls this behavior; the property
 * defaults to false.
 * 

* The SSLSocketFactory used for TLS negotiation can be customized using the * sslSocketFactory property. This allows for example a socket factory that * can load the keystore/truststore using the Spring Resource abstraction. This provides a * much more Spring-like strategy for configuring PKI credentials for authentication, in * addition to allowing application-specific keystores and truststores running in the same * JVM. *

* In some rare occasions there is a need to supply a HostnameVerifier to the * TLS processing instructions in order to have the returned certificate properly * validated. If a HostnameVerifier is supplied to * {@link #setHostnameVerifier(HostnameVerifier)}, that will be applied to the processing. *

* For further information regarding TLS, refer to * this * page. *

* NB: TLS negotiation is an expensive process, which is why you will most likely * want to use connection pooling, to make sure new connections are not created for each * individual request. It is imperative however, that the built-in LDAP connection pooling * is not used in combination with the TLS AuthenticationStrategy implementations - this * will not work. You should use the Spring LDAP PoolingContextSource instead. * * @author Mattias Hellborg Arthursson */ public abstract class AbstractTlsDirContextAuthenticationStrategy implements DirContextAuthenticationStrategy { /** Hostname verifier to use for cert subject validation */ private HostnameVerifier hostnameVerifier; /** Flag to cause graceful shutdown required by some LDAP DSAs */ private boolean shutdownTlsGracefully = false; /** SSL socket factory to use for startTLS negotiation */ private SSLSocketFactory sslSocketFactory; /** * Specify whether the TLS should be shut down gracefully before the target context is * closed. Defaults to false. * @param shutdownTlsGracefully true to shut down the TLS connection * explicitly, false closes the target context immediately. */ public void setShutdownTlsGracefully(boolean shutdownTlsGracefully) { this.shutdownTlsGracefully = shutdownTlsGracefully; } /** * Set the optional HostnameVerifier to use for verifying incoming * certificates. Defaults to null , meaning that the default hostname * verification will take place. * @param hostnameVerifier The HostnameVerifier to use, if any. */ public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; } /** * Sets the optional SSL socket factory used for startTLS negotiation. Defaults to * null to indicate that the default socket factory provided by the * underlying JSSE provider should be used. * @param sslSocketFactory SSL socket factory to use, if any. */ public void setSslSocketFactory(final SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; } /* * (non-Javadoc) * * @see org.springframework.ldap.core.support.DirContextAuthenticationStrategy# * setupEnvironment(java.util.Hashtable, java.lang.String, java.lang.String) */ public final void setupEnvironment(Hashtable env, String userDn, String password) { // Nothing to do in this implementation - authentication should take // place after TLS has been negotiated. } /* * (non-Javadoc) * * @see org.springframework.ldap.core.support.DirContextAuthenticationStrategy# * processContextAfterCreation(javax.naming.directory.DirContext, java.lang.String, * java.lang.String) */ public final DirContext processContextAfterCreation(DirContext ctx, String userDn, String password) throws NamingException { if (ctx instanceof LdapContext) { final LdapContext ldapCtx = (LdapContext) ctx; final StartTlsResponse tlsResponse = (StartTlsResponse) ldapCtx.extendedOperation(new StartTlsRequest()); try { if (this.hostnameVerifier != null) { tlsResponse.setHostnameVerifier(this.hostnameVerifier); } tlsResponse.negotiate(this.sslSocketFactory); // If null, the default SSL // socket factory is used applyAuthentication(ldapCtx, userDn, password); if (this.shutdownTlsGracefully) { // Wrap the target context in a proxy to intercept any calls // to 'close', so that we can shut down the TLS connection // gracefully first. return (DirContext) Proxy.newProxyInstance(DirContextProxy.class.getClassLoader(), new Class[] { LdapContext.class, DirContextProxy.class }, new TlsAwareDirContextProxy(ldapCtx, tlsResponse)); } else { return ctx; } } catch (IOException ex) { LdapUtils.closeContext(ctx); throw new UncategorizedLdapException("Failed to negotiate TLS session", ex); } } else { throw new IllegalArgumentException( "Processed Context must be an LDAPv3 context, i.e. an LdapContext implementation"); } } /** * Apply the actual authentication to the specified LdapContext . * Typically, this will involve adding stuff to the environment. * @param ctx the LdapContext instance. * @param userDn the user dn of the user to authenticate. * @param password the password of the user to authenticate. * @throws NamingException if any error occurs. */ protected abstract void applyAuthentication(LdapContext ctx, String userDn, String password) throws NamingException; private static final class TlsAwareDirContextProxy implements DirContextProxy, InvocationHandler { private static final String GET_TARGET_CONTEXT_METHOD_NAME = "getTargetContext"; private static final String CLOSE_METHOD_NAME = "close"; private final LdapContext target; private final StartTlsResponse tlsResponse; TlsAwareDirContextProxy(LdapContext target, StartTlsResponse tlsResponse) { this.target = target; this.tlsResponse = tlsResponse; } @Override public DirContext getTargetContext() { return this.target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals(CLOSE_METHOD_NAME)) { this.tlsResponse.close(); return method.invoke(this.target, args); } else if (method.getName().equals(GET_TARGET_CONTEXT_METHOD_NAME)) { return this.target; } else { return method.invoke(this.target, args); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy