Como você deseja fazer isso em um script de shell, algumas contribuições em Como verificar a senha no Linux? (no Unix.SE , sugerido pela AB ) são especialmente relevantes:
Para verificar manualmente se uma sequência é realmente a senha de algum usuário, você deve fazer o hash com o mesmo algoritmo de hash que na entrada de sombra do usuário, com o mesmo sal que na entrada de sombra do usuário. Em seguida, pode ser comparado com o hash da senha armazenado lá.
Escrevi um script de trabalho completo demonstrando como fazer isso.
- Se você nomear
chkpass, poderá executar e ele lerá uma linha da entrada padrão e verificará se é a senha.chkpass useruser
- Instale o pacote whois
para obter o mkpasswdutilitário do qual esse script depende.
- Este script deve ser executado como root para ter sucesso.
- Antes de usar este script ou qualquer parte dele para fazer um trabalho real, consulte as Notas de segurança abaixo.
#!/usr/bin/env bash
xcorrect=0 xwrong=1 enouser=2 enodata=3 esyntax=4 ehash=5 IFS=$
die() {
printf '%s: %s\n' "$0" "$2" >&2
exit $1
}
report() {
if (($1 == xcorrect))
then echo 'Correct password.'
else echo 'Wrong password.'
fi
exit $1
}
(($# == 1)) || die $esyntax "Usage: $(basename "$0") <username>"
case "$(getent passwd "$1" | awk -F: '{print $2}')" in
x) ;;
'') die $enouser "error: user '$1' not found";;
*) die $enodata "error: $1's password appears unshadowed!";;
esac
if [ -t 0 ]; then
IFS= read -rsp "[$(basename "$0")] password for $1: " pass
printf '\n'
else
IFS= read -r pass
fi
set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
case "${ent[1]}" in
1) hashtype=md5;; 5) hashtype=sha-256;; 6) hashtype=sha-512;;
'') case "${ent[0]}" in
\*|!) report $xwrong;;
'') die $enodata "error: no shadow entry (are you root?)";;
*) die $enodata 'error: failure parsing shadow entry';;
esac;;
*) die $ehash "error: password hash type is unsupported";;
esac
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
then report $xcorrect
else report $xwrong
fi
Notas de segurança
Pode não ser a abordagem correta.
Se uma abordagem como essa deve ou não ser considerada segura e apropriada depende de detalhes sobre o seu caso de uso que você não forneceu (até o momento em que este artigo foi escrito).
Não foi auditado.
Embora eu tenha tentado tomar cuidado ao escrever esse script, ele não foi auditado corretamente para detectar vulnerabilidades de segurança . Pretende-se como uma demonstração e seria um software "alfa" se lançado como parte de um projeto. Além disso...
Outro usuário que está "assistindo" pode descobrir o sal do usuário .
Devido a limitações na mkpasswdaceitação de dados salt, este script contém uma falha de segurança conhecida , que você pode ou não considerar aceitável, dependendo do caso de uso. Por padrão, os usuários do Ubuntu e da maioria dos outros sistemas GNU / Linux podem visualizar informações sobre processos executados por outros usuários (incluindo raiz), incluindo seus argumentos de linha de comando. Nem a entrada do usuário nem o hash da senha armazenada são passados como um argumento da linha de comandos para qualquer utilitário externo. Mas o salt , extraído do shadowbanco de dados, é fornecido como um argumento de linha de comando mkpasswd, pois essa é a única maneira pela qual o utilitário aceita um salt como entrada.
E se
- outro usuário no sistema ou
- qualquer pessoa que tenha a capacidade de criar uma conta de usuário (por exemplo,
www-data) execute seu código ou
- qualquer pessoa que possa visualizar informações sobre processos em execução (inclusive inspecionando manualmente as entradas
/proc)
é capaz de verificar os argumentos da linha de comandos mkpasswdconforme são executados por esse script, e eles podem obter uma cópia do sal do usuário no shadowbanco de dados. Talvez eles precisem adivinhar quando esse comando é executado, mas isso às vezes é possível.
Um atacante com seu sal não é tão ruim quanto um atacante com seu sal e hash , mas não é o ideal. O sal não fornece informações suficientes para alguém descobrir sua senha. Mas permite que alguém gere tabelas arco-íris ou hashes de dicionário pré-calculados específicos para esse usuário nesse sistema. Inicialmente, isso é inútil, mas se sua segurança for comprometida posteriormente e o hash completo for obtido, poderá ser quebrado mais rapidamente para obter a senha do usuário antes que eles possam alterá-la.
Portanto, essa falha de segurança é um fator exacerbador em um cenário de ataque mais complexo, em vez de uma vulnerabilidade totalmente explorável. E você pode considerar a situação acima exagerada. Mas eu estou relutante em recomendar qualquer método para geral, o uso do mundo real que vazamentos de quaisquer dados não-públicos de /etc/shadowque um usuário não-raiz.
Você pode evitar esse problema completamente:
- escrevendo parte do seu script em Perl ou alguma outra linguagem que permite que você chamar funções C, como mostrado na resposta de Gilles para a questão Unix.SE relacionados , ou
- escrevendo todo o script / programa em um idioma assim, em vez de usar o bash. (Com base na maneira como você marcou a pergunta, parece que você prefere usar o bash.)
Cuidado ao chamar esse script.
Se você permitir que um usuário não confiável execute esse script como root ou execute qualquer processo como root que chame esse script, tenha cuidado . Ao alterar o ambiente, eles podem fazer com que esse script - ou qualquer script que seja executado como root - faça qualquer coisa . A menos que você possa impedir que isso ocorra, você não deve permitir aos usuários privilégios elevados para executar scripts de shell.
Veja 10.4. Linguagens de script de shell (derivadas sh e csh) no HOWTO de programação segura de David A. Wheeler para Linux e UNIX para obter mais informações sobre isso. Embora sua apresentação se concentre em scripts setuid, outros mecanismos podem ser vítimas de alguns dos mesmos problemas se não higienizarem corretamente o ambiente.
Outras notas
Ele suporta hashes de leitura shadowapenas do banco de dados.
As senhas devem estar sombreadas para que esse script funcione (ou seja, seus hashes devem estar em um /etc/shadowarquivo separado no qual somente o root possa ler, não no /etc/passwd).
Este sempre deve ser o caso no Ubuntu. Em qualquer caso, se necessário, o script pode ser estendido trivialmente para ler os hashes de senha passwde também shadow.
Lembre- IFSse de modificar este script.
Defino IFS=$no início, pois os três dados no campo hash de uma entrada de sombra são separados por $.
- Eles também têm uma liderança
$, razão pela qual o tipo de hash e o sal são "${ent[1]}"e, em "${ent[2]}"vez de "${ent[0]}"e "${ent[1]}", respectivamente.
Os únicos lugares neste script onde $IFSdeterminam como o shell divide ou combina palavras são
quando esses dados são divididos em uma matriz, inicializando-os a partir da $( )substituição de comando não citado em:
set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
quando a matriz é reconstituída em uma sequência para comparar com o campo completo de shadow, a "${ent[*]}"expressão em:
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
Se você modificar o script e fazê-lo executar a divisão de palavras (ou junção de palavras) em outras situações, será necessário definir IFSvalores diferentes para comandos diferentes ou partes diferentes do script.
Se você não se lembrar disso e assumir que $IFSestá definido como o espaço em branco habitual ( $' \t\n'), você pode acabar fazendo seu script se comportar de maneiras bem estranhas.