1 # Functions for parsing and updating the LDAP config file
3 BEGIN { push(@INC, ".."); };
7 @base_types = ("passwd", "shadow", "group", "hosts", "networks", "netmasks",
8 "services", "protocols", "aliases", "netgroup");
11 # Parses the NSS LDAP config file into a list of names and values
14 local $file = $_[0] || $config{'auth_ldap'};
15 if (!scalar(@get_config_cache)) {
17 @get_config_cache = ( );
18 &open_readfile(CONF, $file);
22 if (/^(#?)(\S+)\s*(.*)/) {
23 push(@get_config_cache, { 'name' => lc($2),
33 return \@get_config_cache;
36 # find(name, &conf, disabled-mode)
39 local ($name, $conf, $dis) = @_;
40 local @rv = grep { $_->{'name'} eq $name } @$conf;
43 @rv = grep { $_->{'enabled'} } @rv;
47 @rv = grep { !$_->{'enabled'} } @rv;
49 return wantarray ? @rv : $rv[0];
52 # find_value(name, &conf)
55 local ($name, $conf, $dis) = @_;
56 local @rv = map { $_->{'value'} } &find($name, $conf, $dis);
57 return wantarray ? @rv : $rv[0];
62 local $rv = &find_value(@_);
66 # save_directive(&conf, name, [value])
69 local ($conf, $name, $value) = @_;
70 local $old = &find($name, $conf);
71 local $oldcmt = &find($name, $conf, 1);
72 local $lref = &read_file_lines($old ? $old->{'file'} :
73 $oldcmt ? $oldcmt->{'file'} :
74 $config{'auth_ldap'});
75 if (defined($value) && $old) {
77 $old->{'value'} = $value;
78 $lref->[$old->{'line'}] = "$name $value";
80 elsif (defined($value) && $oldcmt) {
81 # Add value after commented version
82 splice(@$lref, $oldcmt->{'line'}+1, 0, "$name $value");
83 &renumber($conf, $oldcmt->{'line'}+1, $oldcmt->{'file'}, 1);
84 push(@$conf, { 'name' => $name,
87 'line' => $oldcmt->{'line'}+1,
88 'file' => $oldcmt->{'file'} });
90 elsif (!defined($value) && $old) {
91 # Delete current value
92 splice(@$lref, $old->{'line'}, 1);
93 &renumber($conf, $old->{'line'}, $old->{'file'}, -1);
94 @$conf = grep { $_ ne $old } @$conf;
97 # Add value at end of file
98 push(@$conf, { 'name' => $name,
101 'line' => scalar(@$lref),
102 'file' => $config{'auth_ldap'} });
103 push(@$lref, "$name $value");
109 local ($conf, $line, $file, $offset) = @_;
110 foreach my $c (@$conf) {
111 if ($c->{'line'} >= $line && $c->{'file'} eq $file) {
112 $c->{'line'} += $offset;
117 # get_rootbinddn_secret()
118 # Returns the password used when the root user connects to the LDAP server
119 sub get_rootbinddn_secret
121 local @secrets = split(/\t+/, $config{'secret'});
122 &open_readfile(SECRET, $secrets[0]) || return undef;
123 local $secret = <SECRET>;
125 $secret =~ s/\r|\n//g;
129 # save_rootbinddn_secret(secret)
130 # Saves the password used when the root user connects to the LDAP server
131 sub save_rootbinddn_secret
133 local @secrets = split(/\t+/, $config{'secret'});
134 if (defined($_[0])) {
135 foreach my $secret (@secrets) {
136 &open_tempfile(SECRET, ">$secret");
137 &print_tempfile(SECRET, $_[0],"\n");
138 &close_tempfile(SECRET);
139 &set_ownership_permissions(0, 0, 0600, $secret);
143 &unlink_file(@secrets);
147 # ldap_connect(return-error, [&host])
148 # Connect to the LDAP server and return a handle to the Net::LDAP object
151 # Load the LDAP module
152 eval "use Net::LDAP";
154 local $err = &text('ldap_emodule', "<tt>Net::LDAP</tt>",
155 "../cpan/download.cgi?source=3&".
156 "cpan=Convert::ASN1%20Net::LDAP&mode=2&".
157 "return=../$module_name/&".
158 "returndesc=".&urlize($module_info{'desc'}));
159 if ($_[0]) { return $err; }
160 else { &error($err); }
162 local $err = &generic_ldap_connect($config{'ldap_hosts'}, $config{'ldap_port'},
163 $config{'ldap_tls'}, $config{'ldap_user'},
164 $config{'ldap_pass'});
165 if (ref($err)) { return $err; } # Worked
166 elsif ($_[0]) { return $err; } # Caller asked for error return
167 else { &error($err); } # Caller asked for error() call
170 # generic_ldap_connect([host], [port], [login], [password])
171 # A generic function for connecting to an LDAP server. Uses the system's
172 # LDAP client config file if any parameters are missing. Returns the LDAP
173 # handle on success or an error message on failure.
174 sub generic_ldap_connect
176 local ($ldap_hosts, $ldap_port, $ldap_ssl, $ldap_user, $ldap_pass) = @_;
178 # Check for perl module and config file
179 eval "use Net::LDAP";
181 return &text('ldap_emodule2', "<tt>Net::LDAP</tt>");
183 if (!-r $config{'auth_ldap'}) {
184 $ldap_hosts && $ldap_user ||
185 return &text('ldap_econf', "<tt>$config{'auth_ldap'}</tt>");
188 # Get the host and port
189 local $conf = &get_config();
190 local $uri = &find_svalue("uri", $conf);
191 local ($ldap, $use_ssl, $err);
192 local $ssl = &find_svalue("ssl", $conf);
193 local $cafile = &find_svalue("tls_cacertfile", $conf);
194 local $certfile = &find_svalue("tls_cert", $conf);
195 local $keyfile = &find_svalue("tls_key", $conf);
196 local $ciphers = &find_svalue("tls_ciphers", $conf);
198 # Using hosts from parameter
199 local @hosts = split(/[ \t,]+/, $ldap_hosts);
200 if ($ldap_ssl ne '') {
201 $use_ssl = $ldap_ssl;
204 $use_ssl = $ssl eq 'yes' ? 1 :
205 $ssl eq 'start_tls' ? 2 : 0;
207 local $port = $ldap_port ||
208 &find_svalue("port", $conf) ||
209 ($use_ssl == 1 ? 636 : 389);
210 foreach my $host (@hosts) {
212 $ldap = Net::LDAP->new($host, port => $port,
213 scheme => $use_ssl == 1 ? 'ldaps' : 'ldap',
214 inet6 => &should_use_inet6($host));
217 $err = &text('ldap_econn2',
218 "<tt>$host</tt>", "<tt>$port</tt>",
222 $err = &text('ldap_econn',
223 "<tt>$host</tt>", "<tt>$port</tt>");
232 # Using uri directive
233 foreach my $u (split(/\s+/, $uri)) {
234 if ($u =~ /^(ldap|ldaps|ldapi):\/\/([a-z0-9\_\-\.]+)(:(\d+))?/i) {
235 ($proto, $host, $port) = ($1, $2, $4);
236 if (!$port && $proto eq "ldap") {
239 elsif (!$port && $proto eq "ldaps") {
242 $ldap = Net::LDAP->new($host, port => $port,
244 inet6 => &should_use_inet6($host));
246 $err = &text('ldap_econn',
247 "<tt>$host</tt>","<tt>$port</tt>");
251 $use_ssl = $proto eq "ldaps" ? 1 :
252 $ssl eq 'start_tls' ? 2 : 0;
257 if (!$ldap && !$err) {
258 $err = &text('ldap_eparse', $uri);
262 # Using host and port directives
263 $use_ssl = $ssl eq 'yes' ? 1 :
264 $ssl eq 'start_tls' ? 2 : 0;
265 local @hosts = split(/[ ,]+/, &find_svalue("host", $conf));
266 local $port = &find_svalue("port", $conf) ||
267 ($use_ssl == 1 ? 636 : 389);
268 @hosts = ( "localhost" ) if (!@hosts);
270 foreach $host (@hosts) {
271 $ldap = Net::LDAP->new($host, port => $port,
272 scheme => $use_ssl == 1 ? 'ldaps' : 'ldap',
273 inet6 => &should_use_inet6($host));
275 $err = &text('ldap_econn',
276 "<tt>$host</tt>", "<tt>$port</tt>");
285 # Start TLS if configured
286 if ($use_ssl == 2 && !$err) {
289 # Use cert to connect
290 eval { $mesg = $ldap->start_tls(
292 clientcert => $certfile,
293 clientkey => $keyfile,
299 eval { $mesg = $ldap->start_tls(); };
301 if ($@ || !$mesg || $mesg->code) {
302 $err = &text('ldap_etls', $@ ? $@ : $mesg ? $mesg->error :
311 local ($dn, $password);
312 local $rootbinddn = &find_svalue("rootbinddn", $conf);
314 # Use login from config
316 $password = $ldap_pass;
318 elsif ($rootbinddn) {
319 # Use the root login if we have one
321 $password = &get_rootbinddn_secret();
324 # Use the normal login
325 $dn = &find_svalue("binddn", $conf);
326 $password = &find_svalue("bindpw", $conf);
330 $mesg = $ldap->bind(dn => $dn, password => $password);
333 $mesg = $ldap->bind(dn => $dn, anonymous => 1);
335 if (!$mesg || $mesg->code) {
336 local $err = &text('ldap_elogin', "<tt>$host</tt>",
337 $dn || $text{'ldap_anon'},
338 $mesg ? $mesg->error : "Unknown error");
339 if ($_[0]) { return $err; }
340 else { &error($err); }
345 # should_use_inet6(host)
346 # Returns 1 if some host has a v6 address but not v4
350 return !&to_ipaddress($host) && &to_ip6address($host);
353 # base_chooser_button(field, node, form)
354 # Returns HTML for a popup LDAP base chooser button
355 sub base_chooser_button
357 local ($field, $node, $form) = @_;
361 if ($gconfig{'db_sizeusers'}) {
362 ($w, $h) = split(/x/, $gconfig{'db_sizeusers'});
364 return "<input type=button onClick='ifield = document.forms[$form].$field; chooser = window.open(\"popup_browser.cgi?node=$node&base=\"+escape(ifield.value), \"chooser\", \"toolbar=no,menubar=no,scrollbars=yes,width=$w,height=$h\"); chooser.ifield = ifield; window.ifield = ifield' value=\"...\">\n";
368 # Returns the hostname probably used for connecting
372 if ($config{'ldap_hosts'}) {
373 @hosts = split(/\s+/, $config{'ldap_hosts'});
375 elsif (!-r $config{'auth_ldap'}) {
379 local $conf = &get_config();
380 local $uri = &find_svalue("uri", $conf);
382 foreach my $u (split(/\s+/, $uri)) {
383 if ($u =~ /^(ldap|ldaps|ldapi):\/\/([a-z0-9\_\-\.]+)(:(\d+))?/) {
389 @hosts = split(/[ ,]+/, &find_svalue("host", $conf));
392 @hosts = ( "localhost" );
395 return wantarray ? @hosts : $hosts[0];