public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] imap-send: modernize the OpenSSL API
@ 2026-03-11 12:11 Beat Bolli
  2026-03-11 12:11 ` [PATCH 1/4] imap-send: use the OpenSSL API to access the subject alternative names Beat Bolli
                   ` (7 more replies)
  0 siblings, 8 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 12:11 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

OpenSSL recently released version 4.0.0-alpha1 [1]. Compiling with this
version revealed some erroneous and deprecated code.

This series aims to update this code to use the documented OpenSSL APIs.
All of the newly used APIs have existed since OpenSSL 1.1.0, the latest
version of which was released in September 2019 [2]. IMHO there is no
need to support even older OpenSSL versions.

- The first two commits are needed to make imap-send.c compile against
  OpenSSL 4.0 (and older!).

- The remaining two are follow-up cleanups that are not strictly
  necessary.

Cc-ing Oswald as the original author of the affected code.

[1] https://github.com/openssl/openssl/tree/openssl-4.0.0-alpha1
[2] https://openssl-library.org/source/old/1.1.0/index.html

Beat Bolli (4):
  imap-send: use the OpenSSL API to access the subject alternative names
  imap-send: use the OpenSSL API to access the subject common name
  imap-send: remove two string length checks
  imap-send: refactor function host_matches()

 imap-send.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

-- 
2.51.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/4] imap-send: use the OpenSSL API to access the subject alternative names
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
@ 2026-03-11 12:11 ` Beat Bolli
  2026-03-11 12:11 ` [PATCH 2/4] imap-send: use the OpenSSL API to access the subject common name Beat Bolli
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 12:11 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

The OpenSSL 4.0 branch has made the ASN1_STRING structure opaque,
forbidding access to its internal fields. Use the official accessor
functions instead. They have existed since OpenSSL 1.1.0.

Signed-off-by: Beat Bolli <dev+git@drbeat.li>
---
 imap-send.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 26dda7f328..1c934c2487 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -244,10 +244,14 @@ static int verify_hostname(X509 *cert, const char *hostname)
 	if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {
 		int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);
 		for (i = 0; !found && i < num_subj_alt_names; i++) {
+			int ntype;
 			GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
-			if (subj_alt_name->type == GEN_DNS &&
-			    strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&
-			    host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))
+			ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);
+
+			if (ntype == GEN_DNS &&
+			    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==
+				    ASN1_STRING_length(subj_alt_str) &&
+			    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))
 				found = 1;
 		}
 		sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 2/4] imap-send: use the OpenSSL API to access the subject common name
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
  2026-03-11 12:11 ` [PATCH 1/4] imap-send: use the OpenSSL API to access the subject alternative names Beat Bolli
@ 2026-03-11 12:11 ` Beat Bolli
  2026-03-11 12:11 ` [PATCH 3/4] imap-send: remove two string length checks Beat Bolli
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 12:11 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

The OpenSSL 4.0 branch has deprecated the X509_NAME_get_text_by_NID
function. Use the recommended replacement APIs instead. They have
existed since OpenSSL 1.1.0.

Pre-4.0 versions of X509_get_subject_name() return a non-const pointer
and more importantly only accept a non-const pointer in
X509_NAME_get_index_by_NID(), so we need a version check to handle both
cases.

Signed-off-by: Beat Bolli <dev+git@drbeat.li>
---
 imap-send.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 1c934c2487..2a904314dd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -233,9 +233,13 @@ static int host_matches(const char *host, const char *pattern)
 
 static int verify_hostname(X509 *cert, const char *hostname)
 {
-	int len;
+#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)
+	const X509_NAME *subj;
+#else
 	X509_NAME *subj;
-	char cname[1000];
+#endif
+	const X509_NAME_ENTRY *cname_entry;
+	const ASN1_STRING *cname;
 	int i, found;
 	STACK_OF(GENERAL_NAME) *subj_alt_names;
 
@@ -262,12 +266,15 @@ static int verify_hostname(X509 *cert, const char *hostname)
 	/* try the common name */
 	if (!(subj = X509_get_subject_name(cert)))
 		return error("cannot get certificate subject");
-	if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
+	if ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1)) < 0 ||
+	    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||
+	    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)
 		return error("cannot get certificate common name");
-	if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
+	if (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&
+	    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
 		return 0;
 	return error("certificate owner '%s' does not match hostname '%s'",
-		     cname, hostname);
+		     ASN1_STRING_get0_data(cname), hostname);
 }
 
 static int ssl_socket_connect(struct imap_socket *sock,
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 3/4] imap-send: remove two string length checks
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
  2026-03-11 12:11 ` [PATCH 1/4] imap-send: use the OpenSSL API to access the subject alternative names Beat Bolli
  2026-03-11 12:11 ` [PATCH 2/4] imap-send: use the OpenSSL API to access the subject common name Beat Bolli
@ 2026-03-11 12:11 ` Beat Bolli
  2026-03-11 13:41   ` Oswald Buddenhagen
  2026-03-11 18:55   ` Junio C Hamano
  2026-03-11 12:11 ` [PATCH 4/4] imap-send: refactor function host_matches() Beat Bolli
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 12:11 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

At this point, these two checks verify that the ASN1_STRINGs are
internally consistent. This may have been ok when the fields were
accessed directly, but now that the API is used, is unnecessary.

Remove the two checks.

Signed-off-by: Beat Bolli <dev+git@drbeat.li>
---
 imap-send.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 2a904314dd..2bb0003f08 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -253,8 +253,6 @@ static int verify_hostname(X509 *cert, const char *hostname)
 			ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);
 
 			if (ntype == GEN_DNS &&
-			    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==
-				    ASN1_STRING_length(subj_alt_str) &&
 			    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))
 				found = 1;
 		}
@@ -270,8 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
 	    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||
 	    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)
 		return error("cannot get certificate common name");
-	if (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&
-	    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
+	if (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
 		return 0;
 	return error("certificate owner '%s' does not match hostname '%s'",
 		     ASN1_STRING_get0_data(cname), hostname);
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 4/4] imap-send: refactor function host_matches()
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
                   ` (2 preceding siblings ...)
  2026-03-11 12:11 ` [PATCH 3/4] imap-send: remove two string length checks Beat Bolli
@ 2026-03-11 12:11 ` Beat Bolli
  2026-03-11 22:10 ` [PATCH v2 0/3] imap-send: modernize the OpenSSL API Beat Bolli
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 12:11 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

Move the ASN1_STRING access and the associated cast into host_matches()
to simplify both callers.

Signed-off-by: Beat Bolli <dev+git@drbeat.li>
---
 imap-send.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 2bb0003f08..789055d7fd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -219,8 +219,9 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
 
 #else
 
-static int host_matches(const char *host, const char *pattern)
+static int host_matches(const char *host, const ASN1_STRING *asn1_str)
 {
+	const char *pattern = (const char *)ASN1_STRING_get0_data(asn1_str);
 	if (pattern[0] == '*' && pattern[1] == '.') {
 		pattern += 2;
 		if (!(host = strchr(host, '.')))
@@ -252,8 +253,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
 			GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
 			ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);
 
-			if (ntype == GEN_DNS &&
-			    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))
+			if (ntype == GEN_DNS && host_matches(hostname, subj_alt_str))
 				found = 1;
 		}
 		sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
@@ -268,7 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
 	    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||
 	    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)
 		return error("cannot get certificate common name");
-	if (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
+	if (host_matches(hostname, cname))
 		return 0;
 	return error("certificate owner '%s' does not match hostname '%s'",
 		     ASN1_STRING_get0_data(cname), hostname);
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH 3/4] imap-send: remove two string length checks
  2026-03-11 12:11 ` [PATCH 3/4] imap-send: remove two string length checks Beat Bolli
@ 2026-03-11 13:41   ` Oswald Buddenhagen
  2026-03-11 21:49     ` Beat Bolli
  2026-03-11 18:55   ` Junio C Hamano
  1 sibling, 1 reply; 14+ messages in thread
From: Oswald Buddenhagen @ 2026-03-11 13:41 UTC (permalink / raw)
  To: Beat Bolli; +Cc: git

On Wed, Mar 11, 2026 at 01:11:06PM +0100, Beat Bolli wrote:
>At this point, these two checks verify that the ASN1_STRINGs are
>internally consistent. This may have been ok when the fields were
>accessed directly, but now that the API is used, is unnecessary.
>
that argumentation makes no sense.
the purpose of this check is to ensure that there are no embedded nulls, 
which the matcher would be unable to deal with, which may be a security 
hole.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 3/4] imap-send: remove two string length checks
  2026-03-11 12:11 ` [PATCH 3/4] imap-send: remove two string length checks Beat Bolli
  2026-03-11 13:41   ` Oswald Buddenhagen
@ 2026-03-11 18:55   ` Junio C Hamano
  2026-03-11 22:00     ` Beat Bolli
  1 sibling, 1 reply; 14+ messages in thread
From: Junio C Hamano @ 2026-03-11 18:55 UTC (permalink / raw)
  To: Beat Bolli; +Cc: git, Oswald Buddenhagen

Beat Bolli <dev+git@drbeat.li> writes:

> At this point, these two checks verify that the ASN1_STRINGs are
> internally consistent. This may have been ok when the fields were
> accessed directly, but now that the API is used, is unnecessary.
>
> Remove the two checks.

Oswald already gave a similar comment, but

 * I am not sure what you meant by "ok" in "may have been ok".  Do
   you mean "with raw access to the fields, it may have been made
   send to ensure validity of ASN1_STRING"?

 * I am also not sure what you meant by "now that the API is used".
   Who in the code uses which API function so that we do not have to
   do our sanity checking?

   The call to host_matches() that these extra checks protect are
   still passing raw "const char *" in this step, and the change to
   pass ASN1_STRING does not happen until [4/4], so you did not mean
   host_matches().  I am not sure what it is.
   
Thanks.

>
> Signed-off-by: Beat Bolli <dev+git@drbeat.li>
> ---
>  imap-send.c | 5 +----
>  1 file changed, 1 insertion(+), 4 deletions(-)
>
> diff --git a/imap-send.c b/imap-send.c
> index 2a904314dd..2bb0003f08 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -253,8 +253,6 @@ static int verify_hostname(X509 *cert, const char *hostname)
>  			ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);
>  
>  			if (ntype == GEN_DNS &&
> -			    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==
> -				    ASN1_STRING_length(subj_alt_str) &&
>  			    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))
>  				found = 1;
>  		}
> @@ -270,8 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
>  	    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||
>  	    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)
>  		return error("cannot get certificate common name");
> -	if (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&
> -	    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
> +	if (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
>  		return 0;
>  	return error("certificate owner '%s' does not match hostname '%s'",
>  		     ASN1_STRING_get0_data(cname), hostname);

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 3/4] imap-send: remove two string length checks
  2026-03-11 13:41   ` Oswald Buddenhagen
@ 2026-03-11 21:49     ` Beat Bolli
  0 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 21:49 UTC (permalink / raw)
  To: Oswald Buddenhagen; +Cc: git

Hi Oswald

On 11.03.2026 14:41, Oswald Buddenhagen wrote:
> On Wed, Mar 11, 2026 at 01:11:06PM +0100, Beat Bolli wrote:
>> At this point, these two checks verify that the ASN1_STRINGs are
>> internally consistent. This may have been ok when the fields were
>> accessed directly, but now that the API is used, is unnecessary.
>>
> that argumentation makes no sense.
> the purpose of this check is to ensure that there are no embedded nulls, 
> which the matcher would be unable to deal with, which may be a security 
> hole.

Thanks for the clarification; this was the piece that I was missing.

I'll send a v2 shortly that removes this change.

Cheers, Beat

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 3/4] imap-send: remove two string length checks
  2026-03-11 18:55   ` Junio C Hamano
@ 2026-03-11 22:00     ` Beat Bolli
  0 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 22:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Oswald Buddenhagen

Hi Junio

On 11.03.2026 19:55, Junio C Hamano wrote:
> Beat Bolli <dev+git@drbeat.li> writes:
> 
>> At this point, these two checks verify that the ASN1_STRINGs are
>> internally consistent. This may have been ok when the fields were
>> accessed directly, but now that the API is used, is unnecessary.
>>
>> Remove the two checks.
> 
> Oswald already gave a similar comment, but
> 
>   * I am not sure what you meant by "ok" in "may have been ok".  Do
>     you mean "with raw access to the fields, it may have been made
>     send to ensure validity of ASN1_STRING"?
> 
>   * I am also not sure what you meant by "now that the API is used".
>     Who in the code uses which API function so that we do not have to
>     do our sanity checking?
> 
>     The call to host_matches() that these extra checks protect are
>     still passing raw "const char *" in this step, and the change to
>     pass ASN1_STRING does not happen until [4/4], so you did not mean
>     host_matches().  I am not sure what it is.
>     
> Thanks.

I didn't realize that the strlen() comparison was meant to check for 
embedded NULs. I'll send v2 shortly that keeps this check.

Cheers, Beat


>> Signed-off-by: Beat Bolli <dev+git@drbeat.li>
>> ---
>>   imap-send.c | 5 +----
>>   1 file changed, 1 insertion(+), 4 deletions(-)
>>
>> diff --git a/imap-send.c b/imap-send.c
>> index 2a904314dd..2bb0003f08 100644
>> --- a/imap-send.c
>> +++ b/imap-send.c
>> @@ -253,8 +253,6 @@ static int verify_hostname(X509 *cert, const char *hostname)
>>   			ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);
>>   
>>   			if (ntype == GEN_DNS &&
>> -			    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==
>> -				    ASN1_STRING_length(subj_alt_str) &&
>>   			    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))
>>   				found = 1;
>>   		}
>> @@ -270,8 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
>>   	    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||
>>   	    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)
>>   		return error("cannot get certificate common name");
>> -	if (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&
>> -	    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
>> +	if (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
>>   		return 0;
>>   	return error("certificate owner '%s' does not match hostname '%s'",
>>   		     ASN1_STRING_get0_data(cname), hostname);


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 0/3] imap-send: modernize the OpenSSL API
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
                   ` (3 preceding siblings ...)
  2026-03-11 12:11 ` [PATCH 4/4] imap-send: refactor function host_matches() Beat Bolli
@ 2026-03-11 22:10 ` Beat Bolli
  2026-03-12  0:25   ` Junio C Hamano
  2026-03-11 22:10 ` [PATCH v2 1/3] imap-send: use the OpenSSL API to access the subject alternative names Beat Bolli
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 22:10 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

OpenSSL recently released version 4.0.0-alpha1 [1]. Compiling with this
version revealed some erroneous and deprecated code.

This series aims to update this code to use the documented OpenSSL APIs.
All of the newly used APIs have existed since OpenSSL 1.1.0, the latest
version of which was released in September 2019 [2]. IMHO there is no
need to support even older OpenSSL versions.

Cc-ing Oswald as the original author of the affected code.

[1] https://github.com/openssl/openssl/tree/openssl-4.0.0-alpha1
[2] https://openssl-library.org/source/old/1.1.0/index.html

Beat Bolli (3):
  imap-send: use the OpenSSL API to access the subject alternative names
  imap-send: use the OpenSSL API to access the subject common name
  imap-send: move common code into function host_matches()

 imap-send.c | 31 ++++++++++++++++++++++---------
 1 file changed, 22 insertions(+), 9 deletions(-)

---
Changes vs v1:
- keep the check for embedded NUL characters

-- 
2.51.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 1/3] imap-send: use the OpenSSL API to access the subject alternative names
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
                   ` (4 preceding siblings ...)
  2026-03-11 22:10 ` [PATCH v2 0/3] imap-send: modernize the OpenSSL API Beat Bolli
@ 2026-03-11 22:10 ` Beat Bolli
  2026-03-11 22:10 ` [PATCH v2 2/3] imap-send: use the OpenSSL API to access the subject common name Beat Bolli
  2026-03-11 22:10 ` [PATCH v2 3/3] imap-send: move common code into function host_matches() Beat Bolli
  7 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 22:10 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

The OpenSSL 4.0 master branch has made the ASN1_STRING structure opaque,
forbidding access to its internal fields. Use the official accessor
functions instead. They have existed since OpenSSL v1.1.0.

Signed-off-by: Beat Bolli <dev+git@drbeat.li>
---
 imap-send.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 26dda7f328..1c934c2487 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -244,10 +244,14 @@ static int verify_hostname(X509 *cert, const char *hostname)
 	if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {
 		int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);
 		for (i = 0; !found && i < num_subj_alt_names; i++) {
+			int ntype;
 			GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
-			if (subj_alt_name->type == GEN_DNS &&
-			    strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&
-			    host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))
+			ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);
+
+			if (ntype == GEN_DNS &&
+			    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==
+				    ASN1_STRING_length(subj_alt_str) &&
+			    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))
 				found = 1;
 		}
 		sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 2/3] imap-send: use the OpenSSL API to access the subject common name
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
                   ` (5 preceding siblings ...)
  2026-03-11 22:10 ` [PATCH v2 1/3] imap-send: use the OpenSSL API to access the subject alternative names Beat Bolli
@ 2026-03-11 22:10 ` Beat Bolli
  2026-03-11 22:10 ` [PATCH v2 3/3] imap-send: move common code into function host_matches() Beat Bolli
  7 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 22:10 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

The OpenSSL 4.0 master branch has deprecated the
X509_NAME_get_text_by_NID function. Use the recommended replacement APIs
instead. They have existed since OpenSSL v1.1.0.

Take care to get the constness right for pre-4.0 versions.

Signed-off-by: Beat Bolli <dev+git@drbeat.li>
---
 imap-send.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 1c934c2487..2a904314dd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -233,9 +233,13 @@ static int host_matches(const char *host, const char *pattern)
 
 static int verify_hostname(X509 *cert, const char *hostname)
 {
-	int len;
+#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)
+	const X509_NAME *subj;
+#else
 	X509_NAME *subj;
-	char cname[1000];
+#endif
+	const X509_NAME_ENTRY *cname_entry;
+	const ASN1_STRING *cname;
 	int i, found;
 	STACK_OF(GENERAL_NAME) *subj_alt_names;
 
@@ -262,12 +266,15 @@ static int verify_hostname(X509 *cert, const char *hostname)
 	/* try the common name */
 	if (!(subj = X509_get_subject_name(cert)))
 		return error("cannot get certificate subject");
-	if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
+	if ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1)) < 0 ||
+	    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||
+	    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)
 		return error("cannot get certificate common name");
-	if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
+	if (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&
+	    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
 		return 0;
 	return error("certificate owner '%s' does not match hostname '%s'",
-		     cname, hostname);
+		     ASN1_STRING_get0_data(cname), hostname);
 }
 
 static int ssl_socket_connect(struct imap_socket *sock,
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 3/3] imap-send: move common code into function host_matches()
  2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
                   ` (6 preceding siblings ...)
  2026-03-11 22:10 ` [PATCH v2 2/3] imap-send: use the OpenSSL API to access the subject common name Beat Bolli
@ 2026-03-11 22:10 ` Beat Bolli
  7 siblings, 0 replies; 14+ messages in thread
From: Beat Bolli @ 2026-03-11 22:10 UTC (permalink / raw)
  To: git; +Cc: Beat Bolli, Oswald Buddenhagen

Move the ASN1_STRING access, the associated cast and the check for
embedded NUL bytes into host_matches() to simplify both callers.

Reformulate the NUL check using memchr() and add a comment to make it
more obvious what it is about.

Signed-off-by: Beat Bolli <dev+git@drbeat.li>
---
 imap-send.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 2a904314dd..af02c6a689 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -219,8 +219,14 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
 
 #else
 
-static int host_matches(const char *host, const char *pattern)
+static int host_matches(const char *host, const ASN1_STRING *asn1_str)
 {
+	const char *pattern = (const char *)ASN1_STRING_get0_data(asn1_str);
+
+	/* embedded NUL characters may open a security hole */
+	if (memchr(pattern, '\0', ASN1_STRING_length(asn1_str)))
+	    return 0;
+
 	if (pattern[0] == '*' && pattern[1] == '.') {
 		pattern += 2;
 		if (!(host = strchr(host, '.')))
@@ -252,10 +258,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
 			GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
 			ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);
 
-			if (ntype == GEN_DNS &&
-			    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==
-				    ASN1_STRING_length(subj_alt_str) &&
-			    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))
+			if (ntype == GEN_DNS && host_matches(hostname, subj_alt_str))
 				found = 1;
 		}
 		sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
@@ -270,8 +273,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
 	    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||
 	    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)
 		return error("cannot get certificate common name");
-	if (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&
-	    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))
+	if (host_matches(hostname, cname))
 		return 0;
 	return error("certificate owner '%s' does not match hostname '%s'",
 		     ASN1_STRING_get0_data(cname), hostname);
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 0/3] imap-send: modernize the OpenSSL API
  2026-03-11 22:10 ` [PATCH v2 0/3] imap-send: modernize the OpenSSL API Beat Bolli
@ 2026-03-12  0:25   ` Junio C Hamano
  0 siblings, 0 replies; 14+ messages in thread
From: Junio C Hamano @ 2026-03-12  0:25 UTC (permalink / raw)
  To: Beat Bolli; +Cc: git, Oswald Buddenhagen

Beat Bolli <dev+git@drbeat.li> writes:
> Changes vs v1:
> - keep the check for embedded NUL characters

... which amounts to this difference, which is a lot more explicit
way to express what is going on.  I like it.

Thanks.

diff --git a/imap-send.c b/imap-send.c
index 789055d7fd..af02c6a689 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -222,6 +222,11 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
 static int host_matches(const char *host, const ASN1_STRING *asn1_str)
 {
 	const char *pattern = (const char *)ASN1_STRING_get0_data(asn1_str);
+
+	/* embedded NUL characters may open a security hole */
+	if (memchr(pattern, '\0', ASN1_STRING_length(asn1_str)))
+	    return 0;
+
 	if (pattern[0] == '*' && pattern[1] == '.') {
 		pattern += 2;
 		if (!(host = strchr(host, '.')))

^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-03-12  0:25 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-11 12:11 [PATCH 0/4] imap-send: modernize the OpenSSL API Beat Bolli
2026-03-11 12:11 ` [PATCH 1/4] imap-send: use the OpenSSL API to access the subject alternative names Beat Bolli
2026-03-11 12:11 ` [PATCH 2/4] imap-send: use the OpenSSL API to access the subject common name Beat Bolli
2026-03-11 12:11 ` [PATCH 3/4] imap-send: remove two string length checks Beat Bolli
2026-03-11 13:41   ` Oswald Buddenhagen
2026-03-11 21:49     ` Beat Bolli
2026-03-11 18:55   ` Junio C Hamano
2026-03-11 22:00     ` Beat Bolli
2026-03-11 12:11 ` [PATCH 4/4] imap-send: refactor function host_matches() Beat Bolli
2026-03-11 22:10 ` [PATCH v2 0/3] imap-send: modernize the OpenSSL API Beat Bolli
2026-03-12  0:25   ` Junio C Hamano
2026-03-11 22:10 ` [PATCH v2 1/3] imap-send: use the OpenSSL API to access the subject alternative names Beat Bolli
2026-03-11 22:10 ` [PATCH v2 2/3] imap-send: use the OpenSSL API to access the subject common name Beat Bolli
2026-03-11 22:10 ` [PATCH v2 3/3] imap-send: move common code into function host_matches() Beat Bolli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox