| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package RADIUS::UserFile; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
RADIUS::UserFile - Perl extension for manipulating a RADIUS users file. |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
use RADIUS::UserFile; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $users = new RADIUS::UserFile |
|
13
|
|
|
|
|
|
|
File => '/etc/raddb/users', |
|
14
|
|
|
|
|
|
|
Check_Items => [ qw(Password Calling-Station-Id) ]; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
$users->load('/usr/local/etc/radius/users'); |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
$users->add(Who => 'joeuser', |
|
19
|
|
|
|
|
|
|
Attributes => { key1 => 'val1', key2 => 'val2' }, |
|
20
|
|
|
|
|
|
|
Comment => 'Created on '. scalar localtime); |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$users->update(File => '/etc/raddb/users', |
|
23
|
|
|
|
|
|
|
Who => qw(joeuser janeuser)); |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
print $users->format('joeuser'); |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 REQUIRES |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Perl5.004, Fcntl, File::Copy, Tie::IxHash |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 EXPORTS |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
Nothing |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
This module provides methods for reading information from and modifying |
|
38
|
|
|
|
|
|
|
a RADIUS users text file. |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head2 PACKAGE METHODS |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=over 4 |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=item new RADIUS::UserFile |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=item new RADIUS::UserFile(File => I<$USERS_FILE>, Who => I<$USER>, |
|
47
|
|
|
|
|
|
|
Check_Items => [ I<@CHECK_ITEMS> ]) |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item new RADIUS::UserFile(File => I<$USERS_FILE>, Who => [ I<@USERS> ], |
|
50
|
|
|
|
|
|
|
Check_Items => [ I<@CHECK_ITEMS> ]) |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Creates and returns a new C object. |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
C specifies the RADIUS users file to load (e.g. "/etc/raddb/users"). |
|
55
|
|
|
|
|
|
|
If no file is specified, one isn't loaded; in this case, the C |
|
56
|
|
|
|
|
|
|
method can be used to retrieve any user data. If an error occurred while |
|
57
|
|
|
|
|
|
|
reading C, 0 is returned instead. |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
C limits the retrieval of user information to the list of users |
|
60
|
|
|
|
|
|
|
specified. A single user can be named using a string, or a set of users |
|
61
|
|
|
|
|
|
|
can be passed as a reference to an array. If Who is left undefined, all |
|
62
|
|
|
|
|
|
|
users will be loaded. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
C is a reference to a list of attributes that should be |
|
65
|
|
|
|
|
|
|
included in the first line of the record. By default, this list includes: |
|
66
|
|
|
|
|
|
|
"Password", "Auth-Type", "Called-Station-Id", "Calling-Station-Id", |
|
67
|
|
|
|
|
|
|
"Client-Port-DNIS", and "Expiration". |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=back |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head2 OBJECT METHODS |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=over 4 |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item ->add(Who => I<$USER>, Attributes => I<\%ATTRS>, Comment => I<$TEXT>, Debug => I) |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Adds information about the named user. This information will henceforth |
|
78
|
|
|
|
|
|
|
be available through C, C, C, etc. Any |
|
79
|
|
|
|
|
|
|
comments are automatically prefixed with "# ". C should be |
|
80
|
|
|
|
|
|
|
specified as a reference to a hash; each value should either be an array |
|
81
|
|
|
|
|
|
|
ref or a string. On success, 1 is returned. On error, 0 is returned |
|
82
|
|
|
|
|
|
|
and STDERR gets an appropriate message. The debug level is used by the |
|
83
|
|
|
|
|
|
|
C function described below. |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item ->attributes(I<$USER>) |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Returns a list of defined attributes for the specified user. If the |
|
88
|
|
|
|
|
|
|
user doesn't exist, undef is returned. |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=item ->comment(I<$USER>) |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
Returns a string representing the comments that would prefix the given |
|
93
|
|
|
|
|
|
|
user's entry in the users file. If the user doesn't exist, undef is |
|
94
|
|
|
|
|
|
|
returned. |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item ->debug(I, I<@messages>) |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Prints out the list of strings in I<@messages> if the debug level is >= |
|
99
|
|
|
|
|
|
|
I. |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=item ->dump(I<$USER>) |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Prints out the attributes of the named user, in alphabetical order. |
|
104
|
|
|
|
|
|
|
$self is returned. |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=item ->files |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Returns a list of files from which we have read user attributes. The list |
|
109
|
|
|
|
|
|
|
is sorted according to the order in which the files were read. If no |
|
110
|
|
|
|
|
|
|
files have yet been read successfully, an empty array is returned. |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=item ->format(I<$USER>) |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Returns a string containing the attributes of the named user, prefixed by |
|
115
|
|
|
|
|
|
|
any comments, according to the format required for the RADIUS users file. |
|
116
|
|
|
|
|
|
|
If the user doesn't exist, an empty string is returned. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=item ->load(File => I<$USERS_FILE>, Who => I<$USER>) |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=item ->load(File => I<$USERS_FILE>, Who => I<\@USERS>) |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
Loads the contents of the specified RADIUS users file. The name of the |
|
123
|
|
|
|
|
|
|
file is stored in a first-in, last-out stack enumerating which "databases" |
|
124
|
|
|
|
|
|
|
have been loaded (see C). The C object is |
|
125
|
|
|
|
|
|
|
returned. The options are the same as described in C. If a |
|
126
|
|
|
|
|
|
|
user already exists and further info is read about that user from the |
|
127
|
|
|
|
|
|
|
specified file, the new information is just added to what is already |
|
128
|
|
|
|
|
|
|
known. On success, 1 is returned; on failure, 0 is returned and an |
|
129
|
|
|
|
|
|
|
appropriate message is sent to STDERR. |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=item ->read_users(I<$USERS_FILE>, I<$USER>) |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=item ->read_users(I<$USERS_FILE>, I<\@USERS>) |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Reads in the contents of the specified RADIUS users file, and returns |
|
136
|
|
|
|
|
|
|
a pair of hashes: one indexed by user name, with each element containing |
|
137
|
|
|
|
|
|
|
a hash of (attribute name => [ values ]) pairs; and another also indexed |
|
138
|
|
|
|
|
|
|
by user name, containing the comments that immediately preceded that |
|
139
|
|
|
|
|
|
|
user's file entry. The options are the same as in C. Each |
|
140
|
|
|
|
|
|
|
comment value is a string. Each user attribute value is a ref to an |
|
141
|
|
|
|
|
|
|
array of strings. This is mainly designed as a utility function to be |
|
142
|
|
|
|
|
|
|
used by C and C, and doesn't affect the calling object. |
|
143
|
|
|
|
|
|
|
On failure, 0 is returned. |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item ->remove(I<$USER> ...) |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Deletes the specified users from the object. The list of users |
|
148
|
|
|
|
|
|
|
successfully deleted is returned. |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=item ->removed() |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Returns a list of users that have been removed from the object. |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=item ->update(File => I<$USERS_FILE>, Who => I<\@USERS>) |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Updates user attributes in a RADIUS users file. If the file is |
|
157
|
|
|
|
|
|
|
specified, its contents are updated; otherwise, the last file read is |
|
158
|
|
|
|
|
|
|
modified. If a list of users is provided, only their entries are |
|
159
|
|
|
|
|
|
|
updated; otherwise, all known users are. All users to be "updated" |
|
160
|
|
|
|
|
|
|
are printed using the results of C. Other users are printed |
|
161
|
|
|
|
|
|
|
as found. It should be noted that some extra newlines can be left |
|
162
|
|
|
|
|
|
|
in a file with this method: if an empty line follows a given record |
|
163
|
|
|
|
|
|
|
that has been Cd, then it will still be there in the file |
|
164
|
|
|
|
|
|
|
being updated. On success, non-zero is returned. On failure, 0 is |
|
165
|
|
|
|
|
|
|
returned and STDERR gets an appropriate message. |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=item ->user(I<$USER>) |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Returns a ref to a hash representing the attributes of the named user. |
|
170
|
|
|
|
|
|
|
If the user doesn't exist, undef is returned. |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=item ->usernames |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Returns a ref to an anonymous array of strings representing the users |
|
175
|
|
|
|
|
|
|
about which we have attributes defined. If no users are defined, a ref |
|
176
|
|
|
|
|
|
|
to an empty anonymous array is returned. |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=item ->users |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
Returns a ref to a hash of user hashes, where each user hash is a set of |
|
181
|
|
|
|
|
|
|
(attribute name => value) pairs. This is the actual data stored in the |
|
182
|
|
|
|
|
|
|
object, so use with caution. |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item ->values(I<$USER>, I<$ATTRIBUTE>) |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
Returns an array of strings representing the values for the named |
|
187
|
|
|
|
|
|
|
attribute of the given user. If the user or attribute doesn't exist, |
|
188
|
|
|
|
|
|
|
undef is returned. |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=back |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=head1 AUTHOR |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Copyright (c) 2001 O'Shaughnessy Evans . |
|
195
|
|
|
|
|
|
|
All rights reserved. This version is distributed under the same |
|
196
|
|
|
|
|
|
|
terms as Perl itself (i.e. it's free), so enjoy. |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
Thanks to Burkhard Weeber, James Golovich, Peter Bannis, and others |
|
199
|
|
|
|
|
|
|
for contributions and comments that have improved this software. |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
L, L, L. |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=cut |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
require 5.004; |
|
208
|
1
|
|
|
1
|
|
787
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
37
|
|
|
209
|
1
|
|
|
1
|
|
5
|
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
86
|
|
|
210
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
101
|
|
|
211
|
1
|
|
|
1
|
|
849
|
use IO::File; |
|
|
1
|
|
|
|
|
16198
|
|
|
|
1
|
|
|
|
|
167
|
|
|
212
|
1
|
|
|
1
|
|
1037
|
use File::Copy; |
|
|
1
|
|
|
|
|
6035
|
|
|
|
1
|
|
|
|
|
76
|
|
|
213
|
1
|
|
|
1
|
|
7
|
use Fcntl qw(:flock); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
162
|
|
|
214
|
1
|
|
|
1
|
|
2548
|
use Tie::IxHash; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
require Exporter; |
|
217
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
|
218
|
|
|
|
|
|
|
@EXPORT_OK = qw(add attributes comment dump files format load new read_users |
|
219
|
|
|
|
|
|
|
update user usernames users values); |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
$VERSION = '1.01'; |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
#my $RADIUS_USERS = '/etc/raddb/users'; # default users info file |
|
224
|
|
|
|
|
|
|
my $ATTR_MAX = 31; # max char len of any attribute name |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
my %fields = ( |
|
227
|
|
|
|
|
|
|
users => undef, |
|
228
|
|
|
|
|
|
|
removed => undef, # cheap hack for remove() |
|
229
|
|
|
|
|
|
|
files => undef, |
|
230
|
|
|
|
|
|
|
comments => undef, |
|
231
|
|
|
|
|
|
|
check_items => undef, |
|
232
|
|
|
|
|
|
|
ERROR => undef, |
|
233
|
|
|
|
|
|
|
DEBUG => undef |
|
234
|
|
|
|
|
|
|
); |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# Create, initialize, and return a new RADIUS::UserFile object. |
|
237
|
|
|
|
|
|
|
# |
|
238
|
|
|
|
|
|
|
sub new |
|
239
|
|
|
|
|
|
|
{ |
|
240
|
|
|
|
|
|
|
my $me = shift; |
|
241
|
|
|
|
|
|
|
my $class = ref($me) || $me; |
|
242
|
|
|
|
|
|
|
my $self = { %fields }; |
|
243
|
|
|
|
|
|
|
bless $self, $class; |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
my %args = @_; |
|
246
|
|
|
|
|
|
|
return $self->_initialize(\%args); |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# Do initial object-creation setup stuff. |
|
251
|
|
|
|
|
|
|
# |
|
252
|
|
|
|
|
|
|
sub _initialize |
|
253
|
|
|
|
|
|
|
{ |
|
254
|
|
|
|
|
|
|
my ($self, $args) = @_; |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
if ($args->{Debug}) { |
|
257
|
|
|
|
|
|
|
$self->{DEBUG} = $args->{Debug}; |
|
258
|
|
|
|
|
|
|
$| = 1; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
if ($args->{Check_Items}) { |
|
262
|
|
|
|
|
|
|
$self->{check_items} = [ @{$args->{Check_Items}} ]; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
else { |
|
265
|
|
|
|
|
|
|
$self->{check_items} = [ "Password", "Auth-Type", |
|
266
|
|
|
|
|
|
|
"Called-Station-Id", "Calling-Station-Id", |
|
267
|
|
|
|
|
|
|
"Client-Port-DNIS", "Expiration" ]; |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
if ($args->{File}) { |
|
271
|
|
|
|
|
|
|
$self->debug(7, "init - loading $args->{File}"); |
|
272
|
|
|
|
|
|
|
my ($users, $comments) = $self->read_users($args->{File}, $args->{Who}); |
|
273
|
|
|
|
|
|
|
return 0 unless defined $comments and defined $users; |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
if ($users) { |
|
276
|
|
|
|
|
|
|
@{$self->{users}}{keys %$users} = values %$users; |
|
277
|
|
|
|
|
|
|
@{$self->{comments}}{keys %$comments} = values %$comments; |
|
278
|
|
|
|
|
|
|
push @{$self->{files}}, $args->{File}; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
return $self; |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Adds the specified user to the collection. The arguments provided should |
|
287
|
|
|
|
|
|
|
# form a hash with the following structure: |
|
288
|
|
|
|
|
|
|
# 'Who' => "user_name" |
|
289
|
|
|
|
|
|
|
# 'Attributes' => { key1 => val1, key2 => [ val2 val3 val4 ], ... } |
|
290
|
|
|
|
|
|
|
# 'Comment' => "optional text to prefix the user's file entry" |
|
291
|
|
|
|
|
|
|
# |
|
292
|
|
|
|
|
|
|
# If there is some type of failure, 0 is returned. Otherwise, 1. |
|
293
|
|
|
|
|
|
|
sub add |
|
294
|
|
|
|
|
|
|
{ |
|
295
|
|
|
|
|
|
|
my ($self, %args) = @_; |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
unless ($args{Who} and ref $args{Attributes} eq 'HASH') { |
|
298
|
|
|
|
|
|
|
carp('Insufficient parameters: missing Who or hash of Attributes.'); |
|
299
|
|
|
|
|
|
|
return 0; |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
$self->debug(6, "add - adding $args{Who}"); |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# Add quotes to each attrib value if it has whitespace and isn't already |
|
304
|
|
|
|
|
|
|
# quoted. |
|
305
|
|
|
|
|
|
|
foreach my $k (keys %{$args{Attributes}}) { |
|
306
|
|
|
|
|
|
|
if (ref $args{Attributes}->{$k} eq 'ARRAY') { |
|
307
|
|
|
|
|
|
|
for (my $i=0; $i <= $#{$args{Attributes}->{$k}}; $i++) { |
|
308
|
|
|
|
|
|
|
$args{Attributes}->{$k}[$i] =~ s/^([^"].*[\s,].*)$/"$1"/; |
|
309
|
|
|
|
|
|
|
} |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
else { |
|
312
|
|
|
|
|
|
|
$args{Attributes}->{$k} =~ s/^([^"].*\s.*)$/"$1"/; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
tie %{$self->{'users'}{$args{Who}}}, 'Tie::IxHash' |
|
317
|
|
|
|
|
|
|
unless tied %{$self->{'users'}{$args{Who}}}; |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
foreach my $k (keys %{$args{Attributes}}) { |
|
320
|
|
|
|
|
|
|
push @{$self->{'users'}{$args{Who}}{$k}}, |
|
321
|
|
|
|
|
|
|
ref $args{Attributes}->{$k} eq 'ARRAY' |
|
322
|
|
|
|
|
|
|
? @{$args{Attributes}->{$k}} |
|
323
|
|
|
|
|
|
|
: $args{Attributes}->{$k} |
|
324
|
|
|
|
|
|
|
} |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
if (exists $args{Comment}) { |
|
327
|
|
|
|
|
|
|
$args{Comment} =~ s/^/# /mg; |
|
328
|
|
|
|
|
|
|
$self->{comments}{$args{Who}} .= $args{Comment}. "\n"; |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
return 1; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Return a list of defined RADIUS attributes for the specified user. |
|
336
|
|
|
|
|
|
|
# |
|
337
|
|
|
|
|
|
|
sub attributes |
|
338
|
|
|
|
|
|
|
{ |
|
339
|
|
|
|
|
|
|
my ($self, $who) = @_; |
|
340
|
|
|
|
|
|
|
my @a = eval { local $^W = undef; keys %{$self->{'users'}{$who}} }; |
|
341
|
|
|
|
|
|
|
return $@ ? undef : @a; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# Return the comment text associated with a user. |
|
346
|
|
|
|
|
|
|
# |
|
347
|
|
|
|
|
|
|
sub comment |
|
348
|
|
|
|
|
|
|
{ |
|
349
|
|
|
|
|
|
|
my ($self, $who) = @_; |
|
350
|
|
|
|
|
|
|
my $text = eval { local $^W = undef; $self->{comments}{$who} }; |
|
351
|
|
|
|
|
|
|
return $@ ? undef : $text; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# Print the attributes of the specified user. |
|
356
|
|
|
|
|
|
|
# |
|
357
|
|
|
|
|
|
|
sub dump |
|
358
|
|
|
|
|
|
|
{ |
|
359
|
|
|
|
|
|
|
my ($self, $who) = @_; |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
return $self unless defined $self->user($who); |
|
362
|
|
|
|
|
|
|
my @attribs = $self->attributes($who); |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
print "RADIUS user $who:\n"; |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
if (@attribs) { |
|
367
|
|
|
|
|
|
|
foreach my $a (@attribs) { |
|
368
|
|
|
|
|
|
|
foreach my $v ($self->values($who, $a)) { |
|
369
|
|
|
|
|
|
|
printf " %-${ATTR_MAX}s => %s\n", $a, $v; |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
} |
|
373
|
|
|
|
|
|
|
else { |
|
374
|
|
|
|
|
|
|
print " no attributes defined.\n"; |
|
375
|
|
|
|
|
|
|
} |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
return $self; |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# Return a ref to a list of files that we have read user info from. |
|
382
|
|
|
|
|
|
|
# |
|
383
|
|
|
|
|
|
|
sub files |
|
384
|
|
|
|
|
|
|
{ |
|
385
|
|
|
|
|
|
|
my $self = shift; |
|
386
|
|
|
|
|
|
|
my @files = eval { local $^W = undef; @{$self->{files}} }; |
|
387
|
|
|
|
|
|
|
return $@ ? () : @files; |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# Return a string containing the attributes for the given user, in the |
|
392
|
|
|
|
|
|
|
# format acceptable to a RADIUS users file. If the user doesn't exist, |
|
393
|
|
|
|
|
|
|
# an empty string is returned. |
|
394
|
|
|
|
|
|
|
sub format |
|
395
|
|
|
|
|
|
|
{ |
|
396
|
|
|
|
|
|
|
my ($self, $who) = @_; |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
return '' unless defined $self->user($who); |
|
399
|
|
|
|
|
|
|
my $str = $self->comment($who); |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
my @attribs = $self->attributes($who); |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# figure out a good way to indent each record |
|
404
|
|
|
|
|
|
|
my $indent = length($who) + 1; |
|
405
|
|
|
|
|
|
|
if ($indent < 24) { $indent = 24 } |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
if (@attribs) { |
|
408
|
|
|
|
|
|
|
my (@attrib_strs); |
|
409
|
|
|
|
|
|
|
my @checks = (); |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
foreach my $a (@attribs) { |
|
412
|
|
|
|
|
|
|
foreach my $v ($self->values($who, $a)) { |
|
413
|
|
|
|
|
|
|
if ($self->_is_check_item($a)) { |
|
414
|
|
|
|
|
|
|
$self->debug(8, "format - check item $a = $v"); |
|
415
|
|
|
|
|
|
|
push @checks, "$a = $v"; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
else { |
|
418
|
|
|
|
|
|
|
push @attrib_strs, |
|
419
|
|
|
|
|
|
|
sprintf("%s%s = %s", ' 'x$indent, $a, $v); |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
$str .= $who. (' 'x($indent - length $who)). join(', ', @checks). "\n"; |
|
424
|
|
|
|
|
|
|
$str .= join(",\n", @attrib_strs). "\n"; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
return $str; |
|
428
|
|
|
|
|
|
|
} |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# Read user attributes from the specified file. If a set of users is |
|
432
|
|
|
|
|
|
|
# specified using "Who", the information is limited to those users. |
|
433
|
|
|
|
|
|
|
# |
|
434
|
|
|
|
|
|
|
sub load |
|
435
|
|
|
|
|
|
|
{ |
|
436
|
|
|
|
|
|
|
my ($self, %args) = @_; |
|
437
|
|
|
|
|
|
|
my $file = $args{File}; |
|
438
|
|
|
|
|
|
|
my $who = $args{Who}; |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
my ($users, $comments) = $self->read_users($file, $who); |
|
441
|
|
|
|
|
|
|
return 0 unless defined $comments and defined $users; |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
foreach my $u (keys %$users) { |
|
444
|
|
|
|
|
|
|
tie(%{$self->{'users'}{$u}}, 'Tie::IxHash'); |
|
445
|
|
|
|
|
|
|
foreach my $a (keys %{$users->{$u}}) { |
|
446
|
|
|
|
|
|
|
push @{$self->{'users'}{$u}{$a}}, @{$users->{$u}{$a}}; |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
foreach my $user (keys %$comments) { |
|
450
|
|
|
|
|
|
|
$self->{comments}{$user} .= $comments->{$user}; |
|
451
|
|
|
|
|
|
|
} |
|
452
|
|
|
|
|
|
|
push @{$self->{'files'}}, $file; |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
return 1; |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# Read in a radius users file, according to the EBNF provided in |
|
459
|
|
|
|
|
|
|
# "users-file-syntax.1", distributed w/the Ascend radius server software. |
|
460
|
|
|
|
|
|
|
# Returns a ref to a hash of user names, where each user element is a hash |
|
461
|
|
|
|
|
|
|
# of (attribute_name => value) pairs. If a second argument is supplied |
|
462
|
|
|
|
|
|
|
# ($who), it specifies the set of users to read in... all others in the |
|
463
|
|
|
|
|
|
|
# file will be ignored. If $who is a string, it is interpreted as a single |
|
464
|
|
|
|
|
|
|
# user name; if it's a reference to an array, it's interpreted as a set |
|
465
|
|
|
|
|
|
|
# of user names. |
|
466
|
|
|
|
|
|
|
# |
|
467
|
|
|
|
|
|
|
sub read_users |
|
468
|
|
|
|
|
|
|
{ |
|
469
|
|
|
|
|
|
|
my ($self, $users_file, $who) = @_; |
|
470
|
|
|
|
|
|
|
my (@fields, $user, %users, $attrib_set, $attrib_input, @who_we_want, |
|
471
|
|
|
|
|
|
|
%comments, $comment, $attr, $val); |
|
472
|
|
|
|
|
|
|
local (*USERS); |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
$self->debug(2, "read_users - loading $users_file"); |
|
475
|
|
|
|
|
|
|
open(USERS, $users_file) |
|
476
|
|
|
|
|
|
|
or carp("Error opening $users_file: $!"), return 0; |
|
477
|
|
|
|
|
|
|
seek USERS, 0, 0; |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
@who_we_want = ref $who eq 'ARRAY' ? @$who : $who if defined $who; |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
while () { |
|
482
|
|
|
|
|
|
|
chomp; |
|
483
|
|
|
|
|
|
|
$self->debug(9, "read_users - in=``$_''"); |
|
484
|
|
|
|
|
|
|
($comment = '', next) unless $_; # Skip if there's nothing useful, |
|
485
|
|
|
|
|
|
|
($comment .= "$_\n", next) if /^#/; # or if it's just a comment. |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
if (/(^[^#,\s]+)\s+(.+)/) { # first line |
|
488
|
|
|
|
|
|
|
$user = $1; |
|
489
|
|
|
|
|
|
|
$attrib_input = $2; |
|
490
|
|
|
|
|
|
|
$comments{$user} = $comment if $comment; |
|
491
|
|
|
|
|
|
|
tie(%{$users{$user}}, 'Tie::IxHash'); |
|
492
|
|
|
|
|
|
|
$self->debug(5, "read_users - new record $user"); |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
else { # secondary line |
|
495
|
|
|
|
|
|
|
$attrib_input = $_; |
|
496
|
|
|
|
|
|
|
} |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
next if @who_we_want and !grep($_ eq $user, @who_we_want); |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
$attrib_set = _parse_attribs($attrib_input, $users_file); |
|
501
|
|
|
|
|
|
|
while (($attr, $val) = splice @$attrib_set, 0, 2) { |
|
502
|
|
|
|
|
|
|
push @{$users{$user}{$attr}}, $val; |
|
503
|
|
|
|
|
|
|
} |
|
504
|
|
|
|
|
|
|
} |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
close USERS; |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
return (\%users, \%comments); |
|
509
|
|
|
|
|
|
|
} |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
# Return a ref to a hash of RADIUS users attributes. We assume that |
|
512
|
|
|
|
|
|
|
# comments have already been stripped from the input string. |
|
513
|
|
|
|
|
|
|
# |
|
514
|
|
|
|
|
|
|
sub _parse_attribs |
|
515
|
|
|
|
|
|
|
{ |
|
516
|
|
|
|
|
|
|
my ($raw, $file) = @_; |
|
517
|
|
|
|
|
|
|
my @attribs; |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
$raw =~ s/^\s+//; # remove leading whitespace. |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
while ($raw =~ s/^(\S+)\s*=\s*(("[^"]*")|[^",\s]+)\s*,?//) { |
|
522
|
|
|
|
|
|
|
if (defined $2) { |
|
523
|
|
|
|
|
|
|
push @attribs, $1, $2; |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
else { |
|
526
|
|
|
|
|
|
|
carp("Couldn't understand line $. in `$file'."); |
|
527
|
|
|
|
|
|
|
last; |
|
528
|
|
|
|
|
|
|
} |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
$raw =~ s/^\s+//; |
|
531
|
|
|
|
|
|
|
} |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
return \@attribs; |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
# Remove the specified users from $self. |
|
538
|
|
|
|
|
|
|
sub remove |
|
539
|
|
|
|
|
|
|
{ |
|
540
|
|
|
|
|
|
|
my ($self, @users) = @_; |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
foreach (@users) { |
|
543
|
|
|
|
|
|
|
delete $self->{'users'}{$_} and push @{$self->{removed}}, $_; |
|
544
|
|
|
|
|
|
|
delete $self->{comments}{$_}; |
|
545
|
|
|
|
|
|
|
} |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
my @removed = eval { local $^W = undef; @{$self->{removed}} }; |
|
548
|
|
|
|
|
|
|
return $@ ? () : @removed; |
|
549
|
|
|
|
|
|
|
} |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub removed |
|
552
|
|
|
|
|
|
|
{ |
|
553
|
|
|
|
|
|
|
my $self = shift; |
|
554
|
|
|
|
|
|
|
my @removed = eval { local $^W = undef; @{$self->{removed}} }; |
|
555
|
|
|
|
|
|
|
return $@ ? () : @removed; |
|
556
|
|
|
|
|
|
|
} |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
# Update user attributes in a RADIUS users file. The arguments should be |
|
560
|
|
|
|
|
|
|
# specified as a hash. If the 'File' element is provided, that filename |
|
561
|
|
|
|
|
|
|
# is used; otherwise, the last file read is used. If the 'Who' element is |
|
562
|
|
|
|
|
|
|
# provided, only the specified users are updated; otherwise, all known |
|
563
|
|
|
|
|
|
|
# users are updated. |
|
564
|
|
|
|
|
|
|
sub update |
|
565
|
|
|
|
|
|
|
{ |
|
566
|
|
|
|
|
|
|
my ($self, %args) = @_; |
|
567
|
|
|
|
|
|
|
my $file = exists $args{File} ? $args{File} : $self->{'files'}->[-1]; |
|
568
|
|
|
|
|
|
|
my @who = exists $args{Who} |
|
569
|
|
|
|
|
|
|
? (ref $args{Who} eq 'ARRAY' ? @{$args{Who}} : $args{Who}) |
|
570
|
|
|
|
|
|
|
: eval { local $^W = undef; keys %{$self->{users}} }; |
|
571
|
|
|
|
|
|
|
my $temp = "$file.new"; |
|
572
|
|
|
|
|
|
|
local (*IN, *TMP); |
|
573
|
|
|
|
|
|
|
my $oldsep = $/; |
|
574
|
|
|
|
|
|
|
local ($/) = ''; # we'll lose multiple blank lines this way |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
carp('No users found'), return 0 unless (@who); |
|
577
|
|
|
|
|
|
|
_setup_files($file, \*IN, $temp, \*TMP) or return 0; |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
my (%who, @recs, $name, $in); |
|
580
|
|
|
|
|
|
|
@who{@who} = (0) x @who; |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
while () { |
|
583
|
|
|
|
|
|
|
undef @recs; |
|
584
|
|
|
|
|
|
|
$in = $_; |
|
585
|
|
|
|
|
|
|
while (/^( |
|
586
|
|
|
|
|
|
|
(?: \#.*\n)* # pre-record comment lines |
|
587
|
|
|
|
|
|
|
[^\#\s]+.*\n # start of record |
|
588
|
|
|
|
|
|
|
(?: # rest of record: |
|
589
|
|
|
|
|
|
|
(?: \s+\S.*\n)| # attribute settings, or |
|
590
|
|
|
|
|
|
|
((?: \#.*\n) # comments not followed by another |
|
591
|
|
|
|
|
|
|
(?! [^\#\s])) # start of record. |
|
592
|
|
|
|
|
|
|
)* |
|
593
|
|
|
|
|
|
|
)/goxm) { |
|
594
|
|
|
|
|
|
|
push @recs, $1; |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
print(TMP $in), next unless @recs; |
|
598
|
|
|
|
|
|
|
foreach my $r (@recs) { |
|
599
|
|
|
|
|
|
|
($name) = $r =~ /^([^#\s]+)/m; |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
if (!$name) { |
|
602
|
|
|
|
|
|
|
print TMP $r; |
|
603
|
|
|
|
|
|
|
} |
|
604
|
|
|
|
|
|
|
elsif (exists $who{$name}) { |
|
605
|
|
|
|
|
|
|
$self->debug(6, "update - existing record $name"); |
|
606
|
|
|
|
|
|
|
print TMP $self->format($name) if $who{$name} == 0; |
|
607
|
|
|
|
|
|
|
$who{$name}++; |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
elsif (!grep($name eq $_, $self->removed)) { |
|
610
|
|
|
|
|
|
|
print TMP $r; |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
} |
|
613
|
|
|
|
|
|
|
print TMP "\n"; # since the input sep is "\n\n" |
|
614
|
|
|
|
|
|
|
} |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# Print out records for anyone we didn't find in $file. |
|
617
|
|
|
|
|
|
|
foreach (grep($who{$_} == 0, keys %who)) { |
|
618
|
|
|
|
|
|
|
$self->debug(6, "update - new record $_"); |
|
619
|
|
|
|
|
|
|
print TMP $self->format($_), "\n"; |
|
620
|
|
|
|
|
|
|
} |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
$/ = $oldsep; |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
# Close out input and output files (original and temporary, respectively) |
|
625
|
|
|
|
|
|
|
_cleanup_files($file, \*IN, $temp, \*TMP) or return 0; |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
return 1; |
|
628
|
|
|
|
|
|
|
} |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# Organizational routine for update(). Sets up file handles for reading |
|
631
|
|
|
|
|
|
|
# from the RADIUS users file. The entire algorithm is like this: |
|
632
|
|
|
|
|
|
|
# open users file for read/write, creating if necessary |
|
633
|
|
|
|
|
|
|
# flock file exclusively |
|
634
|
|
|
|
|
|
|
# compare file opened to file locked, and re-open/lock while not equal |
|
635
|
|
|
|
|
|
|
# read from file, write to temp (handled in update()) |
|
636
|
|
|
|
|
|
|
# close temp (handled by _cleanup_files) |
|
637
|
|
|
|
|
|
|
# rename temp to file |
|
638
|
|
|
|
|
|
|
# close file |
|
639
|
|
|
|
|
|
|
sub _setup_files |
|
640
|
|
|
|
|
|
|
{ |
|
641
|
|
|
|
|
|
|
my ($file, $IN, $temp, $TMP) = @_; |
|
642
|
|
|
|
|
|
|
my $backup = "$file.bak"; |
|
643
|
|
|
|
|
|
|
my $existed = -f $file; |
|
644
|
|
|
|
|
|
|
my ($dev1, $ino1, $dev2, $ino2); |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
while (1) { |
|
647
|
|
|
|
|
|
|
open($IN, "+>>$file") |
|
648
|
|
|
|
|
|
|
or carp("Error opening $file: $!"), return 0; |
|
649
|
|
|
|
|
|
|
($dev1, $ino1) = (stat $IN)[0,1]; |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
flock($IN, LOCK_EX) |
|
652
|
|
|
|
|
|
|
or carp("Error locking $file: $!"), close $IN, return 0; |
|
653
|
|
|
|
|
|
|
($dev2, $ino2) = (stat $IN)[0,1]; |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
last if $dev1 == $dev2 and $ino1 == $ino2; |
|
656
|
|
|
|
|
|
|
close $IN; |
|
657
|
|
|
|
|
|
|
} |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
seek $IN, 0, 0; |
|
660
|
|
|
|
|
|
|
open($TMP, ">$temp") |
|
661
|
|
|
|
|
|
|
or carp("Error creating $temp: $!"), close $IN, return 0; |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
return 1; |
|
664
|
|
|
|
|
|
|
} |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
# We should have new content in $TMP, and old content in $IN. |
|
667
|
|
|
|
|
|
|
# So rename $TMP to $IN and close, releasing the flock on $IN established |
|
668
|
|
|
|
|
|
|
# in _setup_files(). |
|
669
|
|
|
|
|
|
|
sub _cleanup_files |
|
670
|
|
|
|
|
|
|
{ |
|
671
|
|
|
|
|
|
|
my ($file, $IN, $temp, $TMP) = @_; |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
close $TMP or carp("Error closing $temp: $!"), return 0; |
|
674
|
|
|
|
|
|
|
rename($temp, $file) or carp("Error renaming $file to $temp: $!"), return 0; |
|
675
|
|
|
|
|
|
|
close $IN or carp("Error closing $file: $!"), return 0; |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
return 1; |
|
678
|
|
|
|
|
|
|
} |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
# See if attribute is a checkable item (Lucent Radius fix -- Peter Bannis) |
|
682
|
|
|
|
|
|
|
sub _is_check_item |
|
683
|
|
|
|
|
|
|
{ |
|
684
|
|
|
|
|
|
|
my ($self, $attribute) = @_; |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
if ($attribute) { |
|
687
|
|
|
|
|
|
|
return grep(/^$attribute$/i, @{$self->{check_items}}); |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
else { |
|
690
|
|
|
|
|
|
|
return 0; |
|
691
|
|
|
|
|
|
|
} |
|
692
|
|
|
|
|
|
|
} |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
# Return a ref to a hash representing the attributes of the specified user. |
|
696
|
|
|
|
|
|
|
# |
|
697
|
|
|
|
|
|
|
sub user |
|
698
|
|
|
|
|
|
|
{ |
|
699
|
|
|
|
|
|
|
my ($self, $who) = @_; |
|
700
|
|
|
|
|
|
|
my %hash = eval { local $^W = undef; %{$self->{'users'}{$who}} }; |
|
701
|
|
|
|
|
|
|
return $@ ? undef : \%hash; |
|
702
|
|
|
|
|
|
|
} |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
# Return a ref to a list of users we have RADIUS info for, or a ref to an |
|
706
|
|
|
|
|
|
|
# empty anonymous array if no users are defined. |
|
707
|
|
|
|
|
|
|
# |
|
708
|
|
|
|
|
|
|
sub usernames |
|
709
|
|
|
|
|
|
|
{ |
|
710
|
|
|
|
|
|
|
my $self = shift; |
|
711
|
|
|
|
|
|
|
my $users = eval { local $^W = undef; [ keys %{$self->{'users'}} ] }; |
|
712
|
|
|
|
|
|
|
return $@ ? [] : $users; |
|
713
|
|
|
|
|
|
|
} |
|
714
|
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
# Return a ref to a hash of RADIUS users, indexed by user name, each |
|
717
|
|
|
|
|
|
|
# containing a hash of attributes. This is a ref to the actual data |
|
718
|
|
|
|
|
|
|
# in the object, so the user information can be changed here. |
|
719
|
|
|
|
|
|
|
# |
|
720
|
|
|
|
|
|
|
sub users |
|
721
|
|
|
|
|
|
|
{ |
|
722
|
|
|
|
|
|
|
my $self = shift; return $self->{'users'}; |
|
723
|
|
|
|
|
|
|
} |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# Return an array with the values of the given attribute for the named user. |
|
727
|
|
|
|
|
|
|
# |
|
728
|
|
|
|
|
|
|
sub values |
|
729
|
|
|
|
|
|
|
{ |
|
730
|
|
|
|
|
|
|
my ($self, $who, $attr) = @_; |
|
731
|
|
|
|
|
|
|
my @vals = eval { local $^W = undef; @{$self->{'users'}{$who}{$attr}} }; |
|
732
|
|
|
|
|
|
|
return $@ ? undef : @vals; |
|
733
|
|
|
|
|
|
|
} |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
sub debug |
|
736
|
|
|
|
|
|
|
{ |
|
737
|
|
|
|
|
|
|
my ($self, $level, @msg) = @_; |
|
738
|
|
|
|
|
|
|
if ($level <= $self->{DEBUG}) { |
|
739
|
|
|
|
|
|
|
print STDERR join("\n", @msg), "\n"; |
|
740
|
|
|
|
|
|
|
} |
|
741
|
|
|
|
|
|
|
} |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
1; |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
__END__ |