Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I want to check, from the linux command line, if a given cleartext password is the same of a crypted password on a /etc/shadow

(I need this to authenticate web users. I'm running an embedded linux.)

I have access to the /etc/shadow file itself.

share|improve this question
    
Log in as the user with the password? –  Kusalananda Sep 29 '11 at 14:39
    
The test must be done automatically, I can't manually type the password from the web server –  michelemarcon Sep 29 '11 at 14:43
add comment

4 Answers 4

up vote 7 down vote accepted

You can easily extract the encrypted password with awk. You then need to extract the prefix $algorithm$salt$ (assuming that this system isn't using the traditional DES, which is strongly deprecated because can be brute-forced these days).

correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}

For password checking, the underlying C function is crypt, but there's no standard shell command to access it.

On the command line, you can use a Perl one-liner to invoke crypt on the password.

supplied=$(echo "$password" |
           perl -e '$_ = <>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …

On an embedded system without Perl, I'd use a small, dedicated C program. Warning, typed directly into the browser, I haven't even tried to compile. This is meant to illustrate the necessary steps, not as a robust implementation!

/* Usage: echo password | check_password username */
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <shadow.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
    char password[100];
    struct spwd shadow_entry;
    char *p, *correct, *supplied, *salt;
    if (argc < 2) return 2;
    /* Read the password from stdin */
    p = fgets(password, sizeof(password), stdin);
    if (p == NULL) return 2;
    *p = 0;
    /* Read the correct hash from the shadow entry */
    shadow_entry = getspnam(username);
    if (shadow_entry == NULL) return 1;
    correct = shadow_entry->sp_pwdp;
    /* Extract the salt */
    salt = strdup(correct);
    if (salt == NULL) return 2;
    p = strchr(salt + 1, '$');
    if (p == NULL) return 2;
    p = strchr(salt + 1, '$');
    if (p == NULL) return 2;
    p[1] = 0;
    /*Encrypt the supplied password with the salt and compare the results*/
    supplied = crypt(password, salt);
    if (supplied == NULL) return 2;
    return !!strcmp(supplied, correct);
}

A different approach is to use an existing program such as su or login. In fact, if you can, it would be ideal to arrange for the web application to perform whatever it needs via su -c somecommand username. The difficulty here is to feed the password to su; this requires a terminal. The usual tool to emulate a terminal is expect, but it's a big dependency for an embedded system. Also, while su is in BusyBox, it's often omitted because many of its uses require the BusyBox binary to be setuid root. Still, if you can do it, this is the most robust approach from a security point of view.

share|improve this answer
add comment

Have a look at man 5 shadow and man 3 crypt. From the latter, you can learn that password hashes in /etc/shadow have the following form:

 $id$salt$encrypted

where id defines the type of encryption and, reading further, can be one of

          ID  | Method
          ---------------------------------------------------------
          1   | MD5
          2a  | Blowfish (not in mainline glibc; added in some
              | Linux distributions)
          5   | SHA-256 (since glibc 2.7)
          6   | SHA-512 (since glibc 2.7)

Depending on the type of hash, you need to use the appropriate function/tool for generating and verifying the password "by hand". If the system contains mkpasswd program, you can use it as suggested here. (You take the salt from the shadow file, if that wasn't obvious.) For example, with md5 passwords :

 mkpasswd -5 <the_salt> <the_password>

will generate the string that should match /etc/shadow entry.

share|improve this answer
    
On my Debian wheezy I had a completely different syntax for the command mkpasswd, which I had to install using apt-get install whois. The command line for the shadow line <user>:$6$<salt>$<pwd>: was mkpasswd -msha-512 <password> <salt> –  Daniel Alder Jul 9 at 9:34
add comment

Bear in mind that, assuming the system is properly configured, the program will need to be run as root.

A better solution than reading the shadow file directly and writing your own code around crypt would be to just use the pam bindings.

The squid tarball used to come with a simple CLI tool for verifying usernames/passwords using stdio - so simple to adapt to using arguments - although the version I hacked previously was hardly a pin-up poster for structured programming. A quick google and it looks like the more recent versions have been cleaned up significantly but still a few 'goto's in there.

share|improve this answer
add comment

There was a similar question asked on Stack Overflow. cluelessCoder provided a script using expect, which you may or may not have on your embedded system.

#!/bin/bash
#
# login.sh $USERNAME $PASSWORD

#this script doesn't work if it is run as root, since then we don't have to specify a pw for 'su'
if [ $(id -u) -eq 0 ]; then
        echo "This script can't be run as root." 1>&2
        exit 1
fi

if [ ! $# -eq 2 ]; then
        echo "Wrong Number of Arguments (expected 2, got $#)" 1>&2
        exit 1
fi

USERNAME=$1
PASSWORD=$2

#since we use expect inside a bash-script, we have to escape tcl-$.
expect << EOF
spawn su $USERNAME -c "exit" 
expect "Password:"
send "$PASSWORD\r"
#expect eof

set wait_result  [wait]

# check if it is an OS error or a return code from our command
#   index 2 should be -1 for OS erro, 0 for command return code
if {[lindex \$wait_result 2] == 0} {
        exit [lindex \$wait_result 3]
} 
else {
        exit 1 
}
EOF
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.