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'];