send-email: provide whitelist of SMTP AUTH mechanisms

When sending an e-mail, the client and server must agree on an authentication mechanism. Some servers (due to misconfiguration or a bug) deny valid credentials for certain mechanisms. In this patch, a new option --smtp-auth and configuration entry smtpAuth are introduced. If smtp_auth is defined, it works as a whitelist of allowed mechanisms for authentication selected from the ones supported by the installed SASL perl library. Signed-off-by: Jan Viktorin <viktorin@rehivetech.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jan Viktorin committed Aug 12, 2015 at 01:39 UTC 0f2e68b54c7f85656299539de8a4aebab795d369
2 files changed +38 -1
Documentation/git-send-email.txt
+13
@@ -171,6 +171,19 @@ Sending
171 to determine your FQDN automatically. Default is the value of
172 'sendemail.smtpDomain'.
173
174 +--smtp-auth=<mechanisms>::
175 + Whitespace-separated list of allowed SMTP-AUTH mechanisms. This setting
176 + forces using only the listed mechanisms. Example:
177 ++
178 +------
179 +$ git send-email --smtp-auth="PLAIN LOGIN GSSAPI" ...
180 +------
181 ++
182 +If at least one of the specified mechanisms matches the ones advertised by the
183 +SMTP server and if it is supported by the utilized SASL library, the mechanism
184 +is used for authentication. If neither 'sendemail.smtpAuth' nor '--smtp-auth'
185 +is specified, all mechanisms supported by the SASL library can be used.
186 +
187 --smtp-pass[=<password>]::
188 Password for SMTP-AUTH. The argument is optional: If no
189 argument is specified, then the empty string is used as
git-send-email.perl
+25 -1
@@ -75,6 +75,8 @@ git send-email [options] <file | directory | rev-list options >
75 Pass an empty string to disable certificate
76 verification.
77 --smtp-domain <str> * The domain name sent to HELO/EHLO handshake
78 + --smtp-auth <str> * Space-separated list of allowed AUTH mechanisms.
79 + This setting forces to use one of the listed mechanisms.
80 --smtp-debug <0|1> * Disable, enable Net::SMTP debug.
81
82 Automating:
@@ -208,7 +210,7 @@ my ($cover_cc, $cover_to);
210 my ($to_cmd, $cc_cmd);
211 my ($smtp_server, $smtp_server_port, @smtp_server_options);
212 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
211 -my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
213 +my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);
214 my ($validate, $confirm);
215 my (@suppress_cc);
216 my ($auto_8bit_encoding);
@@ -239,6 +241,7 @@ my %config_settings = (
241 "smtppass" => \$smtp_authpass,
242 "smtpsslcertpath" => \$smtp_ssl_cert_path,
243 "smtpdomain" => \$smtp_domain,
244 + "smtpauth" => \$smtp_auth,
245 "to" => \@initial_to,
246 "tocmd" => \$to_cmd,
247 "cc" => \@initial_cc,
@@ -310,6 +313,7 @@ my $rc = GetOptions("h" => \$help,
313 "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path,
314 "smtp-debug:i" => \$debug_net_smtp,
315 "smtp-domain:s" => \$smtp_domain,
316 + "smtp-auth=s" => \$smtp_auth,
317 "identity=s" => \$identity,
318 "annotate!" => \$annotate,
319 "no-annotate" => sub {$annotate = 0},
@@ -1136,6 +1140,12 @@ sub smtp_auth_maybe {
1140 Authen::SASL->import(qw(Perl));
1141 };
1142
1143 + # Check mechanism naming as defined in:
1144 + # https://tools.ietf.org/html/rfc4422#page-8
1145 + if ($smtp_auth !~ /^(\b[A-Z0-9-_]{1,20}\s*)*$/) {
1146 + die "invalid smtp auth: '${smtp_auth}'";
1147 + }
1148 +
1149 # TODO: Authentication may fail not because credentials were
1150 # invalid but due to other reasons, in which we should not
1151 # reject credentials.
@@ -1148,6 +1158,20 @@ sub smtp_auth_maybe {
1158 'password' => $smtp_authpass
1159 }, sub {
1160 my $cred = shift;
1161 +
1162 + if ($smtp_auth) {
1163 + my $sasl = Authen::SASL->new(
1164 + mechanism => $smtp_auth,
1165 + callback => {
1166 + user => $cred->{'username'},
1167 + pass => $cred->{'password'},
1168 + authname => $cred->{'username'},
1169 + }
1170 + );
1171 +
1172 + return !!$smtp->auth($sasl);
1173 + }
1174 +
1175 return !!$smtp->auth($cred->{'username'}, $cred->{'password'});
1176 });
1177