Vous pouvez facilement extraire le mot de passe crypté avec awk. Il faut ensuite extraire le préfixe $algorithm$salt$
(en supposant que ce système n'utilise pas le DES traditionnel, qui est fortement déconseillé car il peut être brutalement forcé de nos jours).
correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}
Pour la vérification du mot de passe, la fonction C sous-jacente est crypt
, mais il n'y a pas de commande shell standard pour y accéder.
Sur la ligne de commande, vous pouvez utiliser une ligne Perl pour invoquer crypt
sur le mot de passe.
supplied=$(echo "$password" |
perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …
Comme cela ne peut pas être fait dans des outils shell purs, si vous disposez de Perl, vous pouvez tout aussi bien le faire en Perl. (Ou Python, Ruby, … tout ce dont vous disposez qui peut appeler le crypt
fonction.) Attention, code non testé.
#!/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);
}
Sur un système embarqué sans Perl, j'utiliserais un petit programme C dédié. Attention, tapé directement dans le navigateur, je n'ai même pas essayé de compiler. Ceci est destiné à illustrer les étapes nécessaires, pas comme une mise en œuvre robuste !
/* 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);
}
Une approche différente consiste à utiliser un programme existant tel que su
ou login
. En fait, si vous le pouvez, il serait idéal de faire en sorte que l'application Web effectue tout ce dont elle a besoin via su -c somecommand username
. La difficulté ici est de fournir le mot de passe à su
; cela nécessite un terminal. L'outil habituel pour émuler un terminal est attendu, mais c'est une grosse dépendance pour un système embarqué. Aussi, tandis que su
est dans BusyBox, il est souvent omis car nombre de ses utilisations nécessitent que le binaire BusyBox soit setuid root. Néanmoins, si vous pouvez le faire, c'est l'approche la plus robuste du point de vue de la sécurité.
Jetez un oeil à man 5 shadow
et man 3 crypt
. De ce dernier, vous pouvez apprendre que le mot de passe est haché en /etc/shadow
avoir la forme suivante :
$id$salt$encrypted
où id
définit le type de cryptage et, en lisant plus loin, peut être l'un des
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)
Selon le type de hachage, vous devez utiliser la fonction/l'outil approprié pour générer et vérifier le mot de passe "à la main". Si le système contient mkpasswd
programme, vous pouvez l'utiliser comme suggéré ici. (Vous prenez le sel du fichier shadow, si ce n'était pas évident.) Par exemple, avec md5
mots de passe :
mkpasswd -5 <the_salt> <the_password>
générera la chaîne qui doit correspondre à /etc/shadow
entrée.
Une question similaire a été posée sur Stack Overflow. cluelessCoder a fourni un script utilisant expect, que vous pouvez ou non avoir sur votre système embarqué.
#!/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