summaryrefslogtreecommitdiffstats
path: root/lib/probes/Radius.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/probes/Radius.pm')
-rw-r--r--lib/probes/Radius.pm212
1 files changed, 138 insertions, 74 deletions
diff --git a/lib/probes/Radius.pm b/lib/probes/Radius.pm
index 2c4fb96..6d0705b 100644
--- a/lib/probes/Radius.pm
+++ b/lib/probes/Radius.pm
@@ -1,55 +1,54 @@
package probes::Radius;
-=head1 NAME
+=head1 301 Moved Permanently
-probes::Radius - a RADIUS authentication probe for SmokePing
-
-=head1 OVERVIEW
-
-Measures RADIUS authentication latency for SmokePing
+This is a Smokeping probe module. Please use the command
-=head1 SYNOPSYS
+C<smokeping -man probes::Radius>
- *** Probes ***
- + Radius
+to view the documentation or the command
- passwordfile = /usr/share/smokeping/etc/password
- secretfile = /etc/raddb/secret
- sleeptime = 0.5 # optional, 1 second by default
- username = test-user # optional, overridden by target
- password = test-password # optional, overridden by target
- secret = test-secret # optional, overridden by target
+C<smokeping -makepod probes::Radius>
- *** Targets ***
+to generate the POD document.
- probe = Radius
+=cut
- + PROBE_CONF
- username = testuser
- secret = myRadiusSecret # if not present in <secretfile>
- password = testuserPass # if not present in <passwordfile>
- port = 1645 # optional
- nas_ip_address = 1.2.3.4 # optional
+use strict;
+use base qw(probes::passwordchecker);
+use Authen::Radius;
+use Time::HiRes qw(gettimeofday sleep);
+use Carp;
-=head1 DESCRIPTION
+my $DEFAULTINTERVAL = 1;
+sub pod_hash {
+ return {
+ name => <<DOC,
+probes::Radius - a RADIUS authentication probe for SmokePing
+DOC
+ overview => <<DOC,
+Measures RADIUS authentication latency for SmokePing
+DOC
+ description => <<DOC,
This probe measures RADIUS (RFC 2865) authentication latency for SmokePing.
The username to be tested is specified in either the probe-specific or the
target-specific variable `username', with the target-specific one overriding
the probe-specific one.
-The password can be specified either (in order of precedence, with the latter
-overriding the former) in the probe-specific variable `password', in the
-target-specific variable `password' or in an external file. The location of
-this file is given in the probe-specific variable `passwordfile'. See
-probes::passwordchecker(3pm) for the format of this file (summary:
-colon-separated triplets of the form `<host>:<username>:<password>')
+The password can be specified either (in order of precedence, with
+the latter overriding the former) in the probe-specific variable
+`password', in an external file or in the target-specific variable
+`password'. The location of this file is given in the probe-specific
+variable `passwordfile'. See probes::passwordchecker(3pm) for the
+format of this file (summary: colon-separated triplets of the form
+`<host>:<username>:<password>')
The RADIUS 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 the
-target-specific variable `secret' or in an external file.
+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 `<host> <secret>'. Comments and blank lines
are OK.
@@ -58,26 +57,18 @@ If the optional probe-specific variable `nas_ip_address' is specified, its
value is inserted into the authentication requests as the `NAS-IP-Address'
RADIUS attribute.
-The probe tries to be nice to the server and sleeps for the probe-specific
-variable `sleeptime' (one second by default) between each authentication
-request.
-
-=head1 AUTHOR
-
-Niko Tyni E<lt>ntyni@iki.fiE<gt>
-
-=head1 BUGS
-
+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',
+Niko Tyni <ntyni@iki.fi>
+DOC
+ bugs => <<DOC,
There should be a more general way of specifying RADIUS attributes.
-
-=cut
-
-use strict;
-use probes::passwordchecker;
-use base qw(probes::passwordchecker);
-use Authen::Radius;
-use Time::HiRes qw(gettimeofday sleep);
-use Carp;
+DOC
+ }
+}
sub ProbeDesc {
return "RADIUS queries";
@@ -91,6 +82,10 @@ sub 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 (<S>) {
@@ -105,10 +100,6 @@ sub new {
close S;
}
- my $sleeptime = $self->{properties}{sleeptime};
- $sleeptime = 1 unless defined $sleeptime;
- $self->sleeptime($sleeptime);
-
}
return $self;
@@ -123,21 +114,21 @@ sub secret {
return $self->{secret}{$host};
}
-sub sleeptime {
- my $self = shift;
- my $newval = shift;
-
- $self->{sleeptime} = $newval if defined $newval;
- return $self->{sleeptime};
-}
-
sub pingone {
my $self = shift;
my $target = shift;
my $host = $target->{addr};
my $vars = $target->{vars};
- my $username = $vars->{username} || $self->{properties}->{username};
- my $secret = $vars->{secret} || $self->secret($host) || $self->{properties}->{secret};
+ my $mininterval = $vars->{mininterval};
+ my $username = $vars->{username};
+ my $secret = $self->secret($host);
+ 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 RADIUS secret for $host"), return
unless defined $secret;
@@ -145,17 +136,27 @@ sub pingone {
$self->do_log("Missing RADIUS username for $host"), return
unless defined $username;
- my $password = $vars->{password} || $self->password($host, $username) || $self->{properties}->{password};
-
- my $port = $vars->{port};
- $host .= ":$port" if defined $port;
+ 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 RADIUS password for $host/$username"), return
unless defined $password;
+ my $port = $vars->{port};
+ $host .= ":$port" if defined $port;
+
my @times;
+ my $elapsed;
for (1..$self->pings($target)) {
- my $r = new Authen::Radius(Host => $host, Secret => $secret);
+ if (defined $elapsed) {
+ my $timeleft = $mininterval - $elapsed;
+ sleep $timeleft if $timeleft > 0;
+ }
+ my $r = new Authen::Radius(Host => $host, Secret => $secret, TimeOut => $timeout);
$r->add_attributes(
{ Name => 1, Value => $username, Type => 'string' },
{ Name => 2, Value => $password, Type => 'string' },
@@ -172,13 +173,76 @@ sub pingone {
$result = "OK" if $c == ACCESS_ACCEPT;
$result = "fail" if $c == ACCESS_REJECT;
} else {
- $result = "no reply";
+ if (defined $r->get_error) {
+ $result = "error: " . $r->strerror;
+ } else {
+ $result = "no reply";
+ }
}
- $self->do_debug("$host: radius query $_: $result, " . ($end - $start));
- push @times, $end - $start if (defined $c and $c == ACCESS_ACCEPT);
- sleep $self->sleeptime; # be nice
+ $elapsed = $end - $start;
+ $self->do_debug("$host: radius query $_: $result, $elapsed");
+ push @times, $elapsed if (defined $c and $c == ACCESS_ACCEPT);
}
return sort { $a <=> $b } @times;
}
+sub probevars {
+ my $class = shift;
+ my $h = $class->SUPER::probevars;
+ delete $h->{timeout};
+ return $class->_makevars($h, {
+ secretfile => {
+ _doc => <<DOC,
+A file containing the RADIUS shared secrets for the targets. It should contain
+whitespace-separated pairs of the form `<host> <secret>'. 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 RADIUS shared secret for the target, if not present in the secrets file.',
+ _example => 'test-secret',
+ },
+ nas_ip_address => {
+ _doc => 'The NAS-IP-Address RADIUS attribute for the authentication requests. Not needed everywhere.',
+ _example => '10.1.2.3',
+ },
+ 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 RADIUS queries.",
+ _re => '\d+',
+ },
+ port => {
+ _doc => 'The RADIUS port to be used',
+ _re => '\d+',
+ _example => 1645,
+ },
+ });
+}
+
1;