O uso do PAM é a melhor solução. Você pode escrever um pequeno código C, ou instalar o pacote python-pam e usar um script python que vem com o pacote python-pam. Veja /usr/share/doc/python-pam/examples/pamtest.py
Eu escrevi um script CGI em Python que invoca os comandos bash
, e ele precisa testar um login bem-sucedido no host.
Como escrevo um teste para isso?
Por exemplo, posso criar um script bash
que testa uma combinação de nome de usuário e senha em relação ao usuário registrado no host?
A abordagem correta para testar se um usuário pode efetuar login é realmente efetuar login como esse usuário.
Então, o que recomendo é que o script CGI use expect
para executar su
, passar uma senha e execute o comando que deve ser executado. Aqui está um rascunho de um script de expectativa que faz exatamente isso (aviso: absolutamente não testado, e eu não sou fluente em esperar). Substitua o nome de usuário, senha e comando (onde eu escrevi bob
, swordfish
e somecommand
); não se esqueça de citar corretamente.
spawn "/bin/su" "bob"
expect "Password:"
send "swordfish\r"
expect "^\$"
send "somecommand"
expect -re "^\$"
send "exit\r"
expect eof
Se você realmente não quer executar o comando através de uma camada de su
(por exemplo, porque o que você faz tem que ser executado pelo próprio processo CGI), então use esperar para executar o comando true
e verifique se o status de retorno é 0.
Outra abordagem seria usar o PAM diretamente no seu aplicativo, por meio de Ligação do PAM do Python .
Para responder mais especificamente: "É possível criar um script bash que testará uma combinação de nome de usuário e senha em relação ao usuário registrado no host?"
Sim.
#!/bin/bash
uid='id -u'
if [ $uid -ne 0 ]; then
echo "You must be root to run this"
exit 1
fi
if [ $# -lt 1 ]; then
echo "You must supply a username to check - ($# supplied)"
exit 1
fi
username=$1
salt='grep $username /etc/shadow | awk -F: ' {print substr($2,4,8)}''
if [ "$salt" != "" ]; then
newpass='openssl passwd -1 -salt $salt'
grep $username /etc/shadow | grep -q $newpass && echo "Success" || echo "Failure"
fi
Existe uma solução PAM 'C', 'Python' citada aqui, deixe-me colocar a perl também: -)
Fonte: link ?
#!/usr/bin/perl
use Authen::PAM;
use POSIX qw(ttyname);
$service = "login";
$username = "foo";
$password = "bar";
$tty_name = ttyname(fileno(STDIN));
sub my_conv_func {
my @res;
while ( @_ ) {
my $code = shift;
my $msg = shift;
my $ans = "";
$ans = $username if ($code == PAM_PROMPT_ECHO_ON() );
$ans = $password if ($code == PAM_PROMPT_ECHO_OFF() );
push @res, (PAM_SUCCESS(),$ans);
}
push @res, PAM_SUCCESS();
return @res;
}
ref($pamh = new Authen::PAM($service, $username, \&my_conv_func)) ||
die "Error code $pamh during PAM init!";
$res = $pamh->pam_set_item(PAM_TTY(), $tty_name);
$res = $pamh->pam_authenticate;
print $pamh->pam_strerror($res),"\n" unless $res == PAM_SUCCESS();
Se você tem acesso root, e está usando senhas md5, e você só precisa comparar senhas, então você pode usar o perl Módulo Crypt :: PasswdMD5 . Pegue o MD5 Hash de / etc / shadow, tire $ 1 $ e depois divida os $ restantes. Campo 1 = Sal, Campo 2 = texto criptografado. Em seguida, insira o texto em seu CGI, compare com o texto criptografado e com Bob, seu tio.
#!/usr/bin/env perl
use Crypt::PasswdMD5;
my $user = $ARGV[0];
my $plain = $ARGV[1];
my $check = qx{ grep $user /etc/shadow | cut -d: -f2 };
chomp($check);
my($salt,$md5txt) = $check =~ m/\\$([^\$].+)\$(.*)$/;
my $pass = unix_md5_crypt($plain, $salt);
if ( "$check" eq "$pass" ) {
print "OK","\n";
} else {
print "ERR","\n";
}
Depois de alguma pesquisa eu escrevi este programa C que pode ser usado a partir do script
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <shadow.h>
#include <string.h>
#include <crypt.h>
#include <unistd.h>
#include <libgen.h>
int main(int argc, char **argv) {
struct spwd *pwd;
if (argc != 3) {
printf("usage:\n\t%s [username] [password]\n", basename(argv[0]));
return 1;
} else if (getuid() == 0) {
pwd = getspnam(argv[1]);
return strcmp(crypt(argv[2], pwd->sp_pwdp), pwd->sp_pwdp);
} else {
printf("You need to be root\n");
return 1;
}
}
Você compila com:
gcc -Wall password_check.c /usr/lib/libcrypt.a -o check_passwd
Você pode usá-lo como
sudo ./check_passwd <user> <password> && echo "success" || echo "failure"
Como você mencionou que está usando CGI em python, provavelmente é apropriado assumir que você está usando o Apache como servidor httpd. Se assim for, deixe o processo de autenticação do seu programa para o Apache e deixe que as pessoas autenticadas executem seus scripts / programas cgi.
Existem amplos módulos que podem fazer autenticação para você no Apache, isso realmente depende do tipo de mecanismo de autenticação que você está procurando. A maneira como você citou a questão parece estar relacionada à autenticação da conta de host local baseada em / etc / passwd, arquivos de sombra. O módulo que vem à minha pesquisa rápida sobre isso é mod_auth_shadow
. Vantagem é que você está permitindo que alguém autoritário (em execução na porta de privilégio 80) autentique o usuário / senha para você e você pode confiar em informações autenticadas do usuário para executar os comandos em nome do usuário, se necessário.
bons links para começar:
Outra abordagem é usar o módulo SuEXEc do Apache, que executa processos (programas cgi) em nome do usuário autenticado.
Esse código usando o PAM funcionou para mim:
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdio.h>
#include <string.h>
// Define custom PAM conversation function
int custom_converation(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr)
{
// Provide password for the PAM conversation response that was passed into appdata_ptr
struct pam_response* reply = (struct pam_response* )malloc(sizeof(struct pam_response));
reply[0].resp = (char*)appdata_ptr;
reply[0].resp_retcode = 0;
*resp = reply;
return PAM_SUCCESS;
}
int main (int argc, char* argv[])
{
if (argc > 2)
{
// Set up a custom PAM conversation passing in authentication password
char* password = (char*)malloc(strlen(argv[2]) + 1);
strcpy(password, argv[2]);
struct pam_conv pamc = { custom_converation, password };
pam_handle_t* pamh;
int retval;
// Start PAM - just associate with something simple like the "whoami" command
if ((retval = pam_start("whoami", argv[1], &pamc, &pamh)) == PAM_SUCCESS)
{
// Authenticate the user
if ((retval = pam_authenticate(pamh, 0)) == PAM_SUCCESS)
fprintf(stdout, "OK\n");
else
fprintf(stderr, "FAIL: pam_authentication failed.\n");
// All done
pam_end(pamh, 0);
return retval;
}
else
{
fprintf(stderr, "FAIL: pam_start failed.\n");
return retval;
}
}
fprintf(stderr, "FAIL: expected two arguments for user name and password.\n");
return 1;
}
A melhor coisa que você pode fazer, se precisar de um script para acessar um host, é configurar uma chave ssh entre os hosts.
Link: link
Eu praticamente tirei isso da página
Primeiro, instale o OpenSSH em duas máquinas UNIX, difíceis e volumosas. Isso funciona melhor usando chaves DSA e SSH2 por padrão, tanto quanto eu posso dizer. Todos os outros HOWTOs que vi parecem lidar com chaves RSA e SSH1, e as instruções não surpreendentemente funcionam com o SSH2. Em cada máquina, digite ssh somemachine.example.com e faça uma conexão com sua senha normal. Isso criará um diretório .ssh em seu diretório pessoal com os perms apropriados. Na sua máquina principal, onde você quer que suas chaves secretas vivam (digamos, hurly), digite
ssh-keygen -t dsa
Isso solicitará uma senha secreta. Se esta for sua chave de identidade primária, use uma boa frase-senha. Se isso funcionar corretamente, você obterá dois arquivos chamados id_dsa e id_dsa.pub em seu diretório .ssh. Nota: é possível apenas pressionar a tecla enter quando for solicitada uma frase secreta, que fará uma chave sem frase-senha. Esta é uma idéia ruim para uma chave de identidade, então não faça isso! Veja abaixo os usos de chaves sem frases secretas.
scp ~/.ssh/id_dsa.pub burly:.ssh/authorized_keys2
Copie o arquivo id_dsa.pub para o diretório .ssh do outro host com o nome authorized_keys2. Agora, o corpulento está pronto para aceitar sua chave ssh. Como dizer quais teclas usar? O comando ssh-add fará isso. Para um teste, digite
ssh-agent sh -c 'ssh-add < /dev/null && bash'
Isso iniciará o ssh-agent, adicionará sua identidade padrão (solicitando sua frase secreta) e gerará um shell bash. A partir deste novo shell você deve ser capaz de:
ssh burly
Você deve conseguir fazer login