Using Salted SHA Hashes With Dovecot Authentication

| Comments

I’ve finally figured out how to do this. The Dovecot documentation is absolutely horrible which is sad because it should be encouraged to always salt your password hashes yet they make it very difficult to figure out.

Dovecot is a IMAP and POP3 plugin for Postfix. Postfix doesn’t do authentication on its own so it’s often found that SASL authentication gets passed to Dovecot for both IMAP mail checking and creating dynamic relay lists.

Goal

We want to get Dovecot authentication to use salted SHA password hashes. The password hashes are stored in a mysql database and were created using ruby, python, or php.

You should never be saving user passwords. It’s better to only store the hash of a user’s passwords. But don’t stop there, add some complexity to the hash. By salting the password hash it significantly complicates the cracking process. It’s also best practices to have a unique salt for every password hash.

Format of the hash

The Dovecot documentation says the password hash must be in the following format when presented to Dovecot for comparing. (It can be in base64 or hex.)

{SSHA256.hex}4a847fefc4f9ab450f16783c5025d64313942a1ceb2599707cdb65940ba901e513fa442f

This is a string with the following in it:

  • {SSHA256} This is the beginning of the string which tells Dovecot what kind of hash this is. In this case ‘SSH256’ means Salted SHA 256 bit.
  • 4a847fefc4f9ab450f16783c5025d64313942a1ceb2599707cdb65940ba901e5 is the salted hash. Dovecot knows only that portion is the salted hash because a SHA256 hash will be 32 bytes long.
  • 13fa442f is the salt. Dovecot knows this is the salt because it assumes everything after the hash will be the salt because we indicated “SSHA” which is a salted SHA hash.

You can actually verify this against Dovecot with the following command line:

$ doveadm pw -t {SSHA256.hex}4a847fefc4f9ab450f16783c5025d64313942a1ceb2599707cdb65940ba901e513fa442f -p pass
{SSHA256.hex}4a847fefc4f9ab450f16783c5025d64313942a1ceb2599707cdb65940ba901e513fa442f (verified)

The command above asks the Dovecot authentication compare program to check the hash given against the password “pass”. The result is verified which means this password matches the hash.

Create the hash

The are many ways to create the hash. You can use php, python, ruby, bash, or even mysql itself to hash the password. In the cases below we are creating a base64 hash.

Ruby

Using ruby or ruby on rails we can create the hash this way:

salt = SecureRandom.hex(32) # Creates a 64 byte random hex string 
hash = Base64.strict_encode64(Digest::SHA512.digest("password"+salt) + salt)

You can test this code using the rails console.

Python

Using python or any of the python web frameworks like django we can create the hash this way:

import base64
import hashlib
import os

salt = os.urandom(64) # Creates a random 64 byte string
shahash = hashlib.sha512()
shahash.update("password")
shahash.update(salt)

shahashsalt = '{}{}'.format(shahash.digest(), salt)

hash = base64.b64encode(hashsalt)

You can test this code from the python console. Thanks to this gist for helping me out with this.

PHP

This stackoverflow question goes over how to create the hash in PHP. I have not tested this one though.

Test the hash using Dovecot

Suppose the following:
Our salt is: 2fec1ee0940e7c436ef2037e89e4c06ca20b281a90dbb2d6cbd3534aa4ce7e19
Our password is: test

This would result in the following salted hash:

4a1e8a61780f449ef6cbc883b5cf57279d32fd004cb7298ddb6f8c46bf246187c03f1bf9447044708767a826e65f977e5c95a490abf8f2c3ca90c7a0ea2b89e8

And when concatenating the salt to the end and converting to base64 we’d get this:

Sh6KYXgPRJ72y8iDtc9XJ50y/QBMtymN22+MRr8kYYfAPxv5RHBEcIdnqCbmX5d+XJWkkKv48sPKkMeg6iuJ6DJmZWMxZWUwOTQwZTdjNDM2ZWYyMDM3ZTg5ZTRjMDZjYTIwYjI4MWE5MGRiYjJkNmNiZDM1MzRhYTRjZTdlMTk=

We can test this string using the doveadm pw command from the linux command line (provided Dovecot is installed):

$ doveadm pw -t {SSHA512}Sh6KYXgPRJ72y8iDtc9XJ50y/QBMtymN22+MRr8kYYfAPxv5RHBEcIdnqCbmX5d+XJWkkKv48sPKkMeg6iuJ6DJmZWMxZWUwOTQwZTdjNDM2ZWYyMDM3ZTg5ZTRjMDZjYTIwYjI4MWE5MGRiYjJkNmNiZDM1MzRhYTRjZTdlMTk= -p test
{SSHA512}Sh6KYXgPRJ72y8iDtc9XJ50y/QBMtymN22+MRr8kYYfAPxv5RHBEcIdnqCbmX5d+XJWkkKv48sPKkMeg6iuJ6DJmZWMxZWUwOTQwZTdjNDM2ZWYyMDM3ZTg5ZTRjMDZjYTIwYjI4MWE5MGRiYjJkNmNiZDM1MzRhYTRjZTdlMTk= (verified)

The -t indicates we are going to give the hash and the -p indicates which password to use for hashing. The hash we give indicates this is a Salted SHA512 and is long enough to indicate the salted hash with the salt appended to the end of the string. So doveadm will take the first 64 bytes of the hash off and the rest of the bytes are the salt. It will then hash the word “test” with SHA512 using the salt provided and then test that against the hash provided. The result is verified which means the hashes match.

Configure Dovecot

Suppose the username and hashed password is stored in the mysql table “users”. Let’s now configure Dovecot to get that information for it’s authentication.

First make sure Dovecot is handling the authentication by configuring Postfix. Add the lines to /etc/postfix/main.conf:

smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

Edit the /etc/dovecot/conf.d/10-auth.conf file and make sure the following are in it:

auth_mechanisms = plain login
!include auth-sql.conf.ext

Edit the /etc/dovecot/conf.d/auth-sql.conf.ext file and put the following in it:

passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}

Edit the /etc/dovecot/dovecot-sql.conf.ext file and put the following in it:

driver = mysql

connect = host=127.0.0.1 dbname=mydatabase user=sqluser password=sqluserpassword

default_pass_scheme = SSHA512

password_query = SELECT username as user, CONCAT("{SSHA512}", password_hash) as password FROM users WHERE username='%n';

You’ll have to adjust the connect and password_query parameters to be what your database requires for the properly mysql authentication and lookups. Take note here that Dovecot expects this command to return a user and password column in the result. So if your mysql columns don’t say that, use the as mysql command to fix that. Also note that we are adding {SSHA512} to the front of the password because this is what Dovecot wants.

Once that is done restart Dovecot by doing sudo services dovecot restart.

Verify/Troubleshoot

At this point you should be able to authenticate to Dovecot with your password which is stored using a salted hash algorithm.

If this isn’t working for you then check /var/log/mail.log to see if there are any errors. Turn on additional debugging by editing the file /etc/dovecot/conf.d/10-logging.conf and add/uncomment this line:

auth_debug_passwords = yes

Now when you check mail.log you may see the following:

auth-worker(27083): sql(testuser,1.1.1.1): Password mismatch
auth-worker(27083): Debug: sql(testuser,1.1.1.1): SSHA512.HEX(test) != '3b98564e1ae8ec5d70cb7b9ea457e1ad833628da9f37211e73d1dc80b8c4c271d65de63a5e4fdde4e0c62cc13995619a3ba22441a892c06f4395d5f46e3fe3df5db034e27bda3045ef9e6057eb4799ea9c05d35c6a3b361697b20689ce44b16d5db034e27bda3045ef9e6057eb4799ea9c05d35c6a3b361697b20689ce44b16d'

This will show you the hash that’s being checked and the hashing type Dovecot is trying. You can test this manually by using the doveadm pw command covered above in the Test section of this page.

My biggest problem was creating a salted hash the correct way. Verify that the hash you’re creating works against doveadm pw before adding the Dovecot configuration.

dovecot, misc, postfix, rails, ruby

Comments