Using the LDAP Password Modify extended operation with Spring LDAP

If you want to change the password for a given user in an LDAP repository then you need to worry about the format in which it is being stored otherwise you will end up with the password held in plain text (although base64 encoded)

Using the password modify extended operation (rfc3062) allows OpenLDAP, in this case, to manage the hashing of the new password.

If you don’t use the extension then you have to hash the value yourself.
This code stores the new password as plaintext and treats the password as if it is any other attribute.
You can implement hashing yourself e.g. by prepending {MD5} and using the base64 encoded md5 hash of the new password – see this forum entry

Don’t use this!

	DistinguishedName dn = new DistinguishedName(dn_string);
	Attribute passwordAttribute = new BasicAttribute(passwordAttr,
			newPassword);
	ModificationItem[] modificationItems = new ModificationItem[1];
	modificationItems[0] = new ModificationItem(
			DirContext.REPLACE_ATTRIBUTE, passwordAttribute);
/*
	Attribute userPasswordChangedAttribute = new BasicAttribute(
			LDAP_PASSWORD_CHANGE_DATE, format.format(convertToUtc(null)
					.getTime()) + "Z");
	ModificationItem newPasswordChanged = new ModificationItem(
			DirContext.REPLACE_ATTRIBUTE, userPasswordChangedAttribute);
	modificationItems[1] = newPasswordChanged;
	*/
	getLdapTemplate().modifyAttributes(dn, modificationItems);

 

This example uses the extended operation which means that password will be stored according to the OpenLDAP settings i.e. SSHA by default.

The ldap template here is an instance of org.springframework.ldap.core.LdapTemplate

 ldapTemplate.executeReadOnly(new ContextExecutor() {
   public Object executeWithContext(DirContext ctx) throws NamingException {
      if (!(ctx instanceof LdapContext)) {
            throw new IllegalArgumentException(
               "Extended operations require LDAPv3 - "
               + "Context must be of type LdapContext");
      }
      LdapContext ldapContext = (LdapContext) ctx;
      ExtendedRequest er = new ModifyPasswordRequest(dn_string, new_password);
      return ldapContext.extendedOperation(er);
    }
   });

This thread gives an idea of what is required however the ModifyPasswordRequest class available from here actually has all the right details implemented.

You will find that other LDAP libraries e.g. ldapChai use the same ModifyPasswordRequest class

4 Replies to “Using the LDAP Password Modify extended operation with Spring LDAP”

  1. Hi, thanks for you comments on using the LDAP Password Modify extended operation with Spring LDAP, i’ve implemented the code as described but am running into difficulties with the below exception:


    org.springframework.ldap.UncategorizedLdapException: Uncategorized exception occured during LDAP processing; nested exception is javax.naming.NamingException: [LDAP: error code 80 - unable to retrieve SASL username]; remaining name ''

    And hoped you might be able to shed some light on it..?

    I haven’t set a password-hash directive in the slapd.conf, (well actually I’ve explicitly set as well) and as such expected to be using the default SSHA hashing, so any help as to why I’m getting an SASL exception would be gratefully received.

    Thanks, Stephen.

  2. Found the answer – wasn’t passing the full dn through thinking Spring would taking care of the baseDN. Working a treat now.

    Thanks.

  3. Hi,

    Tried above code to reset ApacheDS password with SSHA default setting. But run into below exception.
    Any idea on this?

    javax.naming.NamingException: [LDAP: error code 80 – OTHER: Extended operation handler for the specified EXTENSION_OID (1.3.6.1.4.1.4203.1.11.1) has failed to process your request:
    java.lang.ClassCastException: org.apache.directory.api.ldap.codec.api.ExtendedRequestDecorator cannot be cast to org.apache.directory.api.ldap.extras.extended.PwdModifyRequest
    at org.apache.directory.server.ldap.handlers.extended.PwdModifyHandler.handleExtendedOperation(PwdModifyHandler.java:63)
    at org.apache.directory.server.ldap.handlers.request.ExtendedRequestHandler.handle(ExtendedRequestHandler.java:62)
    at org.apache.directory.server.ldap.handlers.request.ExtendedRequestHandler.handle(ExtendedRequestHandler.java:38)
    at org.apache.directory.server.ldap.handlers.LdapRequestHandler.handleMessage(LdapRequestHandler.java:218)
    at org.apache.directory.server.ldap.handlers.LdapRequestHandler.handleMessage(LdapRequestHandler.java:55)
    at org.apache.mina.handler.demux.DemuxingIoHandler.messageReceived(DemuxingIoHandler.java:221)
    at org.apache.directory.server.ldap.LdapProtocolHandler.messageReceived(LdapProtocolHandler.java:217)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.messageReceived(DefaultIoFilterChain.java:690)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:417)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1200(DefaultIoFilterChain.java:47)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:765)
    at org.apache.mina.core.filterchain.IoFilterEvent.fire(IoFilterEvent.java:74)
    at org.apache.mina.core.session.IoEvent.run(IoEvent.java:63)
    at org.apache.mina.filter.executor.UnorderedThreadPoolExecutor$Worker.runTask(UnorderedThreadPoolExecutor.java:474)
    at org.apache.mina.filter.executor.UnorderedThreadPoolExecutor$Worker.run(UnorderedThreadPoolExecutor.java:428)
    at java.lang.Thread.run(Unknown Source)
    ]; remaining name ”
    at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3131)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3033)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2840)
    at com.sun.jndi.ldap.LdapCtx.extendedOperation(LdapCtx.java:3212)
    at javax.naming.ldap.InitialLdapContext.extendedOperation(InitialLdapContext.java:183)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.ldap.transaction.compensating.manager.TransactionAwareDirContextInvocationHandler.invoke(TransactionAwareDirContextInvocationHandler.java:92)
    at com.sun.proxy.$Proxy11.extendedOperation(Unknown Source)
    at com.tieto.piletti.user.mgmt.repository.ldap.dao.UserDetailsLdapDao$2.executeWithContext(UserDetailsLdapDao.java:443)
    at org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:806)
    at org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:792)
    at com.tieto.piletti.user.mgmt.repository.ldap.dao.UserDetailsLdapDao.updatePassword(UserDetailsLdapDao.java:424)
    at com.tieto.piletti.user.mgmt.repository.ldap.dao.UserDetailsLdapDao$$FastClassByCGLIB$$d2f6e544.invoke()
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:627)
    at com.tieto.piletti.user.mgmt.repository.ldap.dao.UserDetailsLdapDao$$EnhancerByCGLIB$$59cdeea.updatePassword()
    at com.tieto.piletti.user.mgmt.dao.UserDetailLDAPDaoTest.testUpdatePassword(UserDetailLDAPDaoTest.java:79)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *