diff --git a/config.inc.php b/config.inc.php index b6699b58..96841a6f 100644 --- a/config.inc.php +++ b/config.inc.php @@ -148,9 +148,21 @@ $CONF['authlib_default_flavor'] = 'md5raw'; // If you use the dovecot encryption method: where is the dovecotpw binary located? $CONF['dovecotpw'] = "/usr/sbin/dovecotpw"; -// Minimum length required for passwords. Postfixadmin will not -// allow users to set passwords which are shorter than this value. -$CONF['min_password_length'] = 5; +// Password validation +// New/changed passwords will be validated using all regular expressions in the array. +// If a password doesn't match one of the regular expressions, the corresponding +// error message from $PALANG (see languages/*) will be displayed. +// See http://de3.php.net/manual/en/reference.pcre.pattern.syntax.php for details +// about the regular expression syntax. +// If you need custom error messages, you can add them using $CONF['language_hook']. +// If a $PALANG text contains a %s, you can add its value after the $PALANG key +// (separated with a space). +$CONF['password_validation'] = array( +# '/regular expression/' => '$PALANG key (optional: + parameter)', + '/.{5}/' => 'password_too_short 5', # minimum length 5 characters + '/([a-zA-Z].*){3}/' => 'password_no_characters 3', # must contain at least 3 characters + '/([0-9].*){2}/' => 'password_no_digits 2', # must contain at least 2 digits +); // Generate Password // Generate a random password for a mailbox or admin and display it. diff --git a/edit-admin.php b/edit-admin.php index 83eb9447..2d105e74 100644 --- a/edit-admin.php +++ b/edit-admin.php @@ -78,12 +78,10 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") // if it has, ensure both fields are the same... if ($fPassword == $fPassword2) { - if(strlen($fPassword) >= $CONF['min_password_length']) { - $fPassword = pacrypt($fPassword); - } - else { + $validpass = validate_password($fPassword); + if(count($validpass) > 0) { + $pAdminEdit_admin_password_text_error = $validpass[0]; # TODO: honor all error messages, not only the first one $error = 1; - $pAdminEdit_admin_password_text_error = sprintf($PALANG['password_too_short'], $CONF['min_password_length']); } } else { @@ -105,6 +103,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") $password_query = ''; if ($fPassword != '') { # do not change password to empty one + $fPassword = pacrypt($fPassword); $password_query = ", password='$fPassword'"; } $result = db_query ("UPDATE $table_admin SET modified=NOW(),active='$sqlActive' $password_query WHERE username='$username'"); diff --git a/edit-mailbox.php b/edit-mailbox.php index 2f5a5e40..ed6c8d6e 100644 --- a/edit-mailbox.php +++ b/edit-mailbox.php @@ -92,12 +92,11 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") if (isset ($_POST['fActive'])) $fActive = escape_string ($_POST['fActive']); if($fPassword != $user_details['password'] || $fPassword2 != $user_details['password']){ - $min_length = $CONF['min_password_length']; - if($fPassword == $fPassword2) { if ($fPassword != "") { - if($min_length > 0 && strlen($fPassword) < $min_length) { - $mailbox_password_text_error = sprintf($PALANG['password_too_short'], $CONF['min_password_length']); + $validpass = validate_password($fPassword); + if(count($validpass) > 0) { + $mailbox_password_text_error = $validpass[0]; # TODO: honor all error messages, not only the first one $error = 1; } $formvars['password'] = pacrypt($fPassword); diff --git a/functions.inc.php b/functions.inc.php index 0081f1ac..6b2d7850 100644 --- a/functions.inc.php +++ b/functions.inc.php @@ -1088,11 +1088,8 @@ function encode_header ($string, $default_charset = "utf-8") { function generate_password () { global $CONF; - //check that password length is sensible - $length = (int) $CONF['min_password_length']; - if ($length < 5 || $length > 32) { - $length = 8; - } + // length of the generated password + $length = 8; // define possible characters $possible = "2345678923456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ"; # skip 0 and 1 to avoid confusion with O and l @@ -1114,6 +1111,35 @@ function generate_password () { +/** + * Check if a password is strong enough based on the conditions in $CONF['password_validation'] + * @param String $password + * @return array of error messages, or empty array if the password is ok + */ +function validate_password($password) { + global $CONF; + global $PALANG; + $result = array(); + + if (isset($CONF['min_password_length'])) { # used up to 2.3.x - check it for backward compatibility + $minlen = (int) $CONF['min_password_length']; + $CONF['password_validation']['/.{' . $minlen . '}/'] = "password_too_short $minlen"; + } + + foreach ($CONF['password_validation'] as $regex => $message) { + if (!preg_match($regex, $password)) { + $msgparts = preg_split("/ /", $message, 2); + if (count($msgparts) == 1) { + $result[] = $PALANG[$msgparts[0]]; + } else { + $result[] = sprintf($PALANG[$msgparts[0]], $msgparts[1]); + } + } + } + return $result; +} + + /** * Encrypt a password, using the apparopriate hashing mechanism as defined in * config.inc.php ($CONF['encrypt']). diff --git a/scripts/shells/mailbox.php b/scripts/shells/mailbox.php index 2afa92fa..519300ea 100644 --- a/scripts/shells/mailbox.php +++ b/scripts/shells/mailbox.php @@ -321,7 +321,7 @@ class PasswordTask extends Shell { if (isset($this->params['g']) && $this->params['g'] == true ) { $random = true; $password = NULL; - } elseif (isset($this->args[1]) && strlen($this->args[1]) > 8) { # TODO use $CONF['min_password_length'] + } elseif (isset($this->args[1]) && strlen($this->args[1]) > 8) { # TODO use validate_password() $password = $this->args[1]; } else { diff --git a/setup.php b/setup.php index 956f1157..f8ca6145 100644 --- a/setup.php +++ b/setup.php @@ -464,10 +464,12 @@ function check_setup_password($password, $lostpw_mode = 0) { list($confsalt, $confpass, $trash) = explode(':', $setuppw . '::'); $pass = encrypt_setup_password($password, $confsalt); + $validpass = validate_password($password); + if ($password == "" ) { # no password specified? $result = "Setup password must be specified
If you didn't set up a setup password yet, enter the password you want to use."; - } elseif (strlen($password) < $CONF['min_password_length']) { # password too short? - $result = "The setup password you entered is too short. Please choose a better one."; + } elseif (count($validpass) > 0) { + $result = $validpass[0]; # TODO: honor all error messages, not only the first one } elseif ($pass == $setuppw && $lostpw_mode == 0) { # correct passsword (and not asking for a new password) $result = "pass_OK"; $error = 0; @@ -479,7 +481,7 @@ function check_setup_password($password, $lostpw_mode = 0) { } else { $result = '

Setup password not specified correctly

'; } - $result .= '

If you want to use the password you entered as setup password, edit config.inc.php and set

'; + $result .= '

If you want to use the password you entered as setup password, edit config.inc.php or config.local.php and set

'; $result .= "
\$CONF['setup_password'] = '$pass';
"; } return array ($error, $result); diff --git a/users/password.php b/users/password.php index d1401406..b0945252 100644 --- a/users/password.php +++ b/users/password.php @@ -47,10 +47,13 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") $fPassword2 = $_POST['fPassword2']; $error = 0; - if(strlen($fPassword) < $CONF['min_password_length']) { + + $validpass = validate_password($fPassword); + if(count($validpass) > 0) { + flash_error($validpass[0]); # TODO: honor all error messages, not only the first one $error += 1; - flash_error(sprintf($PALANG['password_too_short'], $CONF['min_password_length'])); } + if(!MailboxHandler::login($username, $fPassword_current)) { $error += 1; $pPassword_password_current_text = $PALANG['pPassword_password_current_text_error'];