From: David Timber <dxdt@dev.snart.me>
To: git@vger.kernel.org
Cc: David Timber <dxdt@dev.snart.me>
Subject: [PATCH v1 1/1] send-mail: add client certificate options
Date: Fri, 20 Feb 2026 17:17:13 +0900 [thread overview]
Message-ID: <20260220081717.555185-2-dxdt@dev.snart.me> (raw)
In-Reply-To: <20260220081717.555185-1-dxdt@dev.snart.me>
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.
Signed-off-by: David Timber <dxdt@dev.snart.me>
---
Documentation/config/sendemail.adoc | 6 ++++
Documentation/git-send-email.adoc | 17 ++++++++++
git-send-email.perl | 48 ++++++++++++++++++++++-------
3 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/Documentation/config/sendemail.adoc b/Documentation/config/sendemail.adoc
index 90164c734d..3d9925c1e0 100644
--- a/Documentation/config/sendemail.adoc
+++ b/Documentation/config/sendemail.adoc
@@ -12,6 +12,12 @@ sendemail.smtpSSLCertPath::
Path to ca-certificates (either a directory or a single file).
Set it to an empty string to disable certificate verification.
+sendemail.smtpSSLClientCert::
+ Path to a client certificate file to present to the SMTP server.
+
+sendemail.smtpSSLClientKey::
+ Path to the client private key file.
+
sendemail.<identity>.*::
Identity-specific versions of the `sendemail.*` parameters
found below, taking precedence over those when this
diff --git a/Documentation/git-send-email.adoc b/Documentation/git-send-email.adoc
index ebe8853e9f..51177508c1 100644
--- a/Documentation/git-send-email.adoc
+++ b/Documentation/git-send-email.adoc
@@ -290,6 +290,23 @@ must be used for each option.
variable, if set, or the backing SSL library's compiled-in default
otherwise (which should be the best choice on most platforms).
+--smtp-ssl-client-cert <path>::
+ Path to a client certificate file to present to the SMTP server. This option
+ can be used when the server verifies the certificate from the client. The
+ format could be in either PKCS12 or PEM. In the latter case, the private key
+ can be specified using `--smtp-ssl-client-key` option. More more
+ detail, see
+ https://metacpan.org/pod/IO::Socket::SSL#SSL_cert_file-|-SSL_cert-|-SSL_key_file-|-SSL_key
+ Defaults to the value of the `sendemail.smtpSSLClientCert` configuration
+ variable, if set.
+
+--smtp-ssl-client-key <path>::
+ Optional path to the client private key file. If this is not given and a
+ PKCS12 certificate file is used, the private key from the PKCS12 certificate
+ will be used(see `--smtp-ssl-client-cert`). Defaults to the value of the
+ `sendemail.smtpSSLClientKey` configuration variable, if set.
+
+
--smtp-user=<user>::
Username for SMTP-AUTH. Default is the value of `sendemail.smtpUser`;
if a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),
diff --git a/git-send-email.perl b/git-send-email.perl
index cd4b316ddc..49601a91d8 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -66,6 +66,9 @@ sub usage {
--smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file).
Pass an empty string to disable certificate
verification.
+ --smtp-ssl-client-cert <str> * Path to client certificate file to present to SMTP server
+ --smtp-ssl-client-key <str> * Path to the private key file for the client certificate
+ (optional if a PKCS12 client certificate is used)
--smtp-domain <str> * The domain name sent to HELO/EHLO handshake
--smtp-auth <str> * Space-separated list of allowed AUTH mechanisms, or
"none" to disable authentication.
@@ -279,6 +282,7 @@ sub do_edit {
my ($to_cmd, $cc_cmd, $header_cmd);
my ($smtp_server, $smtp_server_port, @smtp_server_options);
my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
+my ($smtp_ssl_client_cert, $smtp_ssl_client_key);
my ($batch_size, $relogin_delay);
my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);
my ($imap_sent_folder);
@@ -350,6 +354,8 @@ sub do_edit {
my %config_path_settings = (
"aliasesfile" => \@alias_files,
"smtpsslcertpath" => \$smtp_ssl_cert_path,
+ "smtpsslclientcert" => \$smtp_ssl_client_cert,
+ "smtpsslclientkey" => \$smtp_ssl_client_key,
"mailmap.file" => \$mailmap_file,
"mailmap.blob" => \$mailmap_blob,
);
@@ -531,6 +537,8 @@ sub config_regexp {
"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
"smtp-encryption=s" => \$smtp_encryption,
"smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path,
+ "smtp-ssl-client-cert=s" => \$smtp_ssl_client_cert,
+ "smtp-ssl-client-key=s" => \$smtp_ssl_client_key,
"smtp-debug:i" => \$debug_net_smtp,
"smtp-domain:s" => \$smtp_domain,
"smtp-auth=s" => \$smtp_auth,
@@ -1520,6 +1528,8 @@ sub handle_smtp_error {
}
sub ssl_verify_params {
+ my %ret = ();
+
eval {
require IO::Socket::SSL;
IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);
@@ -1531,20 +1541,36 @@ sub ssl_verify_params {
if (!defined $smtp_ssl_cert_path) {
# use the OpenSSL defaults
- return (SSL_verify_mode => SSL_VERIFY_PEER());
+ $ret{SSL_verify_mode} = SSL_VERIFY_PEER();
+ }
+ else {
+ if ($smtp_ssl_cert_path eq "") {
+ $ret{SSL_verify_mode} = SSL_VERIFY_NONE();
+ } elsif (-d $smtp_ssl_cert_path) {
+ $ret{SSL_verify_mode} = SSL_VERIFY_PEER();
+ $ret{SSL_ca_path} = $smtp_ssl_cert_path;
+ } elsif (-f $smtp_ssl_cert_path) {
+ $ret{SSL_verify_mode} = SSL_VERIFY_PEER();
+ $ret{SSL_ca_file} = $smtp_ssl_cert_path;
+ } else {
+ die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
+ }
}
- if ($smtp_ssl_cert_path eq "") {
- return (SSL_verify_mode => SSL_VERIFY_NONE());
- } elsif (-d $smtp_ssl_cert_path) {
- return (SSL_verify_mode => SSL_VERIFY_PEER(),
- SSL_ca_path => $smtp_ssl_cert_path);
- } elsif (-f $smtp_ssl_cert_path) {
- return (SSL_verify_mode => SSL_VERIFY_PEER(),
- SSL_ca_file => $smtp_ssl_cert_path);
- } else {
- die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
+ if (defined $smtp_ssl_client_cert) {
+ # The cert could be in PKCS12 format, which can store both cert and key
+ $ret{SSL_cert_file} = $smtp_ssl_client_cert;
+ $ret{SSL_use_cert} = 1;
}
+ if (defined $smtp_ssl_client_key) {
+ if (!defined $smtp_ssl_client_cert) {
+ # doesn't make sense to use a client key only
+ die sprintf(__("Only client key \"%s\" specified"), $smtp_ssl_client_key);
+ }
+ $ret{SSL_key_file} = $smtp_ssl_client_key;
+ }
+
+ return %ret;
}
sub file_name_is_absolute {
--
2.53.0
next prev parent reply other threads:[~2026-02-20 8:17 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-20 8:17 [PATCH v1 0/1] send-email: add client certificate options David Timber
2026-02-20 8:17 ` David Timber [this message]
2026-02-20 16:35 ` [PATCH v1 1/1] send-mail: " Junio C Hamano
2026-02-21 9:16 ` David Timber
2026-02-26 16:41 ` Junio C Hamano
2026-03-02 3:16 ` [PATCH v2 0/1] send-email: " David Timber
2026-03-02 3:16 ` [PATCH v2 1/1] " David Timber
2026-03-02 16:43 ` Junio C Hamano
2026-03-04 14:39 ` David Timber
2026-02-20 16:19 ` [PATCH v1 0/1] " Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260220081717.555185-2-dxdt@dev.snart.me \
--to=dxdt@dev.snart.me \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox