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