diff options
author | Timotheus Pokorra <tp@tbits.net> | 2015-04-17 16:43:00 +0200 |
---|---|---|
committer | Timotheus Pokorra <tp@tbits.net> | 2015-04-17 16:43:00 +0200 |
commit | da251955a799248e7e8b465a1faabf9ea5f25eac (patch) | |
tree | 0659d17870c2fe769d84f62d65a7b9b95c61b6a1 | |
parent | 358428eb8ec573e79c610b3f7aa96cb2365f35f5 (diff) | |
download | webadmin-da251955a799248e7e8b465a1faabf9ea5f25eac.tar.gz |
Password policy improvements.
calculate the special characters automatically if they are not defined in the password policy config value.
prepare to support special characters beyond 7-bit, but not using them to generate a password because the characters might be not known to the user (eg. umlaut).
if 389ds has been configured for 8-bit characters for the password, you can allow such characters in the password policy now.
-rw-r--r-- | doc/sample-kolab.conf | 8 | ||||
-rw-r--r-- | lib/password_policy.php | 84 |
2 files changed, 80 insertions, 12 deletions
diff --git a/doc/sample-kolab.conf b/doc/sample-kolab.conf index 878942d..870dbc9 100644 --- a/doc/sample-kolab.conf +++ b/doc/sample-kolab.conf @@ -29,13 +29,19 @@ secondary_mail = { "{0}@{1}": "format('%(givenname)s.%(surname)s', '%(domain)s')" } } +; Password complexity policy. +; either define specialChars, or the special characters in the range 33 to 127 are used, apart from digits, characters and some potentially misleading characters (` and |) +; by default there is a 7-bit password enforcement plugin enabled on 389ds: +; "7bitPasswordEnforcement"=1: will complain if non 7-bit special characters are used (default installation for ds389) +; "7bitPasswordEnforcement"=0: allows all special characters password_policy = { "minLength" : 8, "minUpper" : 1, "minLower" : 1, "minNumeric" : 1, "minSpecial" : 1, - "specialChars" : "<>#&%!?.,;*/+-=[]{}()" + "specialChars" : "<>#&%!?.,;*/+-=[]{}()", + "7bitPasswordEnforcement": 1 } [ldap] diff --git a/lib/password_policy.php b/lib/password_policy.php index 4375623..9482e76 100644 --- a/lib/password_policy.php +++ b/lib/password_policy.php @@ -28,13 +28,64 @@ class password_policy { { // TODO: get the password policy from LDAP instead? $conf = Conf::get_instance(); - return $conf->get('kolab', 'password_policy', Conf::AUTO); + + $passwordpolicy = $conf->get('kolab', 'password_policy', Conf::AUTO); + + if (!isset($passwordpolicy['specialChars'])) { + $passwordpolicy['specialChars'] = self::get_special_characters($passwordpolicy); + } + + return $passwordpolicy; } /** - * validate if password matches the password policy - * @throws an exception if password is not secure enough - */ + * calculate which special characters will work in the current setup + * + * @return string with the allowed special characters + */ + private static function get_special_characters($passwordpolicy) + { + $special_chars = ''; + + // only use the characters from the 127 bit range. otherwise the user gets characters that he cannot type or does not know, eg Umlaut + $maxcharacter = 127; + + for ($c = 33; $c < $maxcharacter; $c++) { + if ($c >= ord('0') && $c <= ord('9')) { + continue; + } + + if ($c >= ord('a') && $c <= ord('z')) { + continue; + } + + if ($c >= ord('A') && $c <= ord('Z')) { + continue; + } + + if ($c == 127 || $c == 255) { + continue; + } + + # do not use some characters that are not well-known to users + if (chr($c) == '`' || chr($c) == '|') { + continue; + } + + if ($c > 127) { + $special_chars .= mb_convert_encoding('&#' . intval($c) . ';', 'UTF-8', 'HTML-ENTITIES'); + } else { + $special_chars .= chr($c); + } + } + + return $special_chars; + } + + /** + * validate if password matches the password policy + * @throws an exception if password is not secure enough + */ public static function validate_password($password) { $passwordpolicy = self::get_password_policy(); @@ -81,9 +132,12 @@ class password_policy { kolab_api_controller::translate("user.password.morespecial", $passwordpolicy['minSpecial'], $passwordpolicy['specialChars']).'<br/>'; } - if ($countUpperCaseChars + $countLowerCaseChars + $countDigits + $countSpecialChars < strlen($password)) { - $errors .= kolab_api_controller::translate("error").": ". - kolab_api_controller::translate("user.password.notallowed").'<br/>'; + # by default there is a 7-bit password enforcement plugin enabled on 389ds + if ($passwordpolicy['7bitPasswordEnforcement'] == '0') { + // do not test for characters beyond 127 + } else if ($countUpperCaseChars + $countLowerCaseChars + $countDigits + $countSpecialChars < strlen($password)) { + $errors .= kolab_api_controller::translate("error").": ". + kolab_api_controller::translate("user.password.notallowed").'<br/>'; } if ($errors != "") { @@ -123,20 +177,28 @@ class password_policy { if ($passwordpolicy['minSpecial'] > 0) { # using rand instead of /dev/urandom to avoid problems with special characters on bash command line for ($countSpecialChar = 0; $countSpecialChar < $passwordpolicy['minSpecial']; $countSpecialChar++) { - $password .= $passwordpolicy['specialChars'][(rand() % strlen($passwordpolicy['specialChars']))]; + $password .= mb_substr($passwordpolicy['specialChars'], rand() % mb_strlen($passwordpolicy['specialChars']), 1); } } $numberLowerCase = $passwordpolicy['minLower']; - if ($passwordpolicy['minLength'] > strlen($password) + $passwordpolicy['minLower']) { - $numberLowerCase = $passwordpolicy['minLength'] - strlen($password); + if ($passwordpolicy['minLength'] > mb_strlen($password) + $passwordpolicy['minLower']) { + $numberLowerCase = $passwordpolicy['minLength'] - mb_strlen($password); } unset($pwd_snippet); exec("head -c 200 /dev/urandom | tr -dc a-z | head -c".$numberLowerCase, $pwd_snippet); $password .= $pwd_snippet[0]; - return str_shuffle($password); + $len = mb_strlen($password); + $pwd_chars = array(); + while($len-- > 0) { + $pwd_chars[] = mb_substr($password, $len, 1); + } + + shuffle($pwd_chars); + + return join('', $pwd_chars); } } |