[Koha-patches] [PATCH] Add TLS support to LDAP auth. Minor code refactor to clear up bind, search, compare distinction. TLS support requires IO::Socket::SSL, which has been added to dependencies

Ian Walls ian.walls at bywatersolutions.com
Thu May 13 19:02:43 CEST 2010


---
 C4/Auth_with_ldap.pm         |  136 ++++++++++++++++++++++++++----------------
 Makefile.PL                  |    1 +
 about.pl                     |    1 +
 install_misc/debian.packages |    1 +
 4 files changed, 87 insertions(+), 52 deletions(-)

diff --git a/C4/Auth_with_ldap.pm b/C4/Auth_with_ldap.pm
index b25697c..3b4d3ad 100644
--- a/C4/Auth_with_ldap.pm
+++ b/C4/Auth_with_ldap.pm
@@ -18,7 +18,7 @@ package C4::Auth_with_ldap;
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 use strict;
-#use warnings; FIXME - Bug 2505
+# use warnings; almost?
 use Digest::MD5 qw(md5_base64);
 
 use C4::Debug;
@@ -58,7 +58,7 @@ my $prefhost  = $ldap->{hostname}	or die ldapserver_error('hostname');
 my $base      = $ldap->{base}		or die ldapserver_error('base');
 $ldapname     = $ldap->{user}		;
 $ldappassword = $ldap->{pass}		;
-our %mapping  = %{$ldap->{mapping}}; # FIXME dpavlin -- don't die because of || (); from 6eaf8511c70eb82d797c941ef528f4310a15e9f9
+our %mapping  = %{$ldap->{mapping}} or die ldapserver_error('mapping');
 my @mapkeys = keys %mapping;
 $debug and print STDERR "Got ", scalar(@mapkeys), " ldap mapkeys (  total  ): ", join ' ', @mapkeys, "\n";
 @mapkeys = grep {defined $mapping{$_}->{is}} @mapkeys;
@@ -80,68 +80,101 @@ sub description ($) {
 sub search_method {
     my $db     = shift or return;
     my $userid = shift or return;
-	my $uid_field = $mapping{userid}->{is} or die ldapserver_error("mapping for 'userid'");
-	my $filter = Net::LDAP::Filter->new("$uid_field=$userid") or die "Failed to create new Net::LDAP::Filter";
-    my $res = ($config{anonymous}) ? $db->bind : $db->bind($ldapname, password=>$ldappassword);
-    if ($res->code) {		# connection refused
-        warn "LDAP bind failed as ldapuser " . ($ldapname || '[ANONYMOUS]') . ": " . description($res);
-        return 0;
-    }
-	my $search = $db->search(
+	  my $uid_field = $mapping{userid}->{is} or die ldapserver_error("mapping for 'userid'");
+	  my $filter = Net::LDAP::Filter->new("$uid_field=$userid") or die "Failed to create new Net::LDAP::Filter";
+
+	  my $search = $db->search(
 		  base => $base,
-	 	filter => $filter,
-		# attrs => ['*'],
-	) or die "LDAP search failed to return object.";
-	my $count = $search->count;
-	if ($search->code > 0) {
-		warn sprintf("LDAP Auth rejected : %s gets %d hits\n", $filter->as_string, $count) . description($search);
-		return 0;
-	}
-	if ($count != 1) {
-		warn sprintf("LDAP Auth rejected : %s gets %d hits\n", $filter->as_string, $count);
-		return 0;
-	}
+	 	  filter => $filter,
+		  # attrs => ['*'],
+	    ) or die "LDAP search failed to return object.";
+	  my $count = $search->count;
+	  if ($search->code > 0) {
+		  warn sprintf("LDAP Auth rejected : %s gets %d hits\n", $filter->as_string, $count) . description($search);
+		  return 0;
+	  }
+	  if ($count != 1) {
+		  warn sprintf("LDAP Auth rejected : %s gets %d hits\n", $filter->as_string, $count);
+		  return 0;
+	  }
     return $search;
 }
 
-sub checkpw_ldap {
-    my ($dbh, $userid, $password) = @_;
-    my @hosts = split(',', $prefhost);
-    my $db = Net::LDAP->new(\@hosts);
-	#$debug and $db->debug(5);
-    my $userldapentry;
-	if ( $ldap->{auth_by_bind} ) {
-        my $principal_name = $ldap->{principal_name};
-        if ($principal_name and $principal_name =~ /\%/) {
+sub bind_to_ldap {
+   my $db = shift;
+   my $userid = shift;
+   my $password = shift;   
+   my $res;  #error code capture
+   
+   # if auth_by_bind, bind by the supplied userid and password
+   if ($ldap->{auth_by_bind} ) {
+       my $principal_name = $ldap->{principal_name};
+       if ($principal_name and $principal_name =~ /\%/) {
             $principal_name = sprintf($principal_name,$userid);
-        } else {
+       } else {
             $principal_name = $userid;
-        }
-		my $res = $db->bind( $principal_name, password => $password );
-        if ( $res->code ) {
-            $debug and warn "LDAP bind failed as kohauser $principal_name: ". description($res);
+       }
+		   $res = $db->bind( $principal_name, password => $password );
+       if ($res->code ) {   # connection refused
+            warn "LDAP bind failed as kohauser $principal_name: ". description($res);
             return 0;
         }
+    # otherwise, if no ldap user or password, do an anonymous bind
+    } elsif ($config{anonymous}) {
+       $res = $db->bind;
+       if ($res->code) {		# connection refused
+            warn "LDAP bind failed as ANONYMOUS: " . description($res);
+            return 0;
+       }
+   # otherwise, bind by the userid supplied in checkpw_ldap (normally)
+    } else {
+       $res = $db->bind($ldapname, password => $ldappassword);
+       if ($res->code) {		# connection refused
+            warn "LDAP bind failed as ldapuser $ldapname: " . description($res);
+            return 0;
+       }
+   }
+   #return the now bound $db
+   return $db;
+}
 
-	# FIXME dpavlin -- we really need $userldapentry leater on even if using auth_by_bind!
-	my $search = search_method($db, $userid) or return 0;   # warnings are in the sub
-	$userldapentry = $search->shift_entry;
-
-	} else {
-        my $search = search_method($db, $userid) or return 0;   # warnings are in the sub
-        $userldapentry = $search->shift_entry;
-		my $cmpmesg = $db->compare( $userldapentry, attr=>'userpassword', value => $password );
-		if ($cmpmesg->code != 6) {
-			warn "LDAP Auth rejected : invalid password for user '$userid'. " . description($cmpmesg);
-			return 0;
-		}
-	}
+sub checkpw_ldap {
+    my ($dbh, $userid, $password) = @_;
+    my @hosts = split(',', $prefhost);
+    my $db = Net::LDAP->new(\@hosts) or die "$@";
+
+    # start TLS connection if configured.  Uses TLS default settings only
+    if ($ldap->{tls}) {
+       my $tls_msg = $db->start_tls();
+       if ($tls_msg->code) {		# TLS error
+          warn "TLS connection rejected: " . description($tls_msg);
+          return 0;
+       }
+    }
+
+    # Bind to the ldap in the appropriate manner
+    $db = bind_to_ldap($db, $userid, $password);
+
+    # search for the userid
+    my $search = search_method($db, $userid) or return 0;   # warnings are in the sub
 
+    # dump the ldap information into userldapentry for processing
+    my $userldapentry = $search->shift_entry;
+
+		# if we didn't bind to the userid supplied, we need to do a password compare
+    unless ($ldap->{auth_by_bind}) {
+      my $cmpmesg = $db->compare( $userldapentry, attr=>'userpassword', value => $password );
+  		if ($cmpmesg->code != 6) {
+  			warn "LDAP Auth rejected : invalid password for user '$userid'. " . description($cmpmesg);
+  			return 0;
+  		}
+  	}
+  	
     # To get here, LDAP has accepted our user's login attempt.
     # But we still have work to do.  See perldoc below for detailed breakdown.
 
     my (%borrower);
-	my ($borrowernumber,$cardnumber,$local_userid,$savedpw) = exists_local($userid);
+	  my ($borrowernumber,$cardnumber,$local_userid,$savedpw) = exists_local($userid);
 
     if (( $borrowernumber and $config{update}   ) or
         (!$borrowernumber and $config{replicate})   ) {
@@ -155,7 +188,6 @@ sub checkpw_ldap {
             ($cardnumber eq $c2) or warn "update_local returned cardnumber '$c2' instead of '$cardnumber'";
         } else { # C1, D1
             # maybe update just the password?
-		return(1, $cardnumber); # FIXME dpavlin -- don't destroy ExtendedPatronAttributes
         }
     } elsif ($config{replicate}) { # A2, C2
         $borrowernumber = AddMember(%borrower) or die "AddMember failed";
@@ -166,7 +198,6 @@ sub checkpw_ldap {
    		my @types = C4::Members::AttributeTypes::GetAttributeTypes();
 		my @attributes = grep{my $key=$_; any{$_ eq $key}@types;} keys %borrower;
 		my $extended_patron_attributes = map{{code=>$_,value=>$borrower{$_}}}@attributes;
-		my $extended_patron_attributes = [] unless $extended_patron_attributes;
 		my @errors;
 		#Check before add
 		for (my $i; $i< scalar(@$extended_patron_attributes)-1;$i++) {
@@ -385,6 +416,7 @@ Example XML stanza for LDAP configuration in KOHA_CONF.
                                         password comparison, e.g., to use Active Directory -->
     <principal_name>%s at my_domain.com</principal_name>
                                    <!-- optional, for auth_by_bind: a printf format to make userPrincipalName from koha userid -->
+    <tls>0</tls>               <!-- set to 1 to use Transport Layer Security (TLS) -->
     <mapping>                  <!-- match koha SQL field names to your LDAP record field names -->
       <firstname    is="givenname"      ></firstname>
       <surname      is="sn"             ></surname>
diff --git a/Makefile.PL b/Makefile.PL
index c88d5ea..e4f6897 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -570,6 +570,7 @@ WriteMakefile(
                             'HTTP::OAI'                        => 3.20,
                             'HTTP::Request::Common'            => 1.26,
                             'IPC::Cmd'                         => 0.46,
+                            'IO::Socket::SSL'                  => 1.33,
                             'JSON'                             => 2.07, # Needed by admin/item_circulation_alerts.pl
                             'LWP::Simple'                      => 1.41,
                             'LWP::UserAgent'                   => 2.033,
diff --git a/about.pl b/about.pl
index 04a9675..ecdfdc2 100755
--- a/about.pl
+++ b/about.pl
@@ -94,6 +94,7 @@ HTTP::OAI
 HTTP::Request::Common
 HTML::Scrubber
 IPC::Cmd
+IO::Socket::SSL
 JSON
 LWP::Simple
 LWP::UserAgent
diff --git a/install_misc/debian.packages b/install_misc/debian.packages
index 11dfeb4..23a1af6 100644
--- a/install_misc/debian.packages
+++ b/install_misc/debian.packages
@@ -39,6 +39,7 @@ libidzebra-2.0-mod-grs-xml	install
 libidzebra-2.0-mod-text	install
 libidzebra-2.0-modules	install
 libimage-magick-perl install
+libio-socket-ssl-perl install
 libjson-perl	install
 liblingua-ispell-perl	install
 liblingua-stem-perl install
-- 
1.5.6.5




More information about the Koha-patches mailing list