line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::Radius::Client; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
5549
|
use 5.008; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
36
|
|
4
|
1
|
|
|
1
|
|
1981
|
use IO::Socket::INET; |
|
1
|
|
|
|
|
27066
|
|
|
1
|
|
|
|
|
9
|
|
5
|
1
|
|
|
1
|
|
1753
|
use Net::Radius::Dictionary; |
|
1
|
|
|
|
|
2823
|
|
|
1
|
|
|
|
|
41
|
|
6
|
1
|
|
|
1
|
|
993
|
use Net::Radius::Packet; |
|
1
|
|
|
|
|
6092
|
|
|
1
|
|
|
|
|
92
|
|
7
|
1
|
|
|
1
|
|
13
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
39
|
|
8
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
43
|
|
9
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1216
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
require Exporter; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export |
16
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead. |
17
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# This allows declaration use Net::Radius::Client ':all'; |
20
|
|
|
|
|
|
|
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK |
21
|
|
|
|
|
|
|
# will save memory. |
22
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw( |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
) ] ); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our @EXPORT = qw( |
29
|
|
|
|
|
|
|
load |
30
|
|
|
|
|
|
|
query |
31
|
|
|
|
|
|
|
); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
34
|
|
|
|
|
|
|
our $debug = 0; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Preloaded methods go here. |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my $ident = 1; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# subroutine to make string of 16 random bytes |
41
|
|
|
|
|
|
|
sub bigrand() { |
42
|
0
|
|
|
0
|
0
|
0
|
pack "n8", |
43
|
|
|
|
|
|
|
rand(65536), rand(65536), rand(65536), rand(65536), |
44
|
|
|
|
|
|
|
rand(65536), rand(65536), rand(65536), rand(65536); |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
my $dict = undef; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub load { |
50
|
1
|
|
|
1
|
1
|
3
|
my ($d) = @_; |
51
|
|
|
|
|
|
|
# Net::Radius::Dictionary pass silently if |
52
|
|
|
|
|
|
|
# dictionary is not readable (seems like bug) |
53
|
1
|
50
|
|
|
|
260
|
die "Couldn't read dictionary $d\n" unless (-r $d); |
54
|
0
|
0
|
|
|
|
0
|
$dict = new Net::Radius::Dictionary $d |
55
|
|
|
|
|
|
|
or die "Couldn't read dictionary: $!"; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub query { |
59
|
1
|
|
|
1
|
1
|
575
|
my ($servers, $code, $argref) = @_; |
60
|
1
|
|
|
|
|
4
|
my $retref={}; |
61
|
1
|
|
|
|
|
2
|
my ($rec, $req, $rsp); |
62
|
0
|
|
|
|
|
0
|
my $password; |
63
|
|
|
|
|
|
|
|
64
|
1
|
50
|
|
|
|
8
|
if (not defined($dict)) { |
65
|
1
|
|
|
|
|
5
|
load("dictionary"); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
0
|
|
|
|
|
|
$req = new Net::Radius::Packet $dict; |
69
|
0
|
|
|
|
|
|
$req->set_code($code); |
70
|
|
|
|
|
|
|
|
71
|
0
|
|
|
|
|
|
foreach my $vs (keys %$argref) { |
72
|
0
|
|
|
|
|
|
foreach my $a (keys %{$argref->{$vs}}) { |
|
0
|
|
|
|
|
|
|
73
|
0
|
0
|
|
|
|
|
if ($vs) { |
74
|
0
|
|
|
|
|
|
$req->set_vsattr($vs, $a, @{$argref->{$vs}->{$a}}); |
|
0
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
} else { |
76
|
0
|
0
|
|
|
|
|
if ($a eq 'User-Password') { |
77
|
0
|
|
|
|
|
|
$password = $argref->{$vs}->{$a}[0]; |
78
|
|
|
|
|
|
|
} else { |
79
|
0
|
|
|
|
|
|
$req->set_attr($a, $argref->{$vs}->{$a}[0]); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
0
|
|
|
|
|
|
my ($retries, $timeout, $rc); |
86
|
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
|
foreach my $host (keys %$servers) { |
88
|
0
|
|
|
|
|
|
foreach my $port (keys %{$servers->{$host}}) { |
|
0
|
|
|
|
|
|
|
89
|
0
|
0
|
|
|
|
|
if (defined($servers->{$host}->{$port}->{'retries'})) { |
90
|
0
|
|
|
|
|
|
$retries = $servers->{$host}->{$port}->{'retries'}; |
91
|
|
|
|
|
|
|
} else { |
92
|
0
|
|
|
|
|
|
$retries = 3; |
93
|
|
|
|
|
|
|
} |
94
|
0
|
0
|
|
|
|
|
if (defined($servers->{$host}->{$port}->{'timeout'})) { |
95
|
0
|
|
|
|
|
|
$timeout = $servers->{$host}->{$port}->{'timeout'}; |
96
|
|
|
|
|
|
|
} else { |
97
|
0
|
|
|
|
|
|
$timeout = 1; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
0
|
|
|
|
|
|
$ident = ($ident + 1) & 255; |
101
|
0
|
|
|
|
|
|
$req->set_identifier($ident); |
102
|
|
|
|
|
|
|
|
103
|
0
|
0
|
|
|
|
|
if ($code eq 'Access-Request') { |
104
|
0
|
|
|
|
|
|
$req->set_authenticator(bigrand); |
105
|
|
|
|
|
|
|
} else { |
106
|
0
|
|
|
|
|
|
$req->set_authenticator(""); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
0
|
0
|
|
|
|
|
if ($code eq 'Access-Request') { |
110
|
0
|
|
|
|
|
|
$req->unset_attr('User-Password'); |
111
|
0
|
|
|
|
|
|
$req->set_password($password, $servers->{$host}->{$port}->{'secret'}); |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
0
|
0
|
|
|
|
|
$req->dump if ($debug); |
115
|
|
|
|
|
|
|
|
116
|
0
|
|
|
|
|
|
my $pack = $req->pack; # Can generate error 'Unknown RADIUS tuples' |
117
|
|
|
|
|
|
|
# if dictionary has not been loaded (bug?) |
118
|
0
|
0
|
|
|
|
|
if ($code ne 'Access-Request') { |
119
|
0
|
|
|
|
|
|
$pack = auth_resp($pack,$servers->{$host}->{$port}->{'secret'}); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
my $socket = new IO::Socket::INET->new(PeerAddr => $host, |
123
|
|
|
|
|
|
|
PeerPort => $port, |
124
|
|
|
|
|
|
|
Proto => 'udp', |
125
|
|
|
|
|
|
|
Timeout => $timeout); |
126
|
|
|
|
|
|
|
|
127
|
0
|
|
|
|
|
|
while($retries) { |
128
|
0
|
|
|
|
|
|
$retries--; |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
|
$rc = $socket->send($pack); |
131
|
0
|
0
|
|
|
|
|
next if ($rc != length($pack)); |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# Timeout parametor has no effect to recv method; |
134
|
|
|
|
|
|
|
# so we use select to detect timeout |
135
|
0
|
|
|
|
|
|
my $rin = ''; |
136
|
0
|
|
|
|
|
|
vec($rin, fileno($socket), 1) = 1; |
137
|
0
|
|
|
|
|
|
my $nfound = select($rin, undef, undef, $timeout); |
138
|
0
|
0
|
|
|
|
|
next if ($nfound <= 0); # either timeout or end of file |
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
|
my $rec; |
141
|
0
|
|
|
|
|
|
$rc = $socket->recv($rec, 4096); # RFC2866: 20<=size<=4095 |
142
|
0
|
0
|
|
|
|
|
next unless ($rc); |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
|
$rsp = new Net::Radius::Packet $dict, $rec; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# Make sure response is authentic |
147
|
|
|
|
|
|
|
{ |
148
|
0
|
|
|
|
|
|
my $p = $rec; |
|
0
|
|
|
|
|
|
|
149
|
0
|
|
|
|
|
|
substr($p, 4, 16) = $req->authenticator; |
150
|
0
|
|
|
|
|
|
$p = auth_resp($p,$servers->{$host}->{$port}->{'secret'}); |
151
|
0
|
0
|
|
|
|
|
if ($rsp->authenticator ne substr($p, 4, 16)) { |
152
|
0
|
|
|
|
|
|
next; # ignore non-authentic response |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
0
|
0
|
|
|
|
|
$rsp->dump if ($debug); |
157
|
|
|
|
|
|
|
|
158
|
0
|
0
|
|
|
|
|
next if ($rsp->identifier != $ident); |
159
|
|
|
|
|
|
|
|
160
|
0
|
0
|
0
|
|
|
|
if ($code eq 'Access-Request' and |
|
|
|
0
|
|
|
|
|
161
|
|
|
|
|
|
|
$rsp->code ne 'Access-Accept' and |
162
|
|
|
|
|
|
|
$rsp->code ne 'Access-Reject') { |
163
|
0
|
|
|
|
|
|
next; |
164
|
|
|
|
|
|
|
} |
165
|
0
|
0
|
0
|
|
|
|
if ($code eq 'Accounting-Request' and |
166
|
|
|
|
|
|
|
$rsp->code ne 'Accounting-Response') { |
167
|
0
|
|
|
|
|
|
next; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
foreach my $a ($rsp->attributes) { |
171
|
0
|
0
|
|
|
|
|
if (not defined($retref->{0})) { |
172
|
0
|
|
|
|
|
|
$retref->{0} = {}; |
173
|
|
|
|
|
|
|
} |
174
|
0
|
|
|
|
|
|
$retref->{0}->{$a} = [ $rsp->attr($a) ]; |
175
|
|
|
|
|
|
|
} |
176
|
0
|
|
|
|
|
|
foreach my $v ($rsp->vendors) { |
177
|
0
|
|
|
|
|
|
foreach my $a ($rsp->vsattributes($v)) { |
178
|
0
|
0
|
|
|
|
|
if (not defined($retref->{$v})) { |
179
|
0
|
|
|
|
|
|
$retref->{$v} = {}; |
180
|
|
|
|
|
|
|
} |
181
|
0
|
|
|
|
|
|
$retref->{$v}->{$a} = $rsp->vsattr($v, $a); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
0
|
|
|
|
|
|
return ($rsp->code, \%$retref); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
return ('', \%$retref); |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
1; |
194
|
|
|
|
|
|
|
__END__ |