package Smokeping::probes::TacacsPlus; =head1 301 Moved Permanently This is a Smokeping probe module. Please use the command C to view the documentation or the command C to generate the POD document. =cut use strict; use base qw(Smokeping::probes::passwordchecker); use Authen::TacacsPlus; use Time::HiRes qw(gettimeofday sleep); use Carp; my $DEFAULTINTERVAL = 1; sub pod_hash { return { name => < < <::') The TacacsPlus protocol requires a shared secret between the server and the client. This secret can be specified either (in order of precedence, with the latter overriding the former) in the probe-specific variable `secret', in an external file or in the target-specific variable `secret'. This external file is located by the probe-specific variable `secretfile', and it should contain whitespace-separated pairs of the form ` '. Comments and blank lines are OK. The default TacacsPlus authentication type is ASCII. PAP and CHAP are also available. See the Authen::TacacsPlus documentation for more information; The probe tries to be nice to the server and does not send authentication requests more frequently than once every X seconds, where X is the value of the target-specific "min_interval" variable ($DEFAULTINTERVAL by default). DOC authors => <<'DOC', Gary Mikula DOC bugs => <SUPER::new(@_); # no need for this if we run as a cgi unless ($ENV{SERVER_SOFTWARE}) { if (defined $self->{properties}{secretfile}) { my @stat = stat($self->{properties}{secretfile}); my $mode = $stat[2]; carp("Warning: secret file $self->{properties}{secretfile} is world-readable\n") if defined $mode and $mode & 04; open(S, "<$self->{properties}{secretfile}") or croak("Error opening specified secret file $self->{properties}{secretfile}: $!"); while () { chomp; next unless /\S/; next if /^\s*#/; my ($host, $secret) = split; carp("Line $. in $self->{properties}{secretfile} is invalid"), next unless defined $host and defined $secret; $self->secret($host, $secret); } close S; } } return $self; } sub secret { my $self = shift; my $host = shift; my $newval = shift; $self->{secret}{$host} = $newval if defined $newval; return $self->{secret}{$host}; } sub pingone { my $self = shift; my $target = shift; my $host = $target->{addr}; my $vars = $target->{vars}; my $mininterval = $vars->{mininterval}; my $username = $vars->{username}; my $authen_type = $vars->{authtype}; my $secret = $self->secret($host); my @times; my $elapsed; my $result; my $start; my $authen; my $end; if (defined $vars->{secret} and $vars->{secret} ne ($self->{properties}{secret}||"")) { $secret = $vars->{secret}; } $secret ||= $self->{properties}{secret}; my $timeout = $vars->{timeout}; $self->do_log("Missing TacacsPlus secret for $host"), return unless defined $secret; $self->do_log("Missing TacacsPlus username for $host"), return unless defined $username; my $password = $self->password($host, $username); if (defined $vars->{password} and $vars->{password} ne ($self->{properties}{password}||"")) { $password = $vars->{password}; } $password ||= $self->{properties}{password}; $self->do_log("Missing TacacsPlus password for $host/$username"), return unless defined $password; my $port = $vars->{port}; $host .= ":$port" if defined $port; for (1..$self->pings($target)) { if (defined $elapsed) { my $timeleft = $mininterval - $elapsed; sleep $timeleft if $timeleft > 0; } $host =~ s/:[0-9]+//g; my $r = new Authen::TacacsPlus(Host => $host, Key => $secret, Port =>$port); if( $r ) { $start = gettimeofday(); if( $authen_type eq 'PAP' ){ $authen = &Authen::TacacsPlus::TAC_PLUS_AUTHEN_TYPE_PAP; }elsif( $authen_type eq 'CHAP'){ $authen = &Authen::TacacsPlus::TAC_PLUS_AUTHEN_TYPE_CHAP; }else{ $authen = &Authen::TacacsPlus::TAC_PLUS_AUTHEN_TYPE_ASCII; } if( $r->authen($username, $password, $authen ) ) { $end = gettimeofday(); $elapsed = $end - $start; $self->do_debug("$host: TacacsPlus Authen Granted: $elapsed time"); push @times, $elapsed; $r->close(); }else{ $self->do_log("Unable to Autenticate to:$host with ID:$username Key:$secret"); $result = "Unable to Authenticate Msg: " . Authen::TacacsPlus::errmsg(); $self->do_debug("$result"); } }else{ $self->do_log("Unable to Create Constructor Authen::TacacsPlus for host:$host"); $result = "Unable to Build Constructor Msg: " . Authen::TacacsPlus::errmsg(); $self->do_debug("$result"); } } return sort { $a <=> $b } @times; } sub probevars { my $class = shift; my $h = $class->SUPER::probevars; delete $h->{timeout}; return $class->_makevars($h, { secretfile => { _doc => < '. Comments and blank lines are OK. DOC _example => '/another/place/secret', _sub => sub { my $val = shift; -r $val or return "ERROR: secret file $val is not readable."; return undef; }, }, }); } sub targetvars { my $class = shift; return $class->_makevars($class->SUPER::targetvars, { _mandatory => [ 'username' ], username => { _doc => 'The username to be tested.', _example => 'test-user', }, password => { _doc => 'The password for the user, if not present in the password file.', _example => 'test-password', }, secret => { _doc => 'The TacacsPlus shared secret for the target, if not present in the secrets file.', _example => 'test-secret', }, mininterval => { _default => $DEFAULTINTERVAL, _doc => "The minimum interval between each authentication request sent, in (possibly fractional) seconds.", _re => '(\d*\.)?\d+', }, timeout => { _default => 5, _doc => "Timeout in seconds for the TacacsPlus queries.", _re => '\d+', }, port => { _default => 49, _doc => 'The TacacsPlus port to be used', _re => '\d+', _example => 49, }, authtype => { _default => 'ASCII', _doc => 'The TacacsPlus Authentication type:ASCII(default), CHAP, PAP', _re => '(ASCII|CHAP|PAP)', _example => 'CHAP', }, }); } 1;