This discussion is archived
11 Replies Latest reply: Dec 22, 2010 11:10 AM by 825812 RSS

Decode ASN.1/BER response from PasswordPolicyResponse (openldap)

843793 Newbie
Currently Being Moderated
By using a connection request control (PasswordPolicyControl) I manage to get a response from OpenLDAPs password policy (OID: 1.3.6.1.4.1.42.2.27.8.5.1).
The response is encoded by ASN.1/BER and is probably (?) following the draft-behera-ldap-password-policy-09.txt (http://www.ietf.org/internet-drafts/draft-behera-ldap-password-policy-09.txt) page 20.

Is there anybody out there who has already written a BERdecoder that works with OpenLDAPs passwordpolicyresponse? I tried using the code written for the IBM tivoli server (http://www-128.ibm.com/developerworks/tivoli/library/t-ldap-controls/), but I couldn't get the decoding correct.

Thanks in advance.
J�rgen L�kke
  • 1. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    843793 Newbie
    Currently Being Moderated
    I coded a low-level solution which interprets the following cases:
    - timeBeforeExpiration
    - graceLoginsRemaining
    - passwordExpired
    ..it works, but is it stable? And are there any simpler solutions out there?

    My code:
    ...
    //The password policy response control
    byte[] bytes = control.getEncodedValue();
    interpret(bytes);
    ...
    private void interpret (byte[] array) throws Exception {
              DataInputStream dis = new DataInputStream(new ByteArrayInputStream(array));
              dis.readShort(); //Doesn't care about the two first bytes.
              int code = dis.readUnsignedByte();
              if ((code ^ 160) == 0) {
                   //WARNING
                   int length = dis.readUnsignedByte();
                   code = dis.readUnsignedByte();
                   if ((code ^ 160) == 0) {
                        //timeBeforeExpiration
                        length = dis.readUnsignedByte();
                        byte[] tmp = new byte[length];
                        dis.readFully(tmp);
                        System.out.println("Time before expiration: " +getValue(tmp));
                   } else if ((code ^ 160) == 1) {
                        //graceLoginsRemaining
                        length = dis.readUnsignedByte();
                        byte[] tmp = new byte[length];
                        dis.readFully(tmp);
                        System.out.println("Grace logins remaining: " +getValue(tmp));
                   }
              } else if ((code ^ 160) == 1) {
                   //ERROR
                   int length = dis.readUnsignedByte();
                   code = dis.readUnsignedByte();
                   if ((code ^ 0) == 0) {
                        //passwordExpired
                        System.out.println("Password has expired, no grace logins left.");
                   }
              }
         }
    
         /*
         Gets an integer value. Typically time before expiration or number of grace logins remaining.
         */
         private int getValue(byte[] array) throws Exception {
              DataInputStream dis = new DataInputStream(new ByteArrayInputStream(array));
              switch (array.length) {
                   case 1: //Unsigned byte
                        return dis.readUnsignedByte();
    
                   case 2: //Short
                        return dis.readShort();
    
                   case 3: //Hack to expand to integer
                        byte[] bytes = new byte[4];
                        System.arraycopy(array, 0, bytes, 1, array.length);
                        dis = new DataInputStream(new ByteArrayInputStream(bytes));
                        return dis.readInt();
    
                   case 4: //Integer
                        return dis.readInt();
              }
              return -1;
    }
    Thanks in advance!
    J�rgen L�kke
  • 2. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    843793 Newbie
    Currently Being Moderated
    There is an Apache decoder in package:
    org.apache.asn1.codec.stateful.StatefulDecoder
    This seems to work fine.
  • 3. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    843793 Newbie
    Currently Being Moderated
    few posts from openldap.org to help explain some potenial problems with password policy:
    http://www.openldap.org/lists/openldap-software/200606/msg00220.html
    http://www.openldap.org/lists/openldap-software/200606/msg00287.html

    versions prior to 2.3.22 that supported ppolicy have PasswordPolicyResponseControl tag value of 0xA1 and not 0xA0.. so you need 2.3.22+

    Tried to use Netscape library class JDAPBERTagDecoder, but it didn't handle tag 0x81
    http://www.koders.com/java/fid5DD86A39A82ED753BAEC53E84A001BE4D6C6ADF5.aspx?s=JdapBERTagDecoder

    so slightly modified a version of it...
    and handled case..
                case 0x81:  /* Context Specific <Construct> [0]:
                     * v3 Server Control.
                     * SEQUENCE of SEQUENCE of {OID  [critical] [value]}
                     * THIS IS ERROR FROM PASSWORD CONTROL
                     */
                    element = new BERInteger(stream, bytesRead);
                    implicit[0] = true;
                break;
    ..
    more of that file..
    public class OpenLdapBERTagDecoder extends BERTagDecoder
    {
        public BERElement getElement (BERTagDecoder decoder, int tag, InputStream stream, int[] bytesRead,
            boolean[] implicit) throws IOException
        {
            BERElement element = null;
            switch (tag) {
                case 0x60:  /* [APPLICATION 0] For Bind Request */
                case 0x61:  /* [APPLICATION 1] Bind Response */
                case 0x63:  /* [APPLICATION 3] Search Request
                             * If doing search without bind first,
                     * x500.arc.nasa.gov returns tag [APPLICATION 3]
                     * in Search Response. Gee.
                             */
                case 0x64:  /* [APPLICATION 4] Search Response */
                case 0x65:  /* [APPLICATION 5] Search Result */
                case 0x67:  /* [APPLICATION 7] Modify Response */
                case 0x69:  /* [APPLICATION 9] Add Response */
                case 0x6a:  /* [APPLICATION 10] Del Request */
                case 0x6b:  /* [APPLICATION 11] Del Response */
                case 0x6d:  /* [APPLICATION 13] ModifyRDN Response */
                case 0x6f:  /* [APPLICATION 15] Compare Response */
                case 0x78:  /* [APPLICATION 23] Extended Response */
                case 0x73:  /* [APPLICATION 19] SearchResultReference */
                    element = new BERSequence(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0x80:  /* [APPLICATION 16] 64+16 */
                    element = new BERInteger(stream, bytesRead);
                    implicit[0] = true;
                break;
                /* 16/02/97 MS specific */
                case 0x85:  /* Context Specific [5]:
                     * (a) Handle Microsoft v3 referral bugs! (Response)
                     * (b) Handle Microsoft v3 supportedVersion in Bind
                     *     response
                     */
                    element = new BERInteger(stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0x87:  /* Context Specific [7]:
                     * Handle Microsoft Filter "present" in
                     * search request.
                     */
                    element = new BEROctetString(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0x8a:  /* Context Specific [10]:
                             * Handle extended response
                             */
                    element = new BEROctetString(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0x8b:  /* Context Specific [11]:
                             * Handle extended response
                             */
                    element = new BEROctetString(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0xa3:  /* Context Specific <Construct> [3]:
                     * Handle Microsoft v3 sasl bind request
                     */
                    element = new BERSequence(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0xa7:  /* Context Specific <Construct> [7]:
                     * Handle Microsoft v3 serverCred in
                     * bind response. MS encodes it as SEQUENCE OF
                     * while it should be CHOICE OF.
                     */
                    element = new BERSequence(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0xa0:  /* Context Specific <Construct> [0]:
                     * v3 Server Control.
                     * SEQUENCE of SEQUENCE of {OID  [critical] [value]}
                     * THIS IS WARNING FROM PASSWORD CONTROL
                     */
                    element = new BERSequence(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0x81:  /* Context Specific <Construct> [0]:
                     * v3 Server Control.
                     * SEQUENCE of SEQUENCE of {OID  [critical] [value]}
                     * THIS IS ERROR FROM PASSWORD CONTROL
                     */
                    element = new BERInteger(stream, bytesRead);
                    implicit[0] = true;
                break;
                case 0xa1:  /* Context Specific <Construct> [0]:
                     * v3 Server Control.
                     * SEQUENCE of SEQUENCE of {OID  [critical] [value]}
                     */
                    element = new BERSequence(decoder, stream, bytesRead);
                    implicit[0] = true;
                break;
                default:
                    throw new IOException("Tag ID not recognised "+Integer.toHexString(tag));
            }
            return element;        
        }
    had to slightly modify examples from
    http://www-128.ibm.com/developerworks/tivoli/library/t-ldap-controls/ to get openldap working
        public Control getControlInstance (Control ctl)
        {
            Control result = null;
            if (ctl.getID().equals( PasswordPolicyControl.OID ))
            {
                try
                {
                    final PasswordPolicyResponseControl rctl = new PasswordPolicyResponseControl();
                    if (ctl.getEncodedValue() != null)
                    {
                        rctl.setEncodedValue( ctl.getEncodedValue() );
                        ByteArrayInputStream inStream = new ByteArrayInputStream( ctl.getEncodedValue() );
                        OpenLdapBERTagDecoder decoder = new OpenLdapBERTagDecoder();
                        int[] nRead = new int[1];
                        nRead[0] = 0;
                        /* A Sequence */
                        BERSequence aSeq = (BERSequence) BERElement.getElement(decoder,inStream,nRead);
                        for (int i = 0; i < aSeq.size(); i++)
                        {
                            handleSequenceElement( aSeq.elementAt( i ), rctl );
                        }                    
                    }
                    result = rctl;
                }
                catch (IOException e)
                {
                    LOG.info( e );
                }
            }
            return result;
        }
    
    protected void handleSequenceElement (BERElement element, PasswordPolicyResponseControl target)
        {
            final BERTag tag = (BERTag) element;
            // warning -- Haven't checked warning code - but  suspect it mightn't work!!!!
            if ((tag.getTag() ^ BERTag.CONTEXT) == 0)
            {
                BERSequence sequence = (BERSequence) tag.getValue();
                final BERTag elem = (BERTag) sequence.elementAt( 0 );
                sequence = (BERSequence) elem.getValue();
                final BERInteger intValue = (BERInteger) sequence.elementAt( 0 );
                if ((elem.getTag() ^ BERTag.CONTEXT) == 0)
                {
                    target.setTimeBeforeExpiration( intValue.getValue() );
                }
                if ((elem.getTag() ^ BERTag.CONTEXT) == 1)
                {
                    target.setGraceLoginsRemaining( intValue.getValue() );
                }
            }
    
            // error - THIS WORKS see openldap.org link above 
            if ((tag.getTag() ^ BERTag.CONTEXT) == 1)
            {
                //final BERSequence sequence = (BERSequence) tag.getValue();
                //final BEREnumerated berEnum = (BEREnumerated) sequence.elementAt( 0 );
                //target.setErrorCode( berEnum.getValue() );
                final BERInteger berInteger = (BERInteger) tag.getValue();
                target.setErrorCode( berInteger.getValue() );
            }
        }
  • 4. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    843793 Newbie
    Currently Being Moderated
    To retrieve openLDAP warning messages, code in previous message didn't work as I expected it not too.

    as I had compiled OpenLDAP with SASL, needed to handle BERTag.SASLCONTEXT
            if ((tag.getTag() ^ BERTag.CONTEXT) == 0 || (tag.getTag() ^ BERTag.SASLCONTEXT) == 0)
            {
                LOG.info("Got a ppolicy warning....");
                BERSequence sequence = (BERSequence) tag.getValue();
                final BERTag elem = (BERTag) sequence.elementAt( 0 );
                LOG.info( "sequence.elementAt(0) = " +elem );
                LOG.info( "elemType= " +elem.getType() + " -1 is a TAG");
                LOG.info( "elem.Tag " +elem.getTag() );
                final BERInteger berInteger = (BERInteger) elem.getValue();
                LOG.info( "berInteger " + berInteger );
                LOG.info( "berInteger.value " + berInteger.getValue() );
                // 0x80 (128) is a CONTEXT-SPECIFIC, PRIMATIVE [0]
                if (elem.getTag() == 128)
                {
                    target.setTimeBeforeExpiration( berInteger.getValue() );
                }
                // 0x81 (129) is a CONTEXT-SPECIFIC, PRIMATIVE [1]
                if (elem.getTag() == 129)
                {
                    target.setGraceLoginsRemaining( berInteger.getValue() );
                }
            }
    don't forget you need entry in ldap for ppolicy like
     dn: cn=Standard Policy,ou=Policies,dc=example,dc=org
     objectClass: top
     objectClass: device
     objectClass: pwdPolicy
     cn: Standard Policy
     pwdAttribute: userPassword
     pwdLockoutDuration: 0
     pwdInHistory: 3
     pwdCheckQuality: 1
     pwdExpireWarning: 1209600
     pwdMaxAge: 7776000
     pwdMinLength: 6
     pwdGraceAuthnLimit: 3
     pwdAllowUserChange: TRUE
     pwdMustChange: FALSE
     pwdMaxFailure: 3
     pwdFailureCountInterval: 0
     pwdSafeModify: FALSE
     pwdLockout: TRUE
    need to have compiled openLDAP with ppolicy overlay
    need to have configured slapd.conf to use ppolicy overlay and maybe a default ppolicy

    you can check ppolicy bind response using ldapsearch:
    ldapsearch -e ppolicy -x -b "dc=example,dc=org" -D "uid=tony,ou=people,dc=example,dc=org" -W
    should see similar in java code

    if password has expired, can login as directory admin and reset it using ldappasswd
    ldappasswd -v -x -S -D "cn=Manager,dc=example,dc=org" -W -Z "uid=tony,ou=people,dc=example,dc=org"
    supplying users new password and confirming it followed by the directory admin/manager password when prompted
  • 5. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    EJP Guru
    Currently Being Moderated
    Reviving this old thread to mention that that although the code above will probably work OK in most cases, it is not strictly speaking correct, as the response can contain both a warning and an error code. I'll post a corrected version here shortly.

    Note that at present both the code here and mine rely on the Netscape Java LDAP SDK, which is presently available somewhere at mozilla.org.
  • 6. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    EJP Guru
    Currently Being Moderated
    As promised, here are the classes. Note that they depend on the Netscape Java LDAP SDK, which is now available via mozilla.org.

    First, the PasswordRequestControl:
    /*
     * Copyright (c) Esmond Pitt, 2010.
     * All rights reserved.
     *
     * Permission is hereby granted to use this source code as-is
     * provided this notice remains intact.
     */
    package com.verismart.ldap;
    
    import javax.naming.ldap.BasicControl;
    
    /**
     * PasswordRequestControl.
     *
     * @author Esmond Pitt
     */
    public class PasswordRequestControl extends BasicControl
    {
        public static final long    serialVersionUID = 5342767500282457716L;
    
        // @see http://tools.ietf.org/html/draft-behera-ldap-password-policy-10#page-24
    
        public PasswordRequestControl()
        {
            super(PasswordResponseControl.OID);
        }
    
        public PasswordRequestControl(boolean isCritical)
        {
            super(PasswordResponseControl.OID, isCritical, null);
        }
    }
    Then the PasswordResponseControlFactory, which you have to configure as per the Javadoc for javax.naming.ldap.ControlFactory:
    /*
     * Copyright (c) Esmond Pitt, 2010.
     * All rights reserved.
     *
     * Permission is hereby granted to use this source code as-is
     * provided this notice remains intact.
     */
    package com.verismart.ldap;
    
    import java.util.Arrays;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.naming.NamingException;
    import javax.naming.ldap.Control;
    import javax.naming.ldap.ControlFactory;
    
    /**
     * Factory for PasswordResponseControl.
     *
     * @author Esmond Pitt
     */
    public class PasswordResponseControlFactory extends ControlFactory
    {
        private Logger logger = Logger.getLogger(this.getClass().getName());
    
        public PasswordResponseControlFactory()
        {
            logger.fine("constructed");
        }
    
        @Override
        public Control getControlInstance(Control control)
            throws NamingException
        {
            logger.log(Level.FINE, "control ID={0} BER={1}", new Object[]
                {
                    control.getID(), Arrays.toString(control.getEncodedValue())
                });
            String id = control.getID();
    
            // See if it's one of ours
            // It will only be a response control!
            if (id.equals(PasswordResponseControl.OID))
            {
                return new PasswordResponseControl(control.isCritical(), control.getEncodedValue());
            }
            return null;
        }
    }
  • 7. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    EJP Guru
    Currently Being Moderated
    The PasswordResponseControl:
    /*
     * Copyright (c) Esmond Pitt, 2010. All rights reserved.
     * Permission is hereby granted to use this source code as-is provided this notice remains intact.
     */
    package com.verismart.ldap;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import javax.naming.NamingException;
    import javax.naming.ldap.BasicControl;
    
    import netscape.ldap.ber.stream.BERChoice;
    import netscape.ldap.ber.stream.BERElement;
    import netscape.ldap.ber.stream.BEREnumerated;
    import netscape.ldap.ber.stream.BERInteger;
    import netscape.ldap.ber.stream.BERIntegral;
    import netscape.ldap.ber.stream.BERSequence;
    import netscape.ldap.ber.stream.BERTag;
    import netscape.ldap.ber.stream.BERTagDecoder;
    import netscape.ldap.client.JDAPBERTagDecoder;
    /**
     * PasswordResponseControl.
     *
     * @author Esmond Pitt, Verismart Software Inc.
     * @see <a href="http://tools.ietf.org/html/draft-behera-ldap-password-policy-09">the Internet Draft</a>
     */
    public class PasswordResponseControl extends BasicControl
    {
        /** PasswordResponse OID. */
        public static final String OID = "1.3.6.1.4.1.42.2.27.8.5.1";
    
        /** Warning codes. */
        public enum Warning
        {
            timeBeforeExpiration,
            graceAuthNsRemaining,
            none;
        }
        /** Error codes. */
        public enum Error
        {
            /** The password has expired.*/
            passwordExpired,
            accountLocked,
            changeAfterReset,
            passwordModNotAllowed,
            mustSupplyOldPassword,
            insufficientPasswordQuality,
            passwordTooShort,
            passwordTooYoung,
            passwordInHistory,
            /**
             * No info, e.g. user has simply used the wrong password.
             */
            none;
        };
        private int warningCode = Warning.none.ordinal();
        private int timeBeforeExpiration;
        private int graceLoginsRemaining;
        private int errorCode = Error.none.ordinal();
    
        /**
         * Construct an instance. Called by the PasswordResponseControlFactory.
         * @param isCritical true iff critical
         * @param ber  BER encoded data
         * @throws NamingException Decoding problem.
         */
        public PasswordResponseControl(boolean isCritical, byte[] ber)
            throws NamingException
        {
            super(OID, isCritical, ber);
            try
            {
                new NetscapePasswordResponseControlDecoder().decode(ber);
            }
            catch (IOException exc)
            {
                throw new LdapDataAccessException("Failed to parse control value "+java.util.Arrays.toString(ber), exc);
            }
        }
    
        /**
         * @return true if this response has a Warning.
         */
        public boolean isWarningCode()
        {
            return warningCode != Warning.none.ordinal();
        }
        /**
         * Only meaningful if isWarningCode() returns true.
         * @return the Warning
         */
        public Warning getWarning()
        {
            return Warning.values()[warningCode];
        }
        /**
         * Only meaningful if isWarningCode() returns true
         * and getWarning() returns <code>Warning.graceAuthNsRemaining</code>.
         * @return the number of grace logins remaining.
         */
        public int getGraceLoginsRemaining()
        {
            return graceLoginsRemaining;
        }
        /**
         * Only meaningful if isWarningCode() returns true
         * and getWarning() returns <code>Warning.timeBeforeExpiration</code>.
         * @return the time to expiration,
         * measured in seconds since the response that created this object was sent.
         */
        public int getTimeBeforeExpiration()
        {
            return timeBeforeExpiration;
        }
        /**
         * @return true if this response has an Error.
         */
        public boolean isErrorCode()
        {
            return errorCode != Error.none.ordinal();
        }
        /**
         * Only meaningful if isErrorCode() returns true.
         * @return the Error.
         */
        public Error getError()
        {
            return Error.values()[errorCode];
        }
        // toString() omitted for space reasons, use your imagination.
        /**
         * Decoder based on Netscape ldapsdk library.
         * @see Weltman and Dahbura, <i>LDAP Programming with Java,</i>,
         * Addison Wesley 2000, ch. 16.
         */
        private class NetscapePasswordResponseControlDecoder
        {
            public void decode(byte[] encodedValue) throws IOException
            {
                int[] bytesRead = {0};
                BERSequence seq = (BERSequence)BERElement.getElement(new ApplicationTagDecoder(),
                    new ByteArrayInputStream(encodedValue), bytesRead);
                for (int i = 0; i < seq.size(); i++)
                {
                    BERTag tag = (BERTag)seq.elementAt(i);
                    BERElement  element = tag.getValue();
                    if (element instanceof BERChoice)
                    {
                        // ASN.1 Choice: the warning data.
                        BERChoice warning = (BERChoice)element;
                        BERTag content = (BERTag)warning.getValue();
                        int value = ((BERInteger)content.getValue()).getValue();
                        warningCode = content.getTag();
                        switch (warningCode)
                        {
                        case 0x80:
                            timeBeforeExpiration = value;
                            break;
                        case 0x81:
                            graceLoginsRemaining = value;
                            break;
                        default:
                        }
                        warningCode &= ~0x80; // Clear junk
                    }
                    else if (element instanceof BEREnumerated)
                    {
                        // ASN.1 Enumerated: the error code.
                        BEREnumerated error = (BEREnumerated)element;
                        errorCode = error.getValue();
                    }
                    else
                        ;  // error ...
                }
            }
            /**
             * Application-specific decoder.
             */
            class ApplicationTagDecoder extends JDAPBERTagDecoder
            {
                private boolean inChoice;
    
                @Override
                public BERElement getElement(BERTagDecoder decoder, int tag, InputStream stream, int[] bytesRead,
                    boolean[] implicit) throws IOException
                {
                    switch (tag)
                    {
                    case 0xa0:
                        implicit[0] = false;
                        inChoice = true;
                        BERElement.readLengthOctets(stream, bytesRead);
                        int[] componentLength = new int[1];
                        BERChoice choice = new BERChoice(decoder, stream, componentLength);
                        bytesRead[0] += componentLength[0];
                        return choice;
                    case 0x80:
                    case 0x81:
                        if (inChoice)
                        {
                            // Must be time before expiry (0x80)
                            // or graceLogins (0x81)
                            inChoice = false;
                            return new BERInteger(stream, bytesRead);
                        }
                        return new BEREnumerated(stream, bytesRead);
                    default:
                        return super.getElement(decoder, tag, stream, bytesRead, implicit);
                    }
                }
            }
        }
    }
  • 8. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    EJP Guru
    Currently Being Moderated
    Sorry but I had to cut out all logging and the toString() method from the previous post for space reasons, so there could be a couple of minor errors.

    And finally the exception class:
    /*
     * Copyright (c) Esmond Pitt, 2010.
     * All rights reserved.
     *
     * Permission is hereby granted to use this source code as-is
     * provided this notice remains intact.
     */
    package com.verismart.ldap;
    
    import javax.naming.NamingException;
    
    /**
     * Ldap data parsing exception.
     * @author Esmond Pitt
     */
    public class LdapDataAccessException extends NamingException
    {
    
        public LdapDataAccessException(String msg)
        {
            super(msg);
        }
    
        public LdapDataAccessException(String msg, Exception exc)
        {
            super(msg+": "+exc.getMessage());
            super.setRootCause(exc);
            super.setStackTrace(exc.getStackTrace());
        }
    }
    Edited by: EJP on 7/07/2012 17:33
  • 9. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    825812 Newbie
    Currently Being Moderated
    Thanks for this posting this - greatly appreciated! I was able to obtain the grace login warnings from OpenLDAP (2.4) but find the errors [such as PasswordExpired, Account locked out listed] in PasswordRequestControl are never revealed when attempting to create a context using the standard:
                    LDAPContext ctx = new InitialLdapContext(env, ldapResponseCotnrols);
                 handleResponseControls(ctx);
    
                    Control[] ctls =  ctx.getResponseControls();
                    for (int j = 0; ctls != null && j < ctls.length; j++) {
                       PasswordResponseControl basicCtl = (PasswordResponseControl) ctls[j]; 
                              System.out.println("GraceLoginsRemaining:" + basicCtl.getGraceLoginsRemaining());
                       System.out.println("Time remaining until expiration:" + basicCtl.getTimeBeforeExpiration());
                       System.out.println("GetError:" + basicCtl.getError());
                       System.out.println("Get Warning:" + basicCtl.getWarning());
                       System.out.println("Get ID:" + basicCtl.getID());
                       System.out.println(ctls[j].toString());
                    }
    Is there another way to involved the ControlsFactory and bind asynchronously to catch the extended responses from the server instead of throwing a error:49 Invalid Credential?

    Thanks,

    Craig
  • 10. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    EJP Guru
    Currently Being Moderated
    How are you doing the LDAP bind? You have to do
    context.addToEnvironment(Context.SECURITY_PRINCIPAL, username);
    context.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
    context.reconnect(new Control[]{new PasswordRequestControl(true)}); // this is the LDAP bind
    and then check the response controls, both in the success path and the catch blocks. According to my code:

    - NoPermissionException can be accompanied by a pwdMustChange condition
    - AuthenticationException can be accompanied by several of the password response conditions

    When changing the password, if you want the password policy overlay to vet the change for quality, you have to use LdapContext.extendedOperation() with a PasswordModifyExtendedRequest and PasswordModifyExtendedResponse, and:

    - InvalidAttributeException can be accompanied by insufficientPasswordQuality and maybe other conditions when changing the password
  • 11. Re: Decode ASN.1/BER response from PasswordPolicyResponse (openldap)
    825812 Newbie
    Currently Being Moderated
    Okay - I think I see what needs to be done. I hadn't tried the reconnect thinking I needed to somehow initiate the bind with an asynchronous connection. I started binding as an Admin then the reconnect with the user but found the admin bind wasn't necessary and settled on this.
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://10.1.1.2:389/o=business.com");
    env.put(LdapContext.CONTROL_FACTORIES,"com.verismart.ldap.PasswordResponseControlFactory");
    
    LdapContext ctx = new InitialLdapContext(env, null);
    try {
            
            ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, USER_DN);
            ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, USER_PWD);
            ctx.reconnect(new Control[]{new PasswordRequestControl(true)}); 
            System.out.println("Success with bind, checking the controls: " + USER_DN);
            handleResponseControls(ctx);
            
    } catch(NamingException ne) {
             System.out.println("Caught an exception, checking the controls: " + USER_DN);
             handleResponseControls(ctx);
            
    } 
    Thanks again!