#!/usr/bin/perl -w
# $OpenLDAP: olala.pl,v 0.1.0 2012/07/28 walery $
#
## OpenLDAP AccessLog Application (OLALA).
##  OpenLDAP AccessLog processor - stow log records from OpenLDAP AccessLog 
## overlay and execute some code for certain events, such as change user password
## and so on
##
## Copyright 2012 Walery Wysotsky.
## All rights reserved.print join "\n",__PACKAGE__->plugins;
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted only as authorized by the GPL
##

package OLALA;

use strict;
use vars qw(@ISA);
use Net::Server::PreFork;
use Sys::Syslog qw(:standard :macros);

@ISA = qw(Net::Server::PreFork);

my $socket = "/var/run/olala.sock";
my $DEBUG = 0;

OLALA->run(
  port=>"$socket|unix",
  user=>"openldap",
  background=>1,
);



exit;



sub process_request {
 my $self = shift;

 eval {


  local $SIG{ALRM} = sub { die "Timed Out!\n" };
  my $timeout = 5; # give the user 30 seconds to type a line
  alarm($timeout);

  my $request = <STDIN>;
  if ($request eq "UNBIND\n") {
   return;
  }

  openlog("OLALA", "perror,pid", LOG_LOCAL0);

  my %req = ();
  my %mod = ();
  syslog(LOG_DEBUG, "Request: $request") if ($DEBUG == 1);
  if ($request eq "ADD\n") {
   while (my $line = <STDIN>) {
    chomp($line);
    last if $line eq "";
    syslog(LOG_DEBUG, " line: $line") if ($DEBUG == 1);
    if ($line =~ /^reqMod::\s*(.*)$/) {
     next;
    } elsif ($line =~ /^reqMod:\s*(.*):(.)\s*(.*)$/) {
     my @val = ($3,$2);
     $mod{$1} = \@val;
    } elsif ($line =~ /^req(.*?):\s(.*)$/) {
     $req{$1} = $2;
    }
   }
   if (%req) {
    syslog(LOG_DEBUG, " add to log") if ($DEBUG == 1);
    log_request(\%req,\%mod);
    if ((exists $mod{'userPassword'}) || ($req{'Type'} eq "delete")) {
     syslog(LOG_DEBUG, " call chg_password()") if ($DEBUG == 1);
     chg_password(\%req,\%mod);
    }
   }
  } elsif ($request eq "SEARCH\n") {
# do nothing
  } else {
# Not ADD to log ???  
   my $full = "";
   while (my $line = <STDIN>) {
    chomp($line);
    last if $line eq "";
    $full .= "$line | ";
   }
   chomp($request);
   syslog(LOG_WARNING,"%s: %s", $request,$full);
  }

  syslog(LOG_DEBUG, " close syslog") if ($DEBUG == 1);
  closelog();
  if (! ($request eq "UNBIND\n")) {
   print "RESULT\n";
   print "code: 0\n";
  }
 };

 return unless $@;
 if( $@=~/timed out/i ){
  print "RESULT\n";
  print "code: 3\n"; # timeLimitExceeded
  print "info: Timed out\n";
 } else {
  print "RESULT\n";
  print "code: 1\n"; # operationsError
  print "info: $@\n"; # FIXME: remove CR/LF
 }
}


sub log_request() {
 
# my $self = shift;
 my $req = shift;
 my $mod = shift;
 
 if ($DEBUG == 1) {
  my $full = "";
  while( my ($k, $v) = each %{$mod} ) {
   my @val = @{$v};
   $full .= "$k $val[1] $val[0] | ";
  }
  syslog(LOG_DEBUG, "%s: %s %s %s (%s) result=%s", $req->{'Start'},$req->{'AuthzID'},$req->{'Type'},$req->{'DN'},$full,$req->{'Result'});
 } else {
  syslog(LOG_INFO, "%s %s %s", $req->{'AuthzID'},$req->{'Type'},$req->{'DN'});
 }
 return;
}


sub chg_password() {
# my $self = shift;
 my $req = shift;
 my $mod = shift;

 my @dom = ($req->{'DN'} =~ /dc=(\w+)/g);
 my $domain = uc(join(".",@dom));
 my @name = $req->{'DN'} =~ /^cn=(\w+),/;
 my $type = $req->{'Type'};

 my @value;
 if (exists $mod->{'userPassword'}) {
  @value = @{$mod->{'userPassword'}};
 }
 
 my $cmd; # = "/usr/bin/sudo ";
 if ($type eq "add") {
  $cmd .= "/usr/sbin/kadmin.local -r $domain -q 'addprinc -x dn=\"$req->{'DN'}\" -pw $value[0] $name[0]'";
 } elsif ($type eq "modify") {
  if ($value[1] eq "+") {
   $cmd .= "/usr/sbin/kadmin.local -r $domain -q 'addprinc -x dn=\"$req->{'DN'}\" -pw $value[0] $name[0]'";
  } elsif ($value[1] eq "=") {
   $cmd .= "/usr/sbin/kadmin.local -r $domain -q 'cpw -x dn=\"$req->{'DN'}\" -pw $value[0] $name[0]'";
  } else {
   syslog(LOG_WARNING, "Change type [%s] undefined", $value[1]);
   return;
  }
 } elsif ($type eq "delete") {
   $cmd .= "/usr/sbin/kadmin.local -r $domain -q 'delprinc -x dn=\"$req->{'DN'}\" $name[0]'"
 }
 syslog(LOG_DEBUG,$cmd) if ($DEBUG == 1);

 my @res = `$cmd`;

 if ( $? == -1 ) {
  syslog(LOG_WARNING, " command failed: $!\n");
 } else {
  syslog(LOG_WARNING, " command exited with value %d", $? >> 8);
 }

 syslog(LOG_DEBUG, " Result: ", join("\n",@res)) if ($DEBUG == 1);

 return;

}



1;
