I have cobbled together a working AuthcNDS module that allows me to authenticate users
against the netware NDS tree within our office.
My apache server is running on a RH5.2 box with some of the Caldera netware RPMs
installed.
Server Version: Apache/1.3.9 (Unix) mod_fastcgi/2.2.2 PHP/3.0.12 mod_perl/1.21
The module doesn't communicate directly with the NDS, but instead uses a command line
tool (nwlogin) to do the password checking. Unfortunately spawning that process is
rather time consuming so once the user is authenticated, the module sets a cookie
based on the username, IP, a 'secret' and MD5 hashes it.
The module, when asked to authenticate a user, first checks for that cookie. If it
doesn't exist, it then goes to the NDS.
I used one of the Apache auth-cookie modules as a starting point.
I need to fix up the spawning of the nwlogin process as i think the users password
would be visible in the output from 'ps auxw'. Perhaps i should pipe the password to
nwlogin instead of passing it as a command line arguement.
Any other suggestions for tidying it up? Have i made any glaring mistakes? (this is my
first mod_perl effort)
Unfortunately it is not self contained and is reliant upon the nwlogin tool that comes
with the Caldera RPMs.
===
package Apache::AuthcNDS;
#
# file: Apache/AuthcNDS.pm
#
# performs authentication against the NDS and makes use of cookies to cache
# authentication info so that we dont have to reauth for every single hit
# which can be quite time consuming as the auth process requires the use of
# an external shell program.
#
use strict;
use MD5;
use CGI::Cookie;
use Apache::Constants qw(:common);
my $r;
sub handler
{
$r = shift;
my ($cookie , $user, $auth_cookie , $authstring, $md5check, $newcookie ,
$cookie_text);
my ($res, $sent_pw) = $r->get_basic_auth_pw;
return $res if $res != OK;
$user = $r->connection->user;
#
# the secret is the only bit of text not derived from the user
# this stops them from creating a fake cookie ( i hope )
#
my $secret = 'elegiac';
#
# search thru the cookies for ours...
# if our cookie shows up, they must have already authenticated
# if so i can avoid the expensive NDS lookup
#
($auth_cookie) = ( ($r->header_in("Cookie") || "") =~ /Auth_NDS_ID=([^;]+)/);
if ($auth_cookie)
{
$md5check = join('-', $user , &MakeHash($user , $r->auth_type ,
$r->auth_name , $secret));
if ($md5check eq $auth_cookie)
{ return OK; }
}
# failing that we have to actually check their credentials
# against the NDS
if (!&auth_nds ( $user , $sent_pw ))
{
# authentication failed !
# log that and clear the cookie on the users machine
$r->note_basic_auth_failure;
$r->log_reason("Authentication of [$user] against NDS
failed.", $r->filename);
$r->err_headers_out->{'Set-Cookie'} = "Auth_NDS_ID=";
return AUTH_REQUIRED;
}
else
{
# authentication succeeded, so let them know, and set the
cookie for future reference
$cookie_text = join('-', $user , &MakeHash($user ,
$r->auth_type , $r->auth_name , $secret));
$newcookie = new CGI::Cookie(-name => 'Auth_NDS_ID',
-value => $cookie_text);
$r->err_headers_out->{'Set-Cookie'} = $newcookie;
return OK;
}
}
#==
# make up the hash to be placed in the cookie (hash cookies? ;)
#==
sub MakeHash()
{
my ($u , $at , $an , $d) = @_;
my $authstring = $u . $at . $an . $d;
return MD5->hexhash($authstring)
}
#==
# check the username and password against the NDS
#==
sub auth_nds()
{
my ($user_abs , $current, $authenticated);
my ($user,$pass) = @_;
# dont process if username or password is blank
if (!$user || !$pass) { return 0;}
# check to see if someone else is logged in
$current = `nwwhoami`;
# if someone else is currently logged in then note that and wait 1
second
if ($current) { $r->log_reason ("someone already logged in. sleeping
for 1s." , $r->filename) ; sleep 1; }