{"thread":{"id":"65027","subject":"[PATCH v1 0/1] send-email: add client certificate options","startedAt":"2026-02-20T08:17:46Z","lastAt":"2026-03-04T14:39:16Z","messageCount":10,"participants":["David Timber","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":1},"messages":[{"id":"536467","messageId":"20260220081717.555185-1-dxdt@dev.snart.me","threadId":"65027","inReplyTo":null,"subject":"[PATCH v1 0/1] send-email: add client certificate options","fromName":"David Timber","fromEmail":"dxdt@dev.snart.me","sentAt":"2026-02-20T08:17:12Z","receivedAt":"2026-02-20T08:17:46Z","isPatch":true,"sender":{"key":"dxdt@dev.snart.me","avatar":"https://avatars.githubusercontent.com/u/10917528?v=4"},"body":"Additional doc touch up on configurations. No code change :)\n\nDavid Timber (1):\n  send-mail: add client certificate options\n\n Documentation/config/sendemail.adoc |  6 ++++\n Documentation/git-send-email.adoc   | 17 ++++++++++\n git-send-email.perl                 | 48 ++++++++++++++++++++++-------\n 3 files changed, 60 insertions(+), 11 deletions(-)\n\n-- \n2.53.0\n\n"},{"id":"536468","messageId":"20260220081717.555185-2-dxdt@dev.snart.me","threadId":"65027","inReplyTo":"20260220081717.555185-1-dxdt@dev.snart.me","subject":"[PATCH v1 1/1] send-mail: add client certificate options","fromName":"David Timber","fromEmail":"dxdt@dev.snart.me","sentAt":"2026-02-20T08:17:13Z","receivedAt":"2026-02-20T08:17:49Z","isPatch":true,"sender":{"key":"dxdt@dev.snart.me","avatar":"https://avatars.githubusercontent.com/u/10917528?v=4"},"body":"For SMTP servers that do \"mutual certificate verification\", the mail\nclient is required to present its own TLS certificate as well. This\npatch adds --smtp-ssl-client-cert and --smtp-ssl-client-key for such\nservers.\n\nSigned-off-by: David Timber <dxdt@dev.snart.me>\n---\n Documentation/config/sendemail.adoc |  6 ++++\n Documentation/git-send-email.adoc   | 17 ++++++++++\n git-send-email.perl                 | 48 ++++++++++++++++++++++-------\n 3 files changed, 60 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/config/sendemail.adoc b/Documentation/config/sendemail.adoc\nindex 90164c734d..3d9925c1e0 100644\n--- a/Documentation/config/sendemail.adoc\n+++ b/Documentation/config/sendemail.adoc\n@@ -12,6 +12,12 @@ sendemail.smtpSSLCertPath::\n \tPath to ca-certificates (either a directory or a single file).\n \tSet it to an empty string to disable certificate verification.\n \n+sendemail.smtpSSLClientCert::\n+\tPath to a client certificate file to present to the SMTP server.\n+\n+sendemail.smtpSSLClientKey::\n+\tPath to the client private key file.\n+\n sendemail.<identity>.*::\n \tIdentity-specific versions of the `sendemail.*` parameters\n \tfound below, taking precedence over those when this\ndiff --git a/Documentation/git-send-email.adoc b/Documentation/git-send-email.adoc\nindex ebe8853e9f..51177508c1 100644\n--- a/Documentation/git-send-email.adoc\n+++ b/Documentation/git-send-email.adoc\n@@ -290,6 +290,23 @@ must be used for each option.\n \tvariable, if set, or the backing SSL library's compiled-in default\n \totherwise (which should be the best choice on most platforms).\n \n+--smtp-ssl-client-cert <path>::\n+\tPath to a client certificate file to present to the SMTP server. This option\n+\tcan be used when the server verifies the certificate from the client. The\n+\tformat could be in either PKCS12 or PEM. In the latter case, the private key\n+\tcan be specified using `--smtp-ssl-client-key` option. More more\n+\tdetail, see\n+\thttps://metacpan.org/pod/IO::Socket::SSL#SSL_cert_file-|-SSL_cert-|-SSL_key_file-|-SSL_key\n+\tDefaults to the value of the `sendemail.smtpSSLClientCert` configuration\n+\tvariable, if set.\n+\n+--smtp-ssl-client-key <path>::\n+\tOptional path to the client private key file. If this is not given and a\n+\tPKCS12 certificate file is used, the private key from the PKCS12 certificate\n+\twill be used(see `--smtp-ssl-client-cert`). Defaults to the value of the\n+\t`sendemail.smtpSSLClientKey` configuration variable, if set.\n+\n+\n --smtp-user=<user>::\n \tUsername for SMTP-AUTH. Default is the value of `sendemail.smtpUser`;\n \tif a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),\ndiff --git a/git-send-email.perl b/git-send-email.perl\nindex cd4b316ddc..49601a91d8 100755\n--- a/git-send-email.perl\n+++ b/git-send-email.perl\n@@ -66,6 +66,9 @@ sub usage {\n     --smtp-ssl-cert-path    <str>  * Path to ca-certificates (either directory or file).\n                                      Pass an empty string to disable certificate\n                                      verification.\n+    --smtp-ssl-client-cert  <str>  * Path to client certificate file to present to SMTP server\n+    --smtp-ssl-client-key   <str>  * Path to the private key file for the client certificate\n+                                     (optional if a PKCS12 client certificate is used)\n     --smtp-domain           <str>  * The domain name sent to HELO/EHLO handshake\n     --smtp-auth             <str>  * Space-separated list of allowed AUTH mechanisms, or\n                                      \"none\" to disable authentication.\n@@ -279,6 +282,7 @@ sub do_edit {\n my ($to_cmd, $cc_cmd, $header_cmd);\n my ($smtp_server, $smtp_server_port, @smtp_server_options);\n my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);\n+my ($smtp_ssl_client_cert, $smtp_ssl_client_key);\n my ($batch_size, $relogin_delay);\n my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);\n my ($imap_sent_folder);\n@@ -350,6 +354,8 @@ sub do_edit {\n my %config_path_settings = (\n     \"aliasesfile\" => \\@alias_files,\n     \"smtpsslcertpath\" => \\$smtp_ssl_cert_path,\n+    \"smtpsslclientcert\" => \\$smtp_ssl_client_cert,\n+    \"smtpsslclientkey\" => \\$smtp_ssl_client_key,\n     \"mailmap.file\" => \\$mailmap_file,\n     \"mailmap.blob\" => \\$mailmap_blob,\n );\n@@ -531,6 +537,8 @@ sub config_regexp {\n \t\t    \"smtp-ssl\" => sub { $smtp_encryption = 'ssl' },\n \t\t    \"smtp-encryption=s\" => \\$smtp_encryption,\n \t\t    \"smtp-ssl-cert-path=s\" => \\$smtp_ssl_cert_path,\n+\t\t    \"smtp-ssl-client-cert=s\" => \\$smtp_ssl_client_cert,\n+\t\t    \"smtp-ssl-client-key=s\" => \\$smtp_ssl_client_key,\n \t\t    \"smtp-debug:i\" => \\$debug_net_smtp,\n \t\t    \"smtp-domain:s\" => \\$smtp_domain,\n \t\t    \"smtp-auth=s\" => \\$smtp_auth,\n@@ -1520,6 +1528,8 @@ sub handle_smtp_error {\n }\n \n sub ssl_verify_params {\n+\tmy %ret = ();\n+\n \teval {\n \t\trequire IO::Socket::SSL;\n \t\tIO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);\n@@ -1531,20 +1541,36 @@ sub ssl_verify_params {\n \n \tif (!defined $smtp_ssl_cert_path) {\n \t\t# use the OpenSSL defaults\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_PEER());\n+\t\t$ret{SSL_verify_mode} = SSL_VERIFY_PEER();\n+\t}\n+\telse {\n+\t\tif ($smtp_ssl_cert_path eq \"\") {\n+\t\t\t$ret{SSL_verify_mode} = SSL_VERIFY_NONE();\n+\t\t} elsif (-d $smtp_ssl_cert_path) {\n+\t\t\t$ret{SSL_verify_mode} = SSL_VERIFY_PEER();\n+\t\t\t$ret{SSL_ca_path} = $smtp_ssl_cert_path;\n+\t\t} elsif (-f $smtp_ssl_cert_path) {\n+\t\t\t$ret{SSL_verify_mode} = SSL_VERIFY_PEER();\n+\t\t\t$ret{SSL_ca_file} = $smtp_ssl_cert_path;\n+\t\t} else {\n+\t\t\tdie sprintf(__(\"CA path \\\"%s\\\" does not exist\"), $smtp_ssl_cert_path);\n+\t\t}\n \t}\n \n-\tif ($smtp_ssl_cert_path eq \"\") {\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_NONE());\n-\t} elsif (-d $smtp_ssl_cert_path) {\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_PEER(),\n-\t\t\tSSL_ca_path => $smtp_ssl_cert_path);\n-\t} elsif (-f $smtp_ssl_cert_path) {\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_PEER(),\n-\t\t\tSSL_ca_file => $smtp_ssl_cert_path);\n-\t} else {\n-\t\tdie sprintf(__(\"CA path \\\"%s\\\" does not exist\"), $smtp_ssl_cert_path);\n+\tif (defined $smtp_ssl_client_cert) {\n+\t\t# The cert could be in PKCS12 format, which can store both cert and key\n+\t\t$ret{SSL_cert_file} = $smtp_ssl_client_cert;\n+\t\t$ret{SSL_use_cert} = 1;\n \t}\n+\tif (defined $smtp_ssl_client_key) {\n+\t\tif (!defined $smtp_ssl_client_cert) {\n+\t\t\t# doesn't make sense to use a client key only\n+\t\t\tdie sprintf(__(\"Only client key \\\"%s\\\" specified\"), $smtp_ssl_client_key);\n+\t\t}\n+\t\t$ret{SSL_key_file} = $smtp_ssl_client_key;\n+\t}\n+\n+\treturn %ret;\n }\n \n sub file_name_is_absolute {\n-- \n2.53.0\n\n"},{"id":"536532","messageId":"xmqqpl5zz8tp.fsf@gitster.g","threadId":"65027","inReplyTo":"20260220081717.555185-1-dxdt@dev.snart.me","subject":"Re: [PATCH v1 0/1] send-email: add client certificate options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-20T16:19:30Z","receivedAt":"2026-02-20T16:19:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Timber <dxdt@dev.snart.me> writes:\n\n> Additional doc touch up on configurations. No code change :)\n\nJust to unconfuse me, this mention of \"No code change\" is relative to\nhttps://lore.kernel.org/git/20260220075304.536514-1-dxdt@dev.snart.me/?\n\nThanks.\n"},{"id":"536534","messageId":"xmqqh5rbz83b.fsf@gitster.g","threadId":"65027","inReplyTo":"20260220081717.555185-2-dxdt@dev.snart.me","subject":"Re: [PATCH v1 1/1] send-mail: add client certificate options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-20T16:35:20Z","receivedAt":"2026-02-20T16:35:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Timber <dxdt@dev.snart.me> writes:\n\n> +sendemail.smtpSSLClientCert::\n> +\tPath to a client certificate file to present to the SMTP server.\n> +\n> +sendemail.smtpSSLClientKey::\n> +\tPath to the client private key file.\n\nDo we want to add \"that corresponds to the smtpSSLClientCert\" at the\nend, perhaps?\n\n> diff --git a/Documentation/git-send-email.adoc b/Documentation/git-send-email.adoc\n> index ebe8853e9f..51177508c1 100644\n> --- a/Documentation/git-send-email.adoc\n> +++ b/Documentation/git-send-email.adoc\n> @@ -290,6 +290,23 @@ must be used for each option.\n>  \tvariable, if set, or the backing SSL library's compiled-in default\n>  \totherwise (which should be the best choice on most platforms).\n>  \n> +--smtp-ssl-client-cert <path>::\n> +\tPath to a client certificate file to present to the SMTP server. This option\n> +\tcan be used when the server verifies the certificate from the client. The\n\nShouldn't there be a word \"require\" somewhere in the above to\nclarify why a user may want to use this option?  A server may\noptionally verify a certificate only when it is given one, but if it\nlets us do what we want without such verification, we do not have\nmuch incentive to give them a certificate.\n\n> +\tformat could be in either PKCS12 or PEM. In the latter case, the private key\n> +\tcan be specified using `--smtp-ssl-client-key` option. More more\n\nIs that \"can be specified\" or \"should be specified\"?\n\"More more\" -> \"For more\".\n\n> +\tdetail, see\n> +\thttps://metacpan.org/pod/IO::Socket::SSL#SSL_cert_file-|-SSL_cert-|-SSL_key_file-|-SSL_key\n> +\tDefaults to the value of the `sendemail.smtpSSLClientCert` configuration\n> +\tvariable, if set.\n> +\n> +--smtp-ssl-client-key <path>::\n> +\tOptional path to the client private key file. If this is not given and a\n> +\tPKCS12 certificate file is used, the private key from the PKCS12 certificate\n> +\twill be used(see `--smtp-ssl-client-cert`). Defaults to the value of the\n> +\t`sendemail.smtpSSLClientKey` configuration variable, if set.\n> +\n\n\"will be used(see\" -> \"will be used (see\".\n\nThis makes me wonder what the use case is for giving separate key\nfile with a certificate file with its own private key in it.  The\ndocumentation above clearly describes what happens (i.e., the\nseparate key file makes the key embedded in the certificate file\nignored), but I cannot quite think of a reason why anybody would\nwant to do so.  The key in the separate file is still something that\ncorresponds to the public part in the certificate, no?  The certificate\ncan say \"The subject of the certificate may use a private key that\ncorresponds to any of these three public keys\", and the certificate\nfile may only have one or two but not all of these three public\nkeys, or something?\n\n> +\tif (defined $smtp_ssl_client_cert) {\n> +\t\t# The cert could be in PKCS12 format, which can store both cert and key\n\nThe comment confused me initially.  Yes, the cert could be PKCS12\nwith key.  But the mention of the fact supports what design\ndecision?  Not requiring $smtp_ssl_client_key here (unlike the next\nif block that requires cert when key is used)?  If so, perhaps we\nwould want to spell that out?\n\n\t\t# We do not check and die when client_key is not\n                # given, as a separate key file is unneeded for\n                # PKCS12 certs.\n\nor something?\n\n> +\t\t$ret{SSL_cert_file} = $smtp_ssl_client_cert;\n> +\t\t$ret{SSL_use_cert} = 1;\n>  \t}\n> +\tif (defined $smtp_ssl_client_key) {\n> +\t\tif (!defined $smtp_ssl_client_cert) {\n> +\t\t\t# doesn't make sense to use a client key only\n> +\t\t\tdie sprintf(__(\"Only client key \\\"%s\\\" specified\"), $smtp_ssl_client_key);\n\nCan you wrap this overly long line?\n\n\t\t\tdie sprintf(__(\"Only client key \\\"%s\\\" specified\"),\n\t\t\t\t\t$smtp_ssl_client_key);\n\nThanks.\n"},{"id":"536580","messageId":"319bf98c-52df-4bf9-b157-e4bc2bf087d6@dev.snart.me","threadId":"65027","inReplyTo":"xmqqh5rbz83b.fsf@gitster.g","subject":"Re: [PATCH v1 1/1] send-mail: add client certificate options","fromName":"David Timber","fromEmail":"dxdt@dev.snart.me","sentAt":"2026-02-21T09:16:52Z","receivedAt":"2026-02-21T09:16:57Z","isPatch":true,"sender":{"key":"dxdt@dev.snart.me","avatar":"https://avatars.githubusercontent.com/u/10917528?v=4"},"body":"On 2/21/26 01:35, Junio C Hamano wrote:\n> This makes me wonder what the use case is for giving separate key\n> file with a certificate file with its own private key in it.  The\n> documentation above clearly describes what happens (i.e., the\n> separate key file makes the key embedded in the certificate file\n> ignored), but I cannot quite think of a reason why anybody would\n> want to do so. \nI know. The design of the Perl SSL interface is not top notch.\n\nhttps://metacpan.org/dist/IO-Socket-SSL/view/lib/IO/Socket/SSL.pod\n>\n> For each certificate a key is need, which can either be given as a\n> file with SSL_key_file or as an internal representation of an\n> EVP_PKEY* object with SSL_key (like you get from Net::SSLeay or\n> IO::Socket::SSL::Utils::PEM_xxx2key). If a key was already given\n> within the PKCS#12 file specified by SSL_cert_file it will ignore any\n> SSL_key or SSL_key_file. If no SSL_key or SSL_key_file was given it\n> will try to use the PEM file given with SSL_cert_file again, maybe it\n> contains the key too.\n>\nTo summarise:\n\n  * PKCS12: the key in the cert always takes the precedence\n  * PEM: if the key file is not given, it will \"try\" to read one from\n    the cert PEM file\n\nI don't know why anyone would want to put the cert and the key in the\nsame file, not even openssl impose that, but somehow, somewhere, there\nare people who do and Perl had to cater for the kind as it seems.\n\n> The key in the separate file is still something that\n> corresponds to the public part in the certificate, no?  The certificate\n> can say \"The subject of the certificate may use a private key that\n> corresponds to any of these three public keys\", and the certificate\n> file may only have one or two but not all of these three public\n> keys, or something?\nI think this is why they made the logic that way (at least for PKCS12)\nto prevent the exact madness you were describing. Still, I'd agree that\nthe asymmetry is horrendous.\n\nI'm just trying to expose what's possible with the Perl SSL interface to\nthe user as much as possible. Paranoid people(like me who would want to\nset up mtual vertification to deter bot attacks) would want both PEM and\nPKCS12 because major implementations including Thunderbird and K-9 only\naccept PKCS#12(imposed by the application and imposed by the operating\nsystem - Android, respectively) while the openssl x509 stack is more\nsuited for PEM(separate cert and key).\n\nIt's a niche, I know. But something tells me that you'd want to push\nthis feature forward. Thank you for the rest of the comments. Will come\nback with the revised patch that I hope may unconfuse you. Good to know\nthe project is in good hands.\n"},{"id":"537205","messageId":"xmqqo6lb4fuy.fsf@gitster.g","threadId":"65027","inReplyTo":"319bf98c-52df-4bf9-b157-e4bc2bf087d6@dev.snart.me","subject":"Re: [PATCH v1 1/1] send-mail: add client certificate options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-26T16:41:25Z","receivedAt":"2026-02-26T16:41:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Timber <dxdt@dev.snart.me> writes:\n\n> I'm just trying to expose what's possible with the Perl SSL interface to\n> the user as much as possible. Paranoid people(like me who would want to\n> set up mtual vertification to deter bot attacks) would want both PEM and\n> PKCS12 because major implementations including Thunderbird and K-9 only\n> accept PKCS#12(imposed by the application and imposed by the operating\n> system - Android, respectively) while the openssl x509 stack is more\n> suited for PEM(separate cert and key).\n>\n> It's a niche, I know. But something tells me that you'd want to push\n> this feature forward. Thank you for the rest of the comments. Will come\n> back with the revised patch that I hope may unconfuse you. Good to know\n> the project is in good hands.\n\nOK, please incorporate what you explained here in the commit log\nmessage and/or in-code comment, in order to help future readers of\n\"git log -p\".  I'd prefer to see it done before the topic goes out\nof my short-term memory ;-)\n\nThanks.\n"},{"id":"537485","messageId":"20260302032048.260209-1-dxdt@dev.snart.me","threadId":"65027","inReplyTo":"xmqqo6lb4fuy.fsf@gitster.g","subject":"[PATCH v2 0/1] send-email: add client certificate options","fromName":"David Timber","fromEmail":"dxdt@dev.snart.me","sentAt":"2026-03-02T03:16:40Z","receivedAt":"2026-03-02T03:21:20Z","isPatch":true,"sender":{"key":"dxdt@dev.snart.me","avatar":"https://avatars.githubusercontent.com/u/10917528?v=4"},"body":"I'm sorry that I missed last week's submission deadline.\n\nOn 2/21/26 01:35, Junio C Hamano wrote:\n> Shouldn't there be a word \"require\" somewhere in the above to\n> clarify why a user may want to use this option?  A server may\n> optionally verify a certificate only when it is given one, but if it\n> lets us do what we want without such verification, we do not have\n> much incentive to give them a certificate.\n\nRFC 8446 section 4.3.2:\n> The client MUST send a Certificate message if and only if the server\n> has requested client authentication via a CertificateRequest message\n> (Section 4.3.2).\n\nIn other words, the client won't send its cert to the server unless\nrequested by the server. So, the client presenting its cert to the\nserver in the client hello from the get-go is in violation of this\nrequirement. I reflected that in the reroll.\n\nAlso, removed the `$ret{SSL_use_cert} = 1;` line in the code to be in\nline with the requirement. That line was confusing and unnecessary in\nthe first place. Whether to use a client cert or not should be up to\nthe underlying implementation to decide.\n\nRemoved the whole PKCS#12 vs PEM debacle in the change as I reckon it's\na behaviour that could change overnight without a warning. Feels kind\nof defensive, but a reasonable change all things considered. Users\naffected by such library behaviour change can always refer to the\nmanual.\n\nDavid Timber (1):\n  send-email: add client certificate options\n\n Documentation/config/sendemail.adoc | 16 ++++++++++\n Documentation/git-send-email.adoc   | 19 ++++++++++++\n git-send-email.perl                 | 47 ++++++++++++++++++++++-------\n 3 files changed, 71 insertions(+), 11 deletions(-)\n\n-- \n2.53.0.1.ga224b40d3f.dirty\n\n"},{"id":"537486","messageId":"20260302032048.260209-2-dxdt@dev.snart.me","threadId":"65027","inReplyTo":"20260302032048.260209-1-dxdt@dev.snart.me","subject":"[PATCH v2 1/1] send-email: add client certificate options","fromName":"David Timber","fromEmail":"dxdt@dev.snart.me","sentAt":"2026-03-02T03:16:41Z","receivedAt":"2026-03-02T03:21:24Z","isPatch":true,"sender":{"key":"dxdt@dev.snart.me","avatar":"https://avatars.githubusercontent.com/u/10917528?v=4"},"body":"For SMTP servers that do \"mutual certificate verification\", the mail\nclient is required to present its own TLS certificate as well. This\npatch adds --smtp-ssl-client-cert and --smtp-ssl-client-key for such\nservers.\n\nThe problem of which private key for the certificate is chosen arises\nwhen there are private keys in both the certificate and private key\nfile. According to the documentation of IO::Socket::SSL(link supplied),\nthe behaviour(the private key chosen) depends on the format of the\ncertificate. In a nutshell,\n\n\t- PKCS12: the key in the cert always takes the precedence\n\t- PEM: if the key file is not given, it will \"try\" to read one\n\t  from the cert PEM file\n\nMany users may find this discrepancy unintuitive.\n\nIn terms of client certificate, git-send-email is implemented in a way\nthat what's possible with perl's SSL library is exposed to the user as\nmuch as possible. In this instance, the user may choose to use a PEM\nfile that contains both certificate and private key should be\nat their discretion despite the implications.\n\nLink: https://metacpan.org/pod/IO::Socket::SSL#SSL_cert_file-%7C-SSL_cert-%7C-SSL_key_file-%7C-SSL_key\nLink: https://lore.kernel.org/all/319bf98c-52df-4bf9-b157-e4bc2bf087d6@dev.snart.me/\n\nSigned-off-by: David Timber <dxdt@dev.snart.me>\n---\n Documentation/config/sendemail.adoc | 16 ++++++++++\n Documentation/git-send-email.adoc   | 19 ++++++++++++\n git-send-email.perl                 | 47 ++++++++++++++++++++++-------\n 3 files changed, 71 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/config/sendemail.adoc b/Documentation/config/sendemail.adoc\nindex 90164c734d..6560ecc5ab 100644\n--- a/Documentation/config/sendemail.adoc\n+++ b/Documentation/config/sendemail.adoc\n@@ -12,6 +12,22 @@ sendemail.smtpSSLCertPath::\n \tPath to ca-certificates (either a directory or a single file).\n \tSet it to an empty string to disable certificate verification.\n \n+sendemail.smtpSSLClientCert::\n+\tPath to the client certificate file to present if requested by the\n+\tserver. This is required when the server is set up to verify client\n+\tcertificates. If the corresponding private key is not included in the\n+\tfile, it must be supplied using `sendemail.smtpSSLClientKey` or the\n+\t`--smtp-ssl-client-key` option.\n+\n+sendemail.smtpSSLClientKey::\n+\tPath to the client private key file that corresponds to the client\n+\tcertificate. To avoid misconfiguration, this configuration must be used\n+\tin conjunction with `sendemail.smtpSSLClientKey` or the\n+\t`--smtp-ssl-client-cert` option. If the client key is included in the\n+\tclient certificate, the choice of private key depends on the format of\n+\tthe certificate. Visit https://metacpan.org/pod/IO::Socket::SSL for more\n+\tdetails.\n+\n sendemail.<identity>.*::\n \tIdentity-specific versions of the `sendemail.*` parameters\n \tfound below, taking precedence over those when this\ndiff --git a/Documentation/git-send-email.adoc b/Documentation/git-send-email.adoc\nindex ebe8853e9f..ed9a0d3053 100644\n--- a/Documentation/git-send-email.adoc\n+++ b/Documentation/git-send-email.adoc\n@@ -290,6 +290,25 @@ must be used for each option.\n \tvariable, if set, or the backing SSL library's compiled-in default\n \totherwise (which should be the best choice on most platforms).\n \n+--smtp-ssl-client-cert <path>::\n+\tPath to the client certificate file to present if requested by the\n+\tserver. This option is required when the server is set up to verify\n+\tclient certificates. If the corresponding private key is not included in\n+\tthe file, it must be supplied using the `sendemail.smtpSSLClientKey`\n+\tconfiguration variable or the `--smtp-ssl-client-key` option. Defaults\n+\tto the value of the `sendemail.smtpSSLClientCert` configuration\n+\tvariable, if set.\n+\n+--smtp-ssl-client-key <path>::\n+\tPath to the client private key file that corresponds to the client\n+\tcertificate. To avoid misconfiguration, this option must be used in\n+\tconjunction with the `sendemail.smtpSSLClientKey` configuration variable\n+\tor the `--smtp-ssl-client-cert` option. If the client key is included in\n+\tthe client certificate, the choice of private key depends on the format\n+\tof the certificate. Visit https://metacpan.org/pod/IO::Socket::SSL for\n+\tmore details. Defaults to the value of the `sendemail.smtpSSLClientKey`\n+\tconfiguration variable, if set.\n+\n --smtp-user=<user>::\n \tUsername for SMTP-AUTH. Default is the value of `sendemail.smtpUser`;\n \tif a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),\ndiff --git a/git-send-email.perl b/git-send-email.perl\nindex cd4b316ddc..324fa0056c 100755\n--- a/git-send-email.perl\n+++ b/git-send-email.perl\n@@ -66,6 +66,8 @@ sub usage {\n     --smtp-ssl-cert-path    <str>  * Path to ca-certificates (either directory or file).\n                                      Pass an empty string to disable certificate\n                                      verification.\n+    --smtp-ssl-client-cert  <str>  * Path to the client certificate file\n+    --smtp-ssl-client-key   <str>  * Path to the private key file for the client certificate\n     --smtp-domain           <str>  * The domain name sent to HELO/EHLO handshake\n     --smtp-auth             <str>  * Space-separated list of allowed AUTH mechanisms, or\n                                      \"none\" to disable authentication.\n@@ -279,6 +281,7 @@ sub do_edit {\n my ($to_cmd, $cc_cmd, $header_cmd);\n my ($smtp_server, $smtp_server_port, @smtp_server_options);\n my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);\n+my ($smtp_ssl_client_cert, $smtp_ssl_client_key);\n my ($batch_size, $relogin_delay);\n my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);\n my ($imap_sent_folder);\n@@ -350,6 +353,8 @@ sub do_edit {\n my %config_path_settings = (\n     \"aliasesfile\" => \\@alias_files,\n     \"smtpsslcertpath\" => \\$smtp_ssl_cert_path,\n+    \"smtpsslclientcert\" => \\$smtp_ssl_client_cert,\n+    \"smtpsslclientkey\" => \\$smtp_ssl_client_key,\n     \"mailmap.file\" => \\$mailmap_file,\n     \"mailmap.blob\" => \\$mailmap_blob,\n );\n@@ -531,6 +536,8 @@ sub config_regexp {\n \t\t    \"smtp-ssl\" => sub { $smtp_encryption = 'ssl' },\n \t\t    \"smtp-encryption=s\" => \\$smtp_encryption,\n \t\t    \"smtp-ssl-cert-path=s\" => \\$smtp_ssl_cert_path,\n+\t\t    \"smtp-ssl-client-cert=s\" => \\$smtp_ssl_client_cert,\n+\t\t    \"smtp-ssl-client-key=s\" => \\$smtp_ssl_client_key,\n \t\t    \"smtp-debug:i\" => \\$debug_net_smtp,\n \t\t    \"smtp-domain:s\" => \\$smtp_domain,\n \t\t    \"smtp-auth=s\" => \\$smtp_auth,\n@@ -1520,6 +1527,8 @@ sub handle_smtp_error {\n }\n \n sub ssl_verify_params {\n+\tmy %ret = ();\n+\n \teval {\n \t\trequire IO::Socket::SSL;\n \t\tIO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);\n@@ -1531,20 +1540,36 @@ sub ssl_verify_params {\n \n \tif (!defined $smtp_ssl_cert_path) {\n \t\t# use the OpenSSL defaults\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_PEER());\n+\t\t$ret{SSL_verify_mode} = SSL_VERIFY_PEER();\n+\t}\n+\telse {\n+\t\tif ($smtp_ssl_cert_path eq \"\") {\n+\t\t\t$ret{SSL_verify_mode} = SSL_VERIFY_NONE();\n+\t\t} elsif (-d $smtp_ssl_cert_path) {\n+\t\t\t$ret{SSL_verify_mode} = SSL_VERIFY_PEER();\n+\t\t\t$ret{SSL_ca_path} = $smtp_ssl_cert_path;\n+\t\t} elsif (-f $smtp_ssl_cert_path) {\n+\t\t\t$ret{SSL_verify_mode} = SSL_VERIFY_PEER();\n+\t\t\t$ret{SSL_ca_file} = $smtp_ssl_cert_path;\n+\t\t} else {\n+\t\t\tdie sprintf(__(\"CA path \\\"%s\\\" does not exist\"), $smtp_ssl_cert_path);\n+\t\t}\n \t}\n \n-\tif ($smtp_ssl_cert_path eq \"\") {\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_NONE());\n-\t} elsif (-d $smtp_ssl_cert_path) {\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_PEER(),\n-\t\t\tSSL_ca_path => $smtp_ssl_cert_path);\n-\t} elsif (-f $smtp_ssl_cert_path) {\n-\t\treturn (SSL_verify_mode => SSL_VERIFY_PEER(),\n-\t\t\tSSL_ca_file => $smtp_ssl_cert_path);\n-\t} else {\n-\t\tdie sprintf(__(\"CA path \\\"%s\\\" does not exist\"), $smtp_ssl_cert_path);\n+\tif (defined $smtp_ssl_client_cert) {\n+\t\t$ret{SSL_cert_file} = $smtp_ssl_client_cert;\n \t}\n+\tif (defined $smtp_ssl_client_key) {\n+\t\tif (!defined $smtp_ssl_client_cert) {\n+\t\t\t# Accept the client key only when a certificate is given.\n+\t\t\t# We die here because this case is a user error.\n+\t\t\tdie sprintf(__(\"Only client key \\\"%s\\\" specified\"),\n+\t\t\t\t    $smtp_ssl_client_key);\n+\t\t}\n+\t\t$ret{SSL_key_file} = $smtp_ssl_client_key;\n+\t}\n+\n+\treturn %ret;\n }\n \n sub file_name_is_absolute {\n-- \n2.53.0.1.ga224b40d3f.dirty\n\n"},{"id":"537549","messageId":"xmqq7bru41xz.fsf@gitster.g","threadId":"65027","inReplyTo":"20260302032048.260209-2-dxdt@dev.snart.me","subject":"Re: [PATCH v2 1/1] send-email: add client certificate options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-02T16:43:20Z","receivedAt":"2026-03-02T16:43:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Timber <dxdt@dev.snart.me> writes:\n\n> For SMTP servers that do \"mutual certificate verification\", the mail\n> client is required to present its own TLS certificate as well. This\n> patch adds --smtp-ssl-client-cert and --smtp-ssl-client-key for such\n> servers.\n>\n> The problem of which private key for the certificate is chosen arises\n> when there are private keys in both the certificate and private key\n> file. According to the documentation of IO::Socket::SSL(link supplied),\n> the behaviour(the private key chosen) depends on the format of the\n> certificate. In a nutshell,\n>\n> \t- PKCS12: the key in the cert always takes the precedence\n> \t- PEM: if the key file is not given, it will \"try\" to read one\n> \t  from the cert PEM file\n>\n> Many users may find this discrepancy unintuitive.\n>\n> In terms of client certificate, git-send-email is implemented in a way\n> that what's possible with perl's SSL library is exposed to the user as\n> much as possible. In this instance, the user may choose to use a PEM\n> file that contains both certificate and private key should be\n> at their discretion despite the implications.\n>\n> Link: https://metacpan.org/pod/IO::Socket::SSL#SSL_cert_file-%7C-SSL_cert-%7C-SSL_key_file-%7C-SSL_key\n> Link: https://lore.kernel.org/all/319bf98c-52df-4bf9-b157-e4bc2bf087d6@dev.snart.me/\n>\n> Signed-off-by: David Timber <dxdt@dev.snart.me>\n> ---\n>  Documentation/config/sendemail.adoc | 16 ++++++++++\n>  Documentation/git-send-email.adoc   | 19 ++++++++++++\n>  git-send-email.perl                 | 47 ++++++++++++++++++++++-------\n>  3 files changed, 71 insertions(+), 11 deletions(-)\n\nIt's a lot of text but quite informative.  Will replace.\n\nShall we declare victory and mark the topic for 'next' now?\n\nThanks.\n"},{"id":"537782","messageId":"012adf08-d33c-43ca-91ac-802c3c61ab6c@dev.snart.me","threadId":"65027","inReplyTo":"xmqq7bru41xz.fsf@gitster.g","subject":"Re: [PATCH v2 1/1] send-email: add client certificate options","fromName":"David Timber","fromEmail":"dxdt@dev.snart.me","sentAt":"2026-03-04T14:39:04Z","receivedAt":"2026-03-04T14:39:16Z","isPatch":true,"sender":{"key":"dxdt@dev.snart.me","avatar":"https://avatars.githubusercontent.com/u/10917528?v=4"},"body":"On 3/3/26 01:43, Junio C Hamano wrote:\n> Shall we declare victory and mark the topic for 'next' now?\nBy all means! Keep up the good work!\n"}]}