From 6c119aa77f97501357fa8e7bb5fbf556b1f9f53d Mon Sep 17 00:00:00 2001
From: Nemanja Lukic <nemanja.lukic@forgerock.com>
Date: Thu, 03 Nov 2011 10:18:53 +0000
Subject: [PATCH] Patch for OPENDJ-295
---
opends/resource/schema/02-config.ldif | 13 ++
opends/src/server/org/opends/server/extensions/DictionaryPasswordValidator.java | 60 ++++++++--
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DictionaryPasswordValidatorTestCase.java | 180 +++++++++++++++++++++++++++++
opends/src/admin/defn/org/opends/server/admin/std/DictionaryPasswordValidatorConfiguration.xml | 49 ++++++++
opends/resource/config/config.ldif | 1
opends/src/messages/messages/extension.properties | 2
6 files changed, 289 insertions(+), 16 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 836689e..9909f4c 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -1666,6 +1666,7 @@
ds-cfg-dictionary-file: config/wordlist.txt
ds-cfg-case-sensitive-validation: false
ds-cfg-test-reversed-password: true
+ds-cfg-check-substrings: false
dn: cn=Length-Based Password Validator,cn=Password Validators,cn=config
objectClass: top
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 803cd48..3567f18 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -23,6 +23,7 @@
#
# Copyright 2006-2010 Sun Microsystems, Inc.
# Portions Copyright 2010-2011 ForgeRock AS.
+# Portions Copyright 2011 profiq, s.r.o.
#
#
# This file contains the attribute type and objectclass definitions for use
@@ -2712,6 +2713,14 @@
NAME 'ds-cfg-log-control-oids'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.52
+ NAME 'ds-cfg-check-substrings'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.53
+ NAME 'ds-cfg-min-substring-length'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler'
SUP top
@@ -3666,7 +3675,9 @@
STRUCTURAL
MUST ( ds-cfg-dictionary-file $
ds-cfg-case-sensitive-validation $
- ds-cfg-test-reversed-password )
+ ds-cfg-test-reversed-password $
+ ds-cfg-check-substrings )
+ MAY ds-cfg-min-substring-length
X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.95
NAME 'ds-cfg-attribute-value-password-validator'
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/DictionaryPasswordValidatorConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/DictionaryPasswordValidatorConfiguration.xml
index 227c0a3..ae3e5e2 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/DictionaryPasswordValidatorConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/DictionaryPasswordValidatorConfiguration.xml
@@ -24,6 +24,7 @@
!
!
! Copyright 2007-2008 Sun Microsystems, Inc.
+ | Portions Copyright 2011 profiq, s.r.o.
! -->
<adm:managed-object name="dictionary-password-validator"
plural-name="dictionary-password-validators"
@@ -145,4 +146,52 @@
</ldap:attribute>
</adm:profile>
</adm:property>
+ <adm:property name="check-substrings" mandatory="true">
+ <adm:synopsis>
+ Indicates wheather this password validator is to match portions of
+ the password string against dictionary words.
+ </adm:synopsis>
+ <adm:description>
+ If "false" then only match the entire password against words
+ otherwise ("true") check whether the password contains words.
+ </adm:description>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>false</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-check-substrings</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="min-substring-length" mandatory="false">
+ <adm:synopsis>
+ Indicates the minimal length of the substring within the password
+ in case substring checking is enabled.
+ </adm:synopsis>
+ <adm:description>
+ If "check-substrings" option is set to true, then this parameter
+ defines the length of the smallest word which should be used for
+ substring matching. Use with caution because values below 3 might
+ disqualify valid passwords.
+ </adm:description>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>5</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:integer />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-min-substring-length</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
</adm:managed-object>
diff --git a/opends/src/messages/messages/extension.properties b/opends/src/messages/messages/extension.properties
index 2412677..94e5306 100644
--- a/opends/src/messages/messages/extension.properties
+++ b/opends/src/messages/messages/extension.properties
@@ -1123,7 +1123,7 @@
MILD_ERR_VATTR_NOT_SEARCHABLE_459=The %s attribute is not \
searchable and should not be included in otherwise unindexed search filters
MILD_ERR_DICTIONARY_VALIDATOR_PASSWORD_IN_DICTIONARY_460=The provided \
- password was found in the server's dictionary
+ password contained a word from the server's dictionary
MILD_ERR_DICTIONARY_VALIDATOR_NO_SUCH_FILE_461=The specified dictionary file \
%s does not exist
MILD_ERR_DICTIONARY_VALIDATOR_CANNOT_READ_FILE_462=An error occurred while \
diff --git a/opends/src/server/org/opends/server/extensions/DictionaryPasswordValidator.java b/opends/src/server/org/opends/server/extensions/DictionaryPasswordValidator.java
index 172e3b2..c85bc72 100644
--- a/opends/src/server/org/opends/server/extensions/DictionaryPasswordValidator.java
+++ b/opends/src/server/org/opends/server/extensions/DictionaryPasswordValidator.java
@@ -23,6 +23,7 @@
*
*
* Copyright 2008 Sun Microsystems, Inc.
+ * Portions Copyright 2011 profiq, s.r.o.
*/
package org.opends.server.extensions;
import org.opends.messages.Message;
@@ -124,9 +125,7 @@
{
// Get a handle to the current configuration.
DictionaryPasswordValidatorCfg config = currentConfig;
- HashSet<String> dictionary = this.dictionary;
-
-
+
// Check to see if the provided password is in the dictionary in the order
// that it was provided.
String password = newPassword.toString();
@@ -135,26 +134,43 @@
password = toLowerCase(password);
}
- if (dictionary.contains(password))
+ // Check to see if we should verify the whole password or the substrings.
+ // Either way, we initialise the minSubstringLength to the length of
+ // the password which is the default behaviour ('check-substrings: false')
+ int minSubstringLength = password.length();
+
+ if (config.isCheckSubstrings())
+ {
+ // We apply the minimal substring length only if the provided value
+ // is smaller then the actual password length
+ if (config.getMinSubstringLength() < password.length())
+ {
+ minSubstringLength = config.getMinSubstringLength();
+ }
+ }
+
+ // Verify if the dictionary contains the word(s) in the password
+ if (isDictionaryBased(password, minSubstringLength))
{
invalidReason.append(
- ERR_DICTIONARY_VALIDATOR_PASSWORD_IN_DICTIONARY.get());
+ ERR_DICTIONARY_VALIDATOR_PASSWORD_IN_DICTIONARY.get());
return false;
}
-
-
- // If we should try the reversed value, then do that as well.
+
+ // If the reverse password checking is enabled, then verify if the
+ // reverse value of the password is in the dictionary.
if (config.isTestReversedPassword())
{
- if (dictionary.contains(new StringBuilder(password).reverse().toString()))
+ if (isDictionaryBased(
+ new StringBuilder(password).reverse().toString(), minSubstringLength))
{
invalidReason.append(
- ERR_DICTIONARY_VALIDATOR_PASSWORD_IN_DICTIONARY.get());
+ ERR_DICTIONARY_VALIDATOR_PASSWORD_IN_DICTIONARY.get());
return false;
}
}
-
+
// If we've gotten here, then the password is acceptable.
return true;
}
@@ -306,5 +322,25 @@
return new ConfigChangeResult(resultCode, adminActionRequired, messages);
}
-}
+ private boolean isDictionaryBased(String password,
+ int minSubstringLength)
+ {
+ HashSet<String> dictionary = this.dictionary;
+ final int passwordLength = password.length();
+
+ for (int i = 0; i < passwordLength; i++)
+ {
+ for (int j = i + minSubstringLength; j <= passwordLength; j++)
+ {
+ String substring = password.substring(i, j);
+ if (dictionary.contains(substring))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DictionaryPasswordValidatorTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DictionaryPasswordValidatorTestCase.java
index 1197145..c661fc6 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DictionaryPasswordValidatorTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DictionaryPasswordValidatorTestCase.java
@@ -23,6 +23,7 @@
*
*
* Copyright 2006-2008 Sun Microsystems, Inc.
+ * Portions Copyright 2011 profiq, s.r.o.
*/
package org.opends.server.extensions;
@@ -127,6 +128,8 @@
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: false",
"ds-cfg-test-reversed-password: true",
+ "ds-cfg-check-substrings: true",
+ "ds-cfg-min-substring-length: 3",
"",
"dn: cn=Dictionary,cn=Password Validators,cn=config",
"objectClass: top",
@@ -139,6 +142,7 @@
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: true",
"ds-cfg-test-reversed-password: true",
+ "ds-cfg-check-substrings: false",
"",
"dn: cn=Dictionary,cn=Password Validators,cn=config",
"objectClass: top",
@@ -150,7 +154,8 @@
"ds-cfg-enabled: true",
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: false",
- "ds-cfg-test-reversed-password: true");
+ "ds-cfg-test-reversed-password: true",
+ "ds-cfg-check-substrings: true");
Object[][] array = new Object[entries.size()][1];
for (int i=0; i < array.length; i++)
@@ -208,6 +213,7 @@
"ds-cfg-dictionary-file: invalid",
"ds-cfg-case-sensitive-validation: false",
"ds-cfg-test-reversed-password: true",
+ "ds-cfg-check-substrings: false",
"",
// Dictionary file not a file.
"dn: cn=Dictionary,cn=Password Validators,cn=config",
@@ -221,6 +227,7 @@
"ds-cfg-dictionary-file: config",
"ds-cfg-case-sensitive-validation: false",
"ds-cfg-test-reversed-password: true",
+ "ds-cfg-check-substrings: false",
"",
// Invalid case-sensitive-validation
"dn: cn=Dictionary,cn=Password Validators,cn=config",
@@ -234,6 +241,7 @@
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: invalid",
"ds-cfg-test-reversed-password: true",
+ "ds-cfg-check-substrings: false",
"",
// Invalid test-reversed-password
"dn: cn=Dictionary,cn=Password Validators,cn=config",
@@ -246,7 +254,37 @@
"ds-cfg-enabled: true",
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: false",
- "ds-cfg-test-reversed-password: invalid");
+ "ds-cfg-test-reversed-password: invalid",
+ "ds-cfg-check-substrings: false",
+ "",
+ // Invalid check-substrings
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-test-reversed-password: invalid",
+ "ds-cfg-check-substrings: invalid",
+ "",
+ // Invalid min-substring-length
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-test-reversed-password: invalid",
+ "ds-cfg-check-substrings: true",
+ "ds-cfg-min-substring-length: invalid");
Object[][] array = new Object[entries.size()][1];
for (int i=0; i < array.length; i++)
@@ -311,6 +349,7 @@
"ds-cfg-enabled: true",
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-check-substrings: false",
"ds-cfg-test-reversed-password: true"),
"notindictionary",
true
@@ -330,6 +369,7 @@
"ds-cfg-enabled: true",
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-check-substrings: false",
"ds-cfg-test-reversed-password: true"),
"password",
false
@@ -350,6 +390,7 @@
"ds-cfg-enabled: true",
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-check-substrings: false",
"ds-cfg-test-reversed-password: true"),
"PaSsWoRd",
false
@@ -370,6 +411,7 @@
"ds-cfg-enabled: true",
"ds-cfg-dictionary-file: " + dictionaryFile,
"ds-cfg-case-sensitive-validation: true",
+ "ds-cfg-check-substrings: false",
"ds-cfg-test-reversed-password: true"),
"PaSsWoRd",
true
@@ -454,6 +496,140 @@
"dRoWsSaP",
true
},
+
+ // Substrings checking configuration with a word in the dictionary,
+ // case-sensitive matching enabled
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: true",
+ "ds-cfg-check-substrings: true",
+ "ds-cfg-min-substring-length: 3",
+ "ds-cfg-test-reversed-password: true"),
+ "oldpassword",
+ false
+ },
+
+ // Substrings checking configuration with a word in the dictionary,
+ // case-sensitive matching disabled
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-check-substrings: true",
+ "ds-cfg-min-substring-length: 3",
+ "ds-cfg-test-reversed-password: true"),
+ "NewPassword",
+ false
+ },
+
+ // Substrings checking configuration with a word in the dictionary,
+ // case-sensitive matching enabled (dictionary word is lower case)
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: true",
+ "ds-cfg-check-substrings: true",
+ "ds-cfg-min-substring-length: 3",
+ "ds-cfg-test-reversed-password: true"),
+ "NewPassword",
+ true
+ },
+
+ // Substrings checking configuration with a word in the dictionary,
+ // case-sensitive matching disabled, and minimal substring length
+ // of 5 while the password is only 3 characters
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-check-substrings: true",
+ "ds-cfg-min-substring-length: 5",
+ "ds-cfg-test-reversed-password: true"),
+ "god",
+ false
+ },
+
+ // Substrings checking configuration with a word in the dictionary,
+ // case-sensitive matching disabled, and minimal substring length
+ // of 5 while the word in the dictionary is only 3 characters
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-check-substrings: true",
+ "ds-cfg-min-substring-length: 5",
+ "ds-cfg-test-reversed-password: true"),
+ "godblessus",
+ true
+ },
+
+ // Substring checking configuration with a reverse of a word in the
+ // dictionary, reversed matching enabled and case-insensitive
+ // matching enabled
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Dictionary,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-dictionary-password-validator",
+ "cn: Dictionary",
+ "ds-cfg-java-class: org.opends.server.extensions." +
+ "DictionaryPasswordValidator",
+ "ds-cfg-enabled: true",
+ "ds-cfg-dictionary-file: " + dictionaryFile,
+ "ds-cfg-case-sensitive-validation: false",
+ "ds-cfg-test-reversed-password: true",
+ "ds-cfg-check-substrings: true"),
+ "sdfdRoWsSaPqwerty",
+ false
+ },
};
}
--
Gitblit v1.10.0