#!/usr/bin/perl -w

use strict;
use Authen::Krb5;
use Getopt::Long;
use File::MkTemp;
use lib "/usr/lib/nagios/plugins";
use utils qw(%ERRORS &print_revision);
use vars qw($PROGNAME);
use vars qw($opt_V $opt_h $opt_H $opt_r $opt_p $opt_k $opt_P $verbose);

$PROGNAME = "check_krb5";

sub version () {
    print_revision($PROGNAME, '$Revision: 1.0 $');
    exit $ERRORS{"OK"};
}

sub usage () {
    print_revision($PROGNAME, '$Revision: 1.0 $');
    print "Copyright (c) 2005 Jonathan Chen\n";
    print "          (c) 2010 Instituto Superior Tecnico";
    print "\n";
    print "Perl Check Kerberos 5 plugin for Nagios\n";
    print "\n";
    print "Usage: $PROGNAME -H <host> -d <realm> -p <principal> -k <keytab_file> [-p <port>]\n";
    print "\n";
    print "-H, --hostname=HOST\n";
    print "\tName or IP address of host to check\n";
    print "-r, --realm=NAME\n";
    print "\tKerberos realm to authenticate to\n";
    print "-p, --principal=NAME\n";
    print "\tName of principal to try authentication as\n";
    print "-k, --keytab=file\n";
    print "\tPath to keytab file containing key to principal\n";
    print "-P, --port=INTEGER\n";
    print "\tPort the kdc runs on\n";
    print "-v, --verbose\n";
    print "\tPrint extra debugging information\n";
    exit $ERRORS{"OK"};
}

sub dieunknown ($) {
    print "KRB5 UNKNOWN: @_\n";
    exit $ERRORS{"UNKNOWN"};
}

sub diecritical ($) {
    print "Error: @_\n";
    exit $ERRORS{"CRITICAL"};
}

if ($#ARGV == -1) {
    usage();
}

Getopt::Long::Configure("bundling");
GetOptions(
    "V"   => \$opt_V,   "version"     => \$opt_V,
    "h"   => \$opt_h,   "help"        => \$opt_h,
    "H=s" => \$opt_H,   "hostname=s"  => \$opt_H,
    "r=s" => \$opt_r,   "realm=s"     => \$opt_r,
    "p=s" => \$opt_p,   "principal=s" => \$opt_p,
    "k=s" => \$opt_k,   "keytab=s"    => \$opt_k,
    "P:i" => \$opt_P,   "port:i"      => \$opt_P,
    "v"   => \$verbose, "verbose"     => \$verbose
);

if ($opt_V) {
    version();
}

if ($opt_h) {
    usage();
}

($opt_H) || diecritical("Hostname not specified");
my $host = $1 if ($opt_H =~ /^([-_.A-Za-z0-9]+\$?)$/);
($host) || diecritical("Invalid hostname: $opt_H");

($opt_r) || diecritical("Realm not specified");
my $realm = $1 if ($opt_r =~ /^([-_.A-Za-z0-9]+\$?)$/);
($realm) || diecritical("Invalid realm: $opt_r");

($opt_p) || diecritical("Principal not specified");
my $princ= $1 if ($opt_p =~ /^([^\@]+)$/);
($princ) || diecritical("Invalid principal: $opt_p");

($opt_k) || diecritical("Keytab file not specified");
my $keytab = $1 if ($opt_k =~ /^(\/.*)$/);
($keytab) || diecritical("Invalid keytab: $opt_k");

($opt_P) || ($opt_P = 88);
my $port = $1 if ($opt_P =~ /^([0-9]+)$/);
($port) || diecritical("Invalid port: $opt_P");


my ($fh, $template) = mkstempt("krb5.conf.XXXXXX", "/tmp");
if (!defined($fh)) {
    dieunknown("Unable to create temp config file");
}
$template = "/tmp/" . $template;

print $fh "[libdefaults]\n";
print $fh "default_realm = $realm\n";
print $fh "[realms]\n";
print $fh "$realm = {\n";
print $fh "kdc = ${host}:${port}\n";
print $fh "}\n";
close $fh;
$ENV{"KRB5_CONFIG"} = $template;

Authen::Krb5::init_context() || dieunknown("Cannot initialize Kerberos context");
Authen::Krb5::init_ets();

my $cc = Authen::Krb5::cc_resolve("MEMORY:check_krb5");
($cc) || dieunknown("Cannot resolve MEMORY CC");
my $princo = Authen::Krb5::parse_name($princ);
($princo) || dieunknown("Cannot resolve principal $princ");
my $servo = Authen::Krb5::parse_name("krbtgt/" . $realm);
($servo) || dieunknown("Cannot resolve principal krbtgt/$realm");
my $kto = Authen::Krb5::kt_resolve($keytab);
($servo) || dieunknown("Cannot resolve keytab file $keytab");
$cc->initialize($princo);
my $ret = Authen::Krb5::get_in_tkt_with_keytab($princo, $servo, $kto, $cc);

($verbose) && print "ret: $ret\n";
($verbose) && print "err: " . Authen::Krb5::error() . "\n";

my $ecode;

if (defined($ret) && $ret == 1) {
    print "KRB5 OK\n";
    $ecode = $ERRORS{"OK"};
} else {
    print "KRB5 CRITICAL: " . Authen::Krb5::error() . "\n";
    $ecode = $ERRORS{"CRITICAL"};
}

$cc->destroy;
unlink($template);
exit $ecode;
