#!/usr/bin/perl
#
#
# call parameter:  environment REMOTE_ADDR
# returns  "AUTH BREAK" or nothing
#
# required packages: libnetaddr-ip-perl
# storage backend: database
# DB config: /etc/benno-web/benno.conf
# DB tables: remoteaddress_whitelist
# 
#  table remoteaddress_whitelist
#
#    CREATE TABLE `remoteaddress_whitelist` (
#     `address` varchar(128) NOT NULL DEFAULT ''    COMMENT "IPv4 or IPv6 address/network",
#     `userid` varchar(64) NOT NULL DEFAULT ''      COMMENT "username or @userdomain",
#     PRIMARY KEY (`address`,`userid`)
#    );
#
use strict;
use Carp;
use DBI;
use NetAddr::IP qw(:lower);
use lib qw(/usr/share/benno-web);
use Benno::Config;

my $DEBUG = $ENV{DEBUG};

my $error = 0;
my %domains;

my $firstline  = <STDIN>; chomp $firstline;
my $secondline = <STDIN>; chomp $secondline;

my ($bennouser,$bennopass);
if (!$secondline) {
    # AUTHPROTO: STDIN
    ($bennouser,$bennopass) = split /\s+/,$firstline,2;
}
else {
    # AUTHPROTO: STDIN2
    $bennouser = $firstline;
    $bennopass = $secondline;
}

my $configfile = '/etc/benno-web/benno.conf';
my $Config = new Benno::Config($configfile);

my $remote_addr = $ENV{REMOTE_ADDR};
unless ($remote_addr) {
    print STDERR "'REMOTE_ADDR' not set in environment.\n" if $DEBUG >= 2;
    exit 0;
}

my ($userid,$userdomain) = split /\@/, $bennouser, 2;

my $dbh = db_connect($Config);
my $sth = $dbh->prepare('SELECT address FROM remoteaddress_whitelist WHERE userid = ?');

my $userid_found    = 0;
my $net_whitelisted = 0;
$sth->execute($bennouser); 
while (my $wl_network = $sth->fetchrow_arrayref) {
    $userid_found++;
    if (check_address($remote_addr,$wl_network->[0])) {
        $net_whitelisted++;
    }
}
$sth->execute('@'.$userdomain); 
while (my $wl_network = $sth->fetchrow_arrayref) {
    $userid_found++;
    if (check_address($remote_addr,$wl_network->[0])) {
        $net_whitelisted++;
    }
}

if ($userid_found and not $net_whitelisted) {
    print STDERR "Authentication from $remote_addr prohibited for $bennouser.\n";
    print "AUTH BREAK\n";
    exit 0;
}

print STDERR "Authentication from $remote_addr allowed for $bennouser.\n" if $DEBUG >= 2;
exit 0;


### SUBS #############################################

### check_address
sub check_address
{
    my ($remote_addr,$wl_network) = @_;
    my ($Network, $Remote);

    $remote_addr = lc $remote_addr;
    $wl_network  = lc $wl_network;
    if ($wl_network =~ /:/) {
        return unless $remote_addr =~ /:/;      # check IPv6 against IPv6

        print STDERR "Check address $remote_addr on $wl_network\n" if $DEBUG >= 2;
        $Network  = NetAddr::IP->new6($wl_network);
        $Remote   = NetAddr::IP->new6($remote_addr);
    }
    else {
        print STDERR "Check address $remote_addr on $wl_network\n" if $DEBUG >= 2;
        $Network  = NetAddr::IP->new($wl_network);
        $Remote   = NetAddr::IP->new($remote_addr);
    }

    if ($Network->contains($Remote)) {
        return 1;
    }
    return 0;
}


# db_connect
sub db_connect
{   
    my $Config = shift;

    my $dbtype = $Config->get('DBTYPE') || 'sqlite:////var/lib/benno-web/bennoweb.sqlite';
    my $db     = $Config->get('DATABASE');
    my $dbhost = $Config->get('DBHOST') || 'localhost';
    my $dbport = $Config->get('DBPORT') || 3306;
    my $dbuser = $Config->get('DBUSER');
    my $dbpass = $Config->get('DBPASS');
    my $DBH;
    if ($dbtype eq 'mysql') {
        eval {
            my $dsn = "DBI:mysql:database=$db;host=$dbhost;port=$dbport";
            $DBH = DBI->connect($dsn,$dbuser,$dbpass);
        };
        if ($@) {
            print STDERR "ERROR: MySQL Driver not installed.\n";
            exit 1;
        }
    }
    else {
        if ($dbtype =~ /^sqlite:\/\/(\/.\S+)$/) {
            my $dsn = "dbi:SQLite:dbname=$1","","";
            $DBH = DBI->connect($dsn);
        }
    }
    return $DBH;
}




