Comment écrire un test de connexion au système?

9

J'ai écrit un script CGI Python qui appelle des bashcommandes, et il doit tester une connexion réussie sur l'hôte.

Comment puis-je écrire un test pour cela?

Par exemple, pourrais-je créer un bashscript qui teste une combinaison de nom d'utilisateur et de mot de passe donnée contre l'utilisateur enregistré sur l'hôte?

jcubic
la source
1
Vous pourriez peut-être regarder le code derrière le loginprogramme.
Kevin
Pas lié à la question, mais j'espère que vous cryptez le trafic vers votre serveur Web afin que les connexions utilisateur ne puissent pas être détectées.
jw013

Réponses:

8

L'utilisation de PAM est la meilleure solution. Vous pouvez écrire un petit code C, ou installer un package python-pam et utiliser un script python fourni avec le package python-pam. Voir/usr/share/doc/python-pam/examples/pamtest.py

Michał Šrajer
la source
J'essaie PAM mais cela n'a pas fonctionné. Mais j'essaye encore cet exemple et ça marche.
jcubic
1
Dans OpenSUSE 12.3 (python-pam 0.5.0-84.1.1) et 13.1 (0.5.0-87.1.2), le chemin d'accès complet à pamtest.py est /usr/share/doc/packages/python-pam/examples/pamtest.pyLe script pamtest.py peut être utilisé pour tester les informations d'identification sur les systèmes utilisant PAM pour l'authentification, est inclus dans le paquet python-pam (qui nécessite python), et dans certaines distributions le chemin complet est /usr/share/doc/python-pam/examples/pamtest.py.
ShadSterling
5

La bonne approche pour tester si un utilisateur peut se connecter consiste à se connecter en tant que cet utilisateur.

Donc, ce que je recommande, c'est d'utiliser le script CGI expectpour exécuter su, de passer un mot de passe et d'exécuter la commande qui doit être exécutée. Voici un brouillon d'un script expect qui fait exactement cela (avertissement: absolument non testé, et je ne parle pas couramment expect). Remplacer par le nom d'utilisateur, le mot de passe et la commande (là où j'ai écrit bob, swordfishet somecommand); assurez-vous de citer correctement.

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

Si vous ne voulez vraiment pas exécuter la commande via une couche de su(par exemple parce que ce que vous faites doit être effectué par le processus CGI lui-même), utilisez expect pour exécuter la commande trueet vérifiez que l'état de retour est 0.

Une autre approche consisterait à utiliser PAM directement dans votre application, via la liaison PAM de Python .

Gilles 'SO- arrête d'être méchant'
la source
C'est génial, la seule solution sans accès root.
jcubic
cela fonctionne su -c true bob && echo successdommage que su n'accepte pas le mot de passe comme argument
jcubic
J'ai testé à supartir du script CGI et il a besoin d'un terminal pour que cela fonctionne.
jcubic
3
@jcubic C'est une idée vraiment stupide de mettre un mot de passe dans un argument de ligne de commande, car les arguments de ligne de commande sont publics sur un système Unix. La suppression du mot de passe fournirait le même niveau de sécurité que sa mise en ligne de commande. Et ce serait beaucoup plus facile à vérifier: ce /bin/trueserait suffisant.
ceving
2

Pour répondre plus spécifiquement: "Est-il possible de créer un script bash qui testera une combinaison de nom d'utilisateur et de mot de passe donnée contre l'utilisateur inscrit sur l'hôte?"

Oui.

#!/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 Redbeard
la source
2
Est-ce que cela a fonctionné pour vous? Je constate que vous vérifiez un mot de passe caché existant par rapport au mot de passe caché, mais où est le hachage impliqué ici?
Nikhil Mulley
J'ai testé et ça ne marche pas
jcubic
3
Mauvaise idée! Vous supposez que votre programme s'exécute en tant que root, ou du moins en tant que shadowgroupe, ce qui est très fortement déconseillé pour un CGI: vous auriez besoin d'une autre couche d'escalade de privilèges. Et vous supposez un algorithme de hachage de mot de passe particulier (pris en charge par openssl) et un emplacement de stockage de mot de passe ( /etc/shadowpar exemple NIS ou LDAP) qui peuvent ou non être ceux réellement utilisés pour cet utilisateur particulier.
Gilles 'SO- arrête d'être méchant'
2

Il y a une solution PAM 'C', 'Python' citée ici, permettez-moi de mettre celle en perl aussi :-)

Source: 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
la source
Oui, d'accord, mais la question portait sur un script CGI écrit en Python.
Gilles 'SO- arrête d'être méchant'
1

Si vous avez un accès root, et que vous utilisez des mots de passe md5, et que vous avez juste besoin de comparer les mots de passe, vous pouvez utiliser le module perl Crypt :: PasswdMD5 . Prenez le MD5 Hash de / etc / shadow, supprimez le $ 1 $, puis divisez le reste. Champ 1 = sel, champ 2 = texte crypté. Puis hachez la saisie de texte dans votre CGI, comparez-la au texte crypté, et Bob est votre oncle.

#!/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
la source
2
clair et simple :-). Cela fonctionnera tant que / etc / passwd utilise le hachage md5. Dans le cas où l'authentification (nsswitch) est différente pour le système, alors les modules pam sont les meilleurs à utiliser.
Nikhil Mulley
2
Mauvaise idée! Vous supposez que votre programme s'exécute en tant que root, ou du moins en tant que shadowgroupe, ce qui est très fortement déconseillé pour un CGI: vous auriez besoin d'une autre couche d'escalade de privilèges. Et vous supposez un algorithme de hachage de mot de passe particulier (MD5 et non bcrypt ou un autre algorithme recommandé) et un emplacement de stockage de mot de passe ( /etc/shadowpar exemple NIS ou LDAP) qui peut ou non être celui réellement utilisé pour cet utilisateur particulier.
Gilles 'SO- arrête d'être méchant'
0

Après quelques recherches, j'ai écrit ce programme C qui peut être utilisé à partir du 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;
    }
}

Vous le compilez avec:

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

Vous pouvez l'utiliser comme

sudo ./check_passwd <user> <password> && echo "success" || echo "failure"
jcubic
la source
1
Mauvaise idée! Vous supposez que votre programme s'exécute en tant que root, ou du moins en tant que shadowgroupe, ce qui est très fortement déconseillé pour un CGI: vous auriez besoin d'une autre couche d'escalade de privilèges. Et vous supposez un algorithme de hachage de mot de passe particulier (pris en charge par openssl) et un emplacement de stockage de mot de passe ( /etc/shadowpar exemple NIS ou LDAP) qui peuvent ou non être ceux réellement utilisés pour cet utilisateur particulier. Utilisez PAM, il connaît son métier.
Gilles 'SO- arrête d'être méchant'
Oui je sais, mais j'ai pensé que ce n'est pas possible avec sans racine. Toutes les autres solutions utilisent également root, sauf la vôtre.
jcubic
0

Puisque vous avez mentionné que vous utilisez CGI en python, il est probablement approprié de supposer que vous utilisez Apache comme serveur httpd. Si c'est le cas, laissez le processus d'authentification de votre programme à Apache et laissez uniquement les personnes authentifiées exécuter vos scripts / programmes cgi.

Il existe de nombreux modules qui peuvent faire l'authentification pour vous sur Apache, cela dépend vraiment du type de mécanisme d'authentification que vous recherchez. La façon dont vous avez cité la question semble être liée à l'authentification du compte hôte local basée sur / etc / passwd, les fichiers shadow. Le module qui vient à ma recherche rapide à ce sujet est mod_auth_shadow. L'avantage est que vous autorisez une personne faisant autorité (exécutée sur le port de privilège 80) à authentifier l'utilisateur / mot de passe pour vous et que vous pouvez compter sur des informations authentifiées de l'utilisateur pour exécuter les commandes au nom de l'utilisateur, si nécessaire.

De bons liens pour commencer:

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

Une autre approche consiste à utiliser le module SuEXEc d'Apache, qui exécute des processus (programmes cgi) au nom de l'utilisateur authentifié.

Nikhil Mulley
la source
Ce script CGI est un service JSON-RPC appelé via Ajax et j'ai besoin d'une méthode de connexion qui renvoie un jeton, le jeton doit être renvoyé si la connexion réussit. Donc, fondamentalement, chaque utilisateur doit pouvoir exécuter ce script.
jcubic
0

Ce code utilisant PAM a fonctionné pour moi:

#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
la source
Pourriez-vous ajouter des informations contextuelles pour que cela "ressemble" plus à une réponse?
Volker Siegel le
-3

La meilleure chose que vous puissiez faire, si vous avez besoin d'un script pour vous connecter à un hôte, est de configurer une clé ssh entre les hôtes.

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

J'ai quasiment retiré cela de la page


Tout d'abord, installez OpenSSH sur deux machines UNIX, rapidement et massivement. Cela fonctionne mieux en utilisant les clés DSA et SSH2 par défaut pour autant que je sache. Tous les autres HOWTO que j'ai vus semblent traiter des clés RSA et SSH1, et les instructions ne fonctionnent pas, sans surprise, avec SSH2. Sur chaque machine, tapez ssh somemachine.example.com et établissez une connexion avec votre mot de passe habituel. Cela va créer un répertoire .ssh dans votre répertoire personnel avec les perms appropriés. Sur votre machine principale où vous voulez que vos clés secrètes vivent (disons rapidement), tapez

ssh-keygen -t dsa

Cela vous demandera une phrase secrète. S'il s'agit de votre clé d'identité principale, assurez-vous d'utiliser une bonne phrase secrète. Si cela fonctionne correctement, vous obtiendrez deux fichiers appelés id_dsa et id_dsa.pub dans votre répertoire .ssh. Remarque: il est possible d'appuyer simplement sur la touche Entrée lorsque vous êtes invité à saisir une phrase secrète, ce qui créera une clé sans phrase secrète. Ceci est une mauvaise idée ™ pour une clé d'identité, alors ne le faites pas! Voir ci-dessous pour l'utilisation des clés sans mots de passe.

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

Copiez le fichier id_dsa.pub dans le répertoire .ssh de l'autre hôte avec le nom authorized_keys2. Burly est maintenant prêt à accepter votre clé ssh. Comment lui dire quelles touches utiliser? La commande ssh-add le fera. Pour un test, tapez

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

Cela démarrera l'agent ssh, ajoutera votre identité par défaut (vous demandant votre phrase secrète) et générera un shell bash. À partir de ce nouveau shell, vous devriez pouvoir:

ssh burly

Vous devriez pouvoir vous connecter

Roy Rico
la source
Bien que cela soit vrai, cela ne semble pas être pertinent pour la question, qui concerne une application accessible via un navigateur Web.
Gilles 'SO- arrête d'être méchant'