Come faccio a scrivere un test per l'accesso al sistema?


9

Ho scritto uno script Python CGI che richiama i bashcomandi e deve testare un accesso riuscito sull'host.

Come posso scrivere un test per questo?

Ad esempio, posso creare uno bashscript che testa una determinata combinazione di nome utente e password rispetto all'utente registrato sull'host?


1
Forse potresti guardare il codice dietro il loginprogramma.
Kevin,

Non correlato alla domanda, ma spero che tu stia crittografando il traffico sul tuo server web in modo che gli accessi utente non possano essere sniffati via cavo.
jw013,

Risposte:


8

L'uso di PAM è la soluzione migliore. È possibile scrivere un piccolo codice C o installare il pacchetto python-pam e utilizzare uno script python fornito con il pacchetto python-pam. Vedere/usr/share/doc/python-pam/examples/pamtest.py


Provo PAM ma non ha funzionato. Ma riprovo questo esempio e funziona.
jcubic,

1
In OpenSUSE 12.3 (python-pam 0.5.0-84.1.1) e 13.1 (0.5.0-87.1.2), il percorso completo di pamtest.py è /usr/share/doc/packages/python-pam/examples/pamtest.pyLo script pamtest.py può essere usato per testare credenziali su sistemi che usano PAM per l'autenticazione, è incluso nel pacchetto python-pam (che richiede python) e in alcune distribuzioni il percorso completo è /usr/share/doc/python-pam/examples/pamtest.py.
ShadSterling

5

L'approccio corretto per verificare se un utente può accedere è effettivamente accedere come tale utente.

Quindi ciò che consiglio è di utilizzare lo script CGI expectper eseguire su, passare una password ed eseguire il comando che deve essere eseguito. Ecco una bozza di uno script wait che fa proprio questo (avvertimento: assolutamente non testato, e non sono fluente nell'aspettarmi). Sostituire il nome utente, la password e il comando (dove ho scritto bob, swordfishe somecommand); assicurati di citare correttamente.

spawn "/bin/su" "bob"
expect "Password:"
send "swordfish\r"
expect "^\\$"
send "somecommand"
expect -re "^\\$"
send "exit\r"
expect eof

Se davvero non vuoi eseguire il comando attraverso un livello di su(ad esempio perché quello che fai deve essere eseguito dal processo CGI stesso), allora usa prevede di eseguire il comando truee controlla che lo stato di ritorno sia 0.

Un altro approccio sarebbe quello di utilizzare PAM direttamente nella tua applicazione, attraverso l'associazione PAM di Python .


È fantastico, l'unica soluzione che senza accesso root.
jcubic,

questo funziona su -c true bob && echo successsi vergogna che do non accetta la password come argomento
jcubic

Ho testato sudallo script CGI e ha bisogno di un terminale per funzionare.
jcubic,

3
@jcubic È un'idea davvero stupida inserire una password in un argomento della riga di comando, perché gli argomenti della riga di comando sono pubblici su un sistema Unix. La rimozione della password fornirebbe lo stesso livello di sicurezza della sua immissione nella riga di comando. E sarebbe molto più facile controllare: /bin/truesarebbe abbastanza.
ceving

2

Per rispondere in modo più specifico: "È possibile creare uno script bash che testerà una data combinazione di nome utente e password rispetto all'utente registrato sull'host?"

Sì.

#!/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

2
Ha funzionato per te? Sto vedendo che stai controllando una password shadow esistente con la password shadow ma dove è coinvolto l'hashing qui?
Nikhil Mulley,

Ho provato e non funziona
jcubic l'

3
Cattiva idea! Stai assumendo che il tuo programma sia in esecuzione come root, o almeno come shadowgruppo, il che è fortemente sconsigliato per un CGI: avresti bisogno di un altro livello di escalation di privilegi. E stai assumendo un particolare algoritmo di hashing della password (uno supportato da openssl) e una posizione di memorizzazione della password ( /etc/shadowal contrario di NIS o LDAP) che potrebbe essere o meno quella effettivamente utilizzata per quel particolare utente.
Gilles 'SO- smetti di essere malvagio' il

2

C'è una soluzione PAM 'C', 'Python' citata qui, lasciami mettere anche quella perl :-)

Fonte: http://search.cpan.org/~nikip/Authen-PAM-0.16/PAM/FAQ.pod#1._Can_I_authenticate_a_user_non_interactively ?

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

Sì, ok, ma la domanda riguardava uno script CGI scritto in Python.
Gilles 'SO- smetti di essere cattivo' il

1

Se hai accesso come root e stai usando password md5 e devi solo confrontare le password, puoi usare il modulo perl Crypt :: PasswdMD5 . Prendi l'Hash MD5 da / etc / shadow, togli $ 1 $ e poi dividi i $ rimanenti. Campo 1 = Sale, Campo 2 = testo crittografato. Quindi hash l'immissione di testo nel tuo CGI, confrontalo con il testo crittografato e Bob è tuo zio.

#!/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/\$1\$([^\$].+)\$(.*)$/;
my $pass                = unix_md5_crypt($plain, $salt);

if ( "$check" eq "$pass" ) {
        print "OK","\n";
} else {
        print "ERR","\n";
}

2
chiaro e semplice :-). Funzionerà fintanto che / etc / passwd usa hashing md5. All'interno dell'autenticazione (nsswitch) è diversa per il sistema, quindi i moduli pam sono i migliori da usare.
Nikhil Mulley,

2
Cattiva idea! Stai assumendo che il tuo programma sia in esecuzione come root, o almeno come shadowgruppo, il che è fortemente sconsigliato per un CGI: avresti bisogno di un altro livello di escalation di privilegi. E stai assumendo un particolare algoritmo di hashing delle password (MD5 e non bcrypt o altri algoritmi consigliati) e la posizione di archiviazione delle password ( /etc/shadowal contrario, ad esempio, NIS o LDAP) che potrebbe essere o meno quella effettivamente utilizzata per quel particolare utente.
Gilles 'SO- smetti di essere malvagio' il

0

Dopo alcune ricerche ho scritto questo programma C che può essere usato dallo 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;
    }
}

Lo compili con:

gcc -Wall password_check.c /usr/lib/libcrypt.a -o check_passwd

Puoi usarlo come

sudo ./check_passwd <user> <password> && echo "success" || echo "failure"

1
Cattiva idea! Stai assumendo che il tuo programma sia in esecuzione come root, o almeno come shadowgruppo, il che è fortemente sconsigliato per un CGI: avresti bisogno di un altro livello di escalation di privilegi. E stai assumendo un particolare algoritmo di hashing della password (uno supportato da openssl) e una posizione di memorizzazione della password ( /etc/shadowal contrario di NIS o LDAP) che potrebbe essere o meno quella effettivamente utilizzata per quel particolare utente. Usa PAM, conosce il suo lavoro.
Gilles 'SO- smetti di essere malvagio' il

Sì, lo so, ma ho pensato che questo non è possibile senza root. Tutte le altre soluzioni usano anche root, tranne le tue.
jcubic,

0

Dal momento che hai detto che stai usando CGI in Python, è probabilmente appropriato supporre che stai usando Apache come server httpd. In tal caso, lascia il processo di autenticazione del tuo programma su Apache e lascia che le persone autenticate eseguano i tuoi script / programmi cgi.

Ci sono molti moduli che possono fare l'autenticazione su Apache, dipende davvero dal tipo di meccanismo di autenticazione che stai cercando. Il modo in cui hai citato la domanda sembra essere correlato all'autenticazione dell'account host locale basata su / etc / passwd, file shadow. Il modulo che arriva alla mia rapida ricerca in merito è mod_auth_shadow. Il vantaggio è che si sta consentendo a qualcuno autorevole (in esecuzione sulla porta di privilegio 80) di autenticare l'utente / password per l'utente e si può fare affidamento su informazioni autenticate dell'utente per eseguire i comandi per conto dell'utente, se necessario.

Buoni collegamenti per iniziare:

http://adam.shand.net/archives/2008/apache_tips_and_tricks/#index5h2

http://mod-auth-shadow.sourceforge.net/

http://www.howtoforge.com/apache_mod_auth_shadow_debian_ubuntu

Un altro approccio consiste nell'utilizzare il modulo SuEXEc di Apache, che esegue i processi (programmi cgi) per conto dell'utente autenticato.


Questo script CGI è un servizio JSON-RPC chiamato tramite Ajax e ho bisogno del metodo di accesso che restituisca un token, il token dovrebbe essere restituito se l'accesso ha esito positivo. Quindi praticamente ogni utente deve essere in grado di eseguire quello script.
jcubic,

0

Questo codice usando PAM ha funzionato per me:

#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; 
}

Potresti aggiungere alcune informazioni di contesto per farle "sentire" più come una risposta?
Volker Siegel,

-3

La cosa migliore che puoi fare, se hai bisogno di uno script per accedere a un host, è configurare una chiave ssh tra gli host.

Link: http://pkeck.myweb.uga.edu/ssh/

Ho praticamente rimosso questo dalla pagina


Innanzitutto, installa OpenSSH su due macchine UNIX, in modo rapido e corpulento. Funziona meglio usando le chiavi DSA e SSH2 per impostazione predefinita, per quanto ne so. Tutti gli altri HOWTO che ho visto sembrano avere a che fare con chiavi RSA e SSH1 e le istruzioni non sorprendono a non funzionare con SSH2. Su ogni macchina digitare ssh somemachine.example.com e stabilire una connessione con la password normale. Questo creerà una directory .ssh nella directory home con i permessi corretti. Sul tuo computer principale in cui vuoi che vivano le tue chiavi segrete (diciamo in fretta), digita

ssh-keygen -t dsa

Questo ti chiederà una passphrase segreta. Se questa è la chiave di identità primaria, assicurati di utilizzare una buona passphrase. Se funziona correttamente, otterrai due file chiamati id_dsa e id_dsa.pub nella tua directory .ssh. Nota: è possibile premere semplicemente il tasto Invio quando viene richiesto un passphrase, che creerà un tasto senza passphrase. Questa è una Bad Idea ™ per una chiave di identità, quindi non farlo! Vedi sotto per l'uso di chiavi senza passphrase.

scp ~/.ssh/id_dsa.pub burly:.ssh/authorized_keys2

Copia il file id_dsa.pub nella directory .ssh dell'altro host con il nome autorizzato_keys2. Ora burly è pronto per accettare la tua chiave ssh. Come dirgli quali chiavi usare? Il comando ssh-add lo farà. Per un test, digitare

ssh-agent sh -c 'ssh-add < /dev/null && bash'

Questo avvierà l'agente ssh, aggiungerà la tua identità predefinita (chiedendoti la tua passphrase) e genererà una shell bash. Da questa nuova shell dovresti essere in grado di:

ssh burly

Dovresti essere in grado di accedere


Sebbene ciò sia vero, non sembra essere rilevante per la domanda, che riguarda un'applicazione a cui si accede tramite un browser web.
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.