Understanding and generating the hash stored in /etc/shadow
written on Wednesday, March 14, 2012
The /etc/shadow file stores user passwords as hashes in a particular format. If you ever want to verify users passwords against this hash in a non standard way, like from a web app for example, then you need to understand how it works.
Each row in /etc/shadow is a string with 9 fields separated by ':'. A typical line looks like this:
The nine different fields are:
- The local username
- The password hash, more on this later
- Number of days since the start of unix time (01/01/1970) that the password was last changed
- Minimum number of days before the password can be changed
- Maximum number of days before the password must be changed. 99999 means that the user will not be forced to change their password
- Number of days before forcing the password change that the user will be warned.
- The number of days after expiration that the account will be disabled
- Days since the start of unix time that the account has been disabled
- Currently unused but reserved for future use
Most of those fields are generally unused by Linux distros. The important ones are the username and hash. The hash field itself is comprised of three different fields. They are separated by '$' and represent:
- Some characters which represents the cryptographic hashing mechanism used to generate the actual hash
- A randomly generated salt to safeguard against rainbow table attacks
- The hash which results from joining the users password with the stored salt and running it through the hashing mechanism specified in the first field
So that first field before the salt and actual hash can have a finite set of possible values. The standard methods supported by GNU/Linux are:
- Blowfish, with correct handling of 8 bit characters
In practice you shouldn't use anything but sha512. The mkpasswd command will create the hash string for you and can be used by other programs to check an existing hash. Given the password hash above, if you wanted to check if a given password matched it you would run the following command:
mkpasswd --method=sha512 --salt=vb1tLY1qiY PASSWORD
The equivalent using python looks like this:
import crypt crypt.crypt('PASSWORD', '$6$vb1tLY1qiY')
Both will return an exact copy of the hash. for the given salt and password. Notice how using the Python library you actually pass in the method as part of the salt string. A salt string starting with '$5$' would use sha256 for example.
You can then compare this hash with the hash in /etc/shadow, or anywhere else that they might be stored. Like in a central datbase if you are using NSS.
If you don't provide mkpasswd with a salt it will automatically generate a random salt. This is usually what you want to do if you are creating a password for the first time.