Comment vérifier le mot de passe avec Linux?

22

Je veux vérifier, à partir de la ligne de commande linux, si un mot de passe en texte clair donné est le même qu'un mot de passe crypté sur un / etc / shadow

(J'en ai besoin pour authentifier les utilisateurs Web. J'utilise un Linux intégré.)

J'ai accès au fichier / etc / shadow lui-même.

michelemarcon
la source
Connectez-vous en tant qu'utilisateur avec le mot de passe?
Kusalananda
Le test doit être fait automatiquement, je ne peux pas saisir manuellement le mot de passe à partir du serveur Web
michelemarcon

Réponses:

17

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 cryptle 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 cryptfonction.) 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 suou 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 susoit 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é.

Gilles 'SO- arrête d'être méchant'
la source
1
J'aime l' suapproche.
Benjohn
6

Jetez un oeil à man 5 shadowet man 3 crypt. À partir de ce dernier, vous pouvez apprendre que les hachages de mot de passe /etc/shadowont la forme suivante:

 $id$salt$encrypted

iddé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 un mkpasswdprogramme, vous pouvez l'utiliser comme suggéré ici . (Vous prenez le sel du fichier fantôme, si ce n'était pas évident.) Par exemple, avec des md5mots de passe:

 mkpasswd -5 <the_salt> <the_password>

générera la chaîne qui doit correspondre à l' /etc/shadowentrée.

rozcietrzewiacz
la source
1
Sur ma Debian Wheezy, j'avais une syntaxe complètement différente pour la commande mkpasswd, que je devais installer en utilisant apt-get install whois. La ligne de commande pour la ligne d'ombre <user>:$6$<salt>$<pwd>:étaitmkpasswd -msha-512 <password> <salt>
Daniel Alder
1

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
mr.Shu
la source
0

Gardez à l'esprit que, en supposant que le système est correctement configuré, le programme devra être exécuté en tant que root.

Une meilleure solution que de lire le fichier shadow directement et d'écrire votre propre code autour de crypt serait d'utiliser simplement les liaisons pam.

L' archive squid était fournie avec un simple outil CLI pour vérifier les noms d'utilisateur / mots de passe à l'aide de stdio - si simple à adapter à l'utilisation d'arguments - bien que la version que j'ai piratée précédemment n'était guère une affiche de pin-up pour une programmation structurée. Un rapide google et il semble que les versions les plus récentes ont été nettoyées de manière significative mais il y a encore quelques "goto" là-dedans.

symcbean
la source
0
#! /bin/bash
#  (GPL3+) Alberto Salvia Novella (es20490446e)


passwordHash () {
    password=${1}
    salt=${2}
    encryption=${3}

    hashes=$(echo ${password} | openssl passwd -${encryption} -salt ${salt} -stdin)
    echo $(substring ${hashes} "$" "3")
}


passwordIsValid () {
    user=${1}
    password=${2}

    encryption=$(secret "encryption" ${user})
    salt=$(secret "salt" ${user})
    salted=$(secret "salted" ${user})
    hash=$(passwordHash ${password} ${salt} ${encryption})

    [ ${salted} = ${hash} ] && echo "true" || echo "false"
}


secret () {
    secret=${1}
    user=${2}
    shadow=$(shadow ${user})

    if [ ${secret} = "encryption" ]; then
        position=1
    elif [ ${secret} = "salt" ]; then
        position=2
    elif [ ${secret} = "salted" ]; then
        position=3
    fi

    echo $(substring ${shadow} "$" ${position})
}


shadow () {
    user=${1}
    shadow=$(cat /etc/shadow | grep ${user})
    shadow=$(substring ${shadow} ":" "1")
    echo ${shadow}
}


substring () {
    string=${1}
    separator=${2}
    position=${3}

    substring=${string//"${separator}"/$'\2'}
    IFS=$'\2' read -a substring <<< "${substring}"
    echo ${substring[${position}]}
}


passwordIsValid ${@}
Alberto Salvia Novella
la source
Crache une erreurline 61: :: syntax error: operand expected (error token is ":")
hoefling
L'émulateur de terminal doit être Bash 5, et vous devez indiquer à la fois le nom d'utilisateur et le mot de passe comme arguments. J'ai testé ça marche.
Alberto Salvia Novella