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.
9 Answers 9
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).
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 …
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 (!@pwent) my $supplied = ; chomp($supplied); if (crypt($supplied, $pwent[1]) eq $pwent[1]) < exit(0); >else
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 #include #include #include #include #include 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.
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:
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 :
will generate the string that should match /etc/shadow entry.
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 :$6$$: was mkpasswd -msha-512
#!/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 < exit [lindex \$wait_result 3] >else < exit 1 >EOF
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.
The C code of the person I’m responding to has bugs. Never understand why people publish code here without checking it works first, because there’s ALWAYS bugs. Not like I don’t have to check my code. Largest file I’ve ever made without a (syntax) bug on the first try was 1000 lines, and that’s in 30 years of experience.
I’ve tested this under KDE Neon, it needs to be run as root because you need elevated privileges to read /etc/shadow or the user calling it needs to belong to the «shadow» group (that’s the group that /etc/shadow belongs to). It takes the username as an argument.
#define _GNU_SOURCE #include #include #include #include #include #include #include #define DBG() \ do < \ char buf[100]; \ sprintf (buf, "error: %d\n", __LINE__); \ perror (buf); \ >while (0) void chomp (char *str) < while (*str != '\0' && *str != '\n') < str++; >*str = '\0'; > int main(int argc, char *argv[]) < char password[100]; struct spwd *shadow_entry; char *p, *correct, *supplied, *salt; if (argc < 2) < DBG (); return 2; >/* Read the password from stdin */ p = fgets(password, sizeof(password), stdin); if (p == NULL) < DBG (); return 2; >//*p = 0; - this was a pretty obvious error chomp (p); // this is what was intended above printf ("password = %s\n", p); /* Read the correct hash from the shadow entry */ shadow_entry = getspnam( argv[1] ); if (shadow_entry == NULL) < DBG (); return 1; >correct = shadow_entry->sp_pwdp; /* Extract the salt. Remember to free the memory. */ salt = strdup(correct); if (salt == NULL) < DBG (); return 2; >p = strchr(salt + 1, '$'); if (p == NULL) < DBG (); return 2; >p = strchr(p + 1, '$'); if (p == NULL) < DBG (); return 2; >p[1] = 0; /*Encrypt the supplied password with the salt and compare the results*/ supplied = crypt(password, salt); if (supplied == NULL) < DBG (); return 2; >if (strcmp(supplied, correct) == 0) < printf ("pass\n %s\n %s\n", supplied, correct); return (0); >else < printf ("fail\n %s\n %s\n", supplied, correct); return (1); >>
You can remove the printf functions and remove calls to DBG(); but both are useful until you can verify that the program is working properly. I had to add them in to see how and where it was failing. Every error exit should be a different number as well, but that’s just me being anal retentive.
check unix username and password in a shellscript
I want to check in a shell script if a local unix-user’s passed username and password are correct. What is the easiest way to do this? Only thing that I found while googling was using ‘expect’ and ‘su’ and then checking somehow if the ‘su’ was successful or not.
4 Answers 4
the username and passwords are written in the /etc/shadow file. just get the user and the password hash from there ( sed would help), hash your own password and check.
use mkpasswd to generate the hash. you hve to look which salt your version is using. the newest shadow is using sha-512 so :
mkpasswd -m sha-512 password salt
manpages can help you there a lot.
Easier would be to use php and the pam-aut module. there you can check vie php on group access pwd user.
more details about the implementation of this approach: ubuntuforums.org/archive/index.php/t-1232715.html
oh ok, nice. I spend 6 hours figuring this approach out by myself a few month ago. this site would have helped a lot. but i needed this for web authentification, so i finally used the auth-pam module of php
nowhere. it was how I finally solved an similar problem. cluelessCoder has never said what he wants to do. it you want an simple shell solution you have to prse the shadow file. for web authentification this module is easy. Well you can also create a php file and coll this one from batch and parse the output (a bit overkill but easy to accomplish too. you only need to have a php server running on the local machine). Just several ways to accomplish a goal.
Ok, now this is the script that I used to solve my problem. I first tried to write a small c-programm as susgested by Aaron Digulla, but that proved much too difficult.
Perhaps this Script is useful to someone else.
#!/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 # Setting the language to English for the expected "Password:" string, see http://askubuntu.com/a/264709/18014 export LC_ALL=C #since we use expect inside a bash-script, we have to escape tcl-$. expect < exit [lindex \$wait_result 3] >else < exit 1 >EOF
Check username/password in Linux without root
If I have a username and password pair how can I verify that they are actually correct in a Linux system? I know I can use passwd to do so but I want to do it programatically using C. I should not require root privileges (so reading the shadow file is not an option). Thank you.
@Stargateur I see no problem with that. It can be a kernel-mode driver or syscall to service that function.
2 Answers 2
If you are using a PAM , you might be able to make use of checkpassword-pam .
The manual has an example command (with debugging) which should give you a good place to start.
echo -e "username\0password\0timestamp\0" \ | checkpassword-pam -s SERVICE \ --debug --stdout -- /usr/bin/id 3
I see that the function doing the check is authenticate_using_pam (const char* service_name, const char* username, const char* password) What should be the service_name parameter there? AFAIK at least to install a PAM module you need to be root. Don't you to use that API?
This is a simple example to check the password with some python code. No root privilegs are needed.
#!/usr/bin/python3 # simple linux password checker with # standard python import os, pty def check_pass(user, passw): # returns: 0 if check ok # 1 check failed # 2 account locked if type(passw) is str: passw = passw.encode() pid, fd = pty.fork() # try to su a fake shell which returns '-c OK' on ok if not pid: # child argv = ('su', '-c', 'OK', '-s', '/bin/echo', user) os.execlp(argv[0], *argv) return # SHOULD NEVER REACHED okflg = False locked = False while True: try: data = os.read(fd, 1024) ##print('data:', data, flush=True) except OSError: break if not data: break data = data.strip() if data == b'Password:': os.write(fd, passw + b'\r\n') elif data.endswith(b'OK'): okflg = True break elif data.find(b'locked') > -1: # show that account is locked locked = True print(data, flush=True) break os.close(fd) # check result from su and okflg if (not os.waitpid(pid, 0)[1]) and okflg: return 0 return 2 if locked else 1 if __name__ == '__main__': print(check_pass('xx', 'yy'))
check password linux user
I am on debian 9. I have a problem to check the password of a linux user in my scripts. I realized that the different linux tools for creating and modifying a user password gave results of different pattern in /etc/shadow To create a user
pwLinux="abcdef1234" userLinux="toto02" pwCrypt=$(perl -e 'print crypt($ARGV[0], "zzz")' $pwLinux) useradd -m -G adm,dip,plugdev,www-data,sudo -p $pwCrypt $userLinux
toto02:zzDxrNjXuUs3U:17469:0:99999:7.
USERNAME="toto02" PASSWD="abcdef1234" ORIGPASS=`grep -w "$USERNAME" /etc/shadow | cut -d: -f2` ORIGPASS=`echo $ORIGPASS | cut -d"$" -f2` GENPASS=$(perl -e 'print crypt($ARGV[0], "zzz")' $PASSWD) if [ "$GENPASS" == "$ORIGPASS" ]; then echo "Valid Password" exit 0 else echo "Invalid Password" exit 1 fi
# username "toto02", newPwd "aabbcc" echo "$:$" | chpasswd
toto02:$6$rLklwx9K$Brv4lvNjR.S7f8i.Lmt8.iv8pgcbKhwDgINzhT1XwCBbD7XkB98lCtwUK3/4hdylkganoLuh/eIc38PtMArgZ/:17469:0:99999:7.
If i want to check this password i must use a different script.
First problem how to have the same pattern of password in both cases? i use:
#!/bin/bash USERNAME="toto02" PASSWD="aabbcc" ORIGPASS=`grep -w "$USERNAME" /etc/shadow | cut -d: -f2` export ALGO=`echo $ORIGPASS | cut -d"$" -f2` export SALT=`echo $ORIGPASS | cut -d"$" -f3` echo "algo: -$ALGO-" echo "salt: -$SALT-" echo "pwd entré: -$PASSWD-" echo "shadow: -$ORIGPASS-" GENPASS="$(perl -e 'print crypt("$ENV","\$$ENV\$$ENV\$")')" echo "pwd généré: -$GENPASS-" if [ "$GENPASS" == "$ORIGPASS" ]; then echo "Valid Password" exit 0 else echo "Invalid Password" exit 1 fi
algo: -6- salt: -rLklwx9K- pwd entré: -aabbcc- shadow: -$6$rLklwx9K$Brv4lvNjR.S7f8i.Lmt8.iv8pgcbKhwDgINzhT1XwCBbD7XkB98lCtwUK3/4hdylkganoLuh/eIc38PtMArgZ/- pwd généré: -$6$rLklwx9K$AIX1bUMAK9bwdd2g3ST5VtXTvHlHXHxnh4Xj.fLdxjaEkAAvHeeN5islid0wtmZN5u1zWQBup./IP8IH9i6W7/- Invalid Password