#
# $Id: nnrpd_auth.pl.in,v 1.2.2.1 2000/09/18 22:38:52 kondou Exp $
#
# Sample authentication code for nnrpd hook.
#

#
# This file is loaded when nnrpd starts up. If it defines a sub named
# `authenticate', then that function will be called during processing of a
# connect, auth request or disconnect.  Attributes about the connection are 
# passed to the program in the %attributes global variable.  It should return 
# an array with 5 elements:
#
# 1) NNTP response code.  Should be one of the codes from %connectcodes or %authcodes
# 2) Reading Allowed. Should be a boolean value.
# 3) Posting Allowed. Should be a boolean value.
# 4) Wildmat expression that says what groups to provide access to.
# 5) Maximum bytes per second a client is permitted to use for retrieving
#    articles
#
# All five of these are required.  If there is a problem with them then nnrpd
# will die and syslog the exact reason.

#
# Sample Auth program
#

use vars qw(@readerconfig);
use CDB_File;

$authfile = "@DBDIR@/users.cdb";
$defaultgroups = "*";

require "@LIBDIR@/innshellvars.pl";

my (%connectcodes) = ("read/post" => 200, "read" => 201, "authneeded" => 480, "permdenied" => 502);
my (%authcodes)    = ("allowed" => 281, "denied" => 502);

sub loadnnrp {
  my($file) = shift(@_);
  my($block, $perm, $user, $pass, %tmp);
  
  open(F, $file) || die "Could not open $file: $!\n";
  
  while (<F>) {
    my (%tmp);
    
    chomp;	
    s/\#.*//g;
    ($block, $perm, $user, $pass, $tmp{groups}) = split(/:/);
    if (!defined($tmp{groups})) {
      undef %tmp;
      next;
    }
    #   Change the glob-type pattern to a regexp
    if ( ! ($block =~ /(\d+\.\d+\.\d+\.\d+)\/(\d+)/))
    {
      #   turn a glob type pattern into a regexp
      #       escape dots
      $block =~ s/\./\./g;
      #       turn ? into .
      $block =~ s/\?/./g;
      #       turn * into .*
      $block =~ s/\*/.*/g;
    }
    $tmp{block} = $block;

    $tmp{canread} = 1 if ($perm =~ /r/i);
    $tmp{canpost} = 1 if ($perm =~ /p/i);
    unshift(@readerconfig, \%tmp);
  }
  close(F);    
}

# This is called by nnrpd when it first starts up.
sub auth_init {
  &loadnnrp($inn::newsetc . "/nnrp.access");
  tie(%users, 'CDB_File', $authfile) || warn "Could not open CDB file for users: $!\n";
}

# This is called when a user connects or authenticates
sub authenticate {
  my $key;
  foreach $key (keys %attributes) {
  }
  if ($attributes{type} eq "connect") {
    my (@results) = checkhost();
    return @results;
  } elsif ($attributes{type} eq "authenticate") {
    return checkuser();
  } elsif ($attributes{type} eq "disconnect") {
  }
  return 502;
}

sub checkuser {
  my $user = $attributes{'username'};
  my $pass = $attributes{'password'};

  if (!defined($users{$user})) {
    return ($authcodes{'denied'}, undef, undef, undef, undef);
  }
  my ($password, $news_post, $maxcon, $subscription) = split(/\t/, $users{$user});
  my ($salt) = substr($password, 0, 2);
  if (crypt($pass, $salt) ne $password) {
    return ($authcodes{'denied'}, undef, undef, undef, undef);
  }
  $news_post = lc($news_post);
  $news_post = ($news_post eq 'y') ? 1 : 0;
  if (!defined($subscription)) {
    $subscription = $defaultgroups;
  }
  return ($authcodes{'allowed'}, 1, $news_post , $subscription, undef);
}

sub permtocode {
  my ($read, $post) = @_;

  return $connectcodes{'read/post'} if ($post);
  return $connectcodes{'read'} if ($read);
  return $connectcodes{'authneeded'};
}

sub checkhost {
  my ($key, $block, $mask, $ip);

  foreach $key (@readerconfig) {
    # Process CIDR style entries first
    my ($read, $post) = ($key->{canread}, $key->{canpost});

    if ($key->{block} =~ /(\d+\.\d+\.\d+\.\d+)\/(\d+)/) {
      $block = unpack('N', pack('C4', split(/\./, $1)));
      $mask = (0xffffffff << (32 - $2)) & 0xffffffff;
      $block = $block & $mask;
      $ip = unpack('N', pack('C4', split(/\./, $attributes{ipaddress})));
      if (($ip & $mask) == $block) {
        return (permtocode($read, $post) , $read, $post, $key->{groups}, undef);
      }
    }
    if ($attributes{ipaddress} =~ /$key->{block}/) {
      return (permtocode($read, $post), $read, $post, $key->{groups}, undef);
    }
    if ($attributes{hostname} =~ /$key->{block}/) {
      return (permtocode($read, $post), $read, $post, $key->{groups}, undef);
    }
  }
  return ($connectcodes{'permdenied'}, undef, undef, undef, undef);
}

