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.

FossilOrigin-Name: b2bc73d1b624235cf17da960a5e3fd0df9c0ed714bc5f71db2cbea6009f255d5
This commit is contained in:
Mahlon E. Smith 2019-10-13 17:44:16 +00:00
parent 14974b8a9f
commit b9098f4e53
2 changed files with 41 additions and 20 deletions

View file

@ -17,5 +17,6 @@ Peter Marschall <peter@adpm.de>
Rick H. <rickh_shelldap@printstring.com> Rick H. <rickh_shelldap@printstring.com>
Rong-En Fan <rafan@FreeBSD.org> Rong-En Fan <rafan@FreeBSD.org>
Salvatore Bonaccorso <carnil@debian.org> Salvatore Bonaccorso <carnil@debian.org>
Uwe Kleine-König <uwe@kleine-koenig.org>
Yann Cezard <yann.cezard@univ-pau.fr> Yann Cezard <yann.cezard@univ-pau.fr>

View file

@ -166,7 +166,20 @@ credentials.
A space separated list of SASL mechanisms. Requires the Authen::SASL A space separated list of SASL mechanisms. Requires the Authen::SASL
module. 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 =back
@ -709,8 +722,8 @@ sub ldap
You may try connecting insecurely, or install the module and try again.\n} if $@; You may try connecting insecurely, or install the module and try again.\n} if $@;
} }
if ($conf->{'binddn'}) { if ( $conf->{'binddn'} ) {
if($conf->{'promptpass'}) { if ( $conf->{'promptpass'} ) {
# Prompt for a password after disabling local echo. # Prompt for a password after disabling local echo.
# #
print "Bind password: "; print "Bind password: ";
@ -718,9 +731,11 @@ You may try connecting insecurely, or install the module and try again.\n} if $@
chomp( $conf->{'bindpass'} = <STDIN> ); chomp( $conf->{'bindpass'} = <STDIN> );
Term::ReadKey::ReadMode 0; Term::ReadKey::ReadMode 0;
print "\n"; print "\n";
} elsif($conf->{'pass'}) { }
elsif ( $conf->{'pass'} ) {
$conf->{'bindpass'} = $conf->{'pass'} $conf->{'bindpass'} = $conf->{'pass'}
} elsif($conf->{'passfile'}) { }
elsif ( $conf->{'passfile'} ) {
chomp( $conf->{'bindpass'} = slurp($conf->{'passfile'})); chomp( $conf->{'bindpass'} = slurp($conf->{'passfile'}));
} }
} }
@ -762,16 +777,20 @@ You may try connecting insecurely, or install the module and try again.\n} if $@
if ( $use_sasl ) { if ( $use_sasl ) {
my $serv = $conf->{'server'}; my $serv = $conf->{'server'};
$serv =~ s!^ldap[si]?://!!; $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 ); $sasl_conn = $sasl->client_new( 'ldap', $serv );
} }
# bind with sasl # bind with sasl
# #
if ( $sasl_conn ) { if ( $sasl_conn ) {
$rv = $ldap->bind( $conf->{'binddn'}, $rv = $ldap->bind( $conf->{'binddn'}, sasl => $sasl_conn );
sasl => $sasl_conn
);
} }
# simple bind as an authenticated dn # simple bind as an authenticated dn
@ -785,7 +804,7 @@ You may try connecting insecurely, or install the module and try again.\n} if $@
# bind anonymously # bind anonymously
# #
else { else {
$rv = $sasl_conn ? $ldap->bind( sasl => $sasl_conn ) : $ldap->bind(); $rv = $ldap->bind();
} }
my $err = $rv->error(); my $err = $rv->error();
@ -2715,9 +2734,10 @@ Getopt::Long::GetOptions(
'passfile|y=s', 'passfile|y=s',
'timeout=i', 'timeout=i',
'sasl|Y=s', 'sasl|Y=s',
'sasluser|X=s',
'simple|x!' => sub { 'simple|x!' => sub {
my($opt,$arg) = @_; my($opt,$arg) = @_;
$conf->{sasl} = $arg ? undef : 'PLAIN CRAM-MD5 GSSAPI' $conf->{sasl} = $arg ? undef : 'PLAIN DIGEST-MD5 GSSAPI'
}, },
'tls_cacert=s', 'tls_cacert=s',
'tls_cert=s', 'tls_cert=s',
@ -2755,8 +2775,8 @@ $conf->{'attributes'} ||= ['*'];
# Allow command line option --attributes to override settings from # Allow command line option --attributes to override settings from
# config file. # config file.
if($conf->{'cmdline_attributes'}) { if ( $conf->{'cmdline_attributes'} ) {
$conf->{'attributes'} = $conf->{'cmdline_attributes'} $conf->{'attributes'} = $conf->{'cmdline_attributes'};
} }
# create and enter shell loop while also handling Ctrl+C correctly. # create and enter shell loop while also handling Ctrl+C correctly.
@ -2774,9 +2794,9 @@ sub ctrl_c_handler {
} }
my $sigaction = POSIX::SigAction->new( \&ctrl_c_handler, $sigset, 0); my $sigaction = POSIX::SigAction->new( \&ctrl_c_handler, $sigset, 0);
my $old_action = POSIX::SigAction->new; 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(); $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 ### List of default config files
### ###
@ -2813,13 +2833,13 @@ sub load_config
my $conf2 = eval { YAML::Syck::Load( $data ) }; my $conf2 = eval { YAML::Syck::Load( $data ) };
die "Invalid YAML in $confpath\n" if $@; die "Invalid YAML in $confpath\n" if $@;
if( $conf2->{'configfile'} and ($confpath eq $conf2->{'configfile'})) { if ( $conf2->{'configfile'} and ($confpath eq $conf2->{'configfile'}) ) {
delete $conf2->{'configfile'} delete $conf2->{'configfile'};
} }
$conf2->{alias} ||= {}; $conf2->{alias} ||= {};
return($confpath, $conf2); return( $confpath, $conf2 );
} }
### dump YAML config into conf file while making sure that ### dump YAML config into conf file while making sure that
@ -2833,8 +2853,8 @@ sub save_config
my %conf2 = %$conf; my %conf2 = %$conf;
# This check is currently unnecessary because the comparison will always # This check is currently unnecessary because the comparison will always
# be true, but is left here for effect of least surprise in the future. # be true, but is left here for effect of least surprise in the future.
if( $conf->{configfile} and ($confpath eq $conf->{configfile})) { if ( $conf->{configfile} and ($confpath eq $conf->{configfile}) ) {
delete $conf2{'configfile'} delete $conf2{'configfile'};
} }
YAML::Syck::DumpFile( $confpath, \%conf2 ); YAML::Syck::DumpFile( $confpath, \%conf2 );
chmod 0600, $confpath; chmod 0600, $confpath;