From dfcdd0b960fc1efd2fe19e97b973b435727b4c42 Mon Sep 17 00:00:00 2001 From: Beat Bolli Date: Wed, 11 Mar 2026 23:10:25 +0100 Subject: [PATCH 1/3] imap-send: use the OpenSSL API to access the subject alternative names 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 Signed-off-by: Junio C Hamano --- 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); From 08fd302fc4b8eaf0bb32856231a5fb46430e3c7e Mon Sep 17 00:00:00 2001 From: Beat Bolli Date: Wed, 11 Mar 2026 23:10:26 +0100 Subject: [PATCH 2/3] imap-send: use the OpenSSL API to access the subject common name 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 Signed-off-by: Junio C Hamano --- 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, From 6392a0b75d979ba8e23c85d57b85779aace25370 Mon Sep 17 00:00:00 2001 From: Beat Bolli Date: Wed, 11 Mar 2026 23:10:27 +0100 Subject: [PATCH 3/3] imap-send: move common code into function host_matches() 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 Signed-off-by: Junio C Hamano --- 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);