Como verificar senha com o Linux?

19

Eu quero verificar, a partir da linha de comando do linux, se uma senha de texto simples é a mesma de uma senha criptografada em um / etc / shadow

(Eu preciso disso para autenticar os usuários da web. Estou executando um linux incorporado.)

Eu tenho acesso ao arquivo / etc / shadow em si.

    
por michelemarcon 29.09.2011 / 16:33

4 respostas

15

Você pode facilmente extrair a senha criptografada com o awk. Em seguida, você precisa extrair o prefixo $algorithm$salt$ (supondo que esse sistema não esteja usando o DES tradicional, o que é extremamente obsoleto, pois pode ser forçado brutal nos dias de hoje).

correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}

Para a verificação de senha, a função C subjacente é crypt , mas não há um padrão comando shell para acessá-lo.

Na linha de comando, você pode usar um one-liner Perl para invocar crypt na senha.

supplied=$(echo "$password" |
           perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …

Como isso não pode ser feito em ferramentas de shell puro, se você tiver o Perl disponível, é melhor fazer tudo em Perl. (Ou Python, Ruby,… o que você tiver disponível que possa chamar a função crypt .) Aviso, código não testado.

#!/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);
}

Em um sistema embarcado sem Perl, eu usaria um programa C pequeno e dedicado. Aviso, digitado diretamente no navegador, eu nem tentei compilar. Isso serve para ilustrar as etapas necessárias, não como uma implementação robusta!

/* 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);
}

Uma abordagem diferente é usar um programa existente, como su ou login . Na verdade, se você puder, seria ideal organizar o aplicativo da Web para executar o que for necessário por meio de su -c somecommand username . A dificuldade aqui é alimentar a senha para su ; isso requer um terminal. A ferramenta usual para emular um terminal é esperada , mas é uma grande dependência de um sistema embarcado. Além disso, embora su esteja no BusyBox, ele geralmente é omitido porque muitos de seus usos exigem que o binário do BusyBox seja root setuid. Ainda assim, se você puder fazer isso, essa é a abordagem mais robusta do ponto de vista da segurança.

    
por 30.09.2011 / 00:29
5

Dê uma olhada em man 5 shadow e man 3 crypt . A partir deste último, você pode saber que os hashes de senha em /etc/shadow têm o seguinte formato:

 $id$salt$encrypted

onde id define o tipo de criptografia e, lendo mais, pode ser um dos

          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)

Dependendo do tipo de hash, você precisa usar a função / ferramenta apropriada para gerar e verificar a senha "manualmente". Se o sistema contiver o programa mkpasswd , você poderá usá-lo como sugerido aqui . (Você pega o salt do arquivo shadow, se isso não for óbvio.) Por exemplo, com md5 senhas:

 mkpasswd -5 <the_salt> <the_password>

irá gerar a string que deve corresponder a /etc/shadow entry.

    
por 29.09.2011 / 18:27
1

Houve uma pergunta semelhante feita no Stack Overflow . A cluelessCoder forneceu um script usando , que você pode ou não ter em seu sistema incorporado.

#!/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
    
por 29.09.2011 / 18:44
0

Tenha em mente que, supondo que o sistema esteja configurado corretamente, o programa precisará ser executado como root.

Uma solução melhor do que ler o arquivo shadow diretamente e escrever seu próprio código em torno do crypt seria usar apenas as ligações pam.

O squid costumava vir com uma ferramenta CLI simples para verificar nomes de usuários / senhas usando o stdio - tão simples de se adaptar usando argumentos - embora a versão que eu hackasse anteriormente dificilmente fosse um pôster pin-up para programação estruturada. Um rápido google e parece que as versões mais recentes foram limpas significativamente, mas ainda assim 'goto está lá.

    
por 30.09.2011 / 18:02