diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 63c63220d..2bd160fef 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -141,7 +141,7 @@ cat < /etc/dovecot/sql/dovecot-dict-sql-passdb.conf # Autogenerated by mailcow driver = mysql connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}" -default_pass_scheme = SSHA256 +default_pass_scheme = ${MAILCOW_PASS_SCHEME} password_query = SELECT password FROM mailbox WHERE active = '1' AND username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1') AND JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) != '1' AND (JSON_UNQUOTE(JSON_VALUE(attributes, '$.%s_access')) = '1' OR ('%s' != 'imap' AND '%s' != 'pop3')) EOF diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index d3b16fa96..fef7958ba 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -204,7 +204,7 @@ while read -r line gal type sql userPasswordAlgorithm - ssha256 + ${MAILCOW_PASS_SCHEME} prependPasswordScheme YES viewURL diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 8bf8d59fc..761ad86b4 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -84,8 +84,25 @@ function ip_acl($ip, $networks) { return false; } function hash_password($password) { - $salt_str = bin2hex(openssl_random_pseudo_bytes(8)); - return "{SSHA256}".base64_encode(hash('sha256', $password . $salt_str, true) . $salt_str); + // default_pass_scheme is determined in vars.inc.php (or corresponding local file) + // in case default pass scheme is not defined, falling back to BLF-CRYPT. + global $default_pass_scheme; + $pw_hash = NULL; + switch (strtoupper($default_pass_scheme)) { + case "SSHA256": + $salt_str = bin2hex(openssl_random_pseudo_bytes(8)); + $pw_hash = "{SSHA256}".base64_encode(hash('sha256', $password . $salt_str, true) . $salt_str); + break; + case "SSHA512": + $salt_str = bin2hex(openssl_random_pseudo_bytes(8)); + $pw_hash = "{SSHA512}".base64_encode(hash('sha512', $password . $salt_str, true) . $salt_str); + break; + case "BLF-CRYPT": + default: + $pw_hash = "{BLF-CRYPT}" . password_hash($password, PASSWORD_BCRYPT); + break; + } + return $pw_hash; } function last_login($user) { global $pdo; @@ -502,6 +519,12 @@ function verify_hash($hash, $password) { if (password_verify($password, $hash)) { return true; } + } + elseif (preg_match('/^{BLF-CRYPT}/i', $hash)) { + $hash = preg_replace('/^{BLF-CRYPT}/i', '', $hash); + if (password_verify($password, $hash)) { + return true; + } } return false; } diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index 993b79757..1c02bf967 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -17,6 +17,7 @@ $database_name = getenv('DBNAME'); // Other variables $mailcow_hostname = getenv('MAILCOW_HOSTNAME'); +$default_pass_scheme = getenv('MAILCOW_PASS_SCHEME'); // Autodiscover settings // === diff --git a/docker-compose.yml b/docker-compose.yml index d9251a289..651e5101c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -133,6 +133,7 @@ services: - DBUSER=${DBUSER} - DBPASS=${DBPASS} - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - MAILCOW_PASS_SCHEME=${MAILCOW_PASS_SCHEME:-BLF-CRYPT} - IMAP_PORT=${IMAP_PORT:-143} - IMAPS_PORT=${IMAPS_PORT:-993} - POP_PORT=${POP_PORT:-110} @@ -159,7 +160,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.91 + image: mailcow/sogo:1.92 environment: - DBNAME=${DBNAME} - DBUSER=${DBUSER} @@ -167,6 +168,7 @@ services: - TZ=${TZ} - LOG_LINES=${LOG_LINES:-9999} - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - MAILCOW_PASS_SCHEME=${MAILCOW_PASS_SCHEME:-BLF-CRYPT} - ACL_ANYONE=${ACL_ANYONE:-disallow} - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} @@ -221,6 +223,7 @@ services: - DBPASS=${DBPASS} - TZ=${TZ} - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - MAILCOW_PASS_SCHEME=${MAILCOW_PASS_SCHEME:-BLF-CRYPT} - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} - MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-1440} diff --git a/generate_config.sh b/generate_config.sh index e4d23f7ac..0c4b43c39 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -117,6 +117,11 @@ cat << EOF > mailcow.conf MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} +# Password hash algorithm +# Only certain password hash algorithm are supported. For a fully list of supported schemes, +# see https://mailcow.github.io/mailcow-dockerized-docs/model-passwd/ +MAILCOW_PASS_SCHEME=BLF-CRYPT + # ------------------------------ # SQL database configuration # ------------------------------ diff --git a/update.sh b/update.sh index 1bd1fe2ee..adb342669 100755 --- a/update.sh +++ b/update.sh @@ -217,6 +217,7 @@ CONFIG_ARRAY=( "REDIS_PORT" "DOVECOT_MASTER_USER" "DOVECOT_MASTER_PASS" + "MAILCOW_PASS_SCHEME" ) sed -i --follow-symlinks '$a\' mailcow.conf @@ -390,6 +391,14 @@ for option in ${CONFIG_ARRAY[@]}; do echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf echo "DOVECOT_MASTER_PASS=" >> mailcow.conf fi + elif [[ ${option} == "MAILCOW_PASS_SCHEME" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo '# Password hash algorithm' >> mailcow.conf + echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf + echo '# see https://mailcow.github.io/mailcow-dockerized-docs/model-passwd/' >> mailcow.conf + echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf + fi elif ! grep -q ${option} mailcow.conf; then echo "Adding new option \"${option}\" to mailcow.conf" echo "${option}=n" >> mailcow.conf