line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Config::LDAPClient; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
1349
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
use Moose::Util::TypeConstraints; |
5
|
|
|
|
|
|
|
use MooseX::AttributeHelpers; |
6
|
|
|
|
|
|
|
use Config::LDAPClient::Search; |
7
|
|
|
|
|
|
|
use Carp qw(); |
8
|
|
|
|
|
|
|
use warnings; |
9
|
|
|
|
|
|
|
use strict; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my $PAM_SECRET_KEY = 'rootbindpw'; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Here for future support. |
19
|
|
|
|
|
|
|
#has 'c_scope' => ( is => 'rw', isa => enum([qw/ sub one base /])); |
20
|
|
|
|
|
|
|
#has 'c_deref' => ( is => 'rw', isa => enum([qw/ never search find always /])); |
21
|
|
|
|
|
|
|
#has 'c_ssl_key' => ( is => 'rw', isa => 'Str' ); |
22
|
|
|
|
|
|
|
#has 'c_ssl_cert' => ( is => 'rw', isa => 'Str' ); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
has 'c_ssl_type' => ( is => 'rw', isa => enum([qw/ off ssl tls /])); |
25
|
|
|
|
|
|
|
has 'c_ssl_verify' => ( is => 'rw', isa => enum([qw/ none optional require /])); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
has 'c_ssl_capath' => ( is => 'rw', isa => 'Str' ); |
28
|
|
|
|
|
|
|
has 'c_ssl_cafile' => ( is => 'rw', isa => 'Str' ); |
29
|
|
|
|
|
|
|
has 'c_ldap_version' => ( is => 'rw', isa => 'Int' ); |
30
|
|
|
|
|
|
|
has 'c_bind_dn' => ( is => 'rw', isa => 'Str' ); |
31
|
|
|
|
|
|
|
has 'c_bind_password' => ( is => 'rw', isa => 'Str' ); |
32
|
|
|
|
|
|
|
has 'c_base' => ( is => 'rw', isa => 'Str' ); |
33
|
|
|
|
|
|
|
has 'c_search_passwd' => ( is => 'rw', isa => 'Config::LDAPClient::Search' ); |
34
|
|
|
|
|
|
|
has 'c_search_group' => ( is => 'rw', isa => 'Config::LDAPClient::Search' ); |
35
|
|
|
|
|
|
|
has 'c_search_shadow' => ( is => 'rw', isa => 'Config::LDAPClient::Search' ); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
has 'c_uri' => ( |
38
|
|
|
|
|
|
|
is => 'rw', |
39
|
|
|
|
|
|
|
isa => 'ArrayRef[Str]', |
40
|
|
|
|
|
|
|
default => sub { [] }, |
41
|
|
|
|
|
|
|
auto_deref => 1, |
42
|
|
|
|
|
|
|
); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
has 'raw_configs' => ( is => 'rw', isa => 'HashRef', default => sub {{}} ); |
45
|
|
|
|
|
|
|
has 'parsed' => ( is => 'rw', isa => 'HashRef', default => sub {{}} ); |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
has 'diag' => ( |
48
|
|
|
|
|
|
|
metaclass => 'Collection::Array', |
49
|
|
|
|
|
|
|
is => 'ro', |
50
|
|
|
|
|
|
|
isa => 'ArrayRef[Str]', |
51
|
|
|
|
|
|
|
default => sub { [] }, |
52
|
|
|
|
|
|
|
auto_deref => 1, |
53
|
|
|
|
|
|
|
provides => { |
54
|
|
|
|
|
|
|
push => 'add_diag', |
55
|
|
|
|
|
|
|
clear => 'clear_diag', |
56
|
|
|
|
|
|
|
}, |
57
|
|
|
|
|
|
|
); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub debug { |
65
|
|
|
|
|
|
|
my($self) = shift; |
66
|
|
|
|
|
|
|
my $message = join "", @_; |
67
|
|
|
|
|
|
|
$self->add_diag($message); |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub connect { |
74
|
|
|
|
|
|
|
my($self, %args) = @_; |
75
|
|
|
|
|
|
|
my $new = $args{'new'} || {}; |
76
|
|
|
|
|
|
|
Carp::croak("'new' argument must be a hashref") |
77
|
|
|
|
|
|
|
unless ref $new eq 'HASH'; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
require Net::LDAP; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
my $ldap = Net::LDAP->new( |
82
|
|
|
|
|
|
|
[$self->c_uri], |
83
|
|
|
|
|
|
|
onerror => 'die', |
84
|
|
|
|
|
|
|
version => $self->c_ldap_version, |
85
|
|
|
|
|
|
|
%$new, |
86
|
|
|
|
|
|
|
); |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Carp::croak("Unable to connect to LDAP: $@") unless $ldap; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
my $bind_dn = $self->c_bind_dn; |
92
|
|
|
|
|
|
|
my @bind_args; |
93
|
|
|
|
|
|
|
if (defined $bind_dn) { |
94
|
|
|
|
|
|
|
my $pw = $self->c_bind_password; |
95
|
|
|
|
|
|
|
push @bind_args, $bind_dn; |
96
|
|
|
|
|
|
|
push @bind_args, password => $pw if defined $pw; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
{ |
100
|
|
|
|
|
|
|
my $mesg = $ldap->bind(@bind_args); |
101
|
|
|
|
|
|
|
Carp::croak("Unable to bind LDAP connection: ", $mesg->error) |
102
|
|
|
|
|
|
|
if $mesg->is_error; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
my $ssl = $self->c_ssl_type; |
107
|
|
|
|
|
|
|
if ($ssl ne 'off') { |
108
|
|
|
|
|
|
|
my $mesg = $ldap->start_tls( |
109
|
|
|
|
|
|
|
verify => $self->c_ssl_verify, |
110
|
|
|
|
|
|
|
capath => $self->c_ssl_capath, |
111
|
|
|
|
|
|
|
cafile => $self->c_ssl_cafile, |
112
|
|
|
|
|
|
|
sslversion => $ssl eq 'tls' ? 'tlsv1' : 'sslv3', |
113
|
|
|
|
|
|
|
); |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Carp::croak("Unable to start TLS on LDAP connection: ", $mesg->error) |
116
|
|
|
|
|
|
|
if $mesg->is_error; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
return $ldap; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub parse { |
126
|
|
|
|
|
|
|
my $self = shift; |
127
|
|
|
|
|
|
|
my @specs = $self->_validate_specs(@_); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
my %raw_configs; |
130
|
|
|
|
|
|
|
my %final_parsed; |
131
|
|
|
|
|
|
|
$self->clear_diag; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# The user specifies specs in priority order; first overrides second, etc. |
134
|
|
|
|
|
|
|
# So we reverse them, and let the hashes fall out as necessary. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
foreach my $spec (reverse @specs) { |
137
|
|
|
|
|
|
|
my($configs, $secret, $parse) = @$spec; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
foreach my $config (@$configs) { |
140
|
|
|
|
|
|
|
my($parsed, $raw); |
141
|
|
|
|
|
|
|
my $success = eval { |
142
|
|
|
|
|
|
|
($parsed, $raw) = $self->$parse($config, $secret); |
143
|
|
|
|
|
|
|
1; |
144
|
|
|
|
|
|
|
}; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
if ($success) { |
147
|
|
|
|
|
|
|
$raw_configs{$config} = $raw; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
my($dn, $dnpw) = delete @{$parsed}{'bind_dn', 'bind_password'}; |
150
|
|
|
|
|
|
|
if (defined $dn and length $dn) { |
151
|
|
|
|
|
|
|
# These values are associated, so we only assign both if |
152
|
|
|
|
|
|
|
# we have a DN to use. |
153
|
|
|
|
|
|
|
$final_parsed{'bind_dn'} = $dn; |
154
|
|
|
|
|
|
|
$final_parsed{'bind_password'} = $dnpw; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
while (my($k, $v) = each %$parsed) { |
158
|
|
|
|
|
|
|
$final_parsed{$k} = $v |
159
|
|
|
|
|
|
|
if defined $v and length $v; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
} else { |
163
|
|
|
|
|
|
|
$self->debug($@ || "unknown error parsing '$config'"); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
$self->raw_configs(\%raw_configs); |
169
|
|
|
|
|
|
|
$self->parsed (\%final_parsed); |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
foreach my $key (keys %final_parsed) { |
172
|
|
|
|
|
|
|
my $method = $self->can("c_$key"); |
173
|
|
|
|
|
|
|
Carp::croak("Unknown parsed key '$key' found") unless $method; |
174
|
|
|
|
|
|
|
$method->($self, $final_parsed{$key}); |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
return $self; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
my %SPECS = ( |
184
|
|
|
|
|
|
|
pam => { |
185
|
|
|
|
|
|
|
config => '/etc/pam_ldap.conf', |
186
|
|
|
|
|
|
|
secret => '/etc/pam_ldap.secret', |
187
|
|
|
|
|
|
|
parse => 'parse_file_pam', |
188
|
|
|
|
|
|
|
}, |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
nss => { |
191
|
|
|
|
|
|
|
config => '/etc/libnss-ldap.conf', |
192
|
|
|
|
|
|
|
secret => '/etc/libnss-ldap.secret', |
193
|
|
|
|
|
|
|
parse => 'parse_file_nss', |
194
|
|
|
|
|
|
|
}, |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
pam_nss => { |
197
|
|
|
|
|
|
|
config => '/etc/ldap.conf', |
198
|
|
|
|
|
|
|
secret => '/etc/ldap.secret', |
199
|
|
|
|
|
|
|
parse => 'parse_file_nss', |
200
|
|
|
|
|
|
|
}, |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
libldap => { |
203
|
|
|
|
|
|
|
config => '/etc/ldap/ldap.conf', |
204
|
|
|
|
|
|
|
parse => 'parse_file_libldap', |
205
|
|
|
|
|
|
|
}, |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
libldap_home => { |
208
|
|
|
|
|
|
|
config => ["$ENV{HOME}/.ldaprc", "$ENV{HOME}/ldaprc"], |
209
|
|
|
|
|
|
|
parse => 'parse_file_libldap', |
210
|
|
|
|
|
|
|
}, |
211
|
|
|
|
|
|
|
); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub _validate_specs { |
214
|
|
|
|
|
|
|
my $self = shift; |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my @specs; |
217
|
|
|
|
|
|
|
foreach my $reqspec (@_) { |
218
|
|
|
|
|
|
|
my $spec; |
219
|
|
|
|
|
|
|
if (not ref $reqspec) { |
220
|
|
|
|
|
|
|
$spec = $SPECS{$reqspec}; |
221
|
|
|
|
|
|
|
Carp::croak("Unknown parsing specification name '$reqspec'") |
222
|
|
|
|
|
|
|
unless $spec; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
} elsif (ref $reqspec eq 'HASH') { |
225
|
|
|
|
|
|
|
$spec = $reqspec; |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
} else { |
228
|
|
|
|
|
|
|
Carp::croak( |
229
|
|
|
|
|
|
|
"Unknown parsing specification reference '", ref $reqspec, "'" |
230
|
|
|
|
|
|
|
); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
my($configs, $secret, $parse) = @{$spec}{qw( config secret parse )}; |
234
|
|
|
|
|
|
|
$configs = [$configs] unless ref $configs eq 'ARRAY'; |
235
|
|
|
|
|
|
|
$parse = ref $parse eq 'CODE' ? $parse : $self->can($parse); |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
{ |
238
|
|
|
|
|
|
|
my $err; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
$err = "'parse' key must be a subref or valid method name" |
241
|
|
|
|
|
|
|
unless ref $parse eq 'CODE'; |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
$err = "no 'parse' key" |
244
|
|
|
|
|
|
|
unless defined $parse; |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
$err = "no configuration files specified" |
247
|
|
|
|
|
|
|
unless grep { defined } @$configs; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
if ($err) { |
250
|
|
|
|
|
|
|
require Data::Dumper; |
251
|
|
|
|
|
|
|
my $safespec = Data::Dumper->new([$reqspec]) |
252
|
|
|
|
|
|
|
->Terse(1)->Indent(0)->Useqq(1)->Dump; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Carp::croak("Invalid parsing specification $safespec: $err"); |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
push @specs, [$configs, $secret, $parse]; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
return @specs; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub _parse_lokv_file { |
268
|
|
|
|
|
|
|
# Parses a simple line-oriented, key-value pair file format. Each line has |
269
|
|
|
|
|
|
|
# the name of a setting, followed by whitespace and the value. Any line |
270
|
|
|
|
|
|
|
# starting wth #, with any amount of leading whitespace, is treated as a |
271
|
|
|
|
|
|
|
# comment and ignored. Blank lines are ignored. |
272
|
|
|
|
|
|
|
# |
273
|
|
|
|
|
|
|
# Returns a hashref of the parsed key-value pairs. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
my($self, $file) = @_; |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
open(my $conffh, '<', $file) |
278
|
|
|
|
|
|
|
|| die("Unable to open configuration file '$file': $!.\n"); |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
my %config; |
281
|
|
|
|
|
|
|
while (my $line = <$conffh>) { |
282
|
|
|
|
|
|
|
next if $line =~ /^\s*#/ or $line !~ /\S/; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
chomp $line; |
285
|
|
|
|
|
|
|
my($key, $value) = split " ", $line, 2; |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
Carp::carp("Duplicate keys '$key' in file '$file'") |
288
|
|
|
|
|
|
|
if exists $config{$key}; |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
$config{$key} = $value; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
close $conffh; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
return \%config; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub _parse_hosts_uris { |
302
|
|
|
|
|
|
|
my($self, $port, $hosts, $uris) = @_; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
if ($uris) { |
305
|
|
|
|
|
|
|
return split " ", $uris; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
} elsif ($hosts) { |
308
|
|
|
|
|
|
|
my @uri; |
309
|
|
|
|
|
|
|
foreach my $host (split " ", $hosts) { |
310
|
|
|
|
|
|
|
my $uri = "ldap://$host"; |
311
|
|
|
|
|
|
|
$uri .= ":$port" if $port and $host !~ /:/; |
312
|
|
|
|
|
|
|
push @uri, $uri; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
return @uri; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
return; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
my %LIBLDAP_REQCERT = ( |
324
|
|
|
|
|
|
|
never => 'none', |
325
|
|
|
|
|
|
|
allow => 'optional', |
326
|
|
|
|
|
|
|
try => 'optional', |
327
|
|
|
|
|
|
|
demand => 'require', |
328
|
|
|
|
|
|
|
hard => 'require', |
329
|
|
|
|
|
|
|
); |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub parse_file_libldap { |
332
|
|
|
|
|
|
|
my($self, $conf_filename) = @_; |
333
|
|
|
|
|
|
|
my %config = %{ $self->_parse_lokv_file($conf_filename) }; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
my %parsed = ( |
336
|
|
|
|
|
|
|
uri => [ $self->_parse_hosts_uris(@config{qw/ PORT HOST URI /}) ], |
337
|
|
|
|
|
|
|
bind_dn => $config{'BIND_DN'}, |
338
|
|
|
|
|
|
|
ssl_capath => $config{'TLS_CACERTDIR'}, |
339
|
|
|
|
|
|
|
ssl_cacert => $config{'TLS_CACERT'}, |
340
|
|
|
|
|
|
|
ssl_verify => $LIBLDAP_REQCERT{ $config{'TLS_REQCERT'} || 'allow' }, |
341
|
|
|
|
|
|
|
); |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
return (\%parsed, \%config); |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub parse_file_nss { |
350
|
|
|
|
|
|
|
my $self = shift; |
351
|
|
|
|
|
|
|
my($parsed, $config) = $self->parse_file_pam(@_); |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
foreach my $map (qw( passwd group shadow )) { |
354
|
|
|
|
|
|
|
if ($config->{"nss_base_$map"}) { |
355
|
|
|
|
|
|
|
$parsed->{"search_$map"} = Config::LDAPClient::Search->new( |
356
|
|
|
|
|
|
|
split /\?/, $config->{"nss_base_$map"}, 3 |
357
|
|
|
|
|
|
|
); |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
return ($parsed, $config); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub parse_file_pam { |
368
|
|
|
|
|
|
|
my($self, $conf_filename, $secret_filename) = @_; |
369
|
|
|
|
|
|
|
my %config = %{ $self->_parse_lokv_file($conf_filename) }; |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
if (defined $secret_filename) { |
372
|
|
|
|
|
|
|
if (open my $secretfh, '<', $secret_filename) { |
373
|
|
|
|
|
|
|
Carp::carp("Config file '$conf_filename' has a '$PAM_SECRET_KEY' key already") |
374
|
|
|
|
|
|
|
if exists $config{$PAM_SECRET_KEY}; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
chomp($config{$PAM_SECRET_KEY} = <$secretfh>); |
377
|
|
|
|
|
|
|
close $secretfh; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
} else { |
380
|
|
|
|
|
|
|
$self->debug("Unable to open secret file '$secret_filename': $!."); |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
my($dn, $dnpw) = $self->_process_pam_dn(\%config); |
386
|
|
|
|
|
|
|
my %parsed = ( |
387
|
|
|
|
|
|
|
uri => [ $self->_parse_hosts_uris(@config{qw( port host uri )}) ], |
388
|
|
|
|
|
|
|
ldap_version => $config{'ldap_version'}, |
389
|
|
|
|
|
|
|
bind_dn => $dn, |
390
|
|
|
|
|
|
|
bind_password => $dnpw, |
391
|
|
|
|
|
|
|
port => $config{'port'}, |
392
|
|
|
|
|
|
|
base => $config{'base'}, |
393
|
|
|
|
|
|
|
ssl_capath => $config{'tls_cacertdir'}, |
394
|
|
|
|
|
|
|
ssl_cafile => $config{'tls_cacertfile'}, |
395
|
|
|
|
|
|
|
ssl_type => $self->_process_pam_ssl($config{'ssl'}), |
396
|
|
|
|
|
|
|
ssl_verify => |
397
|
|
|
|
|
|
|
lc $config{'tls_checkpeer'} eq 'yes' |
398
|
|
|
|
|
|
|
? 'require' |
399
|
|
|
|
|
|
|
: 'optional', |
400
|
|
|
|
|
|
|
); |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
return (\%parsed, \%config); |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
my %SSL_TYPES = qw( on ssl start_tls tls off off ); |
409
|
|
|
|
|
|
|
sub _process_pam_ssl { $SSL_TYPES{ $_[1] || 'off' } } |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub _process_pam_dn { |
415
|
|
|
|
|
|
|
my($self, $config) = @_; |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
my $root_dn = $config->{'rootbinddn'}; |
418
|
|
|
|
|
|
|
my $root_pw = $config->{'rootbindpw'}; |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
if ($root_dn and defined $root_pw) { |
421
|
|
|
|
|
|
|
return ($root_dn, $root_pw); |
422
|
|
|
|
|
|
|
} else { |
423
|
|
|
|
|
|
|
return ($config->{'binddn'}, $config->{'bindpw'}); |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
1; |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
__END__ |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=head1 NAME |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Config::LDAPClient - parse system configuration for LDAP client settings. |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head1 SYNOPSIS |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
use Config::LDAPClient; |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
my $conf = Config::LDAPClient->new(); |
444
|
|
|
|
|
|
|
$conf->parse( |
445
|
|
|
|
|
|
|
'pam', 'nss', 'libldap', |
446
|
|
|
|
|
|
|
{ config => '/etc/custom-ldap.conf', parse => \&custom_ldap_parser }, |
447
|
|
|
|
|
|
|
); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
print "hosts: ", join(" ", $conf->c_uri), "\n"; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
my $ldap = $conf->connect; |
452
|
|
|
|
|
|
|
# Call Net::LDAP methods on $ldap. |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub custom_ldap_parser { ... } |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=head1 DESCRIPTION |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
*** WARNING *** This is very much alpha software. Testing has been minimal, |
460
|
|
|
|
|
|
|
and the API is somewhat subject to change. |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
On many systems there is existing configuration for how to connect to an LDAP |
463
|
|
|
|
|
|
|
server, usually in order to perform authentication for the system itself. |
464
|
|
|
|
|
|
|
This module reads that configuration, parses it, and presents a common |
465
|
|
|
|
|
|
|
interface that can then be used to connect to the specified LDAP server. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
For a list of configuration files supported see L</Configuration Files>. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=head2 Methods |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
All methods raise exceptions on errors. Currently these are simply string |
473
|
|
|
|
|
|
|
exceptions. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=over 4 |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=item $class->new( ... ) |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
This is the class's constructor. It takes a hashref, or a list of key-value |
480
|
|
|
|
|
|
|
pairs; these are treated as method names, and the methods are called with the |
481
|
|
|
|
|
|
|
associated values. This method is supplied by Moose. |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=item $object->connect |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=item $object->connect( new => \%args ) |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Attempts to connect to an LDAP database using L<Net::LDAP>. Attributes should |
489
|
|
|
|
|
|
|
be set to appropriate values, which means L</parse> probably should be called |
490
|
|
|
|
|
|
|
first. |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
The C<new> argument, if specified, must be a hashref. It is dereferenced and |
493
|
|
|
|
|
|
|
passed to the Net::LDAP constructor. It is used to override any default |
494
|
|
|
|
|
|
|
options set by L<Config::LDAPClient>, and any L<Net::LDAP> defaults. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Currently the only default constructor argument specified by |
497
|
|
|
|
|
|
|
L<Config::LDAPClient> is C<onerror>, which is set to 'die'. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=item $object->parse( @names_or_specifications ) |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
This is the workhorse of the module. The arguments to this method are a |
503
|
|
|
|
|
|
|
series of pre-defined names and/or hashrefs indicating what configuration |
504
|
|
|
|
|
|
|
files to read, and how to parse them. |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
Pre-defined names are listed in L</Configuration Files>. |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
If a hashref is specified, it must contain at least two keys, C<config> and |
509
|
|
|
|
|
|
|
C<parse>. C<config> is a scalar or arrayref listing the configuration files |
510
|
|
|
|
|
|
|
to read; C<parse> is the method or subroutine reference to call. An |
511
|
|
|
|
|
|
|
additional parameter, C<secret>, may be provided; this is the name of the |
512
|
|
|
|
|
|
|
file that contains the bind password required to connect. Any problems |
513
|
|
|
|
|
|
|
opening this file are not fatal, and will be added to C<diag>, but otherwise |
514
|
|
|
|
|
|
|
ignored. |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
The C<parse> subroutine or method is expected to return two values: a hashref |
517
|
|
|
|
|
|
|
of the parsed values, to be passed to C<c_*> methods, and a data structure |
518
|
|
|
|
|
|
|
representing the raw parsed configuration. The C<parse> subroutine or method |
519
|
|
|
|
|
|
|
is called with three arguments: the Config::LDAPClient object, the |
520
|
|
|
|
|
|
|
configuration filename or names, and optionally the secret filename (if it's |
521
|
|
|
|
|
|
|
specified). |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Names and specifications are listed in priority order, meaning the first file |
524
|
|
|
|
|
|
|
found takes precedence over subsequent files. All settings are merged, with |
525
|
|
|
|
|
|
|
the highest priority taking precedence. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=item $object->parse_file_pam($conf_filename, $secret_filename) |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
This parses the PAM LDAP configuration file format. The parsing, and |
531
|
|
|
|
|
|
|
subsequent handling of options, is based on a reading of the pam_ldap.conf(5) |
532
|
|
|
|
|
|
|
man page from libpam-ldap 184-4.2 installed on Debian Lenny. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
This method conforms to the description of the C<parse> argument described in |
535
|
|
|
|
|
|
|
the L<"parse method"/parse> documentation. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=item $object->parse_file_nss($conf_filename, $secret_filename) |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
This parses the NSS LDAP configuration file format. It first callse |
541
|
|
|
|
|
|
|
L</parse_file_pam>, because the formats and most of the options are |
542
|
|
|
|
|
|
|
identical, then does specific handling. The handling of options ceomes from |
543
|
|
|
|
|
|
|
a reading of the libnss-ldap.conf(5) man page from libnss-ldap 261-2.1 |
544
|
|
|
|
|
|
|
installed on Debian Lenny. |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
This method conforms to the description of the C<parse> argument described in |
547
|
|
|
|
|
|
|
the L<"parse method"/parse> documentation. |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=item $object->parse_file_libldap($conf_filename) |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
This parses the libldap configuration file format. The parsing and handling |
553
|
|
|
|
|
|
|
of options comes from a reading of the ldap.conf(5) man page from |
554
|
|
|
|
|
|
|
libldap-2.4-2 installed on Debian Lenny. |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
This method conforms to the description of the C<parse> argument described in |
557
|
|
|
|
|
|
|
the L<"parse method"/parse> documentation. |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=item $object->diag |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=item $object->add_diag($message) |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=item $object->clear_diag |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
The C<diag> method accesses an array of non-fatal errors encountered in a |
567
|
|
|
|
|
|
|
given L</parse> run. C<add_diag> adds an entry, and C<clear_diag> clears the |
568
|
|
|
|
|
|
|
entire array. |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=item $object->raw_configs |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=item $object->raw_configs(\%configs) |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
Accessor for the raw configuration data parsed from files. The hash keys are |
576
|
|
|
|
|
|
|
the filenames, the values the configuration data returned from the parse. |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=item $object->parsed |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=item $object->parsed(\%parsed) |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Accessor for the parsed and processed data. This is all of the original |
584
|
|
|
|
|
|
|
merged data, and should correspond directly to the values returned by the |
585
|
|
|
|
|
|
|
C<c_*> accessors. |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=back |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=head2 Configuration Accessors |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=over 4 |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=item $object->c_uri |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=item $object->c_uri(\@uris) |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
Accessor for URIs to connect to. Corresponds to the HOST argument for the |
599
|
|
|
|
|
|
|
new method in L<Net::LDAP>. Returns a list of URIs, not an arrayref. |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=item $object->c_ldap_version |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
=item $object->c_ldap_version($number) |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
Accessor for the LDAP protocol version to be used. Corresponds to the |
607
|
|
|
|
|
|
|
version argument to the L<Net::LDAP> constructor. |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=item $object->c_bind_dn |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=item $object->c_bind_dn($dn) |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Accessor for the DN to bind to use on connect. Corresponds to the first |
615
|
|
|
|
|
|
|
argument to the bind method in L<Net::LDAP>. |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=item $object->c_bind_password |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=item $object->c_bind_password($password) |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
Accessor for the bind password to use on connect. Corresponds to the |
623
|
|
|
|
|
|
|
password argument to the bind method in L<Net::LDAP>. |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=item $object->c_base |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=item $object->c_base($base) |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
Accessor for the default base DN to use in searches. |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=item $object->c_ssl_type |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
=item $object->c_ssl_type($type) |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
Accessor for the SSL access type; valid values are 'off', 'ssl', or 'tls'. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=item $object->c_ssl_verify |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
=item $object->c_ssl_verify($verify) |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
Accessor for the SSL verification requirement; valid values correspond to |
645
|
|
|
|
|
|
|
the verify argument to start_tls in L<Net::LDAP>, namely 'none', 'optional', |
646
|
|
|
|
|
|
|
and 'require'. |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=item $object->c_ssl_capath |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=item $object->c_ssl_capath($path) |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
Accessor for the directory of CA certificates. Corresponds to the capath |
654
|
|
|
|
|
|
|
argument to start_tls in L<Net::LDAP>. |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=item $object->c_ssl_cafile |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=item $object->c_ssl_cafile($filename) |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
Accessor for the CA certificates file. Corresponds to the cafile argument to |
662
|
|
|
|
|
|
|
start_tls in L<Net::LDAP>. |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
=item $object->c_search_passwd |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=item $object->c_search_passwd($object) |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=item $object->c_search_group |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=item $object->c_search_group($object) |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=item $object->c_search_shadow |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=item $object->c_search_shadow($object) |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
These apply specifically to NSS. They are accessors for base DNs to use for |
678
|
|
|
|
|
|
|
specific lookups. The object used is an L<Config::LDAPClient::Search> |
679
|
|
|
|
|
|
|
object, or subclass thereof. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=back |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=head2 Configuration Files |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
This module comes with several pre-defined paths that it can attempt to |
687
|
|
|
|
|
|
|
parse. These names can be passed directly to L</parse>. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=over 4 |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=item * pam |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
Attempts to parse /etc/pam_ldap.conf and /etc/pam_ldap.secret, using |
694
|
|
|
|
|
|
|
L</parse_file_pam>. The secret file is typically owned by root and mode |
695
|
|
|
|
|
|
|
0600, so unless you run as root, you will get the binddn and bindpw values in |
696
|
|
|
|
|
|
|
L</c_bind_dn> and L</c_bind_password>. |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=item * nss |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
Attempts to parse /etc/libnss-ldap.conf and /etc/libnss-ldap.secret, using |
702
|
|
|
|
|
|
|
L</parse_file_nss>. The secret file is typically owned by root and mode |
703
|
|
|
|
|
|
|
0600, so unless you run as root, you will get the binddn and bindpw values in |
704
|
|
|
|
|
|
|
L</c_bind_dn> and L</c_bind_password>. |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=item * pam_nss |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
Some systems merge their libnss-ldap and pam-ldap configuration files. This |
710
|
|
|
|
|
|
|
attempts to parse /etc/ldap.conf and /etc/ldap.secret. The secret file is |
711
|
|
|
|
|
|
|
typically owned by root and mode 0600, so unless you run as root, you will |
712
|
|
|
|
|
|
|
get the binddn and bindpw values in L</c_bind_dn> and L</c_bind_password>. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=item * libldap |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
Attempts to parse /etc/ldap/ldap.conf using L</parse_file_libldap>. |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=item * libldap_home |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
Attempts to parse C<$ENV{HOME}/.ldaprc> and C<$ENV{HOME}/ldaprc>. |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
=back |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=head1 BUGS |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
The test suite is non-existent. |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
Option handling is not comprehensive. Not all of the options available in |
732
|
|
|
|
|
|
|
pam_ldap.conf and libnss-ldap.conf are actually used, even though they have |
733
|
|
|
|
|
|
|
equivalents in Net::LDAP. |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Currently this only supports common Linux setups (specifically Debian Lenny |
736
|
|
|
|
|
|
|
and Ubuntu Hardy Heron). Support for more systems is forthcoming. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
In order to speed development time, this module uses Moose. This increases |
739
|
|
|
|
|
|
|
the dependency list by a few orders of magnitude, which you may or may not |
740
|
|
|
|
|
|
|
consider a bug. |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
All of the configuration options available are specified as toplevel methods, |
743
|
|
|
|
|
|
|
albeit with 'c_' prefixes. This could be considered a design bug, but it was |
744
|
|
|
|
|
|
|
the simplest way to involve Moose's type checking. |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
Aside from support and design, there are probably more than a few bugs lurking |
747
|
|
|
|
|
|
|
about. This module was written quickly over a weekend with very minimal |
748
|
|
|
|
|
|
|
testing, as the lack of a test suite can attest. |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
=head1 AUTHOR |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
Michael Fowler <mfowler@cpan.org> |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
Copyright 2009 Michael Fowler |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or |
761
|
|
|
|
|
|
|
modify it under the terms of either: |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=over 4 |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=item * the GNU General Public License as published by the Free |
766
|
|
|
|
|
|
|
Software Foundation; either version 1, or (at your option) any |
767
|
|
|
|
|
|
|
later version, or |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
=item * the Artistic License version 2.0. |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
=back |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=cut |