From 63b62237a8a26d463a5cb3ea954966f71e72e9e4 Mon Sep 17 00:00:00 2001 From: Jamie Cameron Date: Tue, 14 Sep 2010 17:45:11 -0700 Subject: [PATCH] Start of work on LDAP support --- acl/acl-lib.pl | 59 ++++++++++++++++++++++++++++-------------------- acl/edit_sql.cgi | 16 ++++++++++++- acl/lang/en | 13 +++++++---- acl/save_sql.cgi | 17 ++++++++++++++ lang/en | 10 ++++++++ web-lib-funcs.pl | 35 ++++++++++++++++++++++++++-- 6 files changed, 118 insertions(+), 32 deletions(-) diff --git a/acl/acl-lib.pl b/acl/acl-lib.pl index 74edf6897..7ab90e10d 100755 --- a/acl/acl-lib.pl +++ b/acl/acl-lib.pl @@ -96,9 +96,7 @@ close(PWFILE); # If a user DB is enabled, get users from it too if ($miniserv{'userdb'}) { - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to user database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Fetch users with SQL @@ -175,9 +173,7 @@ close(GROUPS); # If a user DB is enabled, get groups from it too if ($miniserv{'userdb'}) { - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to group database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Fetch groups with SQL @@ -261,9 +257,7 @@ my @mods = &list_modules(); if ($miniserv{'userdb'} && !$miniserv{'userdb_addto'}) { # Adding to user database - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to user database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Add user with SQL @@ -393,9 +387,7 @@ local $_; if ($user{'proto'}) { # In users and groups DB - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to user database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Get old password, for change detection @@ -657,9 +649,7 @@ if ($miniserv{'session'}) { if ($miniserv{'userdb'}) { # Also delete from user database - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to user database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Find the user with SQL query @@ -724,9 +714,7 @@ my %miniserv; if ($miniserv{'userdb'} && !$miniserv{'userdb_addto'}) { # Adding to group database - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to group database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Add group with SQL @@ -793,9 +781,7 @@ my %miniserv; if ($group{'proto'}) { # In users and groups DB - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to group database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Update primary details @@ -876,9 +862,7 @@ local $lref = &read_file_lines("$config_directory/webmin.groups"); if ($miniserv{'userdb'}) { # Also delete from group database - my ($proto, $user, $pass, $host, $prefix, $args) = - &split_userdb_string($miniserv{'userdb'}); - my $dbh = &connect_userdb($miniserv{'userdb'}); + my ($dbh, $proto) = &connect_userdb($miniserv{'userdb'}); &error("Failed to connect to group database : $dbh") if (!ref($dbh)); if ($proto eq "mysql" || $proto eq "postgresql") { # Find the group with SQL query @@ -1701,7 +1685,32 @@ if ($proto eq "mysql" || $proto eq "postgresql") { return undef; } elsif ($proto eq "ldap") { - # XXX + # Load LDAP module + eval 'use Net::LDAP;'; + return &text('sql_emod', 'Net::LDAP') if ($@); + + # Try to connect + my $dbh = &connect_userdb($str); + ref($dbh) || return $dbh; + + # Check that base DN exists + if (!$notablecheck) { + my $superprefix = $prefix; + $superprefix =~ s/^[^,]+,//; # Make parent DN + my $rv = $dbh->search(base => $superprefix, + scope => 'one'); + my $niceprefix = lc($prefix); + $niceprefix =~ s/\s//g; + my $found = 0; + foreach my $d ($rv->all_entries) { + my $niced = lc($d->dn()); + $niced =~ s/\s//g; + $found++ if ($niced eq $niceprefix); + } + $found || return &text('sql_eldapdn', $prefix); + } + &disconnect_userdb($str, $dbh); + return undef; } else { return "Unknown user database type $proto"; diff --git a/acl/edit_sql.cgi b/acl/edit_sql.cgi index 3e6b6d585..5c51771d9 100755 --- a/acl/edit_sql.cgi +++ b/acl/edit_sql.cgi @@ -49,6 +49,13 @@ $postgresqlgrid = &ui_grid_table(\@postgresqlgrid, 2, 100); push(@ldapgrid, $text{'sql_host'}, &ui_textbox("ldap_host", $proto eq "ldap" ? $host : "", 30)); +push(@ldapgrid, + $text{'sql_ssl'}, + &ui_radio("ldap_ssl", $args->{'scheme'} eq 'ldaps' ? 1 : + $args->{'tls'} ? 2 : 0, + [ [ 0, $text{'sql_ssl0'} ], + [ 1, $text{'sql_ssl1'} ], + [ 2, $text{'sql_ssl2'} ] ])); push(@ldapgrid, $text{'sql_user'}, &ui_textbox("ldap_user", $proto eq "ldap" ? $user : "", 30)); @@ -58,7 +65,14 @@ push(@ldapgrid, push(@ldapgrid, $text{'sql_prefix'}, &ui_textbox("ldap_prefix", $proto eq "ldap" ? $prefix : "", 30)); -# XXX object classes? +push(@ldapgrid, + $text{'sql_userclass'}, + &ui_textbox("ldap_userclass", $proto eq "ldap" && $args->{'userclass'} ? + $args->{'userclass'} : "webminUser", 30)); +push(@ldapgrid, + $text{'sql_groupclass'}, + &ui_textbox("ldap_groupclass", $proto eq "ldap" && $args->{'groupclass'} ? + $args->{'groupclass'} : "webminGroup",30)); $ldapgrid = &ui_grid_table(\@ldapgrid, 2, 100); print &ui_table_row(undef, diff --git a/acl/lang/en b/acl/lang/en index fcf3bfb1d..c0843614f 100644 --- a/acl/lang/en +++ b/acl/lang/en @@ -393,6 +393,14 @@ sql_host=Hostname sql_user=Username sql_pass=Password sql_db=Database name +sql_ssl=Connection encryption +sql_ssl0=None +sql_ssl1=SSL +sql_ssl2=TLS +sql_userclass=Object class for users +sql_groupclass=Object class for groups +sql_euserclass=Missing or invalid object class for users +sql_egroupclass=Missing or invalid object class for groups sql_none=Use only local files to store users and groups sql_mysql=Use MySQL database sql_postgresql=Use PostgreSQL database @@ -401,11 +409,8 @@ sql_prefix=Create under DN sql_addto0=Add new users to database selected above sql_addto1=Add new users to local files sql_emod=Missing required Perl module $1 -sql_emysqldriver=Failed to load MySQL DBI driver -sql_emysqlconnect=Failed to connect to MySQL database : $1 -sql_epostgresqldriver=Failed to load PostgreSQL DBI driver -sql_epostgresqlconnect=Failed to connect to PostgreSQL database : $1 sql_etable=Failed to query required table $1 : $2 +sql_eldapdn=Base LDAP DN $1 was not found sql_err=Failed to save user and group database settings sql_ehost=Missing or un-resolvable hostname sql_euser=Missing or invalid username (no spaces allowed) diff --git a/acl/save_sql.cgi b/acl/save_sql.cgi index 3a747a994..f5d61e3e6 100755 --- a/acl/save_sql.cgi +++ b/acl/save_sql.cgi @@ -27,6 +27,23 @@ elsif ($p eq 'ldap') { $in{$p."_prefix"} =~ /^\S+$/ || &error($text{'sql_eprefix'}); $in{$p."_prefix"} =~ /=/ || &error($text{'sql_eprefix2'}); $prefix = $in{$p."_prefix"}; + $args = { }; + if ($in{'ldap_ssl'} == 0) { + $args->{'scheme'} = 'ldap'; + } + elsif ($in{'ldap_ssl'} == 1) { + $args->{'scheme'} = 'ldaps'; + } + elsif ($in{'ldap_ssl'} == 2) { + $args->{'scheme'} = 'ldap'; + $args->{'tls'} = 1; + } + $in{'ldap_userclass'} =~ /^[a-z0-9]+$/i || + &error($text{'sql_euserclass'}); + $args->{'userclass'} = $in{'ldap_userclass'}; + $in{'ldap_groupclass'} =~ /^[a-z0-9]+$/i || + &error($text{'sql_egroupclass'}); + $args->{'groupclass'} = $in{'ldap_groupclass'}; } # Create and test connection string diff --git a/lang/en b/lang/en index 19cf20914..da5ea33ff 100644 --- a/lang/en +++ b/lang/en @@ -339,3 +339,13 @@ wsearch_hmod=Module wsearch_moddir=URL path /$1/ wsearch_searching=Searching for $1 . . wsearch_found=found $1 results : + +sql_emysqldriver=Failed to load MySQL DBI driver +sql_emysqlconnect=Failed to connect to MySQL database : $1 +sql_epostgresqldriver=Failed to load PostgreSQL DBI driver +sql_epostgresqlconnect=Failed to connect to PostgreSQL database : $1 +sql_eldapdriver=Failed to load LDAP perl module +sql_eldapconnect=Failed to connect to LDAP server $1 +sql_eldaptls=Failed to start TLS encryption for LDAP : $1 +sql_eldaplogin=Failed to login to LDAP server as $1 : $2 + diff --git a/web-lib-funcs.pl b/web-lib-funcs.pl index 161ed39dd..200091875 100755 --- a/web-lib-funcs.pl +++ b/web-lib-funcs.pl @@ -9019,8 +9019,39 @@ elsif ($proto eq "postgresql") { return wantarray ? ($dbh, $proto) : $dbh; } elsif ($proto eq "ldap") { - # XXX - return "LDAP not done yet"; + # Connect with perl LDAP module + eval "use Net::LDAP"; + $@ && return $text{'sql_eldapdriver'}; + my ($host, $port) = split(/:/, $host); + my $scheme = $args->{'scheme'} || 'ldap'; + if (!$port) { + $port = $scheme eq 'ldaps' ? 636 : 389; + } + my $ldap = Net::LDAP->new($host, + port => $port, + 'scheme' => $scheme); + $ldap || return &text('sql_eldapconnect', $host); + my $mesg; + if ($args->{'tls'}) { + # Switch to TLS mode + eval { $mesg = $ldap->start_tls(); }; + if ($@ || !$mesg || $mesg->code) { + return &text('sql_eldaptls', + $@ ? $@ : $mesg ? $mesg->error : "Unknown error"); + } + } + # Login to the server + if ($pass) { + $mesg = $ldap->bind(dn => $user, password => $pass); + } + else { + $mesg = $ldap->bind(dn => $user, anonymous => 1); + } + if (!$mesg || $mesg->code) { + return &text('sql_eldaplogin', $user, + $mesg ? $mesg->error : "Unknown error"); + } + return wantarray ? ($ldap, $proto) : $ldap; } else { return "Unknown protocol $proto";