Having an Additional Password Just for Email
On my server I usually use PAM for services that I only want to provide for internal users because then you only need to manage one password. That doesn't only mean that you have to remember only one password but also that if you change your password, it's changed everywhere. Now there are situations where such a single sign on principle might become a problem. For example if you want to have your emails on your smartphone or check your email in an internet café when travelling the world, but don't want an attacker to be able to have access to all your data if the phone gets stolen or hacked or the internet café computer is infected with a malware.
That's why I have set up my server to allow users to set a second password just for using email in environments that might not be safe. The system password will continue to work as usual. For my mail server I use Postfix and Dovecot. Postfix is only used as an MTA while Dovecot does all the heavy lifting: authenticating remote users for Postfix via SASL, delivering mail to the actual inbox while applying Sieve filters to them and of course providing the emails to end users via IMAP. Because Dovecot does the authentication for Postfix too, I only had to care about Dovecot in order to have the authentication working for IMAP and SMTP.
First I tried using a checkpassword
script but that proved to be pretty complicated because although there is an excellent blog post describing how to do authentication via a checkpassword
script. If you want to adapt to custom interfaces, this could be the way to go unless you want to go all the way to provide a custom key value interface to Dovecot, but for my task it was a bit too sophisticated. I also looked at doing the additional authentication in PAM using the libpam-pwdfile
module, but as I have never worked with PAM before that also posed too big a challenge.
What I did instead was using the passwd-file
module from Dovecot which, lo and behold, allows to authenticate users against a file similar to /etc/passwd
. So I created a mailpasswd file and made it only readable for dovecot:
sudo touch /etc/mailpasswd sudo chown mail:dovecot /etc/mailpasswd sudo chmod 640 /etc/mailpasswd
Then adjusted Dovecots config to read that file. On Debian that's as simple as changing the passdb[args]
parameter in /etc/dovecot/conf.d/auth-passwdfile.conf.ext
to
args = scheme=SHA512-CRYPT username_format=%u /etc/mailpasswd
commenting out the userdb
section in that file (all information that would need to be looked up from a userdb Dovecot can already retrieve via the usual means for system users) and telling Dovecot to use that config file by uncommenting the corresponding line at the bottom of /etc/dovecot/conf.d/10-auth.conf
. Now tell Dovecot to reload its config files by running sudo service dovecot reload
and there you go, dovecot is reading from that file.
Problem is: that file is still empty. So I needed a possibility for my users to set their mail password. Unfortunately passwd
is kind of single-minded and will only set the password on /etc/passwd
or other sources configured via nsswitch.conf
, but even then it's a bit picky and only wants to set password on certain services. So I wrote my own variant /usr/local/bin/mailpasswd
:
#!/bin/sh set -e /usr/bin/sudo -u mail /usr/local/lib/mailpasswd-helper
And /usr/local/lib/mailpasswd-helper
:
#!/bin/sh set -e PWFILE="/etc/mailpasswd" user="$SUDO_USER" if [ -z "$user" ]; then echo "ERROR: please use the mailpasswd command" >&2 exit 1 fi echo "Changing mail password for $user." pw="$(doveadm pw -s SHA512-CRYPT)" if echo | doveadm pw -t "$pw" > /dev/null 2>&1; then echo "Empty password provided. Password not changed" >&2 exit 3 fi tmp="$(grep -v "$user:" "$PWFILE" || true; echo "$user:$pw")" echo "$tmp" > "$PWFILE" echo "Email password successfully changed" exit 0
The first script is just a wrapper so users don't have to remember to call the second script with sudo
(because you can't make scripts setuid for security reasons). So the only thing left is actually allowing all users to call that script by using sudo visudo -f /etc/sudoers.d/mailpasswd
to create a sudoers config with the following contents (if you use an older Debian or another distro it may be that the /etc/sudoers.d/
doesn't exist, then you can just add the lines to the main /etc/sudoers
file):
# Allow all users to change their mail password via mailpasswd
ALL ALL=(mail) NOPASSWD:NOSETENV: /usr/local/lib/mailpasswd-helper
That's it. The mailpasswd-helper
is run as user mail
which can edit the /etc/mailpasswd
file. It checks whether it was properly called via sudo
and reads the user name of the user who called it via sudo
by reading it from the $SUDO_USER
variable. This is to prevent users from changing other users passwords. If a user calls the script by other means and manually sets the $SUDO_USER
variable he either won't have permissions to change the /etc/mailpasswd
file or you gave him permission to execute the script by other means with elevated privileges (e.g. because he may run any command as root) then he could also have called editor /etc/mailpasswd
with those privileges.
Then the script prompts the user to enter the new password two times by using the doveadm pw
command (yay, Dovecot rules!) and because doveadm pw
allows for empty passwords it just checks that case by testing the empty password against the authentication token. If you want to enforce any special password requirements (numbers, special characters etc.) then you probably have to prompt the user yourself, and check those conditions on the input. When the check is successful the script extracts all existing lines except for the one for the user from the mailpasswd
file and then replaces it's contents with those lines plus the new line (using sed --in-place
would probably have been nicer, especially I don't know if there are limitations for variable sizes when using large mailpasswd
files).
At the end you should check if the authentication and user lookup works by executing
sudo doveadm auth $user sudo doveadm user $user
TODO: allow users to delete their mailpasswd password instead of changing it. Maybe by using the empty password case.
- Zum Verfassen von Kommentaren bitte Anmelden.