/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.tools.ldapdecoder.protocol; import java.util.ArrayList; import com.slamd.asn1.ASN1Element; import com.slamd.asn1.ASN1Enumerated; import com.slamd.asn1.ASN1Integer; import com.slamd.asn1.ASN1OctetString; import com.slamd.asn1.ASN1Sequence; /** * This class defines the password policy control, which is defined in * draft-behera-ldap-password-policy. * * * @author Neil A. Wilson */ public class PasswordPolicyControl extends LDAPControl { /** * The OID of the password policy control. */ public static final String PASSWORD_POLICY_CONTROL_OID = "1.3.6.1.4.1.42.2.27.8.5.1"; /** * The BER type for the warning value element. */ public static final byte TYPE_WARNING = (byte) 0x80; /** * The BER type for the error value element. */ public static final byte TYPE_ERROR = (byte) 0x81; /** * The warning type that will be used to specify the length of time in seconds * until expiration. */ public static final byte WARNING_TYPE_TIME_BEFORE_EXPIRATION = (byte) 0x80; /** * The warning type that will be used to specify the number of grace logins * remaining. */ public static final byte WARNING_TYPE_GRACE_AUTHS_REMAINING = (byte) 0x81; /** * The error type that will be used to indicate that the password is expired. */ public static final int ERROR_TYPE_PASSWORD_EXPIRED = 0; /** * The error type that will be used to indicate that the account is locked. */ public static final int ERROR_TYPE_ACCOUNT_LOCKED = 1; /** * The error type that will be used to indicate that the password has been * reset and must be changed. */ public static final int ERROR_TYPE_CHANGE_AFTER_RESET = 2; /** * The error type that will be used to indicate that password changes are not * allowed. */ public static final int ERROR_TYPE_PASSWORD_MOD_NOT_ALLOWED = 3; /** * The error type that will be used to indicate that the current password must * be provided when choosing a new password. */ public static final int ERROR_TYPE_MUST_SUPPLY_OLD_PASSWORD = 4; /** * The error type that will be used to indicate that the provided password is * unacceptable. */ public static final int ERROR_TYPE_INSUFFICIENT_PASSWORD_QUALITY = 5; /** * The error type that will be used to indicate that the provided password is * too short. */ public static final int ERROR_TYPE_PASSWORD_TOO_SHORT = 6; /** * The error type that will be used to indicate that the password is too * young. */ public static final int ERROR_TYPE_PASSWORD_TOO_YOUNG = 7; /** * The error type that will be used to indicate that the password is in the * password history. */ public static final int ERROR_TYPE_PASSWORD_IN_HISTORY = 8; // The warning type for this password policy control. private byte warningType; // The error type for this password policy control. private int errorType; // The warning value for this password policy control. private int warningValue; /** * Creates a new password policy control with no value, which will be suitable * for use in a request. * * @param isCritical Indicates whether the control should be marked * critical. */ public PasswordPolicyControl(boolean isCritical) { super(PASSWORD_POLICY_CONTROL_OID, isCritical, null); warningType = (byte) -1; errorType = -1; warningValue = -1; } /** * Creates a new password policy control with a value containing possible * warning and/or error components. * * @param isCritical Indicates whether the controls should be marked * critical. * @param warningType The warning type for this control, or -1 if there * should not be a warning type. * @param warningValue The value associated with the warning, if any. * @param errorType The error type for this control, or -1 if there * should not be an error type. */ public PasswordPolicyControl(boolean isCritical, byte warningType, int warningValue, int errorType) { super(PASSWORD_POLICY_CONTROL_OID, isCritical, encodeValue(warningType, warningValue, errorType)); this.warningType = warningType; this.warningValue = warningValue; this.errorType = errorType; } /** * Creates a new password policy control by decoding the provided value. * * @param isCritical Indicates whether this control should be marked * critical. * @param controlValue The encoded value for this control. * * @throws ProtocolException If a problem occurs while decoding the control * value. */ public PasswordPolicyControl(boolean isCritical, ASN1OctetString controlValue) throws ProtocolException { super(PASSWORD_POLICY_CONTROL_OID, isCritical, controlValue); warningType = (byte) -1; warningValue = -1; errorType = -1; ASN1Element[] sequenceElements; try { sequenceElements = ASN1Element.decodeAsSequence(controlValue.getValue()).getElements(); } catch (Exception e) { throw new ProtocolException("Unable to decode password policy control " + "value sequence", e); } if (sequenceElements.length > 2) { throw new ProtocolException("There must be no more than two elements " + "in a password policy control value " + "sequence."); } for (int i=0; i < sequenceElements.length; i++) { if (sequenceElements[i].getType() == TYPE_WARNING) { try { ASN1Integer intElement = ASN1Element.decodeAsInteger(sequenceElements[i].getValue()); if ((intElement.getType() == WARNING_TYPE_TIME_BEFORE_EXPIRATION) || (intElement.getType() == WARNING_TYPE_GRACE_AUTHS_REMAINING)) { warningType = intElement.getType(); warningValue = intElement.getIntValue(); } else { throw new ProtocolException("Unable to decode the password " + "policy control value because the " + "warning element had an invalid type " + "of " + intElement.getType()); } } catch (ProtocolException pe) { throw pe; } catch (Exception e) { throw new ProtocolException("Unable to decode the password policy " + "control value sequence because an " + "error occurred while parsing the " + "warning element: " + e); } } else if (sequenceElements[i].getType() == TYPE_ERROR) { try { ASN1Enumerated enumeratedElement = sequenceElements[i].decodeAsEnumerated(); switch (enumeratedElement.getIntValue()) { case ERROR_TYPE_PASSWORD_EXPIRED: case ERROR_TYPE_ACCOUNT_LOCKED: case ERROR_TYPE_CHANGE_AFTER_RESET: case ERROR_TYPE_PASSWORD_MOD_NOT_ALLOWED: case ERROR_TYPE_MUST_SUPPLY_OLD_PASSWORD: case ERROR_TYPE_INSUFFICIENT_PASSWORD_QUALITY: case ERROR_TYPE_PASSWORD_TOO_SHORT: case ERROR_TYPE_PASSWORD_TOO_YOUNG: case ERROR_TYPE_PASSWORD_IN_HISTORY: errorType = enumeratedElement.getIntValue(); break; default: throw new ProtocolException("Unable to decode the password " + "policy control value because the " + "error element had an invalid " + "value of " + enumeratedElement.getIntValue()); } } catch (ProtocolException pe) { throw pe; } catch (Exception e) { throw new ProtocolException("Unable to decode the password policy " + "control value sequence because an " + "error occurred while parsing the " + "error element: " + e); } } else { throw new ProtocolException("Unable to decode the password policy " + "control value sequence because it had " + "an element with an invalid type of " + sequenceElements[i].getType()); } } } /** * Encodes the provided information into an octet string suitable for use as * the value of this control. * * @param warningType The warning type for this control, or -1 if there * should not be a warning type. * @param warningValue The value associated with the warning, if any. * @param errorType The error type for this control, or -1 if there * should not be an error type. * * @return An octet string containing the encoded control value. */ public static ASN1OctetString encodeValue(byte warningType, int warningValue, int errorType) { ArrayList<ASN1Element> elementList = new ArrayList<ASN1Element>(2); if (warningType != (byte) -1) { ASN1Integer warningInteger = new ASN1Integer(warningType, warningValue); elementList.add(new ASN1Element(TYPE_WARNING, warningInteger.encode())); } if (errorType != -1) { elementList.add(new ASN1Enumerated(TYPE_ERROR, errorType)); } ASN1Element[] sequenceElements = new ASN1Element[elementList.size()]; elementList.toArray(sequenceElements); ASN1Sequence valueSequence = new ASN1Sequence(sequenceElements); return new ASN1OctetString(valueSequence.encode()); } /** * Retrieves a string representation of this control with the specified * indent. * * @param indent The number of spaces to indent the output. * * @return A string representation of this control with the specified indent. */ public String toString(int indent) { StringBuilder indentBuf = new StringBuilder(indent); for (int i=0; i < indent; i++) { indentBuf.append(' '); } StringBuilder buffer = new StringBuilder(); ASN1OctetString controlValue = getValue(); if (controlValue == null) { buffer.append(indentBuf).append("LDAP Password Policy Request Control"). append(LDAPMessage.EOL); } else { buffer.append(indentBuf).append("LDAP Password Policy Response Control"). append(LDAPMessage.EOL); } buffer.append(indentBuf).append(" OID: ").append(getControlOID()). append(LDAPMessage.EOL); buffer.append(indentBuf).append(" Criticality: "). append(isCritical()).append(LDAPMessage.EOL); if (controlValue != null) { buffer.append(indentBuf).append(" Value:").append(LDAPMessage.EOL); switch (warningType) { case WARNING_TYPE_TIME_BEFORE_EXPIRATION: buffer.append(indentBuf).append(" Time Before Expiration: "). append(warningValue).append(" seconds").append(LDAPMessage.EOL); break; case WARNING_TYPE_GRACE_AUTHS_REMAINING: buffer.append(indentBuf).append(" Grace Logins Remaining: "). append(warningValue).append(LDAPMessage.EOL); break; case (byte) -1: break; default: buffer.append(indentBuf).append(" Unknown Warining ("). append(warningType).append("): ").append(warningValue). append(LDAPMessage.EOL); break; } switch (errorType) { case ERROR_TYPE_PASSWORD_EXPIRED: buffer.append(indentBuf).append(" Password Expired"). append(LDAPMessage.EOL); break; case ERROR_TYPE_ACCOUNT_LOCKED: buffer.append(indentBuf).append(" Account Locked"). append(LDAPMessage.EOL); break; case ERROR_TYPE_CHANGE_AFTER_RESET: buffer.append(indentBuf).append(" Change After Reset"). append(LDAPMessage.EOL); break; case ERROR_TYPE_PASSWORD_MOD_NOT_ALLOWED: buffer.append(indentBuf).append(" Password Mod Not Allowed"). append(LDAPMessage.EOL); break; case ERROR_TYPE_MUST_SUPPLY_OLD_PASSWORD: buffer.append(indentBuf).append(" Must Supply Old Password"). append(LDAPMessage.EOL); break; case ERROR_TYPE_INSUFFICIENT_PASSWORD_QUALITY: buffer.append(indentBuf). append(" Insufficient Password Quality"). append(LDAPMessage.EOL); break; case ERROR_TYPE_PASSWORD_TOO_SHORT: buffer.append(indentBuf).append(" Password Too Short"). append(LDAPMessage.EOL); break; case ERROR_TYPE_PASSWORD_TOO_YOUNG: buffer.append(indentBuf).append(" Password Too Young"). append(LDAPMessage.EOL); break; case ERROR_TYPE_PASSWORD_IN_HISTORY: buffer.append(indentBuf).append(" Password In History"). append(LDAPMessage.EOL); break; case (byte) -1: break; default: buffer.append(indentBuf).append(" Unknown Errpr ("). append(errorType).append(LDAPMessage.EOL); break; } } return buffer.toString(); } }