#!/usr/bin/perl # ezpauth.pl # (c) 1999-2000 Useful Utilities # http://www.usefulutilities.com # These are a set of core routines for building external authentication # scripts for use with EZproxy # Revised 8/26/99 to correct problems in AuthIII with wrong extraction of # expiration date # Revised 11/12/99 to change last name comparison in AuthIII to deal with # last names containing spaces (eg. Du Bois) # Revised 11/23/99 to correct minor problem with default port number use Socket; use Time::Local; sub Debug { return if $debugFile eq ""; if ($debugOpen != 1) { open(DEBUG, ">>$debugFile") || return; $debugOpen = 1; } flock(DEBUG, LOCK_EX); seek(DEBUG, 0, 2); print DEBUG $_[0]; flock(DEBUG, LOCK_UN); } sub Down { Debug("$_[0]\n"); print "Content-type: text/html\n\n"; print $_[0], "\n"; exit(1); } sub FormEncode { my $temp = $_[0]; $temp =~ s/([&'"])/sprintf("&#%d;",ord($1))/ge; return $temp; } sub URLEncode { my $temp = $_[0]; $temp =~ s/([^A-Za-z0-9])/sprintf("%%%2X", ord($1))/eg; $temp =~ s/%20/+/g; return $temp; } sub ParseFields { my $in, $fieldval, $field, $val, $count; %in = (); if ($ENV{"REQUEST_METHOD"} eq "GET") { # url= is extracted straight from the QUERY_STRING on a GET to preserve # all the characters that follow url=. EZproxy expects this to be handled # in this manner if ($ENV{"QUERY_STRING"} =~ s/&?url=(.*)$//) { $in{"url"} = $1; } $in = $ENV{"QUERY_STRING"}; } elsif ($ENV{"REQUEST_METHOD"} eq "POST") { # In a POST, no special processing of URL is required read(STDIN, $in, $ENV{"CONTENT_LENGTH"}); } else { return 0; } $count = 0; foreach $fieldval (split(/&/, $in)) { $fieldval =~ s/\+/ /g; ($field, $val) = split(/=/, $fieldval, 2); $val =~ s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge; # Remove inresult characters $val =~ s/[^!-~]//g; $in{$field} = $val; $count++; } if (defined $in{"url"}) { $ezpurl = $in{"url"} }; return $count; } sub ParseHostPort { if ($_[0] =~ /^(.*):(\d+)$/) { return ($1, $2); } return @_; } sub MakeConnection { my $fh, $host, $port, $proto, $proto, $oldfh; ($fh, $host, $port) = @_; if ($host =~ s/:(\d+)$//) { $port = $1; } $proto = getprotobyname('tcp'); $iaddr = inet_aton($host) || Down("Unable to lookup host $host: $!"); $paddr = sockaddr_in($port, $iaddr); socket($fh, PF_INET, SOCK_STREAM, $proto) || Down("Unable to create socket: $!"); connect($fh, $paddr) || Down("Unable to connect to $host:$port: $!"); $oldfh = select($fh); $| = 1; select($oldfh); } sub StartSession { my $query; $query = "?user=" . URLEncode($ezpuser) . "&pass=" . URLEncode($ezppass); # If the calling script has set $ezploguser, pass this value to EZproxy # to log to the ezproxy.log file. Note that your ezproxy.cfg must also # contain "O LOGUSER" to enable this feature. if ($ezploguser ne "") { $query .= "&loguser=" . URLEncode($ezploguser); } # We don't URLEncode the query since EZproxy expects it to be literal if ($ezpurl ne "") { $query .= "&url=$ezpurl"; } MakeConnection(EZPROXY, $ezphost, $ezpport); print EZPROXY "GET /login$query HTTP/1.0\n\n"; $skip = ; print while ; close(EZPROXY); } # AuthPOP return 0 if authenticated, 1 if not sub AuthPOP { my $host, $user, $pass, $result, $state; ($host, $user, $pass) = @_; MakeConnection(POP, $host, 110); $result = 1; for ($state = 0; ; $state++) { last unless /^\+OK/; if ($state == 0) { print POP "USER $user\n"; } elsif ($state == 1) { print POP "PASS $pass\n"; } else { $result = 0; last; } } print POP "QUIT\n"; close(POP); return $result; } # AuthIMAP return 0 if authenticated, 1 if not sub AuthIMAP { my $host, $user, $pass, $result, $state; ($host, $user, $pass) = @_; MakeConnection(IMAP, $host, 143); $result = 1; for ($state = 0; ; $state++) { chop; last unless /^. OK/; if ($state == 0) { print IMAP "1 LOGIN $user $pass\n"; } else { $result = 0; last; } } print IMAP "2 LOGOUT\n"; close(IMAP); return $result; } # AuthFTP return 0 if authenticated, 1 if not sub AuthFTP { my $host, $user, $pass, $result, $state; ($host, $user, $pass) = @_; MakeConnection(FTP, $host, 21); $result = 1; for ($state = 0; ; $state++) { chop; if ($state == 0) { next if /^120/; last unless /^220/; print FTP "USER $user\n"; } elsif ($state == 1) { last unless /^331/; print FTP "PASS $pass\n"; } else { last unless /^230/; $result = 0; last; } } print FTP "QUIT\n"; close(FTP); return $result; } # AuthIII return 0 if authenticated, 1 if not found, 2 if expired sub AuthIII { my $host, $user, $pass, $result, $expire, $mon, $day, $year, $last, $part, $type, $types, $typeresult, $nameresult, $idresult; ($host, $user, $pass, $types) = @_; $types = ":$types:"; Debug("Attempt III for $user\n"); MakeConnection(III, $host, 4500); print III "GET /PATRONAPI/$user/dump HTTP/1.0\n\n"; $idresult = 1; $nameresult = 1; $typeresult = ($types eq "::" ? 0 : 3); while () { Debug($_); s/\r?\n?$//; if (/^EXP DATE\[p43\]=(\d{1,2})-(\d{1,2})-(\d{2,4})
/) { ($mon, $day, $year) = ($1, $2, $3); if ($year >= 1900) { $year -= 1900 }; # We've extracted the month, day and year, so now timelocal is used # to change that to a numeric time comparable to the time() function # For timelocal, we must subtract 1 from the month $expire = timelocal(0, 0, 0, $day, $mon - 1, $year); # Return 0 on unexpired number, 2 if found but expired $idresult = time() < $expire ? 0 : 2; } if (/P TYPE\[p47\]=(.+)
$/) { $type = ":$1:"; if ($type ne "::" && $types =~ /$type/) { $typeresult = 0; } } if (/^PATRN NAME\[pn\]=(.+)
$/) { $last = $1; # Remove everything after first comma $last =~ s/,.*//; # Remove all spaces $last =~ s/\s//g; $last =~ tr/a-z/A-Z/; $pass =~ tr/a-z/A-Z/; if ($last eq $pass) { $nameresult = 0; } } } # If the name matched, return the date check result, otherwise return # the "not found" result if ($nameresult == 0) { if ($idresult == 0) { $result = $typeresult; } else { $result = $idresult; } } else { $result = 1; } close(III); Debug("Result for $user is $result\n"); return $result; } $action = $0; $action =~ s/^.*[\/\\]//g; $ezpport = 2048; $| = 1; 1;