Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I've written a python CGI script that invoke bash commands, and it needs to test for a successful login on the host.

Is it possible to create a bash script that will test a given username and password combination against the registred user on the host?

share|improve this question
1  
Perhaps you could look at the code behind the login program. –  Kevin Jan 11 '12 at 1:29
    
Not related to the question, but I hope you are encrypting the traffic to your web server so that user logins can't be sniffed off the wire. –  jw013 Jan 12 '12 at 17:38
add comment

8 Answers

up vote 6 down vote accepted

Using PAM is the best solution. You can write small C code, or install python-pam package and use a python script which comes with the python-pam package. See /usr/share/doc/python-pam/examples/pamtest.py

share|improve this answer
    
I try PAM but it didn't work. But I try again this example and it work. –  jcubic Jan 21 '12 at 0:44
    
In OpenSUSE 12.3 (python-pam 0.5.0-84.1.1) and 13.1 (0.5.0-87.1.2), the full path to pamtest.py is /usr/share/doc/packages/python-pam/examples/pamtest.py The script pamtest.py can be used to test credentials on systems using PAM for authentication, is included in the python-pam package (which requires python), and in some distributions the full path is /usr/share/doc/python-pam/examples/pamtest.py. –  Polyergic Jan 2 at 5:05
add comment

Since you mentioned that you are using CGI in python, its probably appropriate to assume that you are using Apache as your httpd server. If so, leave the authentication process of your program to Apache and only let the authenticated people execute your cgi scripts/programs.

There are ample modules which can do authentication for you on Apache, it really depends on what kind of authentication mechanism you are looking for. The way you quoted in the question seems to be related to the local host account authentication based on /etc/passwd,shadow files. Module that comes to my quick search regarding this is mod_auth_shadow. Advantage is that you are allowing someone authoritative (running on privilege port 80) to authenticate the user/password for you and you can rely on authenticated information of the user to run the commands on behalf of the user, if needed.

Good links to start:

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

Another approach is to use SuEXEc module of Apache, which executes processes(cgi programs) on behalf of the authenticated user.

share|improve this answer
    
This CGI script is JSON-RPC service called via Ajax and I need method login that return a token, token should be returned if login succeed. So basicaly every user need to be able to execute that script. –  jcubic Jan 14 '12 at 0:20
add comment

The right approach to testing whether a user can log in is to actually log in as that user.

So what I recommend is to make the CGI script use expect to run su, pass a password and run the command that must be executed. Here's a draft of an expect script that does just this (warning: absolutely untested, and I'm not fluent in expect). Substitute in the user name, password and command (where I wrote bob, swordfish and somecommand); be sure to quote correctly.

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

If you really don't want to execute the command through a layer of su (for example because what you do has to be performed by the CGI process itself), then use expect to run the command true and check that the return status is 0.

Another approach would be to use PAM directly in your application, through Python's PAM binding.

share|improve this answer
    
It is awesome, The only solution that without root access. –  jcubic Jan 14 '12 at 0:10
    
this works su -c true bob && echo success it shame that su don't accept password as argument –  jcubic Jan 14 '12 at 0:15
    
I tested su from CGI script and it need a terminal for it to work. –  jcubic Jan 21 '12 at 0:39
add comment

There is a 'C', 'Python' PAM solution quoted here, let me put the perl one too :-)

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();
share|improve this answer
    
Yeah, ok, but the question was about a CGI script written in Python. –  Gilles Jan 12 '12 at 1:09
add comment

After some search I wrote this C program that can be used from 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;
    }
}

You compile it with:

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

You can use it as

sudo ./check_passwd <user> <password> && echo "success" || echo "failure"
share|improve this answer
1  
Bad idea! You're assuming that your program is running as root, or at least as the shadow group, which is very strongly not recommended for a CGI: you'd need another layer of privilege escalation. And you're assuming a particular password hashing algorithm (one supported by openssl) and password storage location (/etc/shadow as opposed to e.g. NIS or LDAP) which may or may not be the one actually used for that particular user. Use PAM, it knows its job. –  Gilles Jan 12 '12 at 1:10
    
Yes I know, but thought that this is not posible with without root. All other solutions use root as well, exept yours. –  jcubic Jan 14 '12 at 0:10
add comment

If you have root access, and are using md5 passwords, and you just need to compare passwords, then you can use the perl Crypt::PasswdMD5 module. Take the MD5 Hash from /etc/shadow, strip the $1$, and then split on the remaining $. Field 1 = Salt, Field 2 = crypted text. Then hash the text input into your CGI, compare that to the crypted text, and Bob's your uncle.

#!/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";
}
share|improve this answer
2  
plain and simple :-). This will work as long as /etc/passwd uses md5 hashing.Incase the authentication (nsswitch) is different for the system, then pam modules are best to use. –  Nikhil Mulley Jan 11 '12 at 7:38
2  
Bad idea! You're assuming that your program is running as root, or at least as the shadow group, which is very strongly not recommended for a CGI: you'd need another layer of privilege escalation. And you're assuming a particular password hashing algorithm (MD5 and not bcrypt or other recommended algorithm) and password storage location (/etc/shadow as opposed to e.g. NIS or LDAP) which may or may not be the one actually used for that particular user. –  Gilles Jan 12 '12 at 1:08
add comment

To more specifically answer: "Is it possible to create a bash script that will test a given username and password combination against the registred user on the host?"

Yes.

#!/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
share|improve this answer
2  
Did this work for you? I am seeing that you are checking an existing shadow password against the shadow password but where is the hashing involved here? –  Nikhil Mulley Jan 11 '12 at 7:36
    
I tested and it don't work –  jcubic Jan 11 '12 at 9:44
3  
Bad idea! You're assuming that your program is running as root, or at least as the shadow group, which is very strongly not recommended for a CGI: you'd need another layer of privilege escalation. And you're assuming a particular password hashing algorithm (one supported by openssl) and password storage location (/etc/shadow as opposed to e.g. NIS or LDAP) which may or may not be the one actually used for that particular user. –  Gilles Jan 12 '12 at 1:07
add comment

The best thing you can do, if you need a script to login to a host, is configure a ssh key between the hosts.

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

I pretty much lifted this from the page


First, install OpenSSH on two UNIX machines, hurly and burly. This works best using DSA keys and SSH2 by default as far as I can tell. All the other HOWTOs I've seen seem to deal with RSA keys and SSH1, and the instructions not surprisingly fail to work with SSH2. On each machine type ssh somemachine.example.com and make a connection with your regular password. This will create a .ssh dir in your home directory with the proper perms. On your primary machine where you want your secret keys to live (let's say hurly), type

ssh-keygen -t dsa

This will prompt you for a secret passphrase. If this is your primary identity key, make sure to use a good passphrase. If this works right you will get two files called id_dsa and id_dsa.pub in your .ssh dir. Note: it is possible to just press the enter key when prompted for a passphrase, which will make a key with no passphrase. This is a Bad Idea ™ for an identity key, so don't do it! See below for uses of keys without passphrases.

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

Copy the id_dsa.pub file to the other host's .ssh dir with the name authorized_keys2. Now burly is ready to accept your ssh key. How to tell it which keys to use? The ssh-add command will do it. For a test, type

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

This will start the ssh-agent, add your default identity(prompting you for your passphrase), and spawn a bash shell. From this new shell you should be able to:

ssh burly

You should be able to log in

share|improve this answer
    
While this is true, it does not appear to be relevant to the question, which is about an application accessed through a web browser. –  Gilles Jan 12 '12 at 1:05
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.