Vous pouvez facilement extraire le mot de passe crypté avec awk. Vous devez 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 forcé par la force 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 l'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 unique Perl pour invoquer crypt
le mot de passe.
supplied=$(echo "$password" |
perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …
Étant donné que cela ne peut pas être fait dans des outils shell purs, si Perl est disponible, vous pouvez tout aussi bien faire tout en Perl. (Ou Python, Ruby,… tout ce dont vous disposez qui peut appeler la crypt
fonction.) Attention, code non testé.
#!/usr/bin/env perl
use warnings;
use strict;
my @pwent = getpwnam($ARGV[0]);
if (!@pwent) {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 implémentation 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 exécute 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é. De plus, bien qu'il su
soit dans BusyBox, il est souvent omis car bon nombre de ses utilisations nécessitent que le binaire BusyBox soit défini comme root. Pourtant, si vous pouvez le faire, c'est l'approche la plus robuste du point de vue de la sécurité.