Puoi facilmente estrarre la password crittografata con awk. Devi quindi estrarre il prefisso $algorithm$salt$
(supponendo che questo sistema non utilizzi il DES tradizionale, che è fortemente deprecato perché può essere sottoposto a forza bruta in questi giorni).
correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}
Per il controllo della password, la funzione C sottostante è crypt
, ma non esiste un comando shell standard per accedervi.
Sulla riga di comando, puoi usare un one-liner Perl per invocare crypt
sulla password.
supplied=$(echo "$password" |
perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …
Dal momento che questo non può essere fatto in strumenti shell puri, se hai Perl disponibile, potresti anche fare tutto in Perl. (O Python, Ruby, ... qualunque cosa tu abbia a disposizione che possa chiamare il crypt
function.) Attenzione, codice non testato.
#!/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);
}
Su un sistema embedded senza Perl, userei un piccolo programma C dedicato. Attenzione, digitato direttamente nel browser, non ho nemmeno provato a compilare. Questo ha lo scopo di illustrare i passaggi necessari, non come una solida implementazione!
/* 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);
}
Un approccio diverso consiste nell'usare un programma esistente come su
o login
. Infatti, se puoi, sarebbe l'ideale fare in modo che l'applicazione web esegua tutto ciò di cui ha bisogno tramite su -c somecommand username
. La difficoltà qui è fornire la password a su
; questo richiede un terminale. Il solito strumento per emulare un terminale è previsto, ma è una grande dipendenza per un sistema embedded. Inoltre, mentre su
è in BusyBox, viene spesso omesso perché molti dei suoi usi richiedono che il binario BusyBox sia setuid root. Tuttavia, se puoi farlo, questo è l'approccio più robusto dal punto di vista della sicurezza.
Dai un'occhiata a man 5 shadow
e man 3 crypt
. Da quest'ultimo, puoi apprendere gli hash della password in /etc/shadow
hanno la seguente forma:
$id$salt$encrypted
dove id
definisce il tipo di crittografia e, leggendo oltre, può essere uno di
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)
A seconda del tipo di hash, è necessario utilizzare l'apposita funzione/strumento per generare e verificare la password "a mano". Se il sistema contiene mkpasswd
programma, puoi usarlo come suggerito qui. (Prendi il sale dal file shadow, se non fosse ovvio.) Ad esempio, con md5
password :
mkpasswd -5 <the_salt> <the_password>
genererà la stringa che dovrebbe corrispondere a /etc/shadow
voce.
È stata posta una domanda simile su Stack Overflow. cluelessCoder ha fornito uno script utilizzando expect, che potresti avere o meno sul tuo sistema embedded.
#!/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