How to check password with Linux?

  • 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.

    Log in as the user with the password?

    The test must be done automatically, I can't manually type the password from the web server

  • 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 it 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 '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
    if [ "$supplied" = "$correct" ]; then …
    

    Since this can't be done in pure shell tools, if you have Perl available, you might as well do it all in Perl. (Or Python, Ruby, … whatever you have available that can call the crypt function.) Warning, untested code.

    #!/usr/bin/env perl
    use warnings;
    use strict;
    my @pwent = getpwnam($ARGV[0]);
    if ([email protected]) {die "Invalid username: $ARGV[0]\n";}
    my $supplied = <STDIN>;
    chomp($supplied);
    if (crypt($supplied, $pwent[1]) eq $pwent[1]) {
        exit(0);
    } else {
        print STDERR "Invalid password for $ARGV[0]\n";
        exit(1);
    }
    

    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. Remember to free the memory. */
        salt = strdup(correct);
        if (salt == NULL) return 2;
        p = strchr(salt + 1, '$');
        if (p == NULL) return 2;
        p = strchr(p + 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.

    I like the `su` approach.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM