web-dev-qa-db-fra.com

Comment vérifier le mot de passe avec Linux?

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.

23
michelemarcon

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 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 appeler 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 Perl est disponible, vous pouvez tout aussi bien faire tout en Perl. (Ou Python, Ruby,… tout ce dont vous disposez qui peut appeler la fonction crypt.) 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 attendez , mais c'est une grosse dépendance pour un système embarqué. De plus, tandis que su est dans BusyBox, il est souvent omis car bon nombre de ses utilisations nécessitent que le binaire BusyBox soit défini en tant que racine. Pourtant, si vous pouvez le faire, c'est l'approche la plus robuste du point de vue de la sécurité.

Jettes un coup d'oeil à man 5 shadow et man 3 crypt. À partir de ce dernier, vous pouvez apprendre que le mot de passe est haché dans /etc/shadow se présente sous la forme suivante:

 $id$salt$encrypted

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 le programme mkpasswd, 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 md5 mots de passe:

 mkpasswd -5 <the_salt> <the_password>

générera la chaîne qui doit correspondre à /etc/shadow entrée.

6
rozcietrzewiacz

Il y avait un question similaire posée sur Stack Overflow . cluelessCoder a fourni un script utilisant expect , que vous pouvez avoir ou non 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
1
mr.Shu

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 google rapide et il semble que les versions plus récentes ont été nettoyés de manière significative mais il y a encore quelques "goto" là-dedans.

0
symcbean
#! /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 ${@}
0