{"thread":{"id":"65207","subject":"[PATCH 3/4] imap-send: remove two string length checks","startedAt":"2026-03-11T12:15:35Z","lastAt":"2026-03-12T00:25:42Z","messageCount":14,"participants":["Beat Bolli","Oswald Buddenhagen","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"538588","messageId":"20260311121107.1122387-4-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH 3/4] imap-send: remove two string length checks","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T12:11:06Z","receivedAt":"2026-03-11T12:15:35Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"At this point, these two checks verify that the ASN1_STRINGs are\ninternally consistent. This may have been ok when the fields were\naccessed directly, but now that the API is used, is unnecessary.\n\nRemove the two checks.\n\nSigned-off-by: Beat Bolli <dev+git@drbeat.li>\n---\n imap-send.c | 5 +----\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 2a904314dd..2bb0003f08 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -253,8 +253,6 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t\t\tASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);\n \n \t\t\tif (ntype == GEN_DNS &&\n-\t\t\t    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==\n-\t\t\t\t    ASN1_STRING_length(subj_alt_str) &&\n \t\t\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))\n \t\t\t\tfound = 1;\n \t\t}\n@@ -270,8 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||\n \t    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)\n \t\treturn error(\"cannot get certificate common name\");\n-\tif (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&\n-\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n+\tif (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n \t\treturn 0;\n \treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n \t\t     ASN1_STRING_get0_data(cname), hostname);\n-- \n2.51.0\n\n"},{"id":"538589","messageId":"20260311121107.1122387-5-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH 4/4] imap-send: refactor function host_matches()","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T12:11:07Z","receivedAt":"2026-03-11T12:18:33Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"Move the ASN1_STRING access and the associated cast into host_matches()\nto simplify both callers.\n\nSigned-off-by: Beat Bolli <dev+git@drbeat.li>\n---\n imap-send.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 2bb0003f08..789055d7fd 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -219,8 +219,9 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,\n \n #else\n \n-static int host_matches(const char *host, const char *pattern)\n+static int host_matches(const char *host, const ASN1_STRING *asn1_str)\n {\n+\tconst char *pattern = (const char *)ASN1_STRING_get0_data(asn1_str);\n \tif (pattern[0] == '*' && pattern[1] == '.') {\n \t\tpattern += 2;\n \t\tif (!(host = strchr(host, '.')))\n@@ -252,8 +253,7 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t\t\tGENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);\n \t\t\tASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);\n \n-\t\t\tif (ntype == GEN_DNS &&\n-\t\t\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))\n+\t\t\tif (ntype == GEN_DNS && host_matches(hostname, subj_alt_str))\n \t\t\t\tfound = 1;\n \t\t}\n \t\tsk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);\n@@ -268,7 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||\n \t    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)\n \t\treturn error(\"cannot get certificate common name\");\n-\tif (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n+\tif (host_matches(hostname, cname))\n \t\treturn 0;\n \treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n \t\t     ASN1_STRING_get0_data(cname), hostname);\n-- \n2.51.0\n\n"},{"id":"538591","messageId":"20260311121107.1122387-3-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH 2/4] imap-send: use the OpenSSL API to access the subject common name","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T12:11:05Z","receivedAt":"2026-03-11T12:26:52Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"The OpenSSL 4.0 branch has deprecated the X509_NAME_get_text_by_NID\nfunction. Use the recommended replacement APIs instead. They have\nexisted since OpenSSL 1.1.0.\n\nPre-4.0 versions of X509_get_subject_name() return a non-const pointer\nand more importantly only accept a non-const pointer in\nX509_NAME_get_index_by_NID(), so we need a version check to handle both\ncases.\n\nSigned-off-by: Beat Bolli <dev+git@drbeat.li>\n---\n imap-send.c | 17 ++++++++++++-----\n 1 file changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 1c934c2487..2a904314dd 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -233,9 +233,13 @@ static int host_matches(const char *host, const char *pattern)\n \n static int verify_hostname(X509 *cert, const char *hostname)\n {\n-\tint len;\n+#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)\n+\tconst X509_NAME *subj;\n+#else\n \tX509_NAME *subj;\n-\tchar cname[1000];\n+#endif\n+\tconst X509_NAME_ENTRY *cname_entry;\n+\tconst ASN1_STRING *cname;\n \tint i, found;\n \tSTACK_OF(GENERAL_NAME) *subj_alt_names;\n \n@@ -262,12 +266,15 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t/* try the common name */\n \tif (!(subj = X509_get_subject_name(cert)))\n \t\treturn error(\"cannot get certificate subject\");\n-\tif ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)\n+\tif ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1)) < 0 ||\n+\t    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||\n+\t    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)\n \t\treturn error(\"cannot get certificate common name\");\n-\tif (strlen(cname) == (size_t)len && host_matches(hostname, cname))\n+\tif (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&\n+\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n \t\treturn 0;\n \treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n-\t\t     cname, hostname);\n+\t\t     ASN1_STRING_get0_data(cname), hostname);\n }\n \n static int ssl_socket_connect(struct imap_socket *sock,\n-- \n2.51.0\n\n"},{"id":"538592","messageId":"20260311121107.1122387-1-dev+git@drbeat.li","threadId":"65207","inReplyTo":null,"subject":"[PATCH 0/4] imap-send: modernize the OpenSSL API","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T12:11:03Z","receivedAt":"2026-03-11T12:27:01Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"OpenSSL recently released version 4.0.0-alpha1 [1]. Compiling with this\nversion revealed some erroneous and deprecated code.\n\nThis series aims to update this code to use the documented OpenSSL APIs.\nAll of the newly used APIs have existed since OpenSSL 1.1.0, the latest\nversion of which was released in September 2019 [2]. IMHO there is no\nneed to support even older OpenSSL versions.\n\n- The first two commits are needed to make imap-send.c compile against\n  OpenSSL 4.0 (and older!).\n\n- The remaining two are follow-up cleanups that are not strictly\n  necessary.\n\nCc-ing Oswald as the original author of the affected code.\n\n[1] https://github.com/openssl/openssl/tree/openssl-4.0.0-alpha1\n[2] https://openssl-library.org/source/old/1.1.0/index.html\n\nBeat Bolli (4):\n  imap-send: use the OpenSSL API to access the subject alternative names\n  imap-send: use the OpenSSL API to access the subject common name\n  imap-send: remove two string length checks\n  imap-send: refactor function host_matches()\n\n imap-send.c | 26 +++++++++++++++++---------\n 1 file changed, 17 insertions(+), 9 deletions(-)\n\n-- \n2.51.0\n\n"},{"id":"538593","messageId":"20260311121107.1122387-2-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH 1/4] imap-send: use the OpenSSL API to access the subject alternative names","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T12:11:04Z","receivedAt":"2026-03-11T12:27:07Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"The OpenSSL 4.0 branch has made the ASN1_STRING structure opaque,\nforbidding access to its internal fields. Use the official accessor\nfunctions instead. They have existed since OpenSSL 1.1.0.\n\nSigned-off-by: Beat Bolli <dev+git@drbeat.li>\n---\n imap-send.c | 10 +++++++---\n 1 file changed, 7 insertions(+), 3 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 26dda7f328..1c934c2487 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -244,10 +244,14 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \tif ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {\n \t\tint num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);\n \t\tfor (i = 0; !found && i < num_subj_alt_names; i++) {\n+\t\t\tint ntype;\n \t\t\tGENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);\n-\t\t\tif (subj_alt_name->type == GEN_DNS &&\n-\t\t\t    strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&\n-\t\t\t    host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))\n+\t\t\tASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);\n+\n+\t\t\tif (ntype == GEN_DNS &&\n+\t\t\t    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==\n+\t\t\t\t    ASN1_STRING_length(subj_alt_str) &&\n+\t\t\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))\n \t\t\t\tfound = 1;\n \t\t}\n \t\tsk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);\n-- \n2.51.0\n\n"},{"id":"538600","messageId":"abFw7FMAwHPPWOBT@ugly.lan","threadId":"65207","inReplyTo":"20260311121107.1122387-4-dev+git@drbeat.li","subject":"Re: [PATCH 3/4] imap-send: remove two string length checks","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2026-03-11T13:41:00Z","receivedAt":"2026-03-11T13:41:05Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Wed, Mar 11, 2026 at 01:11:06PM +0100, Beat Bolli wrote:\n>At this point, these two checks verify that the ASN1_STRINGs are\n>internally consistent. This may have been ok when the fields were\n>accessed directly, but now that the API is used, is unnecessary.\n>\nthat argumentation makes no sense.\nthe purpose of this check is to ensure that there are no embedded nulls, \nwhich the matcher would be unable to deal with, which may be a security \nhole.\n"},{"id":"538658","messageId":"xmqq4immp56h.fsf@gitster.g","threadId":"65207","inReplyTo":"20260311121107.1122387-4-dev+git@drbeat.li","subject":"Re: [PATCH 3/4] imap-send: remove two string length checks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-11T18:55:02Z","receivedAt":"2026-03-11T18:55:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Beat Bolli <dev+git@drbeat.li> writes:\n\n> At this point, these two checks verify that the ASN1_STRINGs are\n> internally consistent. This may have been ok when the fields were\n> accessed directly, but now that the API is used, is unnecessary.\n>\n> Remove the two checks.\n\nOswald already gave a similar comment, but\n\n * I am not sure what you meant by \"ok\" in \"may have been ok\".  Do\n   you mean \"with raw access to the fields, it may have been made\n   send to ensure validity of ASN1_STRING\"?\n\n * I am also not sure what you meant by \"now that the API is used\".\n   Who in the code uses which API function so that we do not have to\n   do our sanity checking?\n\n   The call to host_matches() that these extra checks protect are\n   still passing raw \"const char *\" in this step, and the change to\n   pass ASN1_STRING does not happen until [4/4], so you did not mean\n   host_matches().  I am not sure what it is.\n   \nThanks.\n\n>\n> Signed-off-by: Beat Bolli <dev+git@drbeat.li>\n> ---\n>  imap-send.c | 5 +----\n>  1 file changed, 1 insertion(+), 4 deletions(-)\n>\n> diff --git a/imap-send.c b/imap-send.c\n> index 2a904314dd..2bb0003f08 100644\n> --- a/imap-send.c\n> +++ b/imap-send.c\n> @@ -253,8 +253,6 @@ static int verify_hostname(X509 *cert, const char *hostname)\n>  \t\t\tASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);\n>  \n>  \t\t\tif (ntype == GEN_DNS &&\n> -\t\t\t    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==\n> -\t\t\t\t    ASN1_STRING_length(subj_alt_str) &&\n>  \t\t\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))\n>  \t\t\t\tfound = 1;\n>  \t\t}\n> @@ -270,8 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)\n>  \t    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||\n>  \t    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)\n>  \t\treturn error(\"cannot get certificate common name\");\n> -\tif (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&\n> -\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n> +\tif (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n>  \t\treturn 0;\n>  \treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n>  \t\t     ASN1_STRING_get0_data(cname), hostname);\n"},{"id":"538685","messageId":"a64f450b-1044-421f-86ca-aa523608911b@drbeat.li","threadId":"65207","inReplyTo":"abFw7FMAwHPPWOBT@ugly.lan","subject":"Re: [PATCH 3/4] imap-send: remove two string length checks","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T21:49:23Z","receivedAt":"2026-03-11T21:49:32Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"Hi Oswald\n\nOn 11.03.2026 14:41, Oswald Buddenhagen wrote:\n> On Wed, Mar 11, 2026 at 01:11:06PM +0100, Beat Bolli wrote:\n>> At this point, these two checks verify that the ASN1_STRINGs are\n>> internally consistent. This may have been ok when the fields were\n>> accessed directly, but now that the API is used, is unnecessary.\n>>\n> that argumentation makes no sense.\n> the purpose of this check is to ensure that there are no embedded nulls, \n> which the matcher would be unable to deal with, which may be a security \n> hole.\n\nThanks for the clarification; this was the piece that I was missing.\n\nI'll send a v2 shortly that removes this change.\n\nCheers, Beat\n"},{"id":"538688","messageId":"d9338d30-3e73-4cb7-9f8f-cc1ce21c9de4@drbeat.li","threadId":"65207","inReplyTo":"xmqq4immp56h.fsf@gitster.g","subject":"Re: [PATCH 3/4] imap-send: remove two string length checks","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T22:00:25Z","receivedAt":"2026-03-11T22:00:34Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"Hi Junio\n\nOn 11.03.2026 19:55, Junio C Hamano wrote:\n> Beat Bolli <dev+git@drbeat.li> writes:\n> \n>> At this point, these two checks verify that the ASN1_STRINGs are\n>> internally consistent. This may have been ok when the fields were\n>> accessed directly, but now that the API is used, is unnecessary.\n>>\n>> Remove the two checks.\n> \n> Oswald already gave a similar comment, but\n> \n>   * I am not sure what you meant by \"ok\" in \"may have been ok\".  Do\n>     you mean \"with raw access to the fields, it may have been made\n>     send to ensure validity of ASN1_STRING\"?\n> \n>   * I am also not sure what you meant by \"now that the API is used\".\n>     Who in the code uses which API function so that we do not have to\n>     do our sanity checking?\n> \n>     The call to host_matches() that these extra checks protect are\n>     still passing raw \"const char *\" in this step, and the change to\n>     pass ASN1_STRING does not happen until [4/4], so you did not mean\n>     host_matches().  I am not sure what it is.\n>     \n> Thanks.\n\nI didn't realize that the strlen() comparison was meant to check for \nembedded NULs. I'll send v2 shortly that keeps this check.\n\nCheers, Beat\n\n\n>> Signed-off-by: Beat Bolli <dev+git@drbeat.li>\n>> ---\n>>   imap-send.c | 5 +----\n>>   1 file changed, 1 insertion(+), 4 deletions(-)\n>>\n>> diff --git a/imap-send.c b/imap-send.c\n>> index 2a904314dd..2bb0003f08 100644\n>> --- a/imap-send.c\n>> +++ b/imap-send.c\n>> @@ -253,8 +253,6 @@ static int verify_hostname(X509 *cert, const char *hostname)\n>>   \t\t\tASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);\n>>   \n>>   \t\t\tif (ntype == GEN_DNS &&\n>> -\t\t\t    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==\n>> -\t\t\t\t    ASN1_STRING_length(subj_alt_str) &&\n>>   \t\t\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))\n>>   \t\t\t\tfound = 1;\n>>   \t\t}\n>> @@ -270,8 +268,7 @@ static int verify_hostname(X509 *cert, const char *hostname)\n>>   \t    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||\n>>   \t    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)\n>>   \t\treturn error(\"cannot get certificate common name\");\n>> -\tif (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&\n>> -\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n>> +\tif (host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n>>   \t\treturn 0;\n>>   \treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n>>   \t\t     ASN1_STRING_get0_data(cname), hostname);\n\n"},{"id":"538691","messageId":"20260311221027.1404476-2-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH v2 1/3] imap-send: use the OpenSSL API to access the subject alternative names","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T22:10:25Z","receivedAt":"2026-03-11T22:10:46Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"The OpenSSL 4.0 master branch has made the ASN1_STRING structure opaque,\nforbidding access to its internal fields. Use the official accessor\nfunctions instead. They have existed since OpenSSL v1.1.0.\n\nSigned-off-by: Beat Bolli <dev+git@drbeat.li>\n---\n imap-send.c | 10 +++++++---\n 1 file changed, 7 insertions(+), 3 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 26dda7f328..1c934c2487 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -244,10 +244,14 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \tif ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {\n \t\tint num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);\n \t\tfor (i = 0; !found && i < num_subj_alt_names; i++) {\n+\t\t\tint ntype;\n \t\t\tGENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);\n-\t\t\tif (subj_alt_name->type == GEN_DNS &&\n-\t\t\t    strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&\n-\t\t\t    host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))\n+\t\t\tASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);\n+\n+\t\t\tif (ntype == GEN_DNS &&\n+\t\t\t    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==\n+\t\t\t\t    ASN1_STRING_length(subj_alt_str) &&\n+\t\t\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))\n \t\t\t\tfound = 1;\n \t\t}\n \t\tsk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);\n-- \n2.51.0\n\n"},{"id":"538689","messageId":"20260311221027.1404476-1-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH v2 0/3] imap-send: modernize the OpenSSL API","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T22:10:24Z","receivedAt":"2026-03-11T22:10:47Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"OpenSSL recently released version 4.0.0-alpha1 [1]. Compiling with this\nversion revealed some erroneous and deprecated code.\n\nThis series aims to update this code to use the documented OpenSSL APIs.\nAll of the newly used APIs have existed since OpenSSL 1.1.0, the latest\nversion of which was released in September 2019 [2]. IMHO there is no\nneed to support even older OpenSSL versions.\n\nCc-ing Oswald as the original author of the affected code.\n\n[1] https://github.com/openssl/openssl/tree/openssl-4.0.0-alpha1\n[2] https://openssl-library.org/source/old/1.1.0/index.html\n\nBeat Bolli (3):\n  imap-send: use the OpenSSL API to access the subject alternative names\n  imap-send: use the OpenSSL API to access the subject common name\n  imap-send: move common code into function host_matches()\n\n imap-send.c | 31 ++++++++++++++++++++++---------\n 1 file changed, 22 insertions(+), 9 deletions(-)\n\n---\nChanges vs v1:\n- keep the check for embedded NUL characters\n\n-- \n2.51.0\n\n"},{"id":"538690","messageId":"20260311221027.1404476-4-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH v2 3/3] imap-send: move common code into function host_matches()","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T22:10:27Z","receivedAt":"2026-03-11T22:10:47Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"Move the ASN1_STRING access, the associated cast and the check for\nembedded NUL bytes into host_matches() to simplify both callers.\n\nReformulate the NUL check using memchr() and add a comment to make it\nmore obvious what it is about.\n\nSigned-off-by: Beat Bolli <dev+git@drbeat.li>\n---\n imap-send.c | 16 +++++++++-------\n 1 file changed, 9 insertions(+), 7 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 2a904314dd..af02c6a689 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -219,8 +219,14 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,\n \n #else\n \n-static int host_matches(const char *host, const char *pattern)\n+static int host_matches(const char *host, const ASN1_STRING *asn1_str)\n {\n+\tconst char *pattern = (const char *)ASN1_STRING_get0_data(asn1_str);\n+\n+\t/* embedded NUL characters may open a security hole */\n+\tif (memchr(pattern, '\\0', ASN1_STRING_length(asn1_str)))\n+\t    return 0;\n+\n \tif (pattern[0] == '*' && pattern[1] == '.') {\n \t\tpattern += 2;\n \t\tif (!(host = strchr(host, '.')))\n@@ -252,10 +258,7 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t\t\tGENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);\n \t\t\tASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype);\n \n-\t\t\tif (ntype == GEN_DNS &&\n-\t\t\t    strlen((const char *)ASN1_STRING_get0_data(subj_alt_str)) ==\n-\t\t\t\t    ASN1_STRING_length(subj_alt_str) &&\n-\t\t\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(subj_alt_str)))\n+\t\t\tif (ntype == GEN_DNS && host_matches(hostname, subj_alt_str))\n \t\t\t\tfound = 1;\n \t\t}\n \t\tsk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);\n@@ -270,8 +273,7 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||\n \t    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)\n \t\treturn error(\"cannot get certificate common name\");\n-\tif (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&\n-\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n+\tif (host_matches(hostname, cname))\n \t\treturn 0;\n \treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n \t\t     ASN1_STRING_get0_data(cname), hostname);\n-- \n2.51.0\n\n"},{"id":"538692","messageId":"20260311221027.1404476-3-dev+git@drbeat.li","threadId":"65207","inReplyTo":"20260311121107.1122387-1-dev+git@drbeat.li","subject":"[PATCH v2 2/3] imap-send: use the OpenSSL API to access the subject common name","fromName":"Beat Bolli","fromEmail":"dev+git@drbeat.li","sentAt":"2026-03-11T22:10:26Z","receivedAt":"2026-03-11T22:10:47Z","isPatch":true,"sender":{"key":"dev+git@drbeat.li","avatar":"https://avatars.githubusercontent.com/u/21444?v=4"},"body":"The OpenSSL 4.0 master branch has deprecated the\nX509_NAME_get_text_by_NID function. Use the recommended replacement APIs\ninstead. They have existed since OpenSSL v1.1.0.\n\nTake care to get the constness right for pre-4.0 versions.\n\nSigned-off-by: Beat Bolli <dev+git@drbeat.li>\n---\n imap-send.c | 17 ++++++++++++-----\n 1 file changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 1c934c2487..2a904314dd 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -233,9 +233,13 @@ static int host_matches(const char *host, const char *pattern)\n \n static int verify_hostname(X509 *cert, const char *hostname)\n {\n-\tint len;\n+#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)\n+\tconst X509_NAME *subj;\n+#else\n \tX509_NAME *subj;\n-\tchar cname[1000];\n+#endif\n+\tconst X509_NAME_ENTRY *cname_entry;\n+\tconst ASN1_STRING *cname;\n \tint i, found;\n \tSTACK_OF(GENERAL_NAME) *subj_alt_names;\n \n@@ -262,12 +266,15 @@ static int verify_hostname(X509 *cert, const char *hostname)\n \t/* try the common name */\n \tif (!(subj = X509_get_subject_name(cert)))\n \t\treturn error(\"cannot get certificate subject\");\n-\tif ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)\n+\tif ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1)) < 0 ||\n+\t    (cname_entry = X509_NAME_get_entry(subj, i)) == NULL ||\n+\t    (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL)\n \t\treturn error(\"cannot get certificate common name\");\n-\tif (strlen(cname) == (size_t)len && host_matches(hostname, cname))\n+\tif (strlen((const char *)ASN1_STRING_get0_data(cname)) == ASN1_STRING_length(cname) &&\n+\t    host_matches(hostname, (const char *)ASN1_STRING_get0_data(cname)))\n \t\treturn 0;\n \treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n-\t\t     cname, hostname);\n+\t\t     ASN1_STRING_get0_data(cname), hostname);\n }\n \n static int ssl_socket_connect(struct imap_socket *sock,\n-- \n2.51.0\n\n"},{"id":"538702","messageId":"xmqqsea5lwqj.fsf@gitster.g","threadId":"65207","inReplyTo":"20260311221027.1404476-1-dev+git@drbeat.li","subject":"Re: [PATCH v2 0/3] imap-send: modernize the OpenSSL API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-12T00:25:40Z","receivedAt":"2026-03-12T00:25:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Beat Bolli <dev+git@drbeat.li> writes:\n> Changes vs v1:\n> - keep the check for embedded NUL characters\n\n... which amounts to this difference, which is a lot more explicit\nway to express what is going on.  I like it.\n\nThanks.\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 789055d7fd..af02c6a689 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -222,6 +222,11 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,\n static int host_matches(const char *host, const ASN1_STRING *asn1_str)\n {\n \tconst char *pattern = (const char *)ASN1_STRING_get0_data(asn1_str);\n+\n+\t/* embedded NUL characters may open a security hole */\n+\tif (memchr(pattern, '\\0', ASN1_STRING_length(asn1_str)))\n+\t    return 0;\n+\n \tif (pattern[0] == '*' && pattern[1] == '.') {\n \t\tpattern += 2;\n \t\tif (!(host = strchr(host, '.')))\n"}]}