opends/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandler.java
@@ -41,7 +41,6 @@ import org.opends.server.api.ClientConnection; import org.opends.server.api.ConfigurableComponent; import org.opends.server.api.IdentityMapper; import org.opends.server.api.PasswordStorageScheme; import org.opends.server.api.SASLMechanismHandler; import org.opends.server.config.ConfigAttribute; import org.opends.server.config.ConfigEntry; @@ -52,17 +51,13 @@ import org.opends.server.core.DirectoryServer; import org.opends.server.core.InitializationException; import org.opends.server.core.LockManager; import org.opends.server.core.PasswordPolicyState; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.AuthenticationInfo; import org.opends.server.types.ByteString; import org.opends.server.types.ConfigChangeResult; import org.opends.server.types.DN; import org.opends.server.types.Entry; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.ResultCode; import static org.opends.server.config.ConfigConstants.*; @@ -511,87 +506,40 @@ } // Get the password attribute from the user entry and iterate through the // available values. We can only look at reversible values, so all // non-reversible values will be ignored. For each reversible value, see // if we can use it in conjunction with the challenge to construct the // provided digest. // FIXME -- Determine the attribute based on the user's password policy. AttributeType pwType = DirectoryServer.getAttributeType(ATTR_USER_PASSWORD); if (pwType == null) // Get the clear-text passwords from the user entry, if there are any. List<ByteString> clearPasswords; try { pwType = DirectoryServer.getDefaultAttributeType(ATTR_USER_PASSWORD); } List<Attribute> pwAttr = userEntry.getAttribute(pwType); if ((pwAttr == null) || pwAttr.isEmpty()) PasswordPolicyState pwPolicyState = new PasswordPolicyState(userEntry, false, false); clearPasswords = pwPolicyState.getClearPasswords(); if ((clearPasswords == null) || clearPasswords.isEmpty()) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); int msgID = MSGID_SASLCRAMMD5_NO_PW_ATTR; String message = getMessage(msgID, pwType.getNameOrOID()); int msgID = MSGID_SASLCRAMMD5_NO_REVERSIBLE_PASSWORDS; String message = getMessage(msgID, String.valueOf(userEntry.getDN())); bindOperation.setAuthFailureReason(msgID, message); return; } } catch (Exception e) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); int msgID = MSGID_SASLCRAMMD5_CANNOT_GET_REVERSIBLE_PASSWORDS; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), String.valueOf(e)); bindOperation.setAuthFailureReason(msgID, message); return; } boolean reversibleFound = false; // Iterate through the clear-text values and see if any of them can be used // in conjunction with the challenge to construct the provided digest. boolean matchFound = false; for (Attribute a : pwAttr) for (ByteString clearPassword : clearPasswords) { for (AttributeValue v : a.getValues()) { String valueStr = v.getStringValue(); int closePos; if (valueStr.startsWith(STORAGE_SCHEME_PREFIX) && (closePos = valueStr.indexOf(STORAGE_SCHEME_SUFFIX, 2)) > 0) { String schemeName = toLowerCase(valueStr.substring(1, closePos)); PasswordStorageScheme scheme = DirectoryServer.getPasswordStorageScheme(schemeName); if (scheme == null) { // We can't do anything with this. Append a message to the // error message to include in the response and continue. int msgID = MSGID_SASLCRAMMD5_UNKNOWN_STORAGE_SCHEME; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), schemeName); logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.SEVERE_WARNING, message, msgID); continue; } else if (! scheme.isReversible()) { // It's not a reversible scheme, so we can't get the clear-text // password to test. Skip it and go on. continue; } else { ASN1OctetString encodedPassword = new ASN1OctetString(valueStr.substring(closePos+1)); ByteString clearPassword; try { clearPassword = scheme.getPlaintextValue(encodedPassword); reversibleFound = true; } catch (DirectoryException de) { assert debugException(CLASS_NAME, "processSASLBind", de); int msgID = MSGID_SASLCRAMMD5_CANNOT_GET_CLEAR_PASSWORD; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), schemeName, de.getErrorMessage()); logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.SEVERE_WARNING, message, msgID); continue; } byte[] generatedDigest = generateDigest(clearPassword, challenge); if (Arrays.equals(digestBytes, generatedDigest)) { @@ -599,44 +547,16 @@ break; } } } else { reversibleFound = true; byte[] generatedDigest = generateDigest(v.getValue(), challenge); if (Arrays.equals(digestBytes, generatedDigest)) { matchFound = true; break; } } } if (matchFound) { break; } } if (! matchFound) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); if (reversibleFound) { int msgID = MSGID_SASLCRAMMD5_INVALID_PASSWORD; String message = getMessage(msgID); bindOperation.setAuthFailureReason(msgID, message); return; } else { int msgID = MSGID_SASLCRAMMD5_NO_REVERSIBLE_PASSWORDS; String message = getMessage(msgID, String.valueOf(userEntry.getDN())); bindOperation.setAuthFailureReason(msgID, message); return; } } // If we've gotten here, then the authentication was successful. opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -45,7 +45,6 @@ import org.opends.server.api.ClientConnection; import org.opends.server.api.ConfigurableComponent; import org.opends.server.api.IdentityMapper; import org.opends.server.api.PasswordStorageScheme; import org.opends.server.api.SASLMechanismHandler; import org.opends.server.config.ConfigAttribute; import org.opends.server.config.ConfigEntry; @@ -57,10 +56,8 @@ import org.opends.server.core.DirectoryServer; import org.opends.server.core.InitializationException; import org.opends.server.core.LockManager; import org.opends.server.core.PasswordPolicyState; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.AuthenticationInfo; import org.opends.server.types.ByteString; import org.opends.server.types.ConfigChangeResult; @@ -862,6 +859,7 @@ else { // Use the identity mapper to resolve the username to an entry. String userName = responseUserName; if (lowerUserName.startsWith("u:")) { if (lowerUserName.equals("u:")) @@ -874,13 +872,13 @@ return; } responseUserName = responseUserName.substring(2); userName = responseUserName.substring(2); } try { userEntry = identityMapper.getEntryForID(responseUserName); userEntry = identityMapper.getEntryForID(userName); } catch (DirectoryException de) { @@ -913,87 +911,41 @@ } // Get the password attribute from the user entry and iterate through the // available values. We can only look at reversible values, so all // non-reversible values will be ignored. For each reversible value, see // if we can use it in conjunction with the challenge to construct the // provided digest. // FIXME -- Determine the attribute based on the user's password policy. AttributeType pwType = DirectoryServer.getAttributeType(ATTR_USER_PASSWORD); if (pwType == null) // Get the clear-text passwords from the user entry, if there are any. List<ByteString> clearPasswords; try { pwType = DirectoryServer.getDefaultAttributeType(ATTR_USER_PASSWORD); } List<Attribute> pwAttr = userEntry.getAttribute(pwType); if ((pwAttr == null) || pwAttr.isEmpty()) PasswordPolicyState pwPolicyState = new PasswordPolicyState(userEntry, false, false); clearPasswords = pwPolicyState.getClearPasswords(); if ((clearPasswords == null) || clearPasswords.isEmpty()) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); int msgID = MSGID_SASLDIGESTMD5_NO_PW_ATTR; String message = getMessage(msgID, pwType.getNameOrOID()); int msgID = MSGID_SASLDIGESTMD5_NO_REVERSIBLE_PASSWORDS; String message = getMessage(msgID, String.valueOf(userEntry.getDN())); bindOperation.setAuthFailureReason(msgID, message); return; } } catch (Exception e) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); int msgID = MSGID_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), String.valueOf(e)); bindOperation.setAuthFailureReason(msgID, message); return; } boolean reversibleFound = false; // Iterate through the clear-text values and see if any of them can be used // in conjunction with the challenge to construct the provided digest. boolean matchFound = false; byte[] passwordBytes = null; for (Attribute a : pwAttr) for (ByteString clearPassword : clearPasswords) { for (AttributeValue v : a.getValues()) { String valueStr = v.getStringValue(); int closePos; if (valueStr.startsWith(STORAGE_SCHEME_PREFIX) && (closePos = valueStr.indexOf(STORAGE_SCHEME_SUFFIX, 2)) > 0) { String schemeName = toLowerCase(valueStr.substring(1, closePos)); PasswordStorageScheme scheme = DirectoryServer.getPasswordStorageScheme(schemeName); if (scheme == null) { // We can't do anything with this. Append a message to the // error message to include in the response and continue. int msgID = MSGID_SASLDIGESTMD5_UNKNOWN_STORAGE_SCHEME; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), schemeName); logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.SEVERE_WARNING, message, msgID); continue; } else if (! scheme.isReversible()) { // It's not a reversible scheme, so we can't get the clear-text // password to test. Skip it and go on. continue; } else { ASN1OctetString encodedPassword = new ASN1OctetString(valueStr.substring(closePos+1)); ByteString clearPassword; try { clearPassword = scheme.getPlaintextValue(encodedPassword); reversibleFound = true; } catch (DirectoryException de) { assert debugException(CLASS_NAME, "processSASLBind", de); int msgID = MSGID_SASLDIGESTMD5_CANNOT_GET_CLEAR_PASSWORD; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), schemeName, de.getErrorMessage()); logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.SEVERE_WARNING, message, msgID); continue; } byte[] generatedDigest; try { @@ -1001,9 +953,8 @@ generateResponseDigest(responseUserName, responseAuthzID, clearPassword.value(), responseRealm, responseNonce, responseCNonce, responseNonceCountStr, responseDigestURI, responseQoP, responseCharset); responseNonceCountStr, responseDigestURI, responseQoP, responseCharset); } catch (Exception e) { @@ -1016,7 +967,6 @@ continue; } if (Arrays.equals(responseDigest, generatedDigest)) { matchFound = true; @@ -1024,66 +974,16 @@ break; } } } else { reversibleFound = true; byte[] generatedDigest; try { generatedDigest = generateResponseDigest(responseUserName, responseAuthzID, v.getValue().value(), responseRealm, responseNonce, responseCNonce, responseNonceCountStr, responseDigestURI, responseQoP, responseCharset); } catch (Exception e) { assert debugException(CLASS_NAME, "processSASLBind", e); logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.SEVERE_WARNING, MSGID_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_DIGEST, stackTraceToSingleLineString(e)); continue; } if (Arrays.equals(responseDigest, generatedDigest)) { matchFound = true; passwordBytes = v.getValue().value(); break; } } } if (matchFound) { break; } } if (! matchFound) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); if (reversibleFound) { int msgID = MSGID_SASLDIGESTMD5_INVALID_CREDENTIALS; String message = getMessage(msgID); bindOperation.setAuthFailureReason(msgID, message); return; } else { int msgID = MSGID_SASLDIGESTMD5_NO_REVERSIBLE_PASSWORDS; String message = getMessage(msgID); bindOperation.setAuthFailureReason(msgID, message); return; } } // FIXME -- Need to do something with the authzid. opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java
@@ -35,7 +35,6 @@ import org.opends.server.api.ConfigurableComponent; import org.opends.server.api.IdentityMapper; import org.opends.server.api.PasswordStorageScheme; import org.opends.server.api.SASLMechanismHandler; import org.opends.server.config.ConfigAttribute; import org.opends.server.config.ConfigEntry; @@ -46,17 +45,13 @@ import org.opends.server.core.DirectoryServer; import org.opends.server.core.InitializationException; import org.opends.server.core.LockManager; import org.opends.server.core.PasswordPolicyState; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.AuthenticationInfo; import org.opends.server.types.ByteString; import org.opends.server.types.ConfigChangeResult; import org.opends.server.types.DN; import org.opends.server.types.Entry; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.ResultCode; import static org.opends.server.config.ConfigConstants.*; @@ -421,80 +416,13 @@ } // Get the password attribute from the user entry and see if any of the // values match the provided clear-text password. // FIXME -- Determine the attribute based on the user's password policy. AttributeType pwType = DirectoryServer.getAttributeType(ATTR_USER_PASSWORD); if (pwType == null) // Get the password policy for the user and use it to determine if the // provided password was correct. try { pwType = DirectoryServer.getDefaultAttributeType(ATTR_USER_PASSWORD); } List<Attribute> pwAttr = userEntry.getAttribute(pwType); if ((pwAttr == null) || pwAttr.isEmpty()) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); int msgID = MSGID_SASLPLAIN_NO_PW_ATTR; String message = getMessage(msgID, pwType.getNameOrOID()); bindOperation.setAuthFailureReason(msgID, message); return; } ASN1OctetString passwordOS = new ASN1OctetString(password); boolean matchFound = false; for (Attribute a : pwAttr) { for (AttributeValue v : a.getValues()) { String valueStr = v.getStringValue(); int closePos; if (valueStr.startsWith(STORAGE_SCHEME_PREFIX) && (closePos = valueStr.indexOf(STORAGE_SCHEME_SUFFIX, 2)) > 0) { String schemeName = toLowerCase(valueStr.substring(1, closePos)); PasswordStorageScheme scheme = DirectoryServer.getPasswordStorageScheme(schemeName); if (scheme == null) { // We can't do anything with this. Append a message to the // error message to include in the response and continue. int msgID = MSGID_SASLPLAIN_UNKNOWN_STORAGE_SCHEME; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), schemeName); logError(ErrorLogCategory.EXTENSIONS, ErrorLogSeverity.SEVERE_WARNING, message, msgID); } else { ASN1OctetString storedPassword = new ASN1OctetString(valueStr.substring(closePos+1)); if (scheme.passwordMatches(passwordOS, storedPassword)) { matchFound = true; break; } } } else { matchFound = passwordOS.equalsIgnoreType(v.getValue()); if (matchFound) { break; } } } if (matchFound) { break; } } if (! matchFound) PasswordPolicyState pwPolicyState = new PasswordPolicyState(userEntry, false, false); if (! pwPolicyState.passwordMatches(new ASN1OctetString(password))) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); @@ -503,6 +431,19 @@ bindOperation.setAuthFailureReason(msgID, message); return; } } catch (Exception e) { assert debugException(CLASS_NAME, "processSASLBind", e); bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); int msgID = MSGID_SASLPLAIN_CANNOT_CHECK_PASSWORD_VALIDITY; String message = getMessage(msgID, String.valueOf(userEntry.getDN()), String.valueOf(e)); bindOperation.setAuthFailureReason(msgID, message); return; } // FIXME -- Figure out what to do with the authzID if one was provided. opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -3972,6 +3972,37 @@ /** * The message ID for the message that will be used if SASL DIGEST-MD5 * authentication fails because an error occured while trying to get the * clear-text password value(s) from a user's entry. */ public static final int MSGID_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS = CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 376; /** * The message ID for the message that will be used if SASL CRAM-MD5 * authentication fails because an error occured while trying to get the * clear-text password value(s) from a user's entry. */ public static final int MSGID_SASLCRAMMD5_CANNOT_GET_REVERSIBLE_PASSWORDS = CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 377; /** * The message ID for the message that will be used if SASL PLAIN * authentication fails because an error occurred while trying to get the * password policy state. This takes two arguments, which are the user DN and * a message explaining the problem that occurred. */ public static final int MSGID_SASLPLAIN_CANNOT_CHECK_PASSWORD_VALIDITY = CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 378; /** * Associates a set of generic messages with the message IDs defined in this * class. */ @@ -4901,6 +4932,10 @@ "unknown storage scheme of %s."); registerMessage(MSGID_SASLPLAIN_INVALID_PASSWORD, "The provided password is invalid."); registerMessage(MSGID_SASLPLAIN_CANNOT_CHECK_PASSWORD_VALIDITY, "An error occurred while attempting to verify the " + "password for user %s during SASL PLAIN authentication: " + "%s."); registerMessage(MSGID_SASLPLAIN_UPDATED_IDENTITY_MAPPER, "Attribute " + ATTR_IDMAPPER_DN + " in configuration entry %s has been updated. The " + @@ -5057,6 +5092,10 @@ "SASL CRAM-MD5 authentication is not possible for user " + "%s because none of the passwords in the user entry are " + "stored in a reversible form."); registerMessage(MSGID_SASLCRAMMD5_CANNOT_GET_REVERSIBLE_PASSWORDS, "An error occurred while attempting to retrieve the " + "clear-text password(s) for user %s in order to perform " + "SASL CRAM-MD5 authentication: %s."); registerMessage(MSGID_SASLCRAMMD5_UPDATED_IDENTITY_MAPPER, "Attribute " + ATTR_IDMAPPER_DN + " in configuration entry %s has been updated. The " + @@ -5288,6 +5327,10 @@ "SASL DIGEST-MD5 authentication is not possible for user " + "%s because none of the passwords in the user entry are " + "stored in a reversible form."); registerMessage(MSGID_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS, "An error occurred while attempting to retrieve the " + "clear-text password(s) for user %s in order to perform " + "SASL DIGEST-MD5 authentication: %s."); registerMessage(MSGID_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_DIGEST, "An error occurred while attempting to generate a " + "server-side digest to compare with the client " +