#! /usr/bin/perl -T
# (c) 1999 Heiko Schlittermann
# usage: $ME <userlist>
# external authenticator for squid password auth method
# reads: USER PASSWORD on stdin
# writes: OK | ERR on stdout
# NOTE: NOT NIS / NIS+ compatible
# Needs to run set-uid root since it has to read /etc/shadow!

#use strict;

my $SHADOW = "/etc/shadow"; # where to find the shadow iff we need it

my %PASSWD;
sub modified(@);
sub init_passwd($);

BEGIN {
    $ENV{PATH} = "/usr/local/sbin:/usr/sbin:/sbin"
        . ":/usr/local/bin:/usr/bin:/bin";
    ($<,$>) = ($>,$<);  # don't act as root!
}

open(STDERR, ">>/var/log/squid_auth.log");;
$| = 1; # flush!


# --- endless loop
while (<STDIN>) {
    chomp;

    if (modified("/etc/passwd", "/etc/shadow", $ARGV[0])) {
        %PASSWD = init_passwd($ARGV[0]) or die($@);
        #map { print "$_ => $PASSWD{$_}\n"; } keys %PASSWD;
    }

    my ($user, $pw) = (split / /, $_, 2);
    my $crypt = $PASSWD{$user};

    print STDERR "AUTH req: $user / $pw\n";

    if ($PASSWD{$user} && (crypt($pw, $PASSWD{$user}) eq $PASSWD{$user})) {
        print "OK\n";
        print STDERR "OK\n";
        next;
    }

    print "ERR\n";
    print STDERR "ERR\n";
}

# -- 

{
    my %last_modified;
sub modified(@) {
    my $r;

    foreach (@_) {
        my $mtime;
        if (($mtime = (stat)[9]) > $last_modified{$_}) {
            #print STDERR "modified: $_\n";
            $r = $last_modified{$_} = $mtime;
        }
    }

    return $r;
} }

sub init_passwd($) {
    local $_;
    my $userlist = shift @_;
    my %user;

    # read the possible users from the user list
    open(I, $_ = "<$userlist") or die("Can't open $_: $!\n");
    while (<I>) {   chomp;
        my $u;
        /^#/ and next;
        /#DISABLED#/ and next; # for compatibility
        ($u) = (split /[\s:]/ )[0];
        $user{$u} = (getpwnam($u))[1];
    }
    close(I);

    # resolve shadow
    if (-f $SHADOW) {
        my %shadow;

        ($>,$<) = ($<,$>); # become root
        open(I, $_ = "<$SHADOW") or die("Can't open $_: $!\n");
        ($>,$<) = ($<,$>); # revoke root

        while (<I>) {
            my ($u, $p) = (split /:/)[0,1];
            $shadow{$u} = $p;
        }

        map { $user{$_} eq 'x' and $user{$_} = $shadow{$_} } keys %user;
    }
    #map { print "$_ => $user{$_}\n"; } sort keys %user;
    return %user;
}

# vim:set aw:
