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.ldap.handlers.extended;
21
22
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.Set;
26
27 import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyRequest;
28 import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyRequest;
29 import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyResponse;
30 import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyResponseImpl;
31 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
32 import org.apache.directory.api.ldap.model.entry.Attribute;
33 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
34 import org.apache.directory.api.ldap.model.entry.DefaultModification;
35 import org.apache.directory.api.ldap.model.entry.Entry;
36 import org.apache.directory.api.ldap.model.entry.Modification;
37 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
38 import org.apache.directory.api.ldap.model.entry.Value;
39 import org.apache.directory.api.ldap.model.exception.LdapException;
40 import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
41 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
42 import org.apache.directory.api.ldap.model.exception.LdapOperationException;
43 import org.apache.directory.api.ldap.model.message.Control;
44 import org.apache.directory.api.ldap.model.message.ModifyRequest;
45 import org.apache.directory.api.ldap.model.message.ModifyRequestImpl;
46 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
47 import org.apache.directory.api.ldap.model.name.Dn;
48 import org.apache.directory.api.ldap.model.password.PasswordUtil;
49 import org.apache.directory.api.util.Strings;
50 import org.apache.directory.server.core.api.CoreSession;
51 import org.apache.directory.server.core.api.DirectoryService;
52 import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
53 import org.apache.directory.server.core.shared.DefaultCoreSession;
54 import org.apache.directory.server.ldap.ExtendedOperationHandler;
55 import org.apache.directory.server.ldap.LdapServer;
56 import org.apache.directory.server.ldap.LdapSession;
57 import org.apache.mina.core.session.IoSession;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61
62
63
64
65
66
67
68
69 public class PwdModifyHandler implements ExtendedOperationHandler<PasswordModifyRequest, PasswordModifyResponse>
70 {
71 private static final Logger LOG = LoggerFactory.getLogger( PwdModifyHandler.class );
72 public static final Set<String> EXTENSION_OIDS;
73
74 static
75 {
76 Set<String> set = new HashSet<>( 2 );
77 set.add( PasswordModifyRequest.EXTENSION_OID );
78 set.add( PasswordModifyResponse.EXTENSION_OID );
79 EXTENSION_OIDS = Collections.unmodifiableSet( set );
80 }
81
82
83
84
85
86 public String getOid()
87 {
88 return PasswordModifyRequest.EXTENSION_OID;
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 private void modifyUserPassword( CoreSession userSession, Entry userEntry, Dn userDn,
105 byte[] oldPassword, byte[] newPassword, PasswordModifyRequest req )
106 {
107 IoSession ioSession = ( ( DefaultCoreSession ) userSession ).getIoSession();
108
109 if ( newPassword == null )
110 {
111
112 writeResult( ioSession, req, ResultCodeEnum.UNWILLING_TO_PERFORM,
113 "Cannot change a password for user " + userDn + ", exception : null new password" );
114
115 return;
116 }
117
118
119 Attribute userPassword = userEntry.get( SchemaConstants.USER_PASSWORD_AT );
120
121 if ( userPassword == null )
122 {
123
124 writeResult( ioSession, req, ResultCodeEnum.UNWILLING_TO_PERFORM,
125 "Cannot change a password for user " + userDn + ", the user has no existing password" );
126
127 return;
128 }
129
130 if ( userPassword.contains( newPassword ) )
131 {
132
133 PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
134 req.getMessageId(), ResultCodeEnum.SUCCESS );
135
136 Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
137
138 if ( ppolicyControl != null )
139 {
140 pmrl.addControl( ppolicyControl );
141 }
142
143 ioSession.write( pmrl );
144
145 return;
146 }
147
148 if ( oldPassword == null )
149 {
150
151
152
153 ModifyRequest modifyRequest = new ModifyRequestImpl();
154 modifyRequest.setName( userDn );
155
156 Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
157
158 if ( ppolicyControl != null )
159 {
160 modifyRequest.addControl( ppolicyControl );
161 }
162
163 try
164 {
165 Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
166 userPassword.getAttributeType(), newPassword );
167
168 modifyRequest.addModification( modification );
169 ResultCodeEnum errorCode = null;
170 String errorMessage = null;
171
172 try
173 {
174 userSession.modify( modifyRequest );
175
176 if ( LOG.isDebugEnabled() )
177 {
178 LOG.debug( "Password modified for user {}", userDn );
179 }
180
181
182 PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
183 req.getMessageId(), ResultCodeEnum.SUCCESS );
184
185 ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
186
187 if ( ppolicyControl != null )
188 {
189 pmrl.addControl( ppolicyControl );
190 }
191
192 ioSession.write( pmrl );
193
194 return;
195 }
196 catch ( LdapOperationException loe )
197 {
198 errorCode = loe.getResultCode();
199 errorMessage = loe.getMessage();
200 }
201 catch ( LdapException le )
202 {
203
204 errorCode = ResultCodeEnum.OTHER;
205 errorMessage = le.getMessage();
206 }
207
208
209 LOG.error( "Cannot modify the password for user {}, exception : {}", userDn, errorMessage );
210 PasswordModifyResponseImpl errorPmrl = new PasswordModifyResponseImpl(
211 req.getMessageId(), errorCode, "Cannot modify the password for user "
212 + userDn + ", exception : " + errorMessage );
213
214 ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
215
216 if ( ppolicyControl != null )
217 {
218 errorPmrl.addControl( ppolicyControl );
219 }
220
221 ioSession.write( errorPmrl );
222
223 return;
224 }
225 catch ( LdapInvalidAttributeValueException liave )
226 {
227
228 }
229 }
230 else
231 {
232
233 boolean valid = false;
234 Attribute modifiedPassword = new DefaultAttribute( userPassword.getAttributeType() );
235
236 for ( Value value : userPassword )
237 {
238 if ( !valid )
239 {
240 valid = PasswordUtil.compareCredentials( oldPassword, value.getBytes() );
241 }
242
243 try
244 {
245 if ( valid )
246 {
247 modifiedPassword.add( newPassword );
248 }
249 else
250 {
251 modifiedPassword.add( value );
252 }
253 }
254 catch ( LdapInvalidAttributeValueException e )
255 {
256
257 }
258 }
259
260
261
262 if ( valid )
263 {
264 ModifyRequest modifyRequest = new ModifyRequestImpl();
265 modifyRequest.setName( userDn );
266
267 Control ppolicyControl = req.getControl( PasswordPolicyRequest.OID );
268
269 if ( ppolicyControl != null )
270 {
271 modifyRequest.addControl( ppolicyControl );
272 }
273
274 Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
275 modifiedPassword );
276
277 modifyRequest.addModification( modification );
278
279 ResultCodeEnum errorCode = null;
280 String errorMessage = null;
281
282 try
283 {
284 userSession.modify( modifyRequest );
285
286 if ( LOG.isDebugEnabled() )
287 {
288 LOG.debug( "Password modified for user {}", userDn );
289 }
290
291
292 PasswordModifyResponseImpl pmrl = new PasswordModifyResponseImpl(
293 req.getMessageId(), ResultCodeEnum.SUCCESS );
294
295 ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
296
297 if ( ppolicyControl != null )
298 {
299 pmrl.addControl( ppolicyControl );
300 }
301
302 ioSession.write( pmrl );
303
304 return;
305 }
306 catch ( LdapOperationException loe )
307 {
308 errorCode = loe.getResultCode();
309 errorMessage = loe.getMessage();
310 }
311 catch ( LdapException le )
312 {
313
314 errorCode = ResultCodeEnum.OTHER;
315 errorMessage = le.getMessage();
316 }
317
318
319 LOG.error( "Cannot modify the password for user {}, exception : {}", userDn, errorMessage );
320 PasswordModifyResponseImpl errorPmrl = new PasswordModifyResponseImpl(
321 req.getMessageId(), errorCode, "Cannot modify the password for user "
322 + userDn + ", exception : " + errorMessage );
323
324 ppolicyControl = modifyRequest.getResultResponse().getControl( PasswordPolicyRequest.OID );
325
326 if ( ppolicyControl != null )
327 {
328 errorPmrl.addControl( ppolicyControl );
329 }
330
331 ioSession.write( errorPmrl );
332
333 return;
334 }
335 else
336 {
337
338 writeResult( ioSession, req, ResultCodeEnum.INVALID_CREDENTIALS,
339 "Cannot change a password for user " + userDn + ", invalid credentials" );
340
341 return;
342 }
343 }
344 }
345
346
347 private void writeResult( LdapSession requestor, PasswordModifyRequest req, ResultCodeEnum error, String errorMessage )
348 {
349 writeResult( requestor.getIoSession(), req, error, errorMessage );
350
351 }
352
353
354 private void writeResult( IoSession ioSession, PasswordModifyRequest req, ResultCodeEnum error, String errorMessage )
355 {
356 LOG.error( errorMessage );
357 ioSession.write( new PasswordModifyResponseImpl(
358 req.getMessageId(), error, errorMessage ) );
359
360 }
361
362
363 private Entry getModifiedEntry( LdapSession requestor, PasswordModifyRequest req, Dn entryDn )
364 {
365 try
366 {
367 Entry modifiedEntry = requestor.getCoreSession().lookup( entryDn, SchemaConstants.ALL_ATTRIBUTES_ARRAY );
368
369 if ( modifiedEntry == null )
370 {
371
372 writeResult( requestor, req, ResultCodeEnum.NO_SUCH_OBJECT,
373 "The entry does not exist, we can't modify its password" );
374 return null;
375 }
376 else
377 {
378 return modifiedEntry;
379 }
380 }
381 catch ( Exception le )
382 {
383
384 writeResult( requestor, req, ResultCodeEnum.NO_SUCH_OBJECT,
385 "The entry does not exist, we can't modify its password" );
386 return null;
387 }
388 }
389
390
391 private void processAuthenticatedPasswordModify( LdapSession requestor, PasswordModifyRequest req,
392 Dn userDn )
393 {
394 byte[] oldPassword = req.getOldPassword();
395 byte[] newPassword = req.getNewPassword();
396
397
398 Entry modifiedEntry = null;
399
400 Dn principalDn = requestor.getCoreSession().getEffectivePrincipal().getDn();
401
402 LOG.debug( "User {} trying to modify password of user {}", principalDn, userDn );
403
404
405
406
407 if ( ( userDn != null ) && ( !userDn.equals( principalDn ) ) )
408 {
409
410 if ( requestor.getCoreSession().isAdministrator() )
411 {
412 modifiedEntry = getModifiedEntry( requestor, req, userDn );
413
414 if ( modifiedEntry == null )
415 {
416 return;
417 }
418
419
420 modifyUserPassword( requestor.getCoreSession(), modifiedEntry, userDn, oldPassword, newPassword, req );
421 }
422 else
423 {
424
425 writeResult( requestor, req, ResultCodeEnum.INSUFFICIENT_ACCESS_RIGHTS,
426 "Non-admin user cannot access another user's password to modify it" );
427 }
428 }
429 else
430 {
431
432 modifiedEntry = getModifiedEntry( requestor, req, principalDn );
433
434 if ( modifiedEntry == null )
435 {
436 return;
437 }
438
439 modifyUserPassword( requestor.getCoreSession(), modifiedEntry, principalDn, oldPassword, newPassword, req );
440 }
441 }
442
443
444
445
446
447 public void handleExtendedOperation( LdapSession requestor, PasswordModifyRequest req ) throws Exception
448 {
449 LOG.debug( "Password modification requested" );
450
451
452 DirectoryService service = requestor.getLdapServer().getDirectoryService();
453 CoreSession adminSession = service.getAdminSession();
454 String userIdentity = Strings.utf8ToString( req.getUserIdentity() );
455 Dn userDn = null;
456
457 if ( !Strings.isEmpty( userIdentity ) )
458 {
459 try
460 {
461 userDn = service.getDnFactory().create( userIdentity );
462 }
463 catch ( LdapInvalidDnException lide )
464 {
465
466 writeResult( requestor, req, ResultCodeEnum.INVALID_DN_SYNTAX,
467 "The user DN is invalid : " + userDn );
468
469 return;
470 }
471 }
472
473 byte[] oldPassword = req.getOldPassword();
474 byte[] newPassword = req.getNewPassword();
475
476
477 if ( requestor.isAuthenticated() )
478 {
479 processAuthenticatedPasswordModify( requestor, req, userDn );
480 }
481 else
482 {
483
484
485 BindOperationContextnterceptor/context/BindOperationContext.html#BindOperationContext">BindOperationContext bindContext = new BindOperationContext( adminSession );
486 bindContext.setDn( userDn );
487 bindContext.setCredentials( oldPassword );
488
489 try
490 {
491 service.getOperationManager().bind( bindContext );
492 }
493 catch ( LdapException le )
494 {
495
496
497 requestor.getIoSession().write( new PasswordModifyResponseImpl(
498 req.getMessageId(), ResultCodeEnum.INVALID_CREDENTIALS ) );
499
500 return;
501 }
502
503
504
505 modifyUserPassword( requestor.getCoreSession(), bindContext.getEntry(), userDn, oldPassword, newPassword, req );
506 }
507 }
508
509
510
511
512
513 public Set<String> getExtensionOids()
514 {
515 return EXTENSION_OIDS;
516 }
517
518
519
520
521
522 public void setLdapServer( LdapServer ldapServer )
523 {
524 }
525 }