Best mail encryption solution for per-user

Aki Tuomi aki.tuomi at dovecot.fi
Mon May 28 09:33:23 EEST 2018



On 27.05.2018 21:16, mail at sjemm.net wrote:
> May 27, 2018 8:52 AM, "Aki Tuomi" <aki.tuomi at dovecot.fi> wrote:
>>> On 26 May 2018 at 10:36 mail at sjemm.net wrote:
>>>
>>> May 23, 2018 10:10 AM, mail at sjemm.net wrote:
>>> May 23, 2018 9:46 AM, "Aki Tuomi" <aki.tuomi at dovecot.fi> wrote:
>>>
>>> On 23.05.2018 10:15, mail at sjemm.net wrote:
>>>
>>> May 23, 2018 8:31 AM, "Aki Tuomi" <aki.tuomi at dovecot.fi> wrote:
>>>
>>> On 23.05.2018 09:13, mail at sjemm.net wrote:
>>> May 20, 2018 8:01 PM, mail at sjemm.net wrote:
>>> May 20, 2018 2:47 PM, "Aki Tuomi" <aki.tuomi at dovecot.fi> wrote:
>>> On 19 May 2018 at 16:40 mail at sjemm.net wrote:
>>>
>>> May 18, 2018 10:01 PM, "Aki Tuomi" <aki.tuomi at dovecot.fi> wrote:
>>> On 18 May 2018 at 21:44 mail at sjemm.net wrote:
>>>
>>> May 18, 2018 4:43 PM, "Aki Tuomi" <aki.tuomi at dovecot.fi> wrote:
>>> On 18 May 2018 at 17:38 mail at sjemm.net wrote:
>>>
>>> May 18, 2018 4:05 PM, "Aki Tuomi" <aki.tuomi at dovecot.fi> wrote:
>>> On 18 May 2018 at 16:43 mail at sjemm.net wrote:
>>>
>>> Hi Tai74 and Aki,
>>> I followed your conversation with interest on how to setup per user encryption in dovecot.
>>> I have setup my dovecot with the following in a conf file:
>>>
>>> ==============
>>>
>>> mail_attribute_dict = file:%h/Maildir/dovecot-attributes
>>> mail_plugins = $mail_plugins mail_crypt
>>> plugin {
>>>
>>> mail_crypt_curve = secp521r1
>>>
>>> mail_crypt_save_version = 2
>>>
>>> }
>>>
>>> ==============
>>>
>>> This works nice, all emails are being encrypted and every user/folder has keys.
>>> But as I understood from your conversation these keys are not protected. And I want them to be
>>> protected by the users password used by imap.
>>>
>>> Those passwords are stored in a mysql DB file. ( I used a guide from workaround [dot] org to set up
>>> the DB and postfix/dovecot)
>>>
>>> but how would i set it so, that the users password from the DB is used to encrypt the keys?
>>>
>>> should i use mail_crypt_private_password = ?
>>> how do i point it to the mysql db then?
>>> im unsure about this
>>>
>>> Do you have any hints on this?
>>>
>>> Kind regards,
>>> Zjemm
>>>
>>> The passwords in your MySQL database are, hopefully, not in plaintext. If you want to secure your
>>> user's keys using user's login password, you must have a TOOL that manages this.
>>>
>>> You can use mail_crypt_private_password = %w in (mysql) passdb fields to provide the user's login
>>> password as private password. You might want to run it thru some hash, so %{sha1:password} might be
>>> a good option.
>>>
>>> You can change the key password using 'doveadm mailbox cryptokey', this needs to be done every time
>>> user changes his password.
>>>
>>> Also note that if you go down this road, and the user forgets his password, you will not be able to
>>> recover the emails without backup copy of the private key.
>>>
>>> Aki
>>>
>>> Hi Aki
>>>
>>> I used the following command:
>>> dovecot pw -s SHA256-CRYPT
>>>
>>> the output on the chosen password looks like: {SHA256-CRYPT}$5$Rokc06a7In4SF3bO$OQpGQWqg........
>>>
>>> This output is used to store in the password fields in the database. So no plain text passwords no
>>> :)
>>>
>>> You can use mail_crypt_private_password = %w in (mysql) passdb fields to provide the user's login
>>> password as private password.
>>>
>>> can you explain this a bit more for me?
>>>
>>> for now i have in the 10-auth.conf file the following:
>>> ==============
>>> passdb {
>>> driver = sql
>>>
>>> # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
>>> args = /etc/dovecot/dovecot-sql.conf.ext
>>> }
>>>
>>> and:
>>>
>>> userdb {
>>> driver = static
>>> args = uid=vmail gid=vmail home=/var/vmail/%d/%n
>>> }
>>> ==============
>>>
>>> then i have in dovecot-sql.conf.ext
>>> ==============
>>> driver = mysql
>>> connect = host=x.x.x.x dbname=mailserver user=mailuser password=mailpasswordexample
>>> default_pass_scheme = SHA256-CRYPT
>>> password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';
>>> ==============
>>> Where do i need to set : mail_crypt_private_password = %w ?
>>>
>>> password as private password. You might want to run it thru some hash, so %{sha1:password} might be
>>> a good option.
>>>
>>> the passwords are allready hashed in the DB using: dovecot pw -s SHA256-CRYPT to genereate the has.
>>> so this step isnt nesesary anymore am i right?
>>>
>>> Thank you for your quick response, very helpfull
>>>
>>> Zjemm
>>>
>>> You misunderstood a bit. The idea is to use the *plaintext* password as the password for the
>>> private key. Otherwise anyone could just decrypt it by looking at your database where the hashed
>>> password is..
>>>
>>> So:
>>>
>>> password_query = SELECT email as user, password, '%w' AS userdb_mail_crypt_private_password FROM
>>> virtual_users WHERE email='%u'
>>>
>>> Aki
>>>
>>> Hi Aki,
>>>
>>> Thank you very much for your help, i realy appreciate that.
>>>
>>> Ok so if i understand it correctly i'll have to use:
>>>
>>> password_query = SELECT email as user, password, '%w' AS userdb_mail_crypt_private_password FROM
>>> virtual_users WHERE email='%u'
>>>
>>> in my dovecot-sql.conf.ext file
>>>
>>> This query selects the user, the password, and %w
>>>
>>> if i run a little query myself:
>>> MariaDB [mailserver]> SELECT email as user, password, '%w' AS userdb_mail_crypt_private_password
>>> FROM virtual_users;
>>> +------------------+----------------------------------+------------------------------------+
>>> | user | password | userdb_mail_crypt_private_password |
>>> +------------------+----------------------------------+------------------------------------+
>>> | john at example.org | {SHA256-CRYPT}$5$M/GWzmtjsLroRWI | %w |
>>> +------------------+----------------------------------+------------------------------------+
>>>
>>> %w is a dovecot variable, and stands for the plaintext password, but the password is not stored as
>>> plaintext in the DB, %w get filled with the actual plaintext password by dovecot upon the user that
>>> is typing in the password when authenticating.
>>>
>>> is this correct?
>>>
>>> yes.
>>>
>>> so then i have the username the hashed password en the plaintext password as a result of the query.
>>>
>>> yes
>>>
>>> now userdb_mail_crypt_private_password = the plaintext password
>>> do i need to reference it somewhere? or is userdb_mail_crypt_private_password autmatically used by
>>> the dovecot mail_crypt plugin to encrypt the keys? or should it be mail_crypt_private_password?
>>>
>>> It gets injected into the mail process as 'mail_crypt_private_password', as if it was set in plugin
>>> {} section.
>>>
>>> if i have this setup working i'm going to write a blog post on this topic to share this knowledge
>>>
>>> Thanks again and have a great weekend.
>>>
>>> Zjemm
>>>
>>> Aki
>>>
>>> Hi Aki,
>>>
>>> Cool i'm testing it right now.
>>> I have set up a new mailserver (life is great with lxc containers :) )
>>>
>>> postfix and dovecot are working like normal
>>>
>>> next i enable mail_crypt
>>>
>>> i did create a file: /etc/dovecot/conf.d/10-mailcrypt.conf
>>> ==========================
>>> mail_attribute_dict = file:%h/Maildir/dovecot-attributes
>>>
>>> mail_plugins = $mail_plugins mail_crypt
>>>
>>> plugin {
>>> mail_crypt_curve = secp521r1
>>> mail_crypt_save_version = 2
>>> }
>>> ==========================
>>>
>>> and then i changed the file: /etc/dovecot/dovecot-sql.conf.ext
>>>
>>> so the query is now the new query:
>>> password_query = SELECT email as user, password, '%w' AS userdb_mail_crypt_private_password FROM
>>> virtual_users WHERE email='%u';
>>>
>>> then i restarted dovecot and postfix and send a test email to the one and only testuser that is in
>>> there.
>>>
>>> when i open the mailbox with the tool mutt, i can see the new email, and when openening the email
>>> the mutt client drops the connection.
>>>
>>> in the log i can see:
>>>
>>> May 19 13:34:48 mailserver1.example.local dovecot[600]: imap-login: Login: user=<john at example.org>,
>>> method=PLAIN, rip=::1, lip=::1, mpid=713, TLS, session=<E3PnIY9sNM4AAAAAAAAAAAAAAAAAAAAB>
>>> May 19 13:34:49 mailserver1.example.local dovecot[600]: imap(john at example.org): Error: read()
>>> failed:
>>> read(/var/vmail/example.org/john/Maildir/cur/1526736378.M161472P641.mailserver1.example.local,S=559,
>>> =571:2,) failed: Private key not available: Cannot decrypt key
>>> bfc5bb25b1bf64290eea6dc14b516c6a0a25b64551b6e4f0f8677ba7274887cb: error:03070068:bignum
>>> routines:BN_mpi2bn:encoding error (FETCH BODY[] for mailbox INBOX UID 8)
>>>
>>> i think i missed a step, but witch one?
>>>
>>> the userpassword hasnt been changed (that would be the next step in the testing process)
>>>
>>> should i've use doveadm first to encrypt the key with that userpassword? i thought it would do that
>>> on the fly, because the initial keys where only just created when enabling the mail_crypt plugin
>>>
>>> please let me know your thougts
>>> Zjemm
>>> I noticed you replied directly to me, and not to the list, too... fixed that for you.
>>>
>>> mail_crypt_private_password is used when key is created, but if you have created it before using
>>> password, you'll need to encrypt it before turning the setting on.
>>>
>>> Aki
>>> Hi, Ok i'm sorry about that, thank you for fixing that. i'm a bit new to mailing lists :)
>>>
>>> are you a dovecot devoloper? or a enthusiastic user?
>>>
>>> anyway, i managed to get it working now with all your help :)
>>>
>>> to start fresh i did a:
>>> rm -rf /var/vmail/example.org
>>>
>>> so all email is gone, and i assume all dovecot keys are gone that way.
>>>
>>> Then i tried to set a password first:
>>>
>>> root at mailserver1:/var/vmail# doveadm mailbox cryptokey password -u john at example.org -n summersun
>>> result: Changed password for 0 key(s)
>>>
>>> (summersun = the password for this user)
>>>
>>> but then it says there are 0 keys, so then i send the test user an email and try to open the
>>> mailbox, i'll get:
>>>
>>> imap(john at example.org): Error: read() failed:
>>> read(/var/vmail/example.org/john/Maildir/cur/1526827992.M149712P883.mailserver1.example.local,S=549,
>>> =561:2,) failed: Private key not available: Cannot decrypt key
>>> 71849013a70b0c631c06112077e7c2fe39b0b2737b4933b219793841209d4e7f: error:03070068:bignum
>>> routines:BN_mpi2bn:encoding error
>>>
>>> but hey, now the key's are created, so i tried to set the password again:
>>>
>>> root at mailserver1:/var/vmail# doveadm mailbox cryptokey password -u john at example.org -n summersun
>>> result: dcrypt_key_load_private(787701bd677dd69a26842547a3926cbaa625b0b5a91751f06678c3e9708343d9)
>>> failed: password missing
>>> =========
>>>
>>> Lets start over again, i did a new:
>>> rm -rf /var/vmail/example.org
>>>
>>> i disabled the mail_crypt_private_password in the sql query and send an email to the test user.
>>> now again new keys are generated.
>>>
>>> then i did set the password via doveadm:
>>>
>>> root at mailserver1:/var/vmail# doveadm mailbox cryptokey password -u john at example.org -n summersun
>>> result: Changed password for 1 key(s)
>>>
>>> i enabled the mail_crypt_private_password again in the sql query part and now it works :)
>>>
>>> YESSS!!!
>>>
>>> if i now change the password for the user, i cannot read the email anymore. that is what should
>>> happen.
>>> so now i changed the password for this user also with doveadm:
>>> - doveadm mailbox cryptokey password -u john at example.org -n qwerty -o summersun
>>>
>>> and now i'm able to read the emails again. so it all works now as expected.
>>>
>>> i have now only one thing remaining.
>>>
>>> when i want to add a new user, i have to somehow:
>>> - create the new user in the mysql DB
>>> - start with dovecot not using mail_crypt_private_password (so disabled)
>>> - send an initial email to let the users mailbox be created and generate the keys
>>> - set the users password with doveadm
>>> - restart dovecot again with mail_crypt_private_password (so enabled)
>>>
>>> but this would be disruptive to existing users, and i would have to script the whole thing as a
>>> "add-new-user" script to make it usable i guess.
>>>
>>> do you know of a more easy way of acomplishing this?
>>> could i use doveadm for all steps, and get around the mail_crypt_private_password disable part?
>>>
>>> or could i add a column in the mysql db called userdb_mail_crypt_private_password and fill that
>>> column with %w for users that allready exist, and empty for new users.
>>> when being empty, it might not set the mail_crypt_private_password for new users
>>>
>>> or is this the wrong way of thinking?
>>>
>>> let me know your thoughts on this.
>>>
>>> And last but not least, i really want to thank you for all your help. It is a really nice
>>> experience.
>>> Bless you!
>>>
>>> Zjemm
>>> Hi Aki,
>>>
>>> You did fix the posting on the mail archive, i can see our conversation on there, all but the last
>>> email i send to you.
>>> Maybe you did not received the last email?
>>>
>>> would you be willing to take a look at my last question in the email See below:
>>> Thank you very much.
>>>
>>> ================================
>>> i have now only one thing remaining.
>>>
>>> when i want to add a new user, i have to somehow:
>>> - create the new user in the mysql DB
>>> - start with dovecot not using mail_crypt_private_password (so disabled)
>>> - send an initial email to let the users mailbox be created and generate the keys
>>> - set the users password with doveadm
>>> - restart dovecot again with mail_crypt_private_password (so enabled)
>>>
>>> but this would be disruptive to existing users, and i would have to script the whole thing as a
>>> "add-new-user" script to make it usable i guess.
>>>
>>> do you know of a more easy way of acomplishing this?
>>> could i use doveadm for all steps, and get around the mail_crypt_private_password disable part?
>>>
>>> or could i add a column in the mysql db called userdb_mail_crypt_private_password and fill that
>>> column with %w for users that allready exist, and empty for new users.
>>> when being empty, it might not set the mail_crypt_private_password for new users
>>>
>>> or is this the wrong way of thinking?
>>>
>>> let me know your thoughts on this.
>>>
>>> And last but not least, i really want to thank you for all your help. It is a really nice
>>> experience.
>>> Bless you!
>>>
>>> Zjemm
>>>
>>> If you have mail_crypt_private_password set *when* keys are generated,
>>> dovecot should use that password to encrypt the user key.
>>>
>>> Aki
>>> Hi Aki,
>>>
>>> well, if there is a new email user setup in the mysql DB, and no email has been send to that new
>>> user. at that point there are no keys.
>>>
>>> So trying to set a password on those key's wont work. see:
>>> root at mailserver1:/var/vmail/example.org# doveadm mailbox cryptokey password -u john2 at example.org -n
>>> qwerty
>>> result: Changed password for 0 key(s)
>>>
>>> the keys need to be generated first. And that only happens when sending a mail to that new user.
>>> Also, at the point of creating the keys, the mail_crypt_private_password should be disabled
>>> otherwise the keys are generated and also encrypted with no user password, and then im unable to
>>> set a new pasword for that key
>>>
>>> How can i set the password before the keys are generated?
>>> or can i manually generate key's and then set the password?
>>>
>>> so at this point i have to do the following:
>>>
>>> You can use doveadm mailbox cryptokey generate -u user -U
>>>
>>> to generate a user key.
>>>
>>> Aki
>>>
>>> - create the new user in the mysql DB
>>> - start with dovecot not using mail_crypt_private_password (so disabled)
>>> - send an initial email to let the users mailbox be created and generate the keys
>>> - set the users password with doveadm
>>> - restart dovecot again with mail_crypt_private_password (so enabled)
>>> can i somehow enable the mail_crypt_private_password per user?
>>> because this mysql query always returns: %w
>>> and thus allways sets: userdb_mail_crypt_private_password
>>> SELECT email as user, password, '%w' AS userdb_mail_crypt_private_password FROM virtual_users;
>>>
>>> Hi Aki,
>>>
>>> You are correct
>>>
>>> doveadm mailbox cryptokey generate -u user -U generates the keys, but i have to first remove:
>>> '%w' AS userdb_mail_crypt_private_password
>>> from dovecot configuration and restart dovecot.
>>>
>>> if i don't do that, the keys are generated, but also somehow encrypted using: no
>>> password/empty/something else (i don't know)
>>>
>>> here you can see that happening:
>>>
>>> root at mailserver1:/var/vmail/example.org# doveadm mailbox cryptokey generate -u john2 at example.org -U
>>> Folder Public ID
>>> ✓ <userkey> 53004acf9886f887ca081c83d7392a5aa3fac4b5a20ce013db91a341fb4644c4
>>> root at mailserver1:/var/vmail/example.org# doveadm mailbox cryptokey password -u john2 at example.org -n
>>> qwerty
>>> result: dcrypt_key_load_private(53004acf9886f887ca081c83d7392a5aa3fac4b5a20ce013db91a341fb4644c4)
>>> failed: password missing
>>>
>>> it expects an old password wich is never set in my opinion.
>>>
>>> so the last part of this setup would be to figure out a way to disable '%w' AS
>>> userdb_mail_crypt_private_password for new users without manually removing that part from the query
>>> and restarting dovecot. As that would mean connection interruption while adding new users
>>>
>>> if we could add new users to the DB and somehow not give '%w' AS userdb_mail_crypt_private_password
>>> for that new user in the query, we can add the password afterward, and then enable '%w' AS
>>> userdb_mail_crypt_private_password in the query for this user
>>>
>>> could we add a column in the user db lets say called: encrypted (yes/no)
>>>
>>> and then change the existing query: SELECT email as user, password, '%w' AS
>>> userdb_mail_crypt_private_password FROM virtual_users;
>>>
>>> to firrt check if encryption is set to 'yes', if so retrun:
>>> SELECT email as user, password, '%w' AS userdb_mail_crypt_private_password FROM virtual_users;
>>>
>>> if not return:
>>> SELECT email as user, password FROM virtual_users;
>>>
>>> but then in one mysql query so dovecot can work with it
>>>
>>> ?
>>>
>>> Hi Aki,
>>>
>>> would you be willing to respond to my last question i had send to you last week?
>>> its this one:
>>> ==================================
>>> doveadm mailbox cryptokey generate -u user -U generates the keys, but i have to first remove:
>>> '%w' AS userdb_mail_crypt_private_password
>>> from dovecot configuration and restart dovecot.
>>>
>>> if i don't do that, the keys are generated, but also somehow encrypted using: no
>>> password/empty/something else (i don't know)
>>>
>>> here you can see that happening:
>>>
>>> root at mailserver1:/var/vmail/example.org# doveadm mailbox cryptokey generate -u john2 at example.org -U
>>> Folder Public ID
>>> ✓ <userkey> 53004acf9886f887ca081c83d7392a5aa3fac4b5a20ce013db91a341fb4644c4
>>> root at mailserver1:/var/vmail/example.org# doveadm mailbox cryptokey password -u john2 at example.org -n
>>> qwerty
>>> result: dcrypt_key_load_private(53004acf9886f887ca081c83d7392a5aa3fac4b5a20ce013db91a341fb4644c4)
>>> failed: password missing
>>>
>>> it expects an old password wich is never set in my opinion.
>>>
>>> could this be a bug? or am i doing it wrong? if needed i could send in a bug report?
>>>
>>> or might we need to enable the userdb_mail_crypt_private_password "per user" ?
>>>
>>> so the last part of this setup would be to figure out a way to disable '%w' AS
>>> userdb_mail_crypt_private_password for new users without manually removing that part from the query
>>> and restarting dovecot. As that would mean connection interruption while adding new users
>>>
>>> if we could add new users to the DB and somehow not give '%w' AS userdb_mail_crypt_private_password
>>> for that new user in the query, we can add the password afterward, and then enable '%w' AS
>>> userdb_mail_crypt_private_password in the query for this user
>>>
>>> could we add a column in the user db lets say called: encrypted (yes/no)
>>>
>>> and then change the existing query: SELECT email as user, password, '%w' AS
>>> userdb_mail_crypt_private_password FROM virtual_users;
>>>
>>> to firrt check if encryption is set to 'yes', if so retrun:
>>> SELECT email as user, password, '%w' AS userdb_mail_crypt_private_password FROM virtual_users;
>>>
>>> if not return:
>>> SELECT email as user, password FROM virtual_users;
>>>
>>> but then in one mysql query so dovecot can work with it
>>>
>>> Please let me know your thougts, after this last thing im done :)
>>> also let me know if i can support this project in some way, im willing to contribute
>>>
>>> Zjemm
>> You can select it as userdb_mail_crypt_save_version=0 to disable encryption (it won't disable
>> decryption). You can use value '2' for enabling encryption. DO NOT USE 1.
>>
>> Also I tried the SQL based thing, and if you had EMPTY mail_crypt_private_password, try these to
>> change the password. If the one that works is the second one, let me know =)
>>
>> doveadm mailbox cryptokey password -u testuser -nnewpass -o ""
>>
>> doveadm mailbox cryptokey password -u testuser -nnewpass -o "yes"
>>
>> Aki
> Hi Aki,
>
> Thanks for replying
>
> So if i understand you correctly i could enable encryption per user by using: userdb_mail_crypt_save_version= in the database as an extra column. the column would be called "userdb_mail_crypt_save_version" and the value is "0" or "2"
>
> if a user has: userdb_mail_crypt_save_version=0, encryption is disabled for that user
> if a user has: userdb_mail_crypt_save_version=2, encryption is enabled for that user
>
> would this also override the setting: mail_crypt_save_version = 2
> in the mailcryp.conf file where that is a global setting? or should i delete it there and only use userdb_mail_crypt_save_version= from the database?
>
> for the password set command i hve testest some things:
>
> new user with no keys yet:
> doveadm -o plugin/mail_crypt_private_password=password mailbox cryptokey generate -u john2 at example.org -U
>
> i can set a password with:
> new password:
> doveadm mailbox cryptokey password -u john2 at example.org -n newpass -o password
>
>
> if i do not use new user doveadm: (doveadm -o plugin/mail_crypt_private_password=password mailbox cryptokey generate -u john2 at example.org -U)
> then use this:
> doveadm mailbox cryptokey password -u john2 at example.org -n newpass -o "yes"
>
>
> so yes it is the second one
>
> Kind regards,
>
> Zjemm

It's unfortunate problem with 2.2 that it defaults empty userdb
variables into yes. This has been remedied in 2.3.

Unfortunately there is no clean fix to this, they all resort to various
levels of trickery. Variables set in passdb/userdb overwrite the ones in
plugin {} section.

Aki



More information about the dovecot mailing list