¿Cómo escribo una prueba para iniciar sesión en el sistema?

9

He escrito un script Python CGI que invoca bashcomandos, y necesita probar un inicio de sesión exitoso en el host.

¿Cómo escribo una prueba para eso?

Por ejemplo, ¿podría crear un bashscript que pruebe una combinación de nombre de usuario y contraseña contra el usuario registrado en el host?

jcubic
fuente
1
Tal vez podría mirar el código detrás del loginprograma.
Kevin
No está relacionado con la pregunta, pero espero que esté encriptando el tráfico a su servidor web para que los inicios de sesión de los usuarios no puedan ser rastreados.
jw013

Respuestas:

8

Usar PAM es la mejor solución. Puede escribir código C pequeño o instalar el paquete python-pam y usar un script python que viene con el paquete python-pam. Ver/usr/share/doc/python-pam/examples/pamtest.py

Michał Šrajer
fuente
Intento con PAM pero no funcionó. Pero intento nuevamente este ejemplo y funciona.
jcubic
1
En OpenSUSE 12.3 (python-pam 0.5.0-84.1.1) y 13.1 (0.5.0-87.1.2), la ruta completa a pamtest.py es /usr/share/doc/packages/python-pam/examples/pamtest.pyEl script pamtest.py se puede usar para probar credenciales en sistemas que usan PAM para la autenticación, se incluye en el paquete python-pam (que requiere python), y en algunas distribuciones la ruta completa es /usr/share/doc/python-pam/examples/pamtest.py.
ShadSterling
5

El enfoque correcto para probar si un usuario puede iniciar sesión es realmente iniciar sesión como ese usuario.

Entonces, lo que recomiendo es hacer que el script CGI se use expectpara ejecutar su, pasar una contraseña y ejecutar el comando que debe ejecutarse. Aquí hay un borrador de un script de esperar que hace exactamente esto (advertencia: absolutamente no probado, y no soy fluido en esperar). Sustituya en el nombre de usuario, contraseña y comando (donde escribí bob, swordfishy somecommand); asegúrese de citar correctamente.

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

Si realmente no desea ejecutar el comando a través de una capa de su(por ejemplo, porque lo que hace tiene que ser realizado por el proceso CGI), utilice el comando esperar para ejecutar el comando truey verificar que el estado de retorno sea 0.

Otro enfoque sería utilizar PAM directamente en su aplicación, a través del enlace PAM de Python .

Gilles 'SO- deja de ser malvado'
fuente
Es impresionante, la única solución que sin acceso root.
jcubic
esto funciona su -c true bob && echo successes la vergüenza de que su contraseña no aceptan como argumento
jcubic
Probé sudel script CGI y necesita un terminal para que funcione.
jcubic
3
@jcubic Es una idea realmente estúpida poner una contraseña en un argumento de línea de comando, porque los argumentos de línea de comando son públicos en un sistema Unix. Eliminar la contraseña proporcionaría el mismo nivel de seguridad que ponerla en la línea de comando. Y sería mucho más fácil comprobarlo: /bin/truesería suficiente.
ceving
2

Para responder más específicamente: "¿Es posible crear un script bash que pruebe una combinación de nombre de usuario y contraseña en contra del usuario registrado en el host?"

Si.

#!/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
Brian Barba Roja
fuente
2
¿Esto funcionó para ti? Veo que está comprobando una contraseña oculta existente contra la contraseña oculta, pero ¿dónde está involucrado el hash aquí?
Nikhil Mulley
Probé
3
¡Mala idea! Está asumiendo que su programa se está ejecutando como root, o al menos como el shadowgrupo, lo que no es muy recomendable para un CGI: necesitaría otra capa de escalada de privilegios. Y está asumiendo un algoritmo de hashing de contraseña particular (uno compatible con openssl) y una ubicación de almacenamiento de contraseña ( /etc/shadowen lugar de, por ejemplo, NIS o LDAP) que puede o no ser el que realmente se usa para ese usuario en particular.
Gilles 'SO- deja de ser malvado'
2

Hay una solución PAM 'C', 'Python' citada aquí, permítanme poner la perl también :-)

Fuente: 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();
Nikhil Mulley
fuente
Sí, está bien, pero la pregunta era sobre un script CGI escrito en Python.
Gilles 'SO- deja de ser malvado'
1

Si tiene acceso de root y está utilizando contraseñas md5, y solo necesita comparar las contraseñas, puede usar el módulo perl Crypt :: PasswdMD5 . Tome el MD5 Hash de / etc / shadow, elimine los $ 1 $ y luego divida los $ restantes. Campo 1 = Sal, Campo 2 = texto encriptado. Luego, agregue el ingreso de texto en su CGI, compárelo con el texto encriptado y Bob es su tío.

#!/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";
}
Tim Kennedy
fuente
2
así de simple :-). Esto funcionará siempre que / etc / passwd use hashing md5. En caso de que la autenticación (nsswitch) sea diferente para el sistema, entonces los módulos pam son los mejores para usar.
Nikhil Mulley
2
¡Mala idea! Está asumiendo que su programa se está ejecutando como root, o al menos como el shadowgrupo, lo que no es muy recomendable para un CGI: necesitaría otra capa de escalada de privilegios. Y está asumiendo un algoritmo de hashing de contraseña en particular (MD5 y no bcrypt u otro algoritmo recomendado) y una ubicación de almacenamiento de contraseña ( /etc/shadowen lugar de, por ejemplo, NIS o LDAP) que puede o no ser la que realmente se usa para ese usuario en particular.
Gilles 'SO- deja de ser malvado'
0

Después de alguna búsqueda, escribí este programa en C que se puede usar desde el 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 compilas con:

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

Puedes usarlo como

sudo ./check_passwd <user> <password> && echo "success" || echo "failure"
jcubic
fuente
1
¡Mala idea! Está asumiendo que su programa se está ejecutando como root, o al menos como el shadowgrupo, lo que no es muy recomendable para un CGI: necesitaría otra capa de escalada de privilegios. Y está asumiendo un algoritmo de hashing de contraseña particular (uno compatible con openssl) y una ubicación de almacenamiento de contraseña ( /etc/shadowen lugar de, por ejemplo, NIS o LDAP) que puede o no ser el que realmente se usa para ese usuario en particular. Use PAM, conoce su trabajo.
Gilles 'SO- deja de ser malvado'
Sí, lo sé, pero pensé que esto no es posible sin root. Todas las demás soluciones también usan root, excepto la suya.
jcubic
0

Como mencionó que está utilizando CGI en python, probablemente sea apropiado suponer que está utilizando Apache como su servidor httpd. Si es así, deje el proceso de autenticación de su programa a Apache y solo permita que las personas autenticadas ejecuten sus scripts / programas cgi.

Existen amplios módulos que pueden realizar la autenticación por usted en Apache, realmente depende del tipo de mecanismo de autenticación que esté buscando. La forma en que citó en la pregunta parece estar relacionada con la autenticación de la cuenta de host local basada en / etc / passwd, archivos shadow. Módulo que viene a mi búsqueda rápida con respecto a esto es mod_auth_shadow. La ventaja es que está permitiendo que alguien autorizado (que se ejecuta en el puerto de privilegio 80) autentique el usuario / contraseña por usted y puede confiar en la información autenticada del usuario para ejecutar los comandos en nombre del usuario, si es necesario.

Buenos enlaces para comenzar:

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

Otro enfoque es utilizar el módulo SuEXEc de Apache, que ejecuta procesos (programas cgi) en nombre del usuario autenticado.

Nikhil Mulley
fuente
Este script CGI es un servicio JSON-RPC llamado a través de Ajax y necesito un inicio de sesión de método que devuelva un token, el token debe devolverse si el inicio de sesión se realiza correctamente. Básicamente, cada usuario necesita poder ejecutar ese script.
jcubic
0

Este código usando PAM funcionó para mí:

#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; 
}
Ritchie Carroll
fuente
¿Podría agregar alguna información de contexto para que se "sienta" más como una respuesta?
Volker Siegel
-3

Lo mejor que puede hacer, si necesita un script para iniciar sesión en un host, es configurar una clave ssh entre los hosts.

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

Casi levanté esto de la página


Primero, instale OpenSSH en dos máquinas UNIX, rápido y corpulento. Esto funciona mejor usando claves DSA y SSH2 por defecto hasta donde puedo decir. Todos los otros CÓMO que he visto parecen tratar con claves RSA y SSH1, y las instrucciones no sorprendentemente no funcionan con SSH2. En cada máquina, escriba ssh somemachine.example.com y establezca una conexión con su contraseña habitual. Esto creará un directorio .ssh en su directorio de inicio con los permisos adecuados. En su máquina principal donde desea que vivan sus claves secretas (digamos apresuradamente), escriba

ssh-keygen -t dsa

Esto te pedirá una frase secreta. Si esta es su clave de identidad principal, asegúrese de usar una buena frase de contraseña. Si esto funciona correctamente, obtendrá dos archivos llamados id_dsa e id_dsa.pub en su directorio .ssh. Nota: es posible simplemente presionar la tecla Intro cuando se le solicite una frase de contraseña, lo que hará que una tecla sin frase de contraseña. Esta es una Bad Idea ™ para una clave de identidad, ¡así que no lo hagas! Consulte a continuación los usos de las claves sin frases de contraseña.

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

Copie el archivo id_dsa.pub al directorio .ssh del otro host con el nombre autorizado_claves2. Ahora Burly está listo para aceptar su clave ssh. ¿Cómo decirle qué teclas usar? El comando ssh-add lo hará. Para una prueba, escriba

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

Esto iniciará el agente ssh, agregará su identidad predeterminada (solicitándole su frase de contraseña) y generará un shell bash. Desde este nuevo shell deberías poder:

ssh burly

Deberías poder iniciar sesión

Roy Rico
fuente
Si bien esto es cierto, no parece ser relevante para la pregunta, que se trata de una aplicación a la que se accede a través de un navegador web.
Gilles 'SO- deja de ser malvado'