summaryrefslogtreecommitdiffstats
path: root/lib/Smokeping/probes/passwordchecker.pm
blob: d1cc128a6a94b75dda4cd8162e3910d44182d14a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package Smokeping::probes::passwordchecker;

=head1 301 Moved Permanently

This is a Smokeping probe module. Please use the command 

C<smokeping -man Smokeping::probes::passwordchecker>

to view the documentation or the command

C<smokeping -makepod Smokeping::probes::passwordchecker>

to generate the POD document.

=cut

use strict;
use Smokeping::probes::basefork;
use base qw(Smokeping::probes::basefork);
use Carp;

my $e = "=";
sub pod_hash {
	return {
		name => <<DOC,
Smokeping::probes::passwordchecker - A Base Class for implementing SmokePing Probes
DOC
		overview => <<DOC,
Like Smokeping::probes::basefork, but supports a probe-specific configuration file
for storing passwords and a method for accessing them.
DOC

		description => <<DOC,
${e}head2 synopsis with more detail

SmokePing main configuration file:

 *** Probes ***
 + MyPasswordChecker
 # location of the file containing usernames and passwords
 passwordfile = /usr/share/smokeping/etc/passwords

The specified password file:

 # host:username:password
 host1:joe:hardlyasecret
  # comments and whitespace lines are allowed

 host2:sue:notasecreteither

${e}head2 Actual description

In implementing authentication probes, it might not be desirable to store
the necessary cleartext passwords in the SmokePing main configuration
file, since the latter must be readable both by the SmokePing daemon
performing the probes and the CGI that displays the results. If the
passwords are stored in a different file, this file can be made readable
by only the user the daemon runs as. This way we can be sure that nobody
can trick the CGI into displaying the passwords on the Web.

This module reads the passwords in at startup from the file specified
in the probe-specific variable `passwordfile'. The passwords can later
be accessed and modified by the B<password> method, that needs the corresponding
host and username as arguments.

${e}head2 Password file format

The password file format is simply one line for each triplet of host,
username and password, separated from each other by colons (:).

Comment lines, starting with the `#' sign, are ignored, as well as
empty lines.
DOC
		authors => <<'DOC',
Niko Tyni <ntyni@iki.fi>
DOC

		bugs => <<DOC,
The need for storing cleartext passwords can be considered a bug in itself.
DOC

		see_also => <<DOC,
L<Smokeping::probes::basefork>, L<Smokeping::probes::Radius>, L<Smokeping::probes::LDAP>
DOC
	}
}

sub ProbeDesc {
	return "probe that can fork, knows about passwords and doesn't override the ProbeDesc method";
}

sub probevars {
	my $class = shift;
	return $class->_makevars($class->SUPER::probevars, {
		passwordfile => {
			_doc => "Location of the file containing usernames and passwords.",
			_example => '/some/place/secret',
			_sub => sub {
				my $val = shift;
				-r $val or return "ERROR: password file $val is not readable.";
				return undef;
			},
		},
	});
}

sub new {
        my $proto = shift;
        my $class = ref($proto) || $proto;
        my $self = $class->SUPER::new(@_);

	# no need for this if we run as a cgi
	unless ($ENV{SERVER_SOFTWARE}) {

	        if (defined $self->{properties}{passwordfile}) {
			my @stat = stat($self->{properties}{passwordfile});
			my $mode = $stat[2];
			carp("Warning: password file $self->{properties}{passwordfile} is world-readable\n") 
				if defined $mode and $mode & 04;
				
			open(P, "<$self->{properties}{passwordfile}") 
				or croak("Error opening specified password file $self->{properties}{passwordfile}: $!");
			while (<P>) {
				chomp;
				next unless /\S/;
				next if /^\s*#/;
				my ($host, $username, $password) = split(/:/);
				carp("Line $. in $self->{properties}{passwordfile} is invalid"), next unless defined $host and defined $username and defined $password;
				$self->password($host, $username, $password);
			}
			close P;
	        }
	}


        return $self;
}

sub password {
	my $self = shift;
	my $host = shift;
	my $username = shift;
	my $newval = shift;
	$self->{password}{$host}{$username} = $newval if defined $newval;
	return $self->{password}{$host}{$username};
}

1;