001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020package org.apache.directory.server.core.authn;
021
022
023import java.net.SocketAddress;
024
025import javax.net.ssl.TrustManager;
026
027import org.apache.directory.api.ldap.model.constants.AuthenticationLevel;
028import org.apache.directory.api.ldap.model.entry.Entry;
029import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
030import org.apache.directory.api.ldap.model.exception.LdapException;
031import org.apache.directory.api.ldap.model.name.Dn;
032import org.apache.directory.api.util.Strings;
033import org.apache.directory.ldap.client.api.LdapConnectionConfig;
034import org.apache.directory.ldap.client.api.LdapNetworkConnection;
035import org.apache.directory.server.core.api.LdapPrincipal;
036import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
037import org.apache.directory.server.i18n.I18n;
038import org.apache.mina.core.session.IoSession;
039
040
041/**
042 * Authenticator delegating to another LDAP server.
043 *
044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045 */
046public class DelegatingAuthenticator extends AbstractAuthenticator
047{
048    /** A speedup for logger in debug mode */
049    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
050
051    /** The host in charge of delegated authentication */
052    private String delegateHost;
053
054    /** The associated port */
055    private int delegatePort;
056
057    /** Tells if we use SSL to connect */
058    private boolean delegateSsl;
059
060    /** Tells if we use StartTLS to connect */
061    private boolean delegateTls;
062
063    /** The SSL TrustManager FQCN to use */
064    private String delegateSslTrustManagerFQCN;
065
066    /** The startTLS TrustManager FQCN to use */
067    private String delegateTlsTrustManagerFQCN;
068
069
070    /**
071     * Creates a new instance.
072     */
073    public DelegatingAuthenticator()
074    {
075        super( AuthenticationLevel.SIMPLE );
076    }
077
078
079    /**
080     * Creates a new instance.
081     * @see AbstractAuthenticator
082     * @param baseDn The base Dn
083     */
084    public DelegatingAuthenticator( Dn baseDn )
085    {
086        super( AuthenticationLevel.SIMPLE, baseDn );
087    }
088
089
090    /**
091     * Creates a new instance, for a specific authentication level.
092     * @see AbstractAuthenticator
093     * @param type The relevant AuthenticationLevel
094     * @param baseDn The base Dn
095     */
096    protected DelegatingAuthenticator( AuthenticationLevel type, Dn baseDn )
097    {
098        super( type, baseDn );
099    }
100
101
102    /**
103     * @return the delegateHost
104     */
105    public String getDelegateHost()
106    {
107        return delegateHost;
108    }
109
110
111    /**
112     * @param delegateHost the delegateHost to set
113     */
114    public void setDelegateHost( String delegateHost )
115    {
116        this.delegateHost = delegateHost;
117    }
118
119
120    /**
121     * @return the delegatePort
122     */
123    public int getDelegatePort()
124    {
125        return delegatePort;
126    }
127
128
129    /**
130     * @param delegatePort the delegatePort to set
131     */
132    public void setDelegatePort( int delegatePort )
133    {
134        this.delegatePort = delegatePort;
135    }
136
137
138    /**
139     * @return the delegateSsl
140     */
141    public boolean isDelegateSsl()
142    {
143        return delegateSsl;
144    }
145
146
147    /**
148     * @param delegateSsl the delegateSsl to set
149     */
150    public void setDelegateSsl( boolean delegateSsl )
151    {
152        this.delegateSsl = delegateSsl;
153    }
154
155
156    /**
157     * @return the delegateBaseDn
158     */
159    public String getDelegateBaseDn()
160    {
161        return getBaseDn().toString();
162    }
163
164
165    /**
166     * @return the delegateTls
167     */
168    public boolean isDelegateTls()
169    {
170        return delegateTls;
171    }
172
173
174    /**
175     * @param delegateTls the delegateTls to set
176     */
177    public void setDelegateTls( boolean delegateTls )
178    {
179        this.delegateTls = delegateTls;
180    }
181
182
183    /**
184     * @return the delegateSslTrustManagerFQCN
185     */
186    public String getDelegateSslTrustManagerFQCN()
187    {
188        return delegateSslTrustManagerFQCN;
189    }
190
191
192    /**
193     * @param delegateSslTrustManagerFQCN the delegateSslTrustManagerFQCN to set
194     */
195    public void setDelegateSslTrustManagerFQCN( String delegateSslTrustManagerFQCN )
196    {
197        this.delegateSslTrustManagerFQCN = delegateSslTrustManagerFQCN;
198    }
199
200
201    /**
202     * @return the delegateTlsTrustManagerFQCN
203     */
204    public String getDelegateTlsTrustManagerFQCN()
205    {
206        return delegateTlsTrustManagerFQCN;
207    }
208
209
210    /**
211     * @param delegateTlsTrustManagerFQCN the delegateTlsTrustManagerFQCN to set
212     */
213    public void setDelegateTlsTrustManagerFQCN( String delegateTlsTrustManagerFQCN )
214    {
215        this.delegateTlsTrustManagerFQCN = delegateTlsTrustManagerFQCN;
216    }
217
218
219    /**
220     * {@inheritDoc}
221     */
222    @Override
223    public LdapPrincipal authenticate( BindOperationContext bindContext )
224        throws LdapException
225    {
226        LdapPrincipal principal = null;
227
228        if ( IS_DEBUG )
229        {
230            LOG.debug( "Authenticating {}", bindContext.getDn() );
231        }
232
233        // First, check that the Bind DN is under the delegateBaseDn
234        Dn bindDn = bindContext.getDn();
235
236        // Don't authenticate using this authenticator if the Bind ND is not a descendant of the
237        // configured delegate base DN (or if it's null)
238        if ( ( getBaseDn() == null ) || ( !bindDn.isDescendantOf( getBaseDn() ) ) )
239        {
240            return null;
241        }
242
243        LdapConnectionConfig connectionConfig;
244        LdapNetworkConnection ldapConnection;
245
246        // Create a connection on the remote host
247        if ( delegateTls )
248        {
249            connectionConfig = new LdapConnectionConfig();
250            connectionConfig.setLdapHost( delegateHost );
251            connectionConfig.setLdapPort( delegatePort );
252            if ( delegateTlsTrustManagerFQCN != null && !"".equals( delegateTlsTrustManagerFQCN ) )
253            {
254                try
255                {
256                    Class<?> trustManagerClass = Class.forName( delegateTlsTrustManagerFQCN );
257                    TrustManager trustManager = ( TrustManager ) trustManagerClass.newInstance();
258                    connectionConfig.setTrustManagers( trustManager );
259                }
260                catch ( ClassNotFoundException | InstantiationException | IllegalAccessException e )
261                {
262                    String message = "Cannot load " + delegateTlsTrustManagerFQCN;
263                    LOG.error( message );
264                    throw new LdapException( message );
265                }
266            }
267
268            ldapConnection = new LdapNetworkConnection( connectionConfig );
269            ldapConnection.connect();
270            ldapConnection.startTls();
271        }
272        else if ( delegateSsl )
273        {
274            connectionConfig = new LdapConnectionConfig();
275            connectionConfig.setLdapHost( delegateHost );
276            connectionConfig.setUseSsl( true );
277            connectionConfig.setLdapPort( delegatePort );
278            if ( delegateSslTrustManagerFQCN != null && !"".equals( delegateSslTrustManagerFQCN ) )
279            {
280                try
281                {
282                    Class<?> trustManagerClass = Class.forName( delegateSslTrustManagerFQCN );
283                    TrustManager trustManager = ( TrustManager ) trustManagerClass.newInstance();
284                    connectionConfig.setTrustManagers( trustManager );
285                }
286                catch ( ClassNotFoundException | InstantiationException | IllegalAccessException e )
287                {
288                    String message = "Cannot load " + delegateSslTrustManagerFQCN;
289                    LOG.error( message );
290                    throw new LdapException( message );
291                }
292            }
293
294            ldapConnection = new LdapNetworkConnection( connectionConfig );
295            ldapConnection.connect();
296        }
297        else
298        {
299            connectionConfig = new LdapConnectionConfig();
300            connectionConfig.setLdapHost( delegateHost );
301            connectionConfig.setLdapPort( delegatePort );
302
303            ldapConnection = new LdapNetworkConnection( delegateHost, delegatePort );
304            ldapConnection.connect();
305        }
306
307        ldapConnection.setTimeOut( 0L );
308
309        try
310        {
311            // Try to bind
312            try
313            {
314                ldapConnection.bind( bindDn, Strings.utf8ToString( bindContext.getCredentials() ) );
315            }
316            catch ( LdapException le )
317            {
318                String message = I18n.err( I18n.ERR_230, bindDn.getName() );
319                LOG.info( message );
320                throw new LdapAuthenticationException( message );
321            }
322            finally
323            {
324                // no need to remain bound to delegate host
325                ldapConnection.unBind();
326
327                if ( IS_DEBUG )
328                {
329                    LOG.debug( "Authenticated successfully {}", bindContext.getDn() );
330                }
331            }
332
333            // Create the new principal
334            principal = new LdapPrincipal( getDirectoryService().getSchemaManager(), bindDn,
335                AuthenticationLevel.SIMPLE,
336                bindContext.getCredentials() );
337
338            IoSession session = bindContext.getIoSession();
339
340            if ( session != null )
341            {
342                SocketAddress clientAddress = session.getRemoteAddress();
343                principal.setClientAddress( clientAddress );
344                SocketAddress serverAddress = session.getServiceAddress();
345                principal.setServerAddress( serverAddress );
346            }
347
348            return principal;
349        }
350        catch ( LdapException e )
351        {
352            // Bad password ...
353            String message = I18n.err( I18n.ERR_230, bindDn.getName() );
354            LOG.info( message );
355            throw new LdapAuthenticationException( message );
356        }
357        finally
358        {
359            ldapConnection.close();
360        }
361    }
362
363
364    /**
365     * We don't handle any password policy when using a delegated authentication
366     */
367    @Override
368    public void checkPwdPolicy( Entry userEntry ) throws LdapException
369    {
370        // no check for delegating authentication
371    }
372
373
374    /**
375     * We don't handle any cache when using a delegated authentication
376     */
377    @Override
378    public void invalidateCache( Dn bindDn )
379    {
380        // cache is not implemented here
381    }
382}