Linux users password migration

If you have maintained a server for a while, you probably have some users aboard with passwords encrypted with an old and unsecre algorithm, or you might want to migrate from one authentication backend to another (e.g. from /etc/shadow to ldap or vice versa) and want to migrate the passwords without interacting with all your users.

Now, it might be reasonable to require your users to change their passwords from time to time, but that is another discussion. In this case, what you want, is to allow your users to keep their password, while still upgrading the hashing algorithm. But since you do not know their actual password, this means that you can’t just convert your old hashes to a new format, and probably not move them to a new backend very easily.

Fortunlately, PAM allows you to run arbitrary commands on login, and it even has a flag to send the provided password to this command. This means that on a high level, what we do is we make PAM execute a script. This script gets the username and password from PAM, and creates a new hash with the wanted algorithm, and saves the new hash to a file. You can then replace your existing hashes with the new ones, or you can use the new hash in your new auth backend when you migrate.

So let’s go through this step by step.

I made a simple bash-script, but if you want to do more advanced stuff, you can of course do this in whatever language you want. Especially if you want to create a full migration script, you might want to read out the old values for the home directories, the groups, maybe any existing password expiry values etc.



The script is probably well enough documented with the inline comments.

You should to a “touch /root/newshadow && chmod 000 /root/newshadow” and dont forget “chmod 700 /root/bin/” before you do anything more, to ensure that the files are unreadable for any other users.

Next, we need to run this for password-logins from PAM:


The magic line is

auth        optional debug log=/root/convpass.log expose_authtok /root/bin/

Now this is important!

The line needs to be added before the line doing the actual auth.
In my case, before:

auth        sufficient nullok try_first_pass

Also, it is important that the second column in the pam_exec line is “optional”. If you set this to e.g. “sufficent”, ANY login as ANY user to the server might succeed if you have any logic errors in your script and exit with a 0. This is not what you want. Trust me…

With this in place, any user that logs in using a password, no matter if they log in with ssh, pop/imap, smtp, ftp or whatever, will be run through the script, and after letting this stay for a while, you should have new password hashes for all your regular users safely stored.


While I was working on this, I needed a few other tools. First of all, I needed a way to validate the password. This ended up as a part of the script above, but here is also a stand alone version.

Both the two following scripts are sligthly modified versions of the ones found in the comments here:


This one actually validates the existing password of a user. It should be run as a normal user, not root. It requires “expect” to be installed.


Another option, if you want to validate an existing password hash, was this, which requires perl, and must be run as root if you are checking against the actual /etc/shadow



This can be run against any file with the same format as /etc/shadow, and will validate a password against the hash in the file. Files with another format will be left as an exercise for the reader…

Noe over gjennomsnittlig interessert. Kjentmann i IP- og nettverksjungelen, og jobber i nLogic AS.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store