The scenario:
In a bash script, I have to check if a password given by a user is a valid user password.
I.e suppose I have a user A with password PA.. In the script I asked user A to enter his password, So how to check if the string entered is really his password?...
Since you want to do this in a shell script, a couple of contributions in How to check password with Linux? (on Unix.SE, suggested by A.B.) are especially relevant:
/etc/shadow
gives part of the solution.mkpasswd
command present in Debian (and Ubuntu).To manually check if a string is really some user's password, you must hash it with the same hash algorithm as in the user's shadow entry, with the same salt as in the user's shadow entry. Then it can be compared with the password hash stored there.
I've written a complete, working script demonstrating how to do this.
chkpass
, you can runchkpass user
and it will read a line from standard input and check if it'suser
's password.mkpasswd
utility on which this script depends.Security Notes
It might not be the right approach.
Whether or not an approach like this should be considered secure and otherwise appropriate depends on details about your use case that you haven't provided (as of this writing).
It has not been audited.
Although I've tried to exercise care while writing this script, it has not been properly audited for security vulnerabilities. It is intended as a demonstration, and would be "alpha" software if released as part of a project. Furthermore...
Another user who's "watching" may be able to discover the user's salt.
Due to limitations in how
mkpasswd
accepts salt data, this script contains a known security flaw, which you may or may not consider acceptable depending on use case. By default, users on Ubuntu and most other GNU/Linux systems can view information about processes run by other users (including root), including their command-line arguments. Neither the user's input nor the stored password hash is passed as a command-line argument to any external utility. But the salt, extracted from theshadow
database, is given as a command-line argument tomkpasswd
, since this is the only way that utility accepts a salt as input.If
www-data
) run their code, or/proc
)is able to check the command-line arguments to
mkpasswd
as it is run by this script, then they can obtain a copy of the the user's salt from theshadow
database. They might have to be able to guess when that command is run, but that is sometimes achievable.An attacker with your salt is not as bad as an attacker with your salt and hash, but it's not ideal. The salt doesn't provide enough information for someone to discover your password. But it does allow someone to generate rainbow tables or pre-computed dictionary hashes specific to that user on that system. This is initially worthless, but if your security is compromised at a later date and the full hash is obtained, it could then be cracked more quickly to obtain the user's password before they get a chance to change it.
Thus this security flaw is an exacerbating factor in a more complex attack scenario rather than a fully exploitable vulnerability. And you might consider the above situation far-fetched. But I am reluctant to recommend any method for general, real-world use that leaks any non-public data from
/etc/shadow
to a non-root user.You can avoid this problem completely by:
Be careful how you call this script.
If you allow an untrusted user to run this script as root or to run any process as root that calls this script, be careful. By changing the environment, they can make this script--or any script that runs as root--do anything. Unless you can prevent this from occurring, you must not allow users elevated privileges for running shell scripts.
See 10.4. Shell Scripting Languages (sh and csh Derivatives) in David A. Wheeler's Secure Programming for Linux and Unix HOWTO for more information on this. While his presentation focuses on setuid scripts, other mechanisms can fall prey to some of the same problems if they don't correctly sanitize the environment.
Other Notes
It supports reading hashes from the
shadow
database only.Passwords must be shadowed for this script to work (i.e., their hashes should be in a separate
/etc/shadow
file that only root can read, not in/etc/passwd
).This should always be the case in Ubuntu. In any case, if needed the script can be trivially extended to read password hashes from
passwd
as well asshadow
.Keep
IFS
in mind when modifying this script.I set
IFS=$
at the beginning, since the three data in the hash field of a shadow entry are separated by$
.$
, which is why the hash type and salt are"${ent[1]}"
and"${ent[2]}"
rather than"${ent[0]}"
and"${ent[1]}"
, respectively.The only places in this script where
$IFS
determines how the shell splits or combines words arewhen these data are split into an array, by initializing it from the unquoted
$(
)
command substitution in:when the array is reconstituted into a string to compare to the full field from
shadow
, the"${ent[*]}"
expression in:If you modify the script and have it perform word splitting (or word joining) in other situations, you'll need to set
IFS
to different values for different commands or different parts of the script.If you don't keep this in mind and assume
$IFS
is set to the usual whitespace ($' \t\n'
), you could end up making your script behave in some pretty weird-seeming ways.You can misuse
sudo
for this.sudo
has the-l
option, for testing the sudo privileges that the user has, and-S
for reading in the password from stdin. However, no matter what privilege level the user has, if successfully authenticated,sudo
returns with exit status 0. So, you can take any other exit status as indication that the authentication didn't work (assumingsudo
itself doesn't have any problems, like permission errors or invalidsudoers
configuration).Something like:
This script depends quite a bit on the
sudoers
configuration. I have assumed the default setup. Things that can cause it to fail:targetpw
orrunaspw
is setlistpw
isnever
Other problems include (thanks Eliah):
/var/log/auth.log
sudo
must be run as the user you're authenticating for. Unless you havesudo
privileges so that you can runsudo -u foo sudo -lS
, that means you'll have to run the script as the target user.Now, the reason I used here-docs is to hamper eavesdropping. A variable used as part of the command line is more easily revealed by using
top
orps
or other tools to inspect processes.Another method (probably more interesting for its theoretical contents than for its pratical applications).
User's passwords are stored in
/etc/shadow
.The passwords stored here are encrypted, in the latest Ubuntu releases using
SHA-512
.Specifically, upon the password creation, the password in clear text is salted and encrypted through
SHA-512
.One solution would be then to salt / encrypt the given password and match it against the encrypted user's password stored in the given user's
/etc/shadow
entry.To give a quick breakdown of how the passwords are stored in each user
/etc/shadow
entry, here's a sample/etc/shadow
entry for a userfoo
with passwordbar
:foo
: username6
: password's encryption typelWS1oJnmDlaXrx1F
: password's encryption salth4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/
:SHA-512
salted / encrypted passwordIn order to match the given password
bar
for the given userfoo
, the first thing to do is to get the salt:Then one should get the full salted / encrypted password:
Then the given password can be salted / encrypted and matched against the salted / encrypted user's password stored in
/etc/shadow
:They match! Everything put into a
bash
script:Output:
After some searching, I discovered an easy way to check the validity of a user's password using
su
. Here's a short script demonstrating. You can save it to a file, add executable permissions, and then invoke it using./pw_check.sh username
.Basically, it accepts a username as an argument, prompts for the password of that user, and then attempts to run the
true
command as that user usingsu
. We then check the return value of the attempt to run thetrue
command as the given user and use the return value to determine success/failure and thus the validity of the given password.For that I use the following program, which I created: