package Smokeping::probes::LDAP; =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 Smokeping::probes::passwordchecker; use Net::LDAP; use Time::HiRes qw(gettimeofday sleep); use base qw(Smokeping::probes::passwordchecker); # don't bail out if IO::Socket::SSL # can't be loaded, just warn # about it when doing starttls my $havessl = 0; eval "use IO::Socket::SSL;"; $havessl = 1 unless $@; my $DEFAULTINTERVAL = 1; sub pod_hash { return { name => < < <::') 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 DOC bugs => <SUPER::probevars; delete $h->{timeout}; return $h; } sub targetvars { my $class = shift; return $class->_makevars($class->SUPER::targetvars, { _mandatory => [ 'filter' ], port => { _re => '\d+', _doc => "TCP port of the LDAP server", _example => 389, }, version => { _re => '\d+', _doc => "The LDAP version to be used.", _example => 3, }, start_tls => { _doc => "If true, encrypt the connection with the starttls command. Disabled by default.", _sub => sub { my $val = shift; return "ERROR: start_tls defined but IO::Socket::SSL couldn't be loaded" if $val and not $havessl; return undef; }, _example => "1", }, timeout => { _doc => "LDAP query timeout in seconds.", _re => '\d+', _example => 10, _default => 5, }, base => { _doc => "The base to be used in the LDAP query", _example => "dc=foo,dc=bar", }, filter => { _doc => "The actual search to be made", _example => "uid=testuser", }, attrs => { _doc => "The attributes queried.", _example => "uid,someotherattr", }, binddn => { _doc => "If present, authenticate the LDAP bind with this DN.", _example => "uid=testuser,dc=foo,dc=bar", }, password => { _doc => "The password to be used, if not present in .", _example => "mypass", }, mininterval => { _default => $DEFAULTINTERVAL, _doc => "The minimum interval between each query sent, in (possibly fractional) second s.", _re => '(\d*\.)?\d+', }, scope => { _doc => "The scope of the query. Can be either 'base', 'one' or 'sub'. See the Net::LDAP documentation for details.", _example => "one", _re => "(base|one|sub)", _default => "sub", }, }); } sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = $class->SUPER::new(@_); return $self; } sub pingone { my $self = shift; my $target = shift; my $host = $target->{addr}; my $vars = $target->{vars}; my $version = $vars->{version} || 3; my $port = $vars->{port}; my $mininterval = $vars->{mininterval}; my $binddn = $vars->{binddn}; my $timeout = $vars->{timeout}; my $scope = $vars->{scope}; my $password; if (defined $binddn) { $password = $self->password($host, $binddn); if (defined $vars->{password} and $vars->{password} ne ($self->{properties}{password}||"")) { $password = $vars->{password}; } $password ||= $self->{properties}{password}; } my $start_tls = $vars->{start_tls}; my $filter = $vars->{filter}; my $base = $vars->{base}; my $attrs = $vars->{attrs}; my @attrs = split(/,/, $attrs||""); my $attrsref = @attrs ? \@attrs : undef; my @times; my $start; for (1..$self->pings($target)) { if (defined $start) { my $elapsed = gettimeofday() - $start; my $timeleft = $mininterval - $elapsed; sleep $timeleft if $timeleft > 0; } local $IO::Socket::SSL::SSL_Context_obj; # ugly but necessary $start = gettimeofday(); my $ldap = new Net::LDAP($host, port => $port, version => $version, timeout => $timeout) or do { $self->do_log("connection error on $host: $!"); next; }; my $mesg; if ($start_tls) { $mesg = $ldap->start_tls; $mesg->code and do { $self->do_log("start_tls error on $host: " . $mesg->error); $ldap->unbind; next; } } if (defined $binddn and defined $password) { $mesg = $ldap->bind($binddn, password => $password); } else { if (defined $binddn and not defined $password) { $self->do_debug("No password specified for $binddn, doing anonymous bind instead"); } $mesg = $ldap->bind(); } $mesg->code and do { $self->do_log("bind error on $host: " . $mesg->error); $ldap->unbind; next; }; $mesg = $ldap->search(base => $base, filter => $filter, attrs => $attrsref, scope => $scope); $mesg->code and do { $self->do_log("filter error on $host: " . $mesg->error); $ldap->unbind; next; }; $ldap->unbind; my $end = gettimeofday(); my $elapsed = $end - $start; $self->do_debug("$host: LDAP query $_ took $elapsed seconds"); push @times, $elapsed; } return sort { $a <=> $b } @times; } 1;