Skip to content

Commit a8215a2

Browse files
dxdxdtgitster
authored andcommitted
send-email: add client certificate options
For SMTP servers that do "mutual certificate verification", the mail client is required to present its own TLS certificate as well. This patch adds --smtp-ssl-client-cert and --smtp-ssl-client-key for such servers. The problem of which private key for the certificate is chosen arises when there are private keys in both the certificate and private key file. According to the documentation of IO::Socket::SSL(link supplied), the behaviour(the private key chosen) depends on the format of the certificate. In a nutshell, - PKCS12: the key in the cert always takes the precedence - PEM: if the key file is not given, it will "try" to read one from the cert PEM file Many users may find this discrepancy unintuitive. In terms of client certificate, git-send-email is implemented in a way that what's possible with perl's SSL library is exposed to the user as much as possible. In this instance, the user may choose to use a PEM file that contains both certificate and private key should be at their discretion despite the implications. Link: https://metacpan.org/pod/IO::Socket::SSL#SSL_cert_file-%7C-SSL_cert-%7C-SSL_key_file-%7C-SSL_key Link: https://lore.kernel.org/all/319bf98c-52df-4bf9-b157-e4bc2bf087d6@dev.snart.me/ Signed-off-by: David Timber <dxdt@dev.snart.me> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 67ad421 commit a8215a2

3 files changed

Lines changed: 71 additions & 11 deletions

File tree

Documentation/config/sendemail.adoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ sendemail.smtpSSLCertPath::
1212
Path to ca-certificates (either a directory or a single file).
1313
Set it to an empty string to disable certificate verification.
1414

15+
sendemail.smtpSSLClientCert::
16+
Path to the client certificate file to present if requested by the
17+
server. This is required when the server is set up to verify client
18+
certificates. If the corresponding private key is not included in the
19+
file, it must be supplied using `sendemail.smtpSSLClientKey` or the
20+
`--smtp-ssl-client-key` option.
21+
22+
sendemail.smtpSSLClientKey::
23+
Path to the client private key file that corresponds to the client
24+
certificate. To avoid misconfiguration, this configuration must be used
25+
in conjunction with `sendemail.smtpSSLClientKey` or the
26+
`--smtp-ssl-client-cert` option. If the client key is included in the
27+
client certificate, the choice of private key depends on the format of
28+
the certificate. Visit https://metacpan.org/pod/IO::Socket::SSL for more
29+
details.
30+
1531
sendemail.<identity>.*::
1632
Identity-specific versions of the `sendemail.*` parameters
1733
found below, taking precedence over those when this

Documentation/git-send-email.adoc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,25 @@ must be used for each option.
290290
variable, if set, or the backing SSL library's compiled-in default
291291
otherwise (which should be the best choice on most platforms).
292292

293+
--smtp-ssl-client-cert <path>::
294+
Path to the client certificate file to present if requested by the
295+
server. This option is required when the server is set up to verify
296+
client certificates. If the corresponding private key is not included in
297+
the file, it must be supplied using the `sendemail.smtpSSLClientKey`
298+
configuration variable or the `--smtp-ssl-client-key` option. Defaults
299+
to the value of the `sendemail.smtpSSLClientCert` configuration
300+
variable, if set.
301+
302+
--smtp-ssl-client-key <path>::
303+
Path to the client private key file that corresponds to the client
304+
certificate. To avoid misconfiguration, this option must be used in
305+
conjunction with the `sendemail.smtpSSLClientKey` configuration variable
306+
or the `--smtp-ssl-client-cert` option. If the client key is included in
307+
the client certificate, the choice of private key depends on the format
308+
of the certificate. Visit https://metacpan.org/pod/IO::Socket::SSL for
309+
more details. Defaults to the value of the `sendemail.smtpSSLClientKey`
310+
configuration variable, if set.
311+
293312
--smtp-user=<user>::
294313
Username for SMTP-AUTH. Default is the value of `sendemail.smtpUser`;
295314
if a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),

git-send-email.perl

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ sub usage {
6666
--smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file).
6767
Pass an empty string to disable certificate
6868
verification.
69+
--smtp-ssl-client-cert <str> * Path to the client certificate file
70+
--smtp-ssl-client-key <str> * Path to the private key file for the client certificate
6971
--smtp-domain <str> * The domain name sent to HELO/EHLO handshake
7072
--smtp-auth <str> * Space-separated list of allowed AUTH mechanisms, or
7173
"none" to disable authentication.
@@ -279,6 +281,7 @@ sub do_edit {
279281
my ($to_cmd, $cc_cmd, $header_cmd);
280282
my ($smtp_server, $smtp_server_port, @smtp_server_options);
281283
my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
284+
my ($smtp_ssl_client_cert, $smtp_ssl_client_key);
282285
my ($batch_size, $relogin_delay);
283286
my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);
284287
my ($imap_sent_folder);
@@ -350,6 +353,8 @@ sub do_edit {
350353
my %config_path_settings = (
351354
"aliasesfile" => \@alias_files,
352355
"smtpsslcertpath" => \$smtp_ssl_cert_path,
356+
"smtpsslclientcert" => \$smtp_ssl_client_cert,
357+
"smtpsslclientkey" => \$smtp_ssl_client_key,
353358
"mailmap.file" => \$mailmap_file,
354359
"mailmap.blob" => \$mailmap_blob,
355360
);
@@ -531,6 +536,8 @@ sub config_regexp {
531536
"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
532537
"smtp-encryption=s" => \$smtp_encryption,
533538
"smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path,
539+
"smtp-ssl-client-cert=s" => \$smtp_ssl_client_cert,
540+
"smtp-ssl-client-key=s" => \$smtp_ssl_client_key,
534541
"smtp-debug:i" => \$debug_net_smtp,
535542
"smtp-domain:s" => \$smtp_domain,
536543
"smtp-auth=s" => \$smtp_auth,
@@ -1520,6 +1527,8 @@ sub handle_smtp_error {
15201527
}
15211528

15221529
sub ssl_verify_params {
1530+
my %ret = ();
1531+
15231532
eval {
15241533
require IO::Socket::SSL;
15251534
IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);
@@ -1531,20 +1540,36 @@ sub ssl_verify_params {
15311540

15321541
if (!defined $smtp_ssl_cert_path) {
15331542
# use the OpenSSL defaults
1534-
return (SSL_verify_mode => SSL_VERIFY_PEER());
1543+
$ret{SSL_verify_mode} = SSL_VERIFY_PEER();
1544+
}
1545+
else {
1546+
if ($smtp_ssl_cert_path eq "") {
1547+
$ret{SSL_verify_mode} = SSL_VERIFY_NONE();
1548+
} elsif (-d $smtp_ssl_cert_path) {
1549+
$ret{SSL_verify_mode} = SSL_VERIFY_PEER();
1550+
$ret{SSL_ca_path} = $smtp_ssl_cert_path;
1551+
} elsif (-f $smtp_ssl_cert_path) {
1552+
$ret{SSL_verify_mode} = SSL_VERIFY_PEER();
1553+
$ret{SSL_ca_file} = $smtp_ssl_cert_path;
1554+
} else {
1555+
die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
1556+
}
15351557
}
15361558

1537-
if ($smtp_ssl_cert_path eq "") {
1538-
return (SSL_verify_mode => SSL_VERIFY_NONE());
1539-
} elsif (-d $smtp_ssl_cert_path) {
1540-
return (SSL_verify_mode => SSL_VERIFY_PEER(),
1541-
SSL_ca_path => $smtp_ssl_cert_path);
1542-
} elsif (-f $smtp_ssl_cert_path) {
1543-
return (SSL_verify_mode => SSL_VERIFY_PEER(),
1544-
SSL_ca_file => $smtp_ssl_cert_path);
1545-
} else {
1546-
die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
1559+
if (defined $smtp_ssl_client_cert) {
1560+
$ret{SSL_cert_file} = $smtp_ssl_client_cert;
15471561
}
1562+
if (defined $smtp_ssl_client_key) {
1563+
if (!defined $smtp_ssl_client_cert) {
1564+
# Accept the client key only when a certificate is given.
1565+
# We die here because this case is a user error.
1566+
die sprintf(__("Only client key \"%s\" specified"),
1567+
$smtp_ssl_client_key);
1568+
}
1569+
$ret{SSL_key_file} = $smtp_ssl_client_key;
1570+
}
1571+
1572+
return %ret;
15481573
}
15491574

15501575
sub file_name_is_absolute {

0 commit comments

Comments
 (0)