Zašifrované heslo můžete snadno extrahovat pomocí awk. Poté musíte extrahovat předponu $algorithm$salt$
(za předpokladu, že tento systém nepoužívá tradiční DES, které je silně zastaralé, protože v dnešní době může být hrubě vynuceno).
correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}
Pro kontrolu hesla je základní funkcí C crypt
, ale neexistuje žádný standardní příkaz shellu pro přístup.
Na příkazovém řádku můžete použít jednořádkovou linku Perl k vyvolání crypt
na heslo.
supplied=$(echo "$password" |
perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …
Vzhledem k tomu, že to nelze provést v čistě shellových nástrojích, pokud máte k dispozici Perl, můžete to také udělat v Perlu. (Nebo Python, Ruby, … cokoli, co máte k dispozici, co může volat crypt
funkce.) Varování, netestovaný kód.
#!/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);
}
Na vestavěném systému bez Perlu bych použil malý vyhrazený program v C. Varování, zadané přímo do prohlížeče, kompilovat jsem se ani nepokoušel. Toto je myšleno jako ilustrace nezbytných kroků, nikoli jako robustní implementace!
/* 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);
}
Jiný přístup je použít existující program, jako je su
nebo login
. Ve skutečnosti, pokud můžete, bylo by ideální zařídit, aby webová aplikace vykonávala vše, co potřebuje, prostřednictvím su -c somecommand username
. Zde je problém zadat heslo na su
; to vyžaduje terminál. Obvyklý nástroj pro emulaci terminálu se očekává, ale je to velká závislost pro vestavěný systém. Také, zatímco su
je v BusyBox, je často vynechán, protože mnoho jeho použití vyžaduje, aby binární soubor BusyBox byl setuid root. Přesto, pokud to dokážete, je to z bezpečnostního hlediska nejrobustnější přístup.
Podívejte se na man 5 shadow
a man 3 crypt
. Z posledně jmenovaného se můžete dozvědět, že heslo hash v /etc/shadow
mají následující tvar:
$id$salt$encrypted
kde id
definuje typ šifrování a při dalším čtení může být jedním z
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)
V závislosti na typu hashe je potřeba použít příslušnou funkci/nástroj pro vygenerování a ověření hesla „z ruky“. Pokud systém obsahuje mkpasswd
program, můžete jej použít, jak je zde navrženo. (Vezmete si sůl ze stínového souboru, pokud to nebylo zřejmé.) Například pomocí md5
hesla:
mkpasswd -5 <the_salt> <the_password>
vygeneruje řetězec, který by měl odpovídat /etc/shadow
vstup.
Podobná otázka byla položena na Stack Overflow. cluelessCoder poskytl skript používající očekávat, který můžete nebo nemusíte mít ve svém vestavěném systému.
#!/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