View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.authn;
18  
19  
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.Map;
25  
26  import javax.naming.Context;
27  import javax.naming.Name;
28  import javax.naming.NamingEnumeration;
29  import javax.naming.NamingException;
30  import javax.naming.directory.Attributes;
31  import javax.naming.directory.ModificationItem;
32  import javax.naming.directory.SearchControls;
33  
34  import org.apache.ldap.common.exception.LdapAuthenticationException;
35  import org.apache.ldap.common.exception.LdapAuthenticationNotSupportedException;
36  import org.apache.ldap.common.filter.ExprNode;
37  import org.apache.ldap.common.message.ResultCodeEnum;
38  import org.apache.ldap.common.util.StringTools;
39  import org.apache.ldap.server.configuration.AuthenticatorConfiguration;
40  import org.apache.ldap.server.configuration.InterceptorConfiguration;
41  import org.apache.ldap.server.interceptor.Interceptor;
42  import org.apache.ldap.server.interceptor.NextInterceptor;
43  import org.apache.ldap.server.invocation.InvocationStack;
44  import org.apache.ldap.server.jndi.ContextFactoryConfiguration;
45  import org.apache.ldap.server.jndi.ServerContext;
46  
47  
48  /***
49   * An {@link Interceptor} that authenticates users.
50   *
51   * @author Apache Directory Project (dev@directory.apache.org)
52   * @author Alex Karasulu (akarasulu@apache.org)
53   * @author Trustin Lee (trustin@apache.org)
54   * @version $Rev: 226451 $, $Date: 2005-07-29 20:54:58 -0400 (Fri, 29 Jul 2005) $
55   */
56  public class AuthenticationService implements Interceptor
57  {
58      /*** authenticators **/
59      public Map authenticators = new HashMap();
60  
61      private ContextFactoryConfiguration factoryCfg;
62  
63      /***
64       * Creates an authentication service interceptor.
65       */
66      public AuthenticationService()
67      {
68      }
69  
70      /***
71       * Registers and initializes all {@link Authenticator}s to this service.
72       */
73      public void init( ContextFactoryConfiguration factoryCfg, InterceptorConfiguration cfg ) throws NamingException
74      {
75          this.factoryCfg = factoryCfg;
76  
77          // Register all authenticators
78          Iterator i = factoryCfg.getStartupConfiguration().getAuthenticatorConfigurations().iterator();
79          while( i.hasNext() )
80          {
81              try
82              {
83                  this.register( ( AuthenticatorConfiguration ) i.next() );
84              }
85              catch ( Exception e )
86              {
87                  destroy();
88                  throw ( NamingException ) new NamingException(
89                          "Failed to register authenticator." ).initCause( e );
90              }
91          }
92      }
93      
94      /***
95       * Deinitializes and deregisters all {@link Authenticator}s from this service.
96       */
97      public void destroy()
98      {
99          Iterator i = new ArrayList( authenticators.values() ).iterator();
100         while( i.hasNext() )
101         {
102             Iterator j = new ArrayList( ( Collection ) i.next() ).iterator();
103             while( j.hasNext() )
104             {
105                 unregister( ( Authenticator ) j.next() );
106             }
107         }
108         
109         authenticators.clear();
110     }
111 
112     /***
113      * Initializes the specified {@link Authenticator} and registers it to
114      * this service.
115      */
116     private void register( AuthenticatorConfiguration cfg ) throws NamingException
117     {
118         cfg.getAuthenticator().init( factoryCfg, cfg );
119 
120         Collection authenticatorList = getAuthenticators( cfg.getAuthenticator().getAuthenticatorType() );
121         if ( authenticatorList == null )
122         {
123             authenticatorList = new ArrayList();
124             authenticators.put( cfg.getAuthenticator().getAuthenticatorType(), authenticatorList );
125         }
126 
127         authenticatorList.add( cfg.getAuthenticator() );
128     }
129 
130     /***
131      * Deinitializes the specified {@link Authenticator} and deregisters it from
132      * this service.
133      */
134     private void unregister( Authenticator authenticator )
135     {
136         Collection authenticatorList = getAuthenticators( authenticator.getAuthenticatorType() );
137 
138         if ( authenticatorList == null )
139         {
140             return;
141         }
142 
143         authenticatorList.remove( authenticator );
144         
145         try
146         {
147             authenticator.destroy();
148         }
149         catch( Throwable t )
150         {
151             t.printStackTrace();
152         }
153     }
154 
155     /***
156      * Returns the list of {@link Authenticator}s with the specified type.
157      * 
158      * @return <tt>null</tt> if no authenticator is found.
159      */
160     private Collection getAuthenticators( String type )
161     {
162         Collection result = ( Collection ) authenticators.get( type );
163         if( result != null && result.size() > 0 )
164         {
165             return result;
166         }
167         else
168         {
169             return null;
170         }
171     }
172     
173 
174     public void add( NextInterceptor next, String upName, Name normName, Attributes entry ) throws NamingException
175     {
176         authenticate();
177         next.add( upName, normName, entry );
178     }
179 
180 
181     public void delete( NextInterceptor next, Name name ) throws NamingException
182     {
183         authenticate();
184         next.delete( name );
185     }
186 
187 
188     public Name getMatchedName( NextInterceptor next, Name dn, boolean normalized ) throws NamingException
189     {
190         authenticate();
191         return next.getMatchedName( dn, normalized );
192     }
193 
194 
195     public Attributes getRootDSE( NextInterceptor next ) throws NamingException
196     {
197         authenticate();
198         return next.getRootDSE();
199     }
200 
201 
202     public Name getSuffix( NextInterceptor next, Name dn, boolean normalized ) throws NamingException
203     {
204         authenticate();
205         return next.getSuffix( dn, normalized );
206     }
207 
208 
209     public boolean hasEntry( NextInterceptor next, Name name ) throws NamingException
210     {
211         authenticate();
212         return next.hasEntry( name );
213     }
214 
215 
216     public boolean isSuffix( NextInterceptor next, Name name ) throws NamingException
217     {
218         authenticate();
219         return next.isSuffix( name );
220     }
221 
222 
223     public NamingEnumeration list( NextInterceptor next, Name base ) throws NamingException
224     {
225         authenticate();
226         return next.list( base );
227     }
228 
229 
230     public Iterator listSuffixes( NextInterceptor next, boolean normalized ) throws NamingException
231     {
232         authenticate();
233         return next.listSuffixes( normalized );
234     }
235 
236 
237     public Attributes lookup( NextInterceptor next, Name dn, String[] attrIds ) throws NamingException
238     {
239         authenticate();
240         return next.lookup( dn, attrIds );
241     }
242 
243 
244     public Attributes lookup( NextInterceptor next, Name name ) throws NamingException
245     {
246         authenticate();
247         return next.lookup( name );
248     }
249 
250 
251     public void modify( NextInterceptor next, Name name, int modOp, Attributes mods ) throws NamingException
252     {
253         authenticate();
254         next.modify( name, modOp, mods );
255     }
256 
257 
258     public void modify( NextInterceptor next, Name name, ModificationItem[] mods ) throws NamingException
259     {
260         authenticate();
261         next.modify( name, mods );
262     }
263 
264 
265     public void modifyRn( NextInterceptor next, Name name, String newRn, boolean deleteOldRn ) throws NamingException
266     {
267         authenticate();
268         next.modifyRn( name, newRn, deleteOldRn );
269     }
270 
271 
272     public void move( NextInterceptor next, Name oriChildName, Name newParentName, String newRn, boolean deleteOldRn ) throws NamingException
273     {
274         authenticate();
275         next.move( oriChildName, newParentName, newRn, deleteOldRn );
276     }
277 
278 
279     public void move( NextInterceptor next, Name oriChildName, Name newParentName ) throws NamingException
280     {
281         authenticate();
282         next.move( oriChildName, newParentName );
283     }
284 
285 
286     public NamingEnumeration search( NextInterceptor next, Name base, Map env, ExprNode filter, SearchControls searchCtls ) throws NamingException
287     {
288         authenticate();
289         return next.search( base, env, filter, searchCtls );
290     }
291 
292 
293     private void authenticate() throws NamingException
294     {
295         // check if we are already authenticated and if so we return making
296         // sure first that the credentials are not exposed within context
297         ServerContext ctx =
298             ( ServerContext ) InvocationStack.getInstance().peek().getCaller();
299 
300         if ( ctx.getPrincipal() != null )
301         {
302             if ( ctx.getEnvironment().containsKey( Context.SECURITY_CREDENTIALS ) )
303             {
304                 ctx.removeFromEnvironment( Context.SECURITY_CREDENTIALS );
305             }
306             return;
307         }
308 
309         String authList = ( String ) ctx.getEnvironment().get( Context.SECURITY_AUTHENTICATION );
310 
311         if ( authList == null )
312         {
313             if ( ctx.getEnvironment().containsKey( Context.SECURITY_CREDENTIALS ) )
314             {
315                 // authentication type is simple here
316 
317                 authList = "simple";
318             }
319             else
320             {
321                 // authentication type is anonymous
322 
323                 authList = "none";
324             }
325 
326         }
327 
328         authList = StringTools.deepTrim( authList );
329 
330         String[] auth = authList.split( " " );
331 
332         Collection authenticators = null;
333 
334         // pick the first matching authenticator type
335 
336         for ( int i=0; i<auth.length; i++)
337         {
338             authenticators = getAuthenticators( auth[i] );
339 
340             if ( authenticators != null )
341             {
342                 break;
343             }
344         }
345 
346         if ( authenticators == null )
347         {
348             ctx.getEnvironment(); // shut's up idea's yellow light
349 
350             ResultCodeEnum rc = ResultCodeEnum.AUTHMETHODNOTSUPPORTED;
351 
352             throw new LdapAuthenticationNotSupportedException( rc );
353         }
354 
355         // try each authenticators
356         for ( Iterator i = authenticators.iterator(); i.hasNext(); )
357         {
358             try
359             {
360                 Authenticator authenticator = ( Authenticator ) i.next();
361 
362                 // perform the authentication
363 
364                 LdapPrincipal authorizationId = authenticator.authenticate( ctx );
365 
366                 // authentication was successful
367 
368                 ctx.setPrincipal( new TrustedPrincipalWrapper( authorizationId ) );
369 
370                 // remove creds so there is no security risk
371 
372                 ctx.removeFromEnvironment( Context.SECURITY_CREDENTIALS );
373                 return;
374             }
375             catch ( LdapAuthenticationException e )
376             {
377                 // authentication failed, try the next authenticator
378             }
379         }
380 
381         throw new LdapAuthenticationException();
382     }
383 
384 
385     /***
386      * FIXME This doesn't secure anything actually.
387      * 
388      * Created this wrapper to pass to ctx.setPrincipal() which is public for added
389      * security.  This adds more security because an instance of this class is not
390      * easily accessible whereas LdapPrincipals can be accessed easily from a context
391      * althought they cannot be instantiated outside of the authn package.  Malicious
392      * code may not be able to set the principal to what they would like but they
393      * could switch existing principals using the now public ServerContext.setPrincipal()
394      * method.  To avoid this we make sure that this metho takes a TrustedPrincipalWrapper
395      * as opposed to the LdapPrincipal.  Only this service can create and call setPrincipal
396      * with a TrustedPrincipalWrapper.
397      */
398     public final class TrustedPrincipalWrapper
399     {
400         /*** the wrapped ldap principal */
401         private final LdapPrincipal principal;
402 
403 
404         /***
405          * Creates a TrustedPrincipalWrapper around an LdapPrincipal.
406          *
407          * @param principal the LdapPrincipal to wrap
408          */
409         private TrustedPrincipalWrapper( LdapPrincipal principal )
410         {
411             this.principal = principal;
412         }
413 
414 
415         /***
416          * Gets the LdapPrincipal this TrustedPrincipalWrapper wraps.
417          *
418          * @return the wrapped LdapPrincipal
419          */
420         public LdapPrincipal getPrincipal()
421         {
422             return principal;
423         }
424     }
425 }