1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.core.authn;
21
22
23 import static org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyErrorEnum.INSUFFICIENT_PASSWORD_QUALITY;
24 import static org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyErrorEnum.PASSWORD_TOO_SHORT;
25 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_ACCOUNT_LOCKED_TIME_AT;
26 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_CHANGED_TIME_AT;
27 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_FAILURE_TIME_AT;
28 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_GRACE_USE_TIME_AT;
29 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_HISTORY_AT;
30 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_LAST_SUCCESS_AT;
31 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_POLICY_SUBENTRY_AT;
32 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_RESET_AT;
33 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_START_TIME_AT;
34 import static org.apache.directory.api.ldap.model.constants.PasswordPolicySchemaConstants.PWD_END_TIME_AT;
35 import static org.apache.directory.api.ldap.model.entry.ModificationOperation.ADD_ATTRIBUTE;
36 import static org.apache.directory.api.ldap.model.entry.ModificationOperation.REMOVE_ATTRIBUTE;
37 import static org.apache.directory.api.ldap.model.entry.ModificationOperation.REPLACE_ATTRIBUTE;
38
39 import java.io.IOException;
40 import java.security.MessageDigest;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.Collections;
44 import java.util.EnumMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Set;
49
50 import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyRequest;
51 import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyResponse;
52 import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyResponseImpl;
53 import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyErrorEnum;
54 import org.apache.directory.api.ldap.model.constants.AuthenticationLevel;
55 import org.apache.directory.api.ldap.model.constants.LdapSecurityConstants;
56 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
57 import org.apache.directory.api.ldap.model.entry.Attribute;
58 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
59 import org.apache.directory.api.ldap.model.entry.DefaultModification;
60 import org.apache.directory.api.ldap.model.entry.Entry;
61 import org.apache.directory.api.ldap.model.entry.Modification;
62 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
63 import org.apache.directory.api.ldap.model.entry.Value;
64 import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
65 import org.apache.directory.api.ldap.model.exception.LdapException;
66 import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException;
67 import org.apache.directory.api.ldap.model.exception.LdapOperationException;
68 import org.apache.directory.api.ldap.model.exception.LdapOtherException;
69 import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
70 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
71 import org.apache.directory.api.ldap.model.name.Dn;
72 import org.apache.directory.api.ldap.model.password.PasswordUtil;
73 import org.apache.directory.api.ldap.model.schema.AttributeType;
74 import org.apache.directory.api.util.DateUtils;
75 import org.apache.directory.api.util.Strings;
76 import org.apache.directory.server.constants.ServerDNConstants;
77 import org.apache.directory.server.core.api.CoreSession;
78 import org.apache.directory.server.core.api.DirectoryService;
79 import org.apache.directory.server.core.api.InterceptorEnum;
80 import org.apache.directory.server.core.api.LdapPrincipal;
81 import org.apache.directory.server.core.api.authn.ppolicy.CheckQualityEnum;
82 import org.apache.directory.server.core.api.authn.ppolicy.DefaultPasswordValidator;
83 import org.apache.directory.server.core.api.authn.ppolicy.PasswordPolicyConfiguration;
84 import org.apache.directory.server.core.api.authn.ppolicy.PasswordPolicyException;
85 import org.apache.directory.server.core.api.authn.ppolicy.PasswordValidator;
86 import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
87 import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
88 import org.apache.directory.server.core.api.interceptor.Interceptor;
89 import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
90 import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
91 import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext;
92 import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
93 import org.apache.directory.server.core.api.interceptor.context.GetRootDseOperationContext;
94 import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
95 import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
96 import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
97 import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
98 import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
99 import org.apache.directory.server.core.api.interceptor.context.OperationContext;
100 import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
101 import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
102 import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext;
103 import org.apache.directory.server.core.api.partition.Partition;
104 import org.apache.directory.server.core.api.partition.PartitionTxn;
105 import org.apache.directory.server.core.authn.ppolicy.PpolicyConfigContainer;
106 import org.apache.directory.server.core.shared.DefaultCoreSession;
107 import org.apache.directory.server.i18n.I18n;
108 import org.slf4j.Logger;
109 import org.slf4j.LoggerFactory;
110
111
112
113
114
115
116
117 public class AuthenticationInterceptor extends BaseInterceptor
118 {
119 private static final Logger LOG = LoggerFactory.getLogger( AuthenticationInterceptor.class );
120
121
122
123
124 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
125
126
127 private Set<Authenticator> authenticators = new HashSet<>();
128
129
130 private final EnumMap<AuthenticationLevel, Collection<Authenticator>> authenticatorsMapByType = new EnumMap<>( AuthenticationLevel.class );
131
132 private CoreSession adminSession;
133
134
135 private AttributeType pwdResetAT;
136
137 private AttributeType pwdChangedTimeAT;
138
139 private AttributeType pwdHistoryAT;
140
141 private AttributeType pwdFailurTimeAT;
142
143 private AttributeType pwdAccountLockedTimeAT;
144
145 private AttributeType pwdLastSuccessAT;
146
147 private AttributeType pwdGraceUseTimeAT;
148
149 private AttributeType pwdPolicySubentryAT;
150
151 private AttributeType pwdStartTimeAT;
152
153 private AttributeType pwdEndTimeAT;
154
155
156 private PpolicyConfigContainer pwdPolicyContainer;
157
158
159
160
161
162 public AuthenticationInterceptor()
163 {
164 super( InterceptorEnum.AUTHENTICATION_INTERCEPTOR );
165 }
166
167
168
169
170
171 @Override
172 public void init( DirectoryService directoryService ) throws LdapException
173 {
174 super.init( directoryService );
175
176 adminSession = directoryService.getAdminSession();
177
178 if ( ( authenticators == null ) || authenticators.isEmpty() )
179 {
180 setDefaultAuthenticators();
181 }
182
183
184 for ( Authenticator authenticator : authenticators )
185 {
186 register( authenticator, directoryService );
187 }
188
189 loadPwdPolicyStateAttributeTypes();
190 }
191
192
193
194
195
196 private void setDefaultAuthenticators()
197 {
198 if ( authenticators == null )
199 {
200 authenticators = new HashSet<>();
201 }
202
203 authenticators.clear();
204 authenticators.add( new AnonymousAuthenticator( Dn.ROOT_DSE ) );
205 authenticators.add( new SimpleAuthenticator( Dn.ROOT_DSE ) );
206 authenticators.add( new StrongAuthenticator( Dn.ROOT_DSE ) );
207 }
208
209
210 public Set<Authenticator> getAuthenticators()
211 {
212 return authenticators;
213 }
214
215
216
217
218
219 public void setAuthenticators( Set<Authenticator> authenticators )
220 {
221 if ( authenticators == null )
222 {
223 this.authenticators.clear();
224 }
225 else
226 {
227 this.authenticators = authenticators;
228 }
229 }
230
231
232
233
234
235 public void setAuthenticators( Authenticator[] authenticators )
236 {
237 if ( authenticators == null )
238 {
239 throw new IllegalArgumentException( "The given authenticators set is null" );
240 }
241
242 this.authenticators.clear();
243 this.authenticatorsMapByType.clear();
244
245 for ( Authenticator authenticator : authenticators )
246 {
247 try
248 {
249 register( authenticator, directoryService );
250 }
251 catch ( LdapException le )
252 {
253 LOG.error( "Cannot register authenticator {}", authenticator );
254 }
255 }
256 }
257
258
259
260
261
262 @Override
263 public void destroy()
264 {
265 authenticatorsMapByType.clear();
266 Set<Authenticator> copy = new HashSet<>( authenticators );
267 authenticators = new HashSet<>();
268
269 for ( Authenticator authenticator : copy )
270 {
271 authenticator.destroy();
272 }
273 }
274
275
276
277
278
279
280
281
282
283
284 private void register( Authenticator authenticator, DirectoryService directoryService ) throws LdapException
285 {
286 authenticator.init( directoryService );
287 authenticators.add( authenticator );
288
289 Collection<Authenticator> authenticatorList = getAuthenticators( authenticator.getAuthenticatorType() );
290
291 if ( authenticatorList == null )
292 {
293 authenticatorList = new ArrayList<>();
294 authenticatorsMapByType.put( authenticator.getAuthenticatorType(), authenticatorList );
295 }
296
297 if ( !authenticatorList.contains( authenticator ) )
298 {
299 authenticatorList.add( authenticator );
300 }
301 }
302
303
304
305
306
307
308
309
310 private Collection<Authenticator> getAuthenticators( AuthenticationLevel type )
311 {
312 Collection<Authenticator> result = authenticatorsMapByType.get( type );
313
314 if ( ( result != null ) && ( !result.isEmpty() ) )
315 {
316 return result;
317 }
318 else
319 {
320 return null;
321 }
322 }
323
324
325
326
327
328 @Override
329 public void add( AddOperationContext addContext ) throws LdapException
330 {
331 if ( IS_DEBUG )
332 {
333 LOG.debug( "Operation Context: {}", addContext );
334 }
335
336 checkAuthenticated( addContext );
337
338 Entry entry = addContext.getEntry();
339
340 if ( !directoryService.isPwdPolicyEnabled() || addContext.isReplEvent() )
341 {
342 next( addContext );
343 return;
344 }
345
346 PasswordPolicyConfiguration policyConfig = getPwdPolicy( entry );
347
348 boolean isPPolicyReqCtrlPresent = addContext.hasRequestControl( PasswordPolicyRequest.OID );
349
350 checkPwdReset( addContext );
351
352
353 String passwordAttribute = SchemaConstants.USER_PASSWORD_AT;
354
355 if ( isPPolicyReqCtrlPresent )
356 {
357 passwordAttribute = policyConfig.getPwdAttribute();
358 }
359
360 Attribute userPasswordAttribute = entry.get( passwordAttribute );
361
362 if ( userPasswordAttribute != null )
363 {
364 Value userPassword = userPasswordAttribute.get();
365
366 try
367 {
368 check( addContext, entry, userPassword.getBytes(), policyConfig );
369 }
370 catch ( PasswordPolicyException e )
371 {
372 if ( isPPolicyReqCtrlPresent )
373 {
374 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
375 responseControl.setPasswordPolicyError(
376 PasswordPolicyErrorEnum.get( e.getErrorCode() ) );
377 addContext.addResponseControl( responseControl );
378 }
379
380
381 throw new LdapOperationException( ResultCodeEnum.CONSTRAINT_VIOLATION, e.getMessage(), e );
382 }
383
384 String pwdChangedTime = DateUtils.getGeneralizedTime( directoryService.getTimeProvider() );
385
386 if ( ( policyConfig.getPwdMinAge() > 0 ) || ( policyConfig.getPwdMaxAge() > 0 ) )
387 {
388
389 if ( !addContext.getSession().isAnAdministrator()
390 || entry.get( pwdChangedTimeAT ) == null )
391 {
392 Attribute pwdChangedTimeAt = new DefaultAttribute( pwdChangedTimeAT );
393 pwdChangedTimeAt.add( pwdChangedTime );
394 entry.add( pwdChangedTimeAt );
395 }
396 }
397
398 if ( policyConfig.isPwdMustChange() && addContext.getSession().isAnAdministrator() )
399 {
400 Attribute pwdResetAt = new DefaultAttribute( pwdResetAT );
401 pwdResetAt.add( "TRUE" );
402 entry.add( pwdResetAt );
403 }
404
405 if ( policyConfig.getPwdInHistory() > 0 )
406 {
407 Attribute pwdHistoryAt = new DefaultAttribute( pwdHistoryAT );
408 byte[] pwdHistoryVal = new PasswordHistory( pwdChangedTime, userPassword.getBytes() ).getHistoryValue();
409 pwdHistoryAt.add( pwdHistoryVal );
410 entry.add( pwdHistoryAt );
411 }
412 }
413
414 next( addContext );
415 }
416
417
418
419
420
421 private Authenticator selectAuthenticator( Dn bindDn, AuthenticationLevel level )
422 throws LdapUnwillingToPerformException, LdapAuthenticationException
423 {
424 Authenticator selectedAuthenticator = null;
425 Collection<Authenticator> levelAuthenticators = authenticatorsMapByType.get( level );
426
427 if ( ( levelAuthenticators == null ) || levelAuthenticators.isEmpty() )
428 {
429
430 throw new LdapAuthenticationException( "Cannot Bind for Dn "
431 + bindDn.getName() + ", no authenticator for the requested level " + level );
432 }
433
434 if ( levelAuthenticators.size() == 1 )
435 {
436
437 for ( Authenticator authenticator : levelAuthenticators )
438 {
439
440 if ( authenticator.isValid( bindDn ) )
441 {
442 return authenticator;
443 }
444 else
445 {
446 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
447 "Cannot Bind for Dn " + bindDn.getName()
448 + ", its not a descendant of the authenticator base DN '" + authenticator.getBaseDn() + "'" );
449 }
450 }
451 }
452
453
454
455 Dn innerDn = Dn.ROOT_DSE;
456
457 for ( Authenticator authenticator : levelAuthenticators )
458 {
459 if ( authenticator.isValid( bindDn ) )
460 {
461
462 if ( innerDn.isAncestorOf( authenticator.getBaseDn() ) )
463 {
464 innerDn = authenticator.getBaseDn();
465 selectedAuthenticator = authenticator;
466 }
467 }
468 }
469
470 if ( selectedAuthenticator == null )
471 {
472 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
473 "Cannot Bind for Dn " + bindDn.getName() + ", there is no authenticator for it" );
474 }
475
476 return selectedAuthenticator;
477 }
478
479
480 private void internalModify( OperationContext opContext, ModifyOperationContext bindModCtx ) throws LdapException
481 {
482 Partition partition = opContext.getPartition();
483 bindModCtx.setPartition( partition );
484 PartitionTxn partitionTxn = null;
485
486 try
487 {
488 partitionTxn = partition.beginWriteTransaction();
489 bindModCtx.setTransaction( partitionTxn );
490
491 directoryService.getPartitionNexus().modify( bindModCtx );
492
493 partitionTxn.commit();
494 }
495 catch ( LdapException le )
496 {
497 try
498 {
499 if ( partitionTxn != null )
500 {
501 partitionTxn.abort();
502 }
503
504 throw le;
505 }
506 catch ( IOException ioe )
507 {
508 throw new LdapOtherException( ioe.getMessage(), ioe );
509 }
510 }
511 catch ( IOException ioe )
512 {
513 try
514 {
515 partitionTxn.abort();
516
517 throw new LdapOtherException( ioe.getMessage(), ioe );
518 }
519 catch ( IOException ioe2 )
520 {
521 throw new LdapOtherException( ioe2.getMessage(), ioe2 );
522 }
523 }
524 }
525
526
527
528
529
530 @Override
531 public void bind( BindOperationContext bindContext ) throws LdapException
532 {
533 if ( IS_DEBUG )
534 {
535 LOG.debug( "Operation Context: {}", bindContext );
536 }
537
538 CoreSession session = bindContext.getSession();
539 Dn bindDn = bindContext.getDn();
540
541 if ( ( session != null )
542 && ( session.getEffectivePrincipal() != null )
543 && ( !session.isAnonymous() )
544 && ( !session.isAdministrator() ) )
545 {
546
547 bindContext.setCredentials( null );
548 }
549
550
551 AuthenticationLevel level = bindContext.getAuthenticationLevel();
552
553 if ( level == AuthenticationLevel.UNAUTHENT )
554 {
555
556
557
558 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, "Cannot Bind for Dn "
559 + bindDn.getName() );
560 }
561
562 PasswordPolicyException ppe = null;
563 boolean isPPolicyReqCtrlPresent = bindContext.hasRequestControl( PasswordPolicyRequest.OID );
564 PasswordPolicyResponse pwdRespCtrl = new PasswordPolicyResponseImpl();
565 boolean authenticated = false;
566
567 Authenticator authenticator = selectAuthenticator( bindDn, level );
568
569 try
570 {
571
572 LdapPrincipal principal = authenticator.authenticate( bindContext );
573
574 if ( principal != null )
575 {
576 LdapPrincipal/org/apache/directory/server/core/api/LdapPrincipal.html#LdapPrincipal">LdapPrincipal clonedPrincipal = ( LdapPrincipal ) ( principal.clone() );
577
578
579 bindContext.setCredentials( null );
580 clonedPrincipal.setUserPassword( Strings.EMPTY_BYTES );
581
582
583 CoreSession newSession = new DefaultCoreSession( clonedPrincipal, directoryService );
584 bindContext.setSession( newSession );
585
586 authenticated = true;
587 }
588 }
589 catch ( PasswordPolicyException e )
590 {
591 ppe = e;
592 }
593 catch ( LdapAuthenticationException e )
594 {
595
596 LOG.info( "Authenticator {} failed to authenticate: {}", authenticator, bindContext.getDn() );
597 }
598 catch ( Exception e )
599 {
600
601 LOG.info( "Unexpected failure for Authenticator {} : {}", authenticator, bindContext.getDn() );
602 }
603
604 if ( ppe != null )
605 {
606 if ( isPPolicyReqCtrlPresent )
607 {
608 pwdRespCtrl.setPasswordPolicyError( PasswordPolicyErrorEnum.get( ppe.getErrorCode() ) );
609 bindContext.addResponseControl( pwdRespCtrl );
610 }
611
612 throw ppe;
613 }
614
615 Entry userEntry = bindContext.getEntry();
616
617 PasswordPolicyConfiguration policyConfig = getPwdPolicy( userEntry );
618
619
620 if ( policyConfig != null )
621 {
622 LookupOperationContexttor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( adminSession, bindDn,
623 SchemaConstants.ALL_ATTRIBUTES_ARRAY );
624 lookupContext.setPartition( bindContext.getPartition() );
625 lookupContext.setTransaction( bindContext.getTransaction() );
626
627 userEntry = directoryService.getPartitionNexus().lookup( lookupContext );
628 }
629
630
631
632 if ( authenticated && ( userEntry == null ) && directoryService.isAllowAnonymousAccess() )
633 {
634 return;
635 }
636
637 if ( !authenticated )
638 {
639 if ( LOG.isInfoEnabled() )
640 {
641 LOG.info( "Cannot bind to the server " );
642 }
643
644 if ( ( policyConfig != null ) && ( userEntry != null ) )
645 {
646 Attribute pwdFailTimeAt = userEntry.get( pwdFailurTimeAT );
647
648 if ( pwdFailTimeAt == null )
649 {
650 pwdFailTimeAt = new DefaultAttribute( pwdFailurTimeAT );
651 }
652 else
653 {
654 purgeFailureTimes( policyConfig, pwdFailTimeAt );
655 }
656
657 String failureTime = DateUtils.getGeneralizedTime( directoryService.getTimeProvider() );
658 pwdFailTimeAt.add( failureTime );
659 Modification pwdFailTimeMod = new DefaultModification( REPLACE_ATTRIBUTE, pwdFailTimeAt );
660
661 List<Modification> mods = new ArrayList<>();
662 mods.add( pwdFailTimeMod );
663
664 int numFailures = pwdFailTimeAt.size();
665
666 if ( policyConfig.isPwdLockout() && ( numFailures >= policyConfig.getPwdMaxFailure() ) )
667 {
668
669
670 if ( !userEntry.getDn().equals( new Dn( schemaManager, ServerDNConstants.ADMIN_SYSTEM_DN ) ) )
671 {
672 Attribute pwdAccountLockedTimeAt = new DefaultAttribute( pwdAccountLockedTimeAT );
673
674
675 if ( policyConfig.getPwdLockoutDuration() == 0 )
676 {
677 pwdAccountLockedTimeAt.add( "000001010000Z" );
678 }
679 else
680 {
681 pwdAccountLockedTimeAt.add( failureTime );
682 }
683
684 Modification pwdAccountLockedMod = new DefaultModification( REPLACE_ATTRIBUTE,
685 pwdAccountLockedTimeAt );
686 mods.add( pwdAccountLockedMod );
687
688 pwdRespCtrl.setPasswordPolicyError( PasswordPolicyErrorEnum.ACCOUNT_LOCKED );
689 }
690 }
691 else if ( policyConfig.getPwdMinDelay() > 0 )
692 {
693 int numDelay = numFailures * policyConfig.getPwdMinDelay();
694 int maxDelay = policyConfig.getPwdMaxDelay();
695
696 if ( numDelay > maxDelay )
697 {
698 numDelay = maxDelay;
699 }
700
701 try
702 {
703 Thread.sleep( numDelay * 1000L );
704 }
705 catch ( InterruptedException e )
706 {
707 LOG.warn(
708 "Interrupted while delaying to send the failed authentication response for the user {}",
709 bindDn, e );
710 }
711 }
712
713 if ( !mods.isEmpty() )
714 {
715 String csnVal = directoryService.getCSN().toString();
716 Modification csnMod = new DefaultModification( REPLACE_ATTRIBUTE, directoryService.getAtProvider()
717 .getEntryCSN(), csnVal );
718 mods.add( csnMod );
719 ModifyOperationContextceptor/context/ModifyOperationContext.html#ModifyOperationContext">ModifyOperationContext bindModCtx = new ModifyOperationContext( adminSession );
720 bindModCtx.setDn( bindDn );
721 bindModCtx.setEntry( userEntry );
722 bindModCtx.setModItems( mods );
723 bindModCtx.setPushToEvtInterceptor( true );
724
725 internalModify( bindContext, bindModCtx );
726 }
727 }
728
729 String upDn = bindDn == null ? "" : bindDn.getName();
730 throw new LdapAuthenticationException( I18n.err( I18n.ERR_229, upDn ) );
731 }
732 else if ( policyConfig != null )
733 {
734 List<Modification> mods = new ArrayList<>();
735
736 if ( policyConfig.getPwdMaxIdle() > 0 )
737 {
738 Attribute pwdLastSuccesTimeAt = new DefaultAttribute( pwdLastSuccessAT );
739 pwdLastSuccesTimeAt.add( DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
740 Modification pwdLastSuccesTimeMod = new DefaultModification( REPLACE_ATTRIBUTE, pwdLastSuccesTimeAt );
741 mods.add( pwdLastSuccesTimeMod );
742 }
743
744 Attribute pwdFailTimeAt = userEntry.get( pwdFailurTimeAT );
745
746 if ( pwdFailTimeAt != null )
747 {
748 Modification pwdFailTimeMod = new DefaultModification( REMOVE_ATTRIBUTE, pwdFailTimeAt );
749 mods.add( pwdFailTimeMod );
750 }
751
752 Attribute pwdAccLockedTimeAt = userEntry.get( pwdAccountLockedTimeAT );
753
754 if ( pwdAccLockedTimeAt != null )
755 {
756 Modification pwdAccLockedTimeMod = new DefaultModification( REMOVE_ATTRIBUTE, pwdAccLockedTimeAt );
757 mods.add( pwdAccLockedTimeMod );
758 }
759
760
761 if ( ( policyConfig.getPwdMaxAge() > 0 ) && ( policyConfig.getPwdGraceAuthNLimit() > 0 ) )
762 {
763 Attribute pwdChangeTimeAttr = userEntry.get( pwdChangedTimeAT );
764
765 if ( pwdChangeTimeAttr != null )
766 {
767 boolean expired = PasswordUtil.isPwdExpired( pwdChangeTimeAttr.getString(),
768 policyConfig.getPwdMaxAge(), directoryService.getTimeProvider() );
769
770 if ( expired )
771 {
772 Attribute pwdGraceUseAttr = userEntry.get( pwdGraceUseTimeAT );
773 int numGraceAuth;
774
775 if ( pwdGraceUseAttr != null )
776 {
777 numGraceAuth = policyConfig.getPwdGraceAuthNLimit() - ( pwdGraceUseAttr.size() + 1 );
778 }
779 else
780 {
781 pwdGraceUseAttr = new DefaultAttribute( pwdGraceUseTimeAT );
782 numGraceAuth = policyConfig.getPwdGraceAuthNLimit() - 1;
783 }
784
785 pwdRespCtrl.setGraceAuthNRemaining( numGraceAuth );
786
787 pwdGraceUseAttr.add( DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
788 Modification pwdGraceUseMod = new DefaultModification( ADD_ATTRIBUTE, pwdGraceUseAttr );
789 mods.add( pwdGraceUseMod );
790 }
791 }
792 }
793
794 if ( !mods.isEmpty() )
795 {
796 String csnVal = directoryService.getCSN().toString();
797 Modification csnMod = new DefaultModification( REPLACE_ATTRIBUTE, directoryService.getAtProvider()
798 .getEntryCSN(), csnVal );
799 mods.add( csnMod );
800
801 ModifyOperationContextceptor/context/ModifyOperationContext.html#ModifyOperationContext">ModifyOperationContext bindModCtx = new ModifyOperationContext( adminSession );
802 bindModCtx.setDn( bindDn );
803 bindModCtx.setEntry( userEntry );
804 bindModCtx.setModItems( mods );
805 bindModCtx.setPushToEvtInterceptor( true );
806
807 internalModify( bindContext, bindModCtx );
808 }
809
810 if ( isPPolicyReqCtrlPresent )
811 {
812 int expiryWarnTime = getPwdTimeBeforeExpiry( userEntry, policyConfig );
813
814 if ( expiryWarnTime > 0 )
815 {
816 pwdRespCtrl.setTimeBeforeExpiration( expiryWarnTime );
817 }
818
819 if ( isPwdMustReset( userEntry ) )
820 {
821 pwdRespCtrl.setPasswordPolicyError( PasswordPolicyErrorEnum.CHANGE_AFTER_RESET );
822 bindContext.getSession().setPwdMustChange( true );
823 }
824
825 bindContext.addResponseControl( pwdRespCtrl );
826 }
827 }
828 }
829
830
831
832
833
834 @Override
835 public boolean compare( CompareOperationContext compareContext ) throws LdapException
836 {
837 if ( IS_DEBUG )
838 {
839 LOG.debug( "Operation Context: {}", compareContext );
840 }
841
842 checkAuthenticated( compareContext );
843 checkPwdReset( compareContext );
844 return next( compareContext );
845 }
846
847
848
849
850
851 @Override
852 public void delete( DeleteOperationContext deleteContext ) throws LdapException
853 {
854 if ( IS_DEBUG )
855 {
856 LOG.debug( "Operation Context: {}", deleteContext );
857 }
858
859 checkAuthenticated( deleteContext );
860 checkPwdReset( deleteContext );
861 next( deleteContext );
862 invalidateAuthenticatorCaches( deleteContext.getDn() );
863 }
864
865
866
867
868
869 @Override
870 public Entry getRootDse( GetRootDseOperationContext getRootDseContext ) throws LdapException
871 {
872 if ( IS_DEBUG )
873 {
874 LOG.debug( "Operation Context: {}", getRootDseContext );
875 }
876
877 checkAuthenticated( getRootDseContext );
878 checkPwdReset( getRootDseContext );
879
880 return next( getRootDseContext );
881 }
882
883
884
885
886
887 @Override
888 public boolean hasEntry( HasEntryOperationContext hasEntryContext ) throws LdapException
889 {
890 if ( IS_DEBUG )
891 {
892 LOG.debug( "Operation Context: {}", hasEntryContext );
893 }
894
895 checkAuthenticated( hasEntryContext );
896 checkPwdReset( hasEntryContext );
897
898 return next( hasEntryContext );
899 }
900
901
902
903
904
905 @Override
906 public Entry lookup( LookupOperationContext lookupContext ) throws LdapException
907 {
908 if ( IS_DEBUG )
909 {
910 LOG.debug( "Operation Context: {}", lookupContext );
911 }
912
913 checkAuthenticated( lookupContext );
914 checkPwdReset( lookupContext );
915
916 return next( lookupContext );
917 }
918
919
920 private void invalidateAuthenticatorCaches( Dn principalDn )
921 {
922 for ( AuthenticationLevel authMech : authenticatorsMapByType.keySet() )
923 {
924
925 for ( Authenticator authenticator : getAuthenticators( authMech ) )
926 {
927 authenticator.invalidateCache( principalDn );
928 }
929 }
930 }
931
932
933
934
935
936 @Override
937 public void modify( ModifyOperationContext modifyContext ) throws LdapException
938 {
939 if ( IS_DEBUG )
940 {
941 LOG.debug( "Operation Context: {}", modifyContext );
942 }
943
944 checkAuthenticated( modifyContext );
945
946 if ( !directoryService.isPwdPolicyEnabled() || modifyContext.isReplEvent() )
947 {
948 processStandardModify( modifyContext );
949 }
950 else
951 {
952 processPasswordPolicydModify( modifyContext );
953 }
954 }
955
956
957
958
959
960 private void processStandardModify( ModifyOperationContext modifyContext ) throws LdapException
961 {
962 next( modifyContext );
963
964 List<Modification> modifications = modifyContext.getModItems();
965
966 for ( Modification modification : modifications )
967 {
968 if ( directoryService.getAtProvider().getUserPassword()
969 .equals( modification.getAttribute().getAttributeType() ) )
970 {
971 invalidateAuthenticatorCaches( modifyContext.getDn() );
972 break;
973 }
974 }
975 }
976
977
978
979
980
981 private void processPasswordPolicydModify( ModifyOperationContext modifyContext ) throws LdapException
982 {
983
984 PasswordPolicyConfiguration policyConfig = getPwdPolicy( modifyContext.getEntry() );
985
986 PwdModDetailsHolder pwdModDetails = getPwdModDetails( modifyContext, policyConfig );
987
988 if ( !pwdModDetails.isPwdModPresent() )
989 {
990
991 next( modifyContext );
992 }
993 else
994 {
995
996 CoreSession userSession = modifyContext.getSession();
997 boolean isPPolicyReqCtrlPresent = modifyContext.hasRequestControl( PasswordPolicyRequest.OID );
998
999
1000 checkPwdMustChange( modifyContext, userSession, pwdModDetails, isPPolicyReqCtrlPresent );
1001
1002
1003 checkOldPwdRequired( modifyContext, policyConfig, pwdModDetails, isPPolicyReqCtrlPresent );
1004
1005
1006 checkChangePwdAllowed( modifyContext, policyConfig, isPPolicyReqCtrlPresent );
1007
1008 Entry entry = modifyContext.getEntry();
1009
1010 boolean removePwdReset = false;
1011
1012 List<Modification> mods = new ArrayList<>();
1013
1014 if ( pwdModDetails.isAddOrReplace() )
1015 {
1016 if ( isPwdTooYoung( modifyContext, entry, policyConfig ) )
1017 {
1018 if ( isPPolicyReqCtrlPresent )
1019 {
1020 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
1021 responseControl.setPasswordPolicyError(
1022 PasswordPolicyErrorEnum.PASSWORD_TOO_YOUNG );
1023 modifyContext.addResponseControl( responseControl );
1024 }
1025
1026 throw new LdapOperationException( ResultCodeEnum.CONSTRAINT_VIOLATION,
1027 "password is too young to update" );
1028 }
1029
1030 byte[] newPassword = pwdModDetails.getNewPwd();
1031
1032 try
1033 {
1034 check( modifyContext, entry, newPassword, policyConfig );
1035 }
1036 catch ( PasswordPolicyException e )
1037 {
1038 if ( isPPolicyReqCtrlPresent )
1039 {
1040 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
1041 responseControl.setPasswordPolicyError(
1042 PasswordPolicyErrorEnum.get( e.getErrorCode() ) );
1043 modifyContext.addResponseControl( responseControl );
1044 }
1045
1046
1047 throw new LdapOperationException( ResultCodeEnum.CONSTRAINT_VIOLATION, e.getMessage(), e );
1048 }
1049
1050 int histSize = policyConfig.getPwdInHistory();
1051 Modification pwdRemHistMod = null;
1052 Modification pwdAddHistMod = null;
1053 String pwdChangedTime = DateUtils.getGeneralizedTime( directoryService.getTimeProvider() );
1054
1055 if ( histSize > 0 )
1056 {
1057 Attribute pwdHistoryAt = entry.get( pwdHistoryAT );
1058
1059 if ( pwdHistoryAt == null )
1060 {
1061 pwdHistoryAt = new DefaultAttribute( pwdHistoryAT );
1062 }
1063
1064
1065 pwdRemHistMod = buildPwdHistory( modifyContext, pwdHistoryAt, histSize,
1066 newPassword, isPPolicyReqCtrlPresent );
1067
1068 PasswordHistorythn/PasswordHistory.html#PasswordHistory">PasswordHistory newPwdHist = new PasswordHistory( pwdChangedTime, newPassword );
1069 pwdHistoryAt.add( newPwdHist.getHistoryValue() );
1070 pwdAddHistMod = new DefaultModification( REPLACE_ATTRIBUTE, pwdHistoryAt );
1071 }
1072
1073 next( modifyContext );
1074
1075 invalidateAuthenticatorCaches( modifyContext.getDn() );
1076
1077 LookupOperationContexttor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( adminSession, modifyContext.getDn(),
1078 SchemaConstants.ALL_ATTRIBUTES_ARRAY );
1079 lookupContext.setPartition( modifyContext.getPartition() );
1080 lookupContext.setTransaction( modifyContext.getTransaction() );
1081
1082 entry = directoryService.getPartitionNexus().lookup( lookupContext );
1083
1084 if ( ( policyConfig.getPwdMinAge() > 0 ) || ( policyConfig.getPwdMaxAge() > 0 ) )
1085 {
1086 Attribute pwdChangedTimeAt = new DefaultAttribute( pwdChangedTimeAT );
1087 pwdChangedTimeAt.add( pwdChangedTime );
1088 Modification pwdChangedTimeMod = new DefaultModification( REPLACE_ATTRIBUTE, pwdChangedTimeAt );
1089 mods.add( pwdChangedTimeMod );
1090 }
1091
1092 if ( pwdAddHistMod != null )
1093 {
1094 mods.add( pwdAddHistMod );
1095 }
1096
1097 if ( pwdRemHistMod != null )
1098 {
1099 mods.add( pwdRemHistMod );
1100 }
1101
1102 if ( policyConfig.isPwdMustChange() )
1103 {
1104 Attribute pwdMustChangeAt = new DefaultAttribute( pwdResetAT );
1105 Modification pwdMustChangeMod;
1106
1107 if ( modifyContext.getSession().isAnAdministrator() )
1108 {
1109 pwdMustChangeAt.add( "TRUE" );
1110 pwdMustChangeMod = new DefaultModification( REPLACE_ATTRIBUTE, pwdMustChangeAt );
1111 }
1112 else
1113 {
1114 pwdMustChangeMod = new DefaultModification( REMOVE_ATTRIBUTE, pwdMustChangeAt );
1115 removePwdReset = true;
1116 }
1117
1118 mods.add( pwdMustChangeMod );
1119 }
1120 }
1121
1122
1123 processModifyAddPwdAttributes( entry, mods, pwdModDetails );
1124
1125 String csnVal = directoryService.getCSN().toString();
1126 Modification csnMod = new DefaultModification( REPLACE_ATTRIBUTE, directoryService.getAtProvider()
1127 .getEntryCSN(), csnVal );
1128 mods.add( csnMod );
1129
1130 ModifyOperationContextcontext/ModifyOperationContext.html#ModifyOperationContext">ModifyOperationContext internalModifyCtx = new ModifyOperationContext( adminSession );
1131 internalModifyCtx.setPushToEvtInterceptor( true );
1132 internalModifyCtx.setDn( modifyContext.getDn() );
1133 internalModifyCtx.setEntry( entry );
1134 internalModifyCtx.setModItems( mods );
1135
1136 internalModify( modifyContext, internalModifyCtx );
1137
1138 if ( removePwdReset || pwdModDetails.isDelete() )
1139 {
1140 userSession.setPwdMustChange( false );
1141 }
1142 }
1143 }
1144
1145
1146
1147
1148
1149 Modification buildPwdHistory( ModifyOperationContext modifyContext, Attribute pwdHistoryAt,
1150 int histSize, byte[] newPassword, boolean isPPolicyReqCtrlPresent ) throws LdapOperationException
1151 {
1152 List<PasswordHistory> pwdHistLst = new ArrayList<>();
1153
1154 for ( Value value : pwdHistoryAt )
1155 {
1156 PasswordHistoryore/authn/PasswordHistory.html#PasswordHistory">PasswordHistory pwdh = new PasswordHistory( Strings.utf8ToString( value.getBytes() ) );
1157
1158
1159
1160 if ( !modifyContext.getSession().isAnAdministrator() )
1161 {
1162 boolean matched = MessageDigest.isEqual( newPassword, pwdh.getPassword() );
1163
1164 if ( matched )
1165 {
1166 if ( isPPolicyReqCtrlPresent )
1167 {
1168 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
1169 responseControl.setPasswordPolicyError(
1170 PasswordPolicyErrorEnum.PASSWORD_IN_HISTORY );
1171 modifyContext.addResponseControl( responseControl );
1172 }
1173
1174 throw new LdapOperationException( ResultCodeEnum.CONSTRAINT_VIOLATION,
1175 "invalid reuse of password present in password history" );
1176 }
1177 }
1178
1179 pwdHistLst.add( pwdh );
1180 }
1181
1182 Modification pwdRemHistMod = null;
1183
1184 if ( pwdHistLst.size() >= histSize )
1185 {
1186
1187 Collections.sort( pwdHistLst );
1188
1189
1190 PasswordHistory/../org/apache/directory/server/core/authn/PasswordHistory.html#PasswordHistory">PasswordHistory remPwdHist = ( PasswordHistory ) pwdHistLst.toArray()[histSize - 1];
1191 Attribute tempAt = new DefaultAttribute( pwdHistoryAT );
1192 tempAt.add( remPwdHist.getHistoryValue() );
1193 pwdRemHistMod = new DefaultModification( REMOVE_ATTRIBUTE, tempAt );
1194 }
1195
1196 return pwdRemHistMod;
1197 }
1198
1199
1200
1201
1202
1203 private void processModifyAddPwdAttributes( Entry entry, List<Modification> mods, PwdModDetailsHolder pwdModDetails )
1204 {
1205 Attribute pwdFailureTimeAt = entry.get( pwdFailurTimeAT );
1206
1207 if ( pwdFailureTimeAt != null )
1208 {
1209 mods.add( new DefaultModification( REMOVE_ATTRIBUTE, pwdFailureTimeAt ) );
1210 }
1211
1212 Attribute pwdGraceUseTimeAt = entry.get( pwdGraceUseTimeAT );
1213
1214 if ( pwdGraceUseTimeAt != null )
1215 {
1216 mods.add( new DefaultModification( REMOVE_ATTRIBUTE, pwdGraceUseTimeAt ) );
1217 }
1218
1219 if ( pwdModDetails.isDelete() )
1220 {
1221 Attribute pwdHistory = entry.get( pwdHistoryAT );
1222
1223 if ( pwdHistory != null )
1224 {
1225 mods.add( new DefaultModification( REMOVE_ATTRIBUTE, pwdHistory ) );
1226 }
1227
1228 Attribute pwdChangedTimeAt = entry.get( pwdChangedTimeAT );
1229
1230 if ( pwdChangedTimeAt != null )
1231 {
1232 mods.add( new DefaultModification( REMOVE_ATTRIBUTE, pwdChangedTimeAt ) );
1233 }
1234
1235 Attribute pwdMustChangeAt = entry.get( pwdResetAT );
1236
1237 if ( pwdMustChangeAt != null )
1238 {
1239 mods.add( new DefaultModification( REMOVE_ATTRIBUTE, pwdMustChangeAt ) );
1240 }
1241
1242 Attribute pwdAccountLockedTimeAt = entry.get( pwdAccountLockedTimeAT );
1243
1244 if ( pwdAccountLockedTimeAt != null )
1245 {
1246 mods.add( new DefaultModification( REMOVE_ATTRIBUTE, pwdAccountLockedTimeAt ) );
1247 }
1248 }
1249 }
1250
1251
1252
1253
1254
1255 private void checkPwdMustChange( ModifyOperationContext modifyContext, CoreSession userSession,
1256 PwdModDetailsHolder pwdModDetails, boolean isPPolicyReqCtrlPresent ) throws LdapNoPermissionException
1257 {
1258 if ( userSession.isPwdMustChange() && !pwdModDetails.isDelete() && pwdModDetails.isOtherModExists() )
1259 {
1260 if ( isPPolicyReqCtrlPresent )
1261 {
1262 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
1263 responseControl.setPasswordPolicyError(
1264 PasswordPolicyErrorEnum.CHANGE_AFTER_RESET );
1265 modifyContext.addResponseControl( responseControl );
1266 }
1267
1268 throw new LdapNoPermissionException(
1269 "Password should be reset before making any changes to this entry" );
1270 }
1271 }
1272
1273
1274
1275
1276
1277
1278 private void checkOldPwdRequired( ModifyOperationContext modifyContext, PasswordPolicyConfiguration policyConfig,
1279 PwdModDetailsHolder pwdModDetails, boolean isPPolicyReqCtrlPresent ) throws LdapNoPermissionException
1280 {
1281 if ( policyConfig.isPwdSafeModify() && !pwdModDetails.isDelete() && pwdModDetails.isAddOrReplace() )
1282 {
1283 String msg = "trying to update password attribute without the supplying the old password";
1284 LOG.debug( msg );
1285
1286 if ( isPPolicyReqCtrlPresent )
1287 {
1288 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
1289 responseControl.setPasswordPolicyError(
1290 PasswordPolicyErrorEnum.MUST_SUPPLY_OLD_PASSWORD );
1291 modifyContext.addResponseControl( responseControl );
1292 }
1293
1294 throw new LdapNoPermissionException( msg );
1295 }
1296 }
1297
1298
1299
1300
1301
1302
1303 private void checkChangePwdAllowed( ModifyOperationContext modifyContext, PasswordPolicyConfiguration policyConfig,
1304 boolean isPPolicyReqCtrlPresent ) throws LdapNoPermissionException
1305 {
1306 if ( !policyConfig.isPwdAllowUserChange() && !modifyContext.getSession().isAnAdministrator() )
1307
1308 {
1309 if ( isPPolicyReqCtrlPresent )
1310 {
1311 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
1312 responseControl.setPasswordPolicyError(
1313 PasswordPolicyErrorEnum.PASSWORD_MOD_NOT_ALLOWED );
1314 modifyContext.addResponseControl( responseControl );
1315 }
1316
1317 throw new LdapNoPermissionException();
1318 }
1319 }
1320
1321
1322
1323
1324
1325 @Override
1326 public void move( MoveOperationContext moveContext ) throws LdapException
1327 {
1328 if ( IS_DEBUG )
1329 {
1330 LOG.debug( "Operation Context: {}", moveContext );
1331 }
1332
1333 checkAuthenticated( moveContext );
1334 checkPwdReset( moveContext );
1335 next( moveContext );
1336 invalidateAuthenticatorCaches( moveContext.getDn() );
1337 }
1338
1339
1340
1341
1342
1343 @Override
1344 public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
1345 {
1346 if ( IS_DEBUG )
1347 {
1348 LOG.debug( "Operation Context: {}", moveAndRenameContext );
1349 }
1350
1351 checkAuthenticated( moveAndRenameContext );
1352 checkPwdReset( moveAndRenameContext );
1353 next( moveAndRenameContext );
1354 invalidateAuthenticatorCaches( moveAndRenameContext.getDn() );
1355 }
1356
1357
1358
1359
1360
1361 @Override
1362 public void rename( RenameOperationContext renameContext ) throws LdapException
1363 {
1364 if ( IS_DEBUG )
1365 {
1366 LOG.debug( "Operation Context: {}", renameContext );
1367 }
1368
1369 checkAuthenticated( renameContext );
1370 checkPwdReset( renameContext );
1371 next( renameContext );
1372 invalidateAuthenticatorCaches( renameContext.getDn() );
1373 }
1374
1375
1376
1377
1378
1379 @Override
1380 public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException
1381 {
1382 if ( IS_DEBUG )
1383 {
1384 LOG.debug( "Operation Context: {}", searchContext );
1385 }
1386
1387 checkAuthenticated( searchContext );
1388 checkPwdReset( searchContext );
1389
1390 return next( searchContext );
1391 }
1392
1393
1394
1395
1396
1397 @Override
1398 public void unbind( UnbindOperationContext unbindContext ) throws LdapException
1399 {
1400 next( unbindContext );
1401 }
1402
1403
1404
1405
1406
1407
1408
1409
1410 private void checkAuthenticated( OperationContext operation ) throws LdapException
1411 {
1412 if ( operation.getSession().isAnonymous() && !directoryService.isAllowAnonymousAccess()
1413 && !operation.getDn().isEmpty() )
1414 {
1415 String msg = I18n.err( I18n.ERR_5, operation.getName() );
1416 LOG.error( msg );
1417 throw new LdapNoPermissionException( msg );
1418 }
1419 }
1420
1421
1422
1423
1424
1425
1426
1427 public void loadPwdPolicyStateAttributeTypes() throws LdapException
1428 {
1429 pwdResetAT = schemaManager.lookupAttributeTypeRegistry( PWD_RESET_AT );
1430 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdResetAT );
1431
1432 pwdChangedTimeAT = schemaManager.lookupAttributeTypeRegistry( PWD_CHANGED_TIME_AT );
1433 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdChangedTimeAT );
1434
1435 pwdHistoryAT = schemaManager.lookupAttributeTypeRegistry( PWD_HISTORY_AT );
1436 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdHistoryAT );
1437
1438 pwdFailurTimeAT = schemaManager.lookupAttributeTypeRegistry( PWD_FAILURE_TIME_AT );
1439 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdFailurTimeAT );
1440
1441 pwdAccountLockedTimeAT = schemaManager.lookupAttributeTypeRegistry( PWD_ACCOUNT_LOCKED_TIME_AT );
1442 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdAccountLockedTimeAT );
1443
1444 pwdLastSuccessAT = schemaManager.lookupAttributeTypeRegistry( PWD_LAST_SUCCESS_AT );
1445 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdLastSuccessAT );
1446
1447 pwdGraceUseTimeAT = schemaManager.lookupAttributeTypeRegistry( PWD_GRACE_USE_TIME_AT );
1448 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdGraceUseTimeAT );
1449
1450 pwdPolicySubentryAT = schemaManager.lookupAttributeTypeRegistry( PWD_POLICY_SUBENTRY_AT );
1451 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdPolicySubentryAT );
1452
1453 pwdStartTimeAT = schemaManager.lookupAttributeTypeRegistry( PWD_START_TIME_AT );
1454 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdStartTimeAT );
1455
1456 pwdEndTimeAT = schemaManager.lookupAttributeTypeRegistry( PWD_END_TIME_AT );
1457 PWD_POLICY_STATE_ATTRIBUTE_TYPES.add( pwdEndTimeAT );
1458 }
1459
1460
1461
1462 private void check( OperationContext operationContext, Entry entry,
1463 byte[] password, PasswordPolicyConfiguration policyConfig )
1464 throws LdapException
1465 {
1466
1467 if ( operationContext.getSession().isAnAdministrator() )
1468 {
1469 return;
1470 }
1471 final CheckQualityEnum qualityVal = policyConfig.getPwdCheckQuality();
1472
1473 if ( qualityVal == CheckQualityEnum.NO_CHECK )
1474 {
1475 return;
1476 }
1477
1478 LdapSecurityConstants secConst = PasswordUtil.findAlgorithm( password );
1479
1480
1481
1482 if ( secConst != null )
1483 {
1484 if ( qualityVal == CheckQualityEnum.CHECK_ACCEPT )
1485 {
1486 return;
1487 }
1488 else
1489 {
1490 throw new PasswordPolicyException( "cannot verify the quality of the non-cleartext passwords",
1491 INSUFFICIENT_PASSWORD_QUALITY.getValue() );
1492 }
1493 }
1494
1495 String strPassword = Strings.utf8ToString( password );
1496
1497
1498 validatePasswordLength( strPassword, policyConfig );
1499
1500 PasswordValidator passwordValidator = policyConfig.getPwdValidator();
1501
1502 if ( passwordValidator == null )
1503 {
1504
1505 passwordValidator = new DefaultPasswordValidator();
1506 }
1507
1508 passwordValidator.validate( strPassword, entry );
1509 }
1510
1511
1512
1513
1514
1515 private void validatePasswordLength( String password, PasswordPolicyConfiguration policyConfig )
1516 throws PasswordPolicyException
1517 {
1518 int maxLen = policyConfig.getPwdMaxLength();
1519 int minLen = policyConfig.getPwdMinLength();
1520
1521 int pwdLen = password.length();
1522
1523 if ( ( maxLen > 0 ) && ( pwdLen > maxLen ) )
1524 {
1525 throw new PasswordPolicyException( "Password should not have more than " + maxLen + " characters",
1526 INSUFFICIENT_PASSWORD_QUALITY.getValue() );
1527 }
1528
1529 if ( ( minLen > 0 ) && ( pwdLen < minLen ) )
1530 {
1531 throw new PasswordPolicyException( "Password should have a minimum of " + minLen + " characters",
1532 PASSWORD_TOO_SHORT.getValue() );
1533 }
1534 }
1535
1536
1537 private int getPwdTimeBeforeExpiry( Entry userEntry, PasswordPolicyConfiguration policyConfig )
1538 throws LdapException
1539 {
1540 if ( policyConfig.getPwdMaxAge() == 0 )
1541 {
1542 return 0;
1543 }
1544
1545 int warningAge = policyConfig.getPwdExpireWarning();
1546
1547 if ( warningAge <= 0 )
1548 {
1549 return 0;
1550 }
1551
1552 Attribute pwdChangedTimeAt = userEntry.get( pwdChangedTimeAT );
1553 if ( pwdChangedTimeAt == null )
1554 {
1555 pwdChangedTimeAt = userEntry.get( directoryService.getAtProvider().getCreateTimestamp() );
1556 }
1557 long changedTime = DateUtils.getDate( pwdChangedTimeAt.getString() ).getTime();
1558
1559 long currentTime = directoryService.getTimeProvider().currentIimeMillis();
1560 long pwdAge = ( currentTime - changedTime ) / 1000;
1561
1562 if ( pwdAge > policyConfig.getPwdMaxAge() )
1563 {
1564 return 0;
1565 }
1566
1567 warningAge = policyConfig.getPwdMaxAge() - warningAge;
1568
1569 if ( pwdAge >= warningAge )
1570 {
1571 long timeBeforeExpiration = ( ( long ) policyConfig.getPwdMaxAge() ) - pwdAge;
1572
1573 if ( timeBeforeExpiration > Integer.MAX_VALUE )
1574 {
1575 timeBeforeExpiration = Integer.MAX_VALUE;
1576 }
1577
1578 return ( int ) timeBeforeExpiration;
1579 }
1580
1581 return 0;
1582 }
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592 private boolean isPwdTooYoung( OperationContext operationContext,
1593 Entry userEntry, PasswordPolicyConfiguration policyConfig ) throws LdapException
1594 {
1595
1596 if ( operationContext.getSession().isAnAdministrator() )
1597 {
1598 return false;
1599 }
1600 if ( policyConfig.getPwdMinAge() == 0 )
1601 {
1602 return false;
1603 }
1604
1605 CoreSession userSession = operationContext.getSession();
1606
1607
1608 if ( policyConfig.isPwdMustChange() && userSession.isPwdMustChange() )
1609 {
1610 return false;
1611 }
1612
1613 Attribute pwdChangedTimeAt = userEntry.get( pwdChangedTimeAT );
1614
1615 if ( pwdChangedTimeAt != null )
1616 {
1617 long changedTime = DateUtils.getDate( pwdChangedTimeAt.getString() ).getTime();
1618 changedTime += policyConfig.getPwdMinAge() * 1000L;
1619
1620 long currentTime = directoryService.getTimeProvider().currentIimeMillis();
1621
1622 if ( changedTime > currentTime )
1623 {
1624 return true;
1625 }
1626 }
1627
1628 return false;
1629 }
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639 private boolean isPwdMustReset( Entry userEntry ) throws LdapException
1640 {
1641 boolean mustChange = false;
1642
1643 Attribute pwdResetAt = userEntry.get( pwdResetAT );
1644
1645 if ( pwdResetAt != null )
1646 {
1647 mustChange = Boolean.parseBoolean( pwdResetAt.getString() );
1648 }
1649
1650 return mustChange;
1651 }
1652
1653
1654 private PwdModDetailsHolder getPwdModDetails( ModifyOperationContext modifyContext,
1655 PasswordPolicyConfiguration policyConfig ) throws LdapException
1656 {
1657 PwdModDetailsHolder pwdModDetails = new PwdModDetailsHolder();
1658
1659 List<Modification> mods = modifyContext.getModItems();
1660
1661 for ( Modification m : mods )
1662 {
1663 Attribute at = m.getAttribute();
1664 AttributeType passwordAttribute = schemaManager.lookupAttributeTypeRegistry( policyConfig.getPwdAttribute() );
1665
1666 if ( at.getAttributeType().equals( passwordAttribute ) )
1667 {
1668 pwdModDetails.setPwdModPresent( true );
1669 ModificationOperation op = m.getOperation();
1670
1671 if ( op == REMOVE_ATTRIBUTE )
1672 {
1673 pwdModDetails.setDelete( true );
1674 }
1675 else if ( op == REPLACE_ATTRIBUTE || op == ADD_ATTRIBUTE )
1676 {
1677 pwdModDetails.setAddOrReplace( true );
1678 pwdModDetails.setNewPwd( at.getBytes() );
1679 }
1680 }
1681 else
1682 {
1683 pwdModDetails.setOtherModExists( true );
1684 }
1685 }
1686
1687 return pwdModDetails;
1688 }
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698 private void checkPwdReset( OperationContext opContext ) throws LdapException
1699 {
1700 if ( directoryService.isPwdPolicyEnabled() )
1701 {
1702 CoreSession session = opContext.getSession();
1703
1704 if ( session.isPwdMustChange() )
1705 {
1706 boolean isPPolicyReqCtrlPresent = opContext
1707 .hasRequestControl( PasswordPolicyRequest.OID );
1708
1709 if ( isPPolicyReqCtrlPresent )
1710 {
1711 PasswordPolicyResponse responseControl = new PasswordPolicyResponseImpl();
1712 responseControl.setPasswordPolicyError( PasswordPolicyErrorEnum.CHANGE_AFTER_RESET );
1713 opContext.addResponseControl( responseControl );
1714 }
1715
1716 throw new LdapNoPermissionException( "password needs to be reset before performing this operation" );
1717 }
1718 }
1719 }
1720
1721 private static class PwdModDetailsHolder
1722 {
1723 private boolean pwdModPresent = false;
1724
1725 private boolean isDelete = false;
1726
1727 private boolean isAddOrReplace = false;
1728
1729 private boolean otherModExists = false;
1730
1731 private byte[] newPwd;
1732
1733
1734 public boolean isPwdModPresent()
1735 {
1736 return pwdModPresent;
1737 }
1738
1739
1740 public void setPwdModPresent( boolean pwdModPresent )
1741 {
1742 this.pwdModPresent = pwdModPresent;
1743 }
1744
1745
1746 public boolean isDelete()
1747 {
1748 return isDelete;
1749 }
1750
1751
1752 public void setDelete( boolean isDelete )
1753 {
1754 this.isDelete = isDelete;
1755 }
1756
1757
1758 public boolean isAddOrReplace()
1759 {
1760 return isAddOrReplace;
1761 }
1762
1763
1764 public void setAddOrReplace( boolean isAddOrReplace )
1765 {
1766 this.isAddOrReplace = isAddOrReplace;
1767 }
1768
1769
1770 public boolean isOtherModExists()
1771 {
1772 return otherModExists;
1773 }
1774
1775
1776 public void setOtherModExists( boolean otherModExists )
1777 {
1778 this.otherModExists = otherModExists;
1779 }
1780
1781
1782 public byte[] getNewPwd()
1783 {
1784 return newPwd;
1785 }
1786
1787
1788 public void setNewPwd( byte[] newPwd )
1789 {
1790 this.newPwd = newPwd;
1791 }
1792 }
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805 public PasswordPolicyConfiguration getPwdPolicy( Entry userEntry ) throws LdapException
1806 {
1807 if ( pwdPolicyContainer == null )
1808 {
1809 return null;
1810 }
1811
1812 if ( userEntry == null )
1813 {
1814 return pwdPolicyContainer.getDefaultPolicy();
1815 }
1816
1817 if ( pwdPolicyContainer.hasCustomConfigs() )
1818 {
1819 Attribute pwdPolicySubentry = userEntry.get( pwdPolicySubentryAT );
1820
1821 if ( pwdPolicySubentry != null )
1822 {
1823 Dn configDn = dnFactory.create( pwdPolicySubentry.getString() );
1824
1825 PasswordPolicyConfiguration custom = pwdPolicyContainer.getPolicyConfig( configDn );
1826
1827 if ( custom != null )
1828 {
1829 return custom;
1830 }
1831 else
1832 {
1833 LOG.warn(
1834 "The custom password policy for the user entry {} is not found, returning default policy configuration",
1835 userEntry.getDn() );
1836 }
1837 }
1838 }
1839
1840 return pwdPolicyContainer.getDefaultPolicy();
1841 }
1842
1843
1844
1845
1846
1847
1848
1849
1850 public void setPwdPolicies( PpolicyConfigContainer policyContainer )
1851 {
1852 this.pwdPolicyContainer = policyContainer;
1853 }
1854
1855
1856
1857
1858
1859 public boolean isPwdPolicyEnabled()
1860 {
1861 return ( pwdPolicyContainer != null )
1862 && ( ( pwdPolicyContainer.getDefaultPolicy() != null )
1863 || ( pwdPolicyContainer.hasCustomConfigs() ) );
1864 }
1865
1866
1867
1868
1869
1870 public PpolicyConfigContainer getPwdPolicyContainer()
1871 {
1872 return pwdPolicyContainer;
1873 }
1874
1875
1876
1877
1878
1879 public void setPwdPolicyContainer( PpolicyConfigContainer pwdPolicyContainer )
1880 {
1881 this.pwdPolicyContainer = pwdPolicyContainer;
1882 }
1883
1884
1885
1886
1887
1888
1889 private void purgeFailureTimes( PasswordPolicyConfiguration config, Attribute pwdFailTimeAt )
1890 {
1891 long interval = config.getPwdFailureCountInterval();
1892
1893 if ( interval == 0 )
1894 {
1895 return;
1896 }
1897
1898 interval *= 1000;
1899
1900 long currentTime = directoryService.getTimeProvider().currentIimeMillis();
1901
1902 Iterator<Value> itr = pwdFailTimeAt.iterator();
1903
1904 while ( itr.hasNext() )
1905 {
1906 Value value = itr.next();
1907 String failureTime = value.getString();
1908 long time = DateUtils.getDate( failureTime ).getTime();
1909 time += interval;
1910
1911 if ( currentTime >= time )
1912 {
1913 itr.remove();
1914 }
1915 }
1916 }
1917 }