Fix sasl for DIGEST-MD5, PLAIN, and LOGIN mechanisms, which I don't think ever actually worked properly.
authorMahlon E. Smith <mahlon@martini.nu>
Sun, 13 Oct 2019 10:44:16 -0700
changeset 131 3b6cb1117ffb
parent 130 af3c3c0a1f3b
child 132 c06315e8f0ac
Fix sasl for DIGEST-MD5, PLAIN, and LOGIN mechanisms, which I don't think ever actually worked properly. Add a sasluser argument if a specific identity is required for the backend, which if unsupplied, tries to guess if a binddn is present. (Uwe's previous commit fixed EXTERNAL and GSSAPI, which did work, but randomly failed due to hash ordering.) Minor style cleanups, remove duplicate/unecessary logic for anonymous binds.
CONTRIBUTORS
shelldap
--- a/CONTRIBUTORS	Sun Oct 13 10:19:40 2019 -0700
+++ b/CONTRIBUTORS	Sun Oct 13 10:44:16 2019 -0700
@@ -17,5 +17,6 @@
 Rick H. <rickh_shelldap@printstring.com>
 Rong-En Fan <rafan@FreeBSD.org>
 Salvatore Bonaccorso <carnil@debian.org>
+Uwe Kleine-König <uwe@kleine-koenig.org>
 Yann Cezard <yann.cezard@univ-pau.fr>
 
--- a/shelldap	Sun Oct 13 10:19:40 2019 -0700
+++ b/shelldap	Sun Oct 13 10:44:16 2019 -0700
@@ -166,7 +166,20 @@
 A space separated list of SASL mechanisms.  Requires the Authen::SASL
 module.
 
-    --sasl 'PLAIN CRAM-MD5 GSSAPI'
+    --sasl 'PLAIN DIGEST-MD5 EXTERNAL GSSAPI'
+    -Y 'PLAIN DIGEST-MD5 EXTERNAL GSSAPI'
+
+=back
+
+=over 4
+
+=item B<sasluser>
+
+SASL authorization identity, if one is explicitly required by your
+backend mechanism.
+
+    --sasluser mahlon
+    -X mahlon
 
 =back
 
@@ -709,8 +722,8 @@
 You may try connecting insecurely, or install the module and try again.\n} if $@;
 	}
 
-	if ($conf->{'binddn'}) {
-		if($conf->{'promptpass'}) {
+	if ( $conf->{'binddn'} ) {
+		if ( $conf->{'promptpass'} ) {
 			# Prompt for a password after disabling local echo.
 			#
 			print "Bind password: ";
@@ -718,9 +731,11 @@
 			chomp( $conf->{'bindpass'} = <STDIN> );
 			Term::ReadKey::ReadMode 0;
 			print "\n";
-		} elsif($conf->{'pass'}) {
+		}
+		elsif ( $conf->{'pass'} ) {
 			$conf->{'bindpass'} = $conf->{'pass'}
-		} elsif($conf->{'passfile'}) {
+		}
+		elsif ( $conf->{'passfile'} ) {
 			chomp( $conf->{'bindpass'} = slurp($conf->{'passfile'}));
 		}
 	}
@@ -762,16 +777,20 @@
 	if ( $use_sasl ) {
 		my $serv = $conf->{'server'};
 		$serv =~ s!^ldap[si]?://!!;
-		$sasl = Authen::SASL->new( mechanism => $conf->{'sasl'} );
+		my $user = $1 if $conf->{'binddn'} && $conf->{'binddn'} =~ /uid=([^,]*),/i;
+		my $callback = {
+			pass => $conf->{'bindpass'},
+			user => $conf->{'sasluser'} || $user
+		};
+
+		$sasl = Authen::SASL->new( mechanism => $conf->{'sasl'}, callback => $callback );
 		$sasl_conn = $sasl->client_new( 'ldap', $serv );
 	}
 
 	# bind with sasl
 	#
 	if ( $sasl_conn ) {
-		$rv = $ldap->bind( $conf->{'binddn'},
-			sasl     => $sasl_conn
-		);
+		$rv = $ldap->bind( $conf->{'binddn'}, sasl => $sasl_conn );
 	}
 
 	# simple bind as an authenticated dn
@@ -785,7 +804,7 @@
 	# bind anonymously
 	#
 	else {
-		$rv = $sasl_conn ? $ldap->bind( sasl => $sasl_conn ) : $ldap->bind();
+		$rv = $ldap->bind();
 	}
 
 	my $err = $rv->error();
@@ -2715,9 +2734,10 @@
 	'passfile|y=s',
 	'timeout=i',
 	'sasl|Y=s',
+	'sasluser|X=s',
 	'simple|x!' => sub {
 		my($opt,$arg) = @_;
-		$conf->{sasl} = $arg ? undef : 'PLAIN CRAM-MD5 GSSAPI'
+		$conf->{sasl} = $arg ? undef : 'PLAIN DIGEST-MD5 GSSAPI'
 	},
 	'tls_cacert=s',
 	'tls_cert=s',
@@ -2755,8 +2775,8 @@
 
 # Allow command line option --attributes to override settings from
 # config file.
-if($conf->{'cmdline_attributes'}) {
-	$conf->{'attributes'} = $conf->{'cmdline_attributes'}
+if ( $conf->{'cmdline_attributes'} ) {
+	$conf->{'attributes'} = $conf->{'cmdline_attributes'};
 }
 
 # create and enter shell loop while also handling Ctrl+C correctly.
@@ -2774,9 +2794,9 @@
 }
 my $sigaction = POSIX::SigAction->new( \&ctrl_c_handler, $sigset, 0);
 my $old_action = POSIX::SigAction->new;
-POSIX::sigaction(&POSIX::SIGINT, $sigaction, $old_action); # save default one
+POSIX::sigaction( &POSIX::SIGINT, $sigaction, $old_action ); # save default one
 $shell->cmdloop();
-POSIX::sigaction(&POSIX::SIGINT, $old_action); # restore default one
+POSIX::sigaction( &POSIX::SIGINT, $old_action ); # restore default one
 
 ### List of default config files
 ###
@@ -2813,13 +2833,13 @@
 	my $conf2 = eval { YAML::Syck::Load( $data ) };
 	die "Invalid YAML in $confpath\n" if $@;
 
-	if( $conf2->{'configfile'} and ($confpath eq $conf2->{'configfile'})) {
-		delete $conf2->{'configfile'}
+	if ( $conf2->{'configfile'} and ($confpath eq $conf2->{'configfile'}) ) {
+		delete $conf2->{'configfile'};
 	}
 
 	$conf2->{alias} ||= {};
 
-	return($confpath, $conf2);
+	return( $confpath, $conf2 );
 }
 
 ### dump YAML config into conf file while making sure that
@@ -2833,8 +2853,8 @@
 	my %conf2 = %$conf;
 	# This check is currently unnecessary because the comparison will always
 	# be true, but is left here for effect of least surprise in the future.
-	if( $conf->{configfile} and ($confpath eq $conf->{configfile})) {
-	 			 delete $conf2{'configfile'}
+	if ( $conf->{configfile} and ($confpath eq $conf->{configfile}) ) {
+		delete $conf2{'configfile'};
 	}
 	YAML::Syck::DumpFile( $confpath, \%conf2 );
 	chmod 0600, $confpath;