This documentation is for Dovecot v2.x, see wiki1 for v1.x documentation.

Introduction

Through the years computers are being faster and faster, and so with it the encryption of passwords have to more secure. In this example we convert passwords stored in MySQL with basic CRYPT-encryption to SSHA256-encryption (Salted SHA256).

See Authentication/PasswordSchemes for a list of supported password schemes.

We used php to generate the new passwords, but you can use any language you want

Alternative example can be found from https://kaworu.ch/blog/2016/04/20/strong-crypt-scheme-with-dovecot-postfixadmin-and-roundcube

Example

# Comment default_pass_scheme so dovecot will look at the prefix
# default_pass_scheme = CRYPT

# update your password_query so it will look at the new field
# AND add a %w field in the query so we have the plain password in our Enviroment ($PLAIN_PASS)
password_query = SELECT id as user, newpw as password, home as userdb_home, \
uid as userdb_uid, gid as userdb_gid, '%w' as userdb_plain_pass FROM users WHERE id = '%u'

# Alternatively, here is another config that worked for me with SHA512-CRYPT (note: uncomment the lines relevant for your setup):
#
# driver = mysql
# connect = host=127.0.0.1 user=mailauth password=secret dbname=postfixadmin
# default_pass_scheme = SHA512-CRYPT
# password_query = SELECT username AS user, password, CONCAT('/var/mail/vdomains/', maildir) as userdb_home, 'vmail' as userdb_uid, 'vmail' as userdb_gid, '%w' as userdb_plain_pass FROM mailbox WHERE username = '%u'
# user_query = SELECT CONCAT('/var/mail/vdomains/', maildir) AS home, 'vmail' AS uid, 'vmail' AS gid, password FROM mailbox WHERE username = '%u' AND active = 1

 userdb {
  driver = prefetch
}

   1 #!/bin/sh
   2 /usr/local/etc/convertpw.php $USER $PLAIN_PASS
   3 exec "$@"

* Make the php-script which updates the password and save it as /usr/local/etc/convertpw.php

   1 #!/usr/local/bin/php
   2 <?php
   3 $mysqlhost  = "localhost";
   4 $mysqluser  = "mysqlusername"; // username which is used to connect to the database
   5 $mysqlpass  = "mysqlpassword"; // password which is used to connect to the database
   6 $mysqldb    = "databasename";  // databasename where the passwords are stored
   7 $mysqltable = "users";         // table where the passwords are stored
   8 $idfield    = "id";            // fieldname where the userlogin is stored
   9 $passfield  = "newpw";         // fieldname where the passwords is stored
  10 
  11 $usr = $argv[1];
  12 $ruw = $argv[2];
  13 function hexToAscii($hex){
  14     $strLength = strlen($hex);
  15     $returnVal = '';
  16     for($i=0; $i<$strLength; $i += 2) {
  17         $dec_val = hexdec(substr($hex, $i, 2));
  18         $returnVal .= chr($dec_val);
  19     }
  20     return $returnVal;
  21 }
  22 $link = mysql_connect ("$mysqlhost", "$mysqluser", "$mysqlpass")  or die ("Could not connect");
  23 @mysql_select_db("$mysqldb") or die( "Unable to select database");
  24 $result = mysql_query("SELECT $passfield FROM $mysqltable WHERE $idfield = '$usr' AND $passfield like '{SSHA%'");
  25 if (mysql_num_rows($result)==0){
  26         $salt=substr(sha1(uniqid()),18,8);
  27         $salt_ascii = hexToAscii($salt);
  28         $newq= "UPDATE $mysqltable SET $passfield='{SSHA256.hex}".hash('sha256',$ruw.$salt_ascii).$salt."' WHERE $idfield='".$usr."'";
  29         $res2 = mysql_query($newq);
  30 }
  31 exit;
  32 ?>

# insert these lines so dovecot uses our scripts
service pop3 {
  executable = pop3 pop3-postlogin
}
service pop3-postlogin {
  executable = script-login /usr/local/etc/popafter.sh
  user = $default_internal_user
  unix_listener pop3-postlogin {
  }
}
# end insert

As of now each user which connects through POP will convert their password to SSHA256. If you look at the database you will see for example {SSHA256.hex}fb0e7f39c88c1d7017169f7f6b9cd6977d1e3291149382b90da4a390a31e81bab3cdced8 instead off {CRYPT}$1$.gvrgDqc$Slvoapz5zkpVmmJAxi.0k1

If you are using IMAP, you will need to add the same kind of commands (i.e. imap-postlogin) to your config, too.

When every record is updated you can update dovecot.conf (remove the extra lines), and dovecot-sql (remove the %w-part).

SHA512-CRYPT

To use SHA512-CRYPT passwords use /usr/local/etc/popafter.sh

   1 #!/bin/sh
   2 DOVECOTPW=$(doveadm pw -s SHA512-CRYPT -p $PLAIN_PASS)
   3 /usr/local/etc/convertpw.php $USER $DOVECOTPW
   4 exec "$@"

A variant that does not leak the password to the process list:

   1 #!/bin/sh
   2 NEWPASSWORD=$(doveadm pw -s SHA512-CRYPT <<EOF
   3 $PLAIN_PASS
   4 $PLAIN_PASS
   5 EOF
   6 )
   7 /etc/dovecot/convert-password.php "$USER" "$NEWPASSWORD"
   8 exec "$@"

/usr/local/etc/convertpw.php

   1 # Here is an alterate version that I used with SHA512-CRYPT and bash (note: uncomment the lines relevant for your setup including the ones I added for debugging purposes if needed):
   2 #!/usr/local/bin/bash
   3 # echo "USER: $USER" >> /tmp/log
   4 # echo "PLAIN-PASS: $PLAIN_PASS" >> /tmp/log
   5 DOVECOTPW=$(/usr/local/bin/doveadm pw -s SHA512-CRYPT -p "$PLAIN_PASS")
   6 # echo $DOVECOTPW >> /tmp/log
   7 /usr/local/etc/convertpw.php $USER $DOVECOTPW
   8 exec "$@"
   9 # note: if enabled, some of the lines above will log passwords to /tmp/log. Create the file first, and delete it when no longer needed - 
  10 # this while approach is a security risk and should *never* be done in a production system. I had to use it for troubleshooting for a very limited period of time.
  11 #
  12 

/usr/local/etc/convertpw.php - alternate version with debugging logs

   1 #!/usr/bin/php
   2 <?php
   3 $mysqlhost  = "127.0.0.1";
   4 $mysqluser  = "postfix"; // username which is used to connect to the database
   5 $mysqlpass  = "password"; // password which is used to connect to the database
   6 $mysqldb    = "postfix";  // databasename where the passwords are stored
   7 $mysqltable = "mailbox";         // table where the passwords are stored
   8 $idfield    = "username";            // fieldname where the userlogin is stored
   9 $passfield  = "password";         // fieldname where the passwords is stored
  10 
  11 $usr = $argv[1];
  12 $dov = $argv[2];
  13 function hexToAscii($hex){
  14     $strLength = strlen($hex);
  15     $returnVal = '';
  16     for($i=0; $i<$strLength; $i += 2) {
  17         $dec_val = hexdec(substr($hex, $i, 2));
  18         $returnVal .= chr($dec_val);
  19     }
  20     return $returnVal;
  21 }
  22 $link = mysql_connect ("$mysqlhost", "$mysqluser", "$mysqlpass")  or die ("Could not connect");
  23 @mysql_select_db("$mysqldb") or die( "Unable to select database");
  24 $result = mysql_query("SELECT $passfield FROM $mysqltable WHERE $idfield = '$usr' AND $passfield like '{SHA%'");
  25 if (mysql_num_rows($result)==0){
  26         $salt=substr(sha1(uniqid()),18,8);
  27         $salt_ascii = hexToAscii($salt);
  28         $newq= "UPDATE $mysqltable SET $passfield='".$dov."' WHERE $idfield='".$usr."'";
  29         $res2 = mysql_query($newq);
  30 }
  31 exit;
  32 ?>

selinux

   1 chcon -u system_u /usr/local/etc/convertpw.php
   2 chcon -t bin_t /usr/local/etc/convertpw.php
   3 chcon -u system_u /usr/local/etc/popafter.sh
   4 chcon -t bin_t /usr/local/etc/popafter.sh

Example for SHA512-Crypt with passwd-files

This example has been tested on Dovecot 2.2.19 in a virtual user setup.

Create a new service for the postlogin script and reference it in the imap service section.

 /etc/dovecot/conf.d/10-master.conf

   1 service imap {
   2   executable = imap imap-postlogin
   3    unix_listener imap-master {
   4     user = dovecot
   5   }
   6 }
   7 
   8 service imap-postlogin {
   9   executable = script-login /var/vmail/conf.d/scripts/postlogin.sh
  10   user = vmail
  11   unix_listener imap-postlogin {
  12   }
  13 }

Enable the plain_pass variable in the auth-passwdfile configuration.

 /etc/dovecot/conf.d/auth-passwdfile.conf.ext 

   1 passdb {
   2   driver = passwd-file
   3   args = username_format=%u /var/vmail/auth.d/%d/passwd
   4 }
   5 
   6 userdb {
   7   driver = passwd-file
   8   args = username_format=%u /var/vmail/auth.d/%d/passwd
   9   default_fields = plain_pass=%w
  10 }

This script will act on all users for a particular domain specified via the MIGRATE_DOMAIN variable.

 /var/vmail/conf.d/scripts/postlogin.sh 

   1 #!/bin/sh
   2 # Split out domain part from $USER user@domain
   3 MAIL_ALIAS=${USER%@*}
   4 MAIL_DOMAIN=${USER#*@}
   5 MIGRATE_DOMAIN="domain.tld"
   6 
   7 case "$MAIL_DOMAIN" in
   8         $MIGRATE_DOMAIN)
   9         DOVECOTPW=$(/usr/bin/doveadm pw -s SHA512-CRYPT -p "$PLAIN_PASS")
  10         echo "user: $USER" >> /tmp/log
  11         echo $DOVECOTPW >> /tmp/log
  12         ;;
  13 esac
  14 
  15 exec "$@"

Exemplary directory permissions (Setup is using vmail context for the users):

   1 $ l /var/vmail/conf.d/scripts/
   2 total 4
   3 -r-x------ 1 vmail vmail 322 Nov 23 09:58 postlogin.sh
   4 
   5 $  l /tmp/log
   6 -rw------- 1 vmail root 1160 Nov 23 10:27 /tmp/log

HowTo/ConvertPasswordSchemes (last edited 2018-12-09 20:31:10 by p54A9A6E3)