#!/usr/bin/perl -w
# Coondog-helper
# (c) 2006 Erik Meitner <emeitner@f2o.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#

use strict;
use Config::General;
use POSIX qw/WNOHANG/;

sub usage {
		print <<USAGE;

$0 [ setperms | cleanup | start | stop | setspd | flushspd | checkpol LOCALIP GATE LOCALADDR REMOTEADDR ]
	
USAGE
}

$ENV{'PATH'} = '';

die "This program must be SUID root to function properly." unless $> == 0;

my $coondog_directory = '.coondog';
my $racoon_path = '/usr/sbin/racoon';
my $setkey_path = '/usr/sbin/setkey';

if( @ARGV < 1 ){
	usage;
	exit;
}

my $command = lc shift;
die "'$command' is not a valid command." if ! ($command =~ /^setperms|cleanup|start|stop|setspd|flushspd|checkpol$/);

my @uinfo = getpwuid( $< );
my $homedir = $uinfo[7];
die "Unable to determine home directory for UID $<\n" unless $homedir;

my $confdir = "$homedir/$coondog_directory";
my $racoon_config = "$confdir/racoon.conf";
my $racoon_pid_file = "$confdir/racoon.pid";
my $racoon_log = "$confdir/racoon.log";
my $setkey_config = "$confdir/setkey.conf";
my $psk_file = "$confdir/psk.txt";
my $config_file = $confdir.'/coondog.conf';

die "'$confdir' is a symlimk, not a directory." if( -l $confdir );
die "Directory '$confdir' does not exist. " if( ! -d $confdir );

if( $command eq 'setperms' or $command eq 'cleanup' ){
	my @files = ( $psk_file, $racoon_config, $setkey_config );
	foreach my $file ( @files ){
		die "'$file' is a symlink." if( -l $file );
		next if ! -f $file;
		if( $command eq 'setperms' ){
			die "Unable to chmod '$file'." unless chmod 0600, $file;
			die "Unable to chown '$file'." unless chown 0,0,$file;
		}else{
			print "Unable to unlink '$file'." unless unlink $file;
		}
	}
}elsif( $command eq 'start' ){
	my $arg = '';
	$arg = 'd' if lc $ARGV[0] eq 'debug';
	my $pid;
	if ($pid = fork) {
		umask 0033;
		die "Unable to open PID file for writing." if( ! open PIDFILE, ">$racoon_pid_file" );
		print PIDFILE $pid;
		close PIDFILE;
		print "racoon started as PID $pid\n";
	} else {
		$<=$>; #racoon doesn't like suid
		# am child, run racoon.
	    print "cannot fork: $!" unless defined $pid;
	    open(STDOUT, ">$racoon_log") or print "Can't redirect stdout: $!";
		open(STDERR, ">&STDOUT") or print "Can't dup stdout: $!";
		select( STDOUT);
		$|=1;
		exec( $racoon_path, "-v4LF$arg", '-f', $racoon_config ) or die "Can't exec: $!\n";
	}
}elsif( $command eq 'stop' ){
		die "Racoon PID file does not exist." unless -e $racoon_pid_file;
		die "Unable to open racoon PID file.\n" unless open PIDFILE,$racoon_pid_file ;
		my $pid = <PIDFILE>;
		$pid =~ /^([0-9]+)/; $pid=$1;  # Um.. bypass taint checks...
		close PIDFILE;
		kill( 15, $pid );
		unlink $racoon_pid_file;
}elsif( $command eq 'setspd' ){
		exit system( $setkey_path, "-f", $setkey_config );
}elsif( $command eq 'flushspd' ){
		exit system( $setkey_path, '-FP' );
}elsif( $command eq 'checkpol' ){
		if( @ARGV != 4 ){
			usage;
			exit;	
		}
		my ($localip,$gateip, $local, $remote ) = @ARGV;
		my $policies = `/bin/echo "dump;spddump;" | $setkey_path -c`;
		# check for SPD
		if( $policies =~ m%^$remote\[any\] $local\[any\] any\s+in prio def ipsec\s+esp/tunnel/$gateip-$localip/unique#(\d+)%m ){
			my $reqid = $1;
			#check for SAD
			while( $policies =~ m%^$gateip $localip\s+\w+ mode=\w+ spi=[^)]+\) reqid=(\d+)\(%mg ){
				exit 0 if $1 eq $reqid;
			}
		}
		exit 1; # no matching policy found
}

exit 0;
