Commit 2a34692e5e for openssl.org

commit 2a34692e5efb6c97a50e701363da9a57240794b1
Author: Dr. David von Oheimb <dev@ddvo.net>
Date:   Fri Apr 11 20:19:46 2025 +0200

    apps/cmp.c: fix use of SSL_set_tlsext_host_name() for SNI and X509_VERIFY_PARAM_set1_host()

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    MergeDate: Wed Jun 10 06:27:00 2026
    (Merged from https://github.com/openssl/openssl/pull/27357)

diff --git a/apps/cmp.c b/apps/cmp.c
index 35bd787a0c..8739e860d3 100644
--- a/apps/cmp.c
+++ b/apps/cmp.c
@@ -472,13 +472,13 @@ const OPTIONS cmp_options[] = {
         "NOTE: -server, -proxy, and -no_proxy not supported due to no-sock/no-http build" },
 #else
     { "server", OPT_SERVER, 's',
-        "[http[s]://]address[:port][/path] of CMP server. Default port 80 or 443." },
+        "[http[s]://]host[:port][/path] of CMP server to use. Default port 80 or 443." },
     { OPT_MORE_STR, 0, 0,
-        "address may be a DNS name or an IP address; path can be overridden by -path" },
+        "host may be a DNS name or an IP address; path can be overridden by -path" },
     { "proxy", OPT_PROXY, 's',
-        "[http[s]://]address[:port][/path] of HTTP(S) proxy to use; path is ignored" },
+        "[http[s]://]host[:port][/path] of HTTP(S) proxy to use; path is ignored" },
     { "no_proxy", OPT_NO_PROXY, 's',
-        "List of addresses of servers not to use HTTP(S) proxy for" },
+        "List of servers not to use HTTP(S) proxy for" },
     { OPT_MORE_STR, 0, 0,
         "Default from environment variable 'no_proxy', else 'NO_PROXY', else none" },
 #endif
@@ -597,7 +597,7 @@ const OPTIONS cmp_options[] = {
         "Trusted certificates to use for verifying the TLS server certificate;" },
     { OPT_MORE_STR, 0, 0, "this implies hostname validation" },
     { "tls_host", OPT_TLS_HOST, 's',
-        "Address to be checked (rather than -server) during TLS hostname validation" },
+        "Host name/address (rather than -server) to verify in TLS server cert" },
 #endif

     OPT_SECTION("Client-side debugging"),
@@ -861,7 +861,11 @@ static X509 *load_cert_pwd(const char *uri, const char *source, const char *desc
     return cert;
 }

-/* set expected hostname/IP addr and clears the email addr in the given ts */
+/*
+ * Set expected hostname/IP address and clears any email address in the given ts.
+ * If the host is NULL, host name/address verification is disabled.
+ * It is interpreted as an IP address when possible, otherwise as a domain name.
+ */
 static int truststore_set_host_etc(X509_STORE *ts, const char *host)
 {
     X509_VERIFY_PARAM *ts_vpm = X509_STORE_get0_param(ts);
@@ -871,10 +875,14 @@ static int truststore_set_host_etc(X509_STORE *ts, const char *host)
         || !X509_VERIFY_PARAM_set1_ip(ts_vpm, NULL, 0)
         || !X509_VERIFY_PARAM_set1_email(ts_vpm, NULL, 0))
         return 0;
+    if (host == NULL)
+        return 1;
+
     X509_VERIFY_PARAM_set_hostflags(ts_vpm,
         X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT | X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
-    return (host != NULL && X509_VERIFY_PARAM_set1_ip_asc(ts_vpm, host))
-        || X509_VERIFY_PARAM_set1_host(ts_vpm, host, 0);
+    return host_is_ip_address(host)
+        ? X509_VERIFY_PARAM_set1_ip_asc(ts_vpm, host)
+        : X509_VERIFY_PARAM_set1_host(ts_vpm, host, 0);
 }

 /* write OSSL_CMP_MSG DER-encoded to the specified file name item */
@@ -1583,8 +1591,7 @@ static SSL_CTX *setup_ssl_ctx(OSSL_CMP_CTX *ctx, const char *host)
          * If we did this before checking our own TLS cert
          * the expected hostname would mislead the check.
          */
-        if (!truststore_set_host_etc(trust_store,
-                opt_tls_host != NULL ? opt_tls_host : host))
+        if (!truststore_set_host_etc(trust_store, host))
             goto err;
     }
     return ssl_ctx;
@@ -2369,9 +2376,9 @@ set_path:
             goto err;
         APP_HTTP_TLS_INFO_free(OSSL_CMP_CTX_get_http_cb_arg(ctx));
         (void)OSSL_CMP_CTX_set_http_cb_arg(ctx, info);
-        info->ssl_ctx = setup_ssl_ctx(ctx, host);
+        info->ssl_ctx = setup_ssl_ctx(ctx, opt_tls_host != NULL ? opt_tls_host : host);
         info->server = host;
-        host = NULL; /* prevent deallocation */
+        host = NULL; /* ownership has been transferred to info structure */
         if ((info->port = OPENSSL_strdup(server_port)) == NULL)
             goto err;
         /* workaround for callback design flaw, see #17088: */
@@ -3999,11 +4006,7 @@ err:
         /* cannot free info already here, as it may be used indirectly by: */
         OSSL_CMP_CTX_free(cmp_ctx);
 #if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
-        if (info != NULL) {
-            OPENSSL_free((char *)info->server);
-            OPENSSL_free((char *)info->port);
-            APP_HTTP_TLS_INFO_free(info);
-        }
+        APP_HTTP_TLS_INFO_free(info);
 #endif
     }
     X509_VERIFY_PARAM_free(vpm);
diff --git a/apps/include/apps.h b/apps/include/apps.h
index 97c6669e3a..80bd775ffb 100644
--- a/apps/include/apps.h
+++ b/apps/include/apps.h
@@ -323,6 +323,8 @@ int check_cert_might_be_valid(BIO *bio, BIO *bio_err, X509 *x,

 void store_setup_crl_download(X509_STORE *st);

+int host_is_ip_address(const char *host);
+
 typedef struct app_http_tls_info_st {
     const char *server;
     const char *port;
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
index 467f36ae1f..40938c0410 100644
--- a/apps/lib/apps.c
+++ b/apps/lib/apps.c
@@ -2868,6 +2868,34 @@ void store_setup_crl_download(X509_STORE *st)
     X509_STORE_set_lookup_crls_cb(st, crls_http_cb);
 }

+int host_is_ip_address(const char *host)
+{
+#ifndef INET6_ADDRSTRLEN /* not defined on OPENSSL_NO_SOCK */
+#define INET6_ADDRSTRLEN 46
+#endif
+    char stripped_host_ipv6[INET6_ADDRSTRLEN + 1];
+    ASN1_OCTET_STRING *str;
+    int ret;
+
+    if (host == NULL)
+        return 0;
+
+    if (host[0] == '[') { /* OSSL_parse_url() returns IPv6 addresses enclosed in [ and ] */
+        size_t len = strlen(++host);
+        if (len == 0 || len > sizeof(stripped_host_ipv6) || host[--len] != ']')
+            return 0;
+        strncpy(stripped_host_ipv6, host, sizeof(stripped_host_ipv6));
+        stripped_host_ipv6[len] = '\0';
+        host = stripped_host_ipv6;
+    }
+    ERR_set_mark();
+    str = a2i_IPADDRESS(host);
+    ret = str != NULL;
+    ERR_pop_to_mark();
+    ASN1_OCTET_STRING_free(str);
+    return ret;
+}
+
 #if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
 static const char *tls_error_hint(void)
 {
@@ -2917,15 +2945,12 @@ BIO *app_http_tls_cb(BIO *bio, void *arg, int connect, int detail)
 {
     APP_HTTP_TLS_INFO *info = (APP_HTTP_TLS_INFO *)arg;
     SSL_CTX *ssl_ctx = info->ssl_ctx;
+    BIO *sbio = NULL;

     if (ssl_ctx == NULL) /* not using TLS */
         return bio;
     if (connect) {
         SSL *ssl;
-        BIO *sbio = NULL;
-        X509_STORE *ts = SSL_CTX_get_cert_store(ssl_ctx);
-        X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts);
-        char *host = vpm == NULL ? NULL : X509_VERIFY_PARAM_get0_host(vpm, 0 /* first hostname */);

         /* adapt after fixing callback design flaw, see #17088 */
         if ((info->use_proxy
@@ -2935,27 +2960,35 @@ BIO *app_http_tls_cb(BIO *bio, void *arg, int connect, int detail)
             || (sbio = BIO_new(BIO_f_ssl())) == NULL) {
             return NULL;
         }
-        if ((ssl = SSL_new(ssl_ctx)) == NULL) {
-            BIO_free(sbio);
-            return NULL;
-        }
-
-        if (vpm != NULL)
-            SSL_set_tlsext_host_name(ssl, host /* may be NULL */);
+        if ((ssl = SSL_new(ssl_ctx)) == NULL)
+            goto err;

         SSL_set_connect_state(ssl);
-        BIO_set_ssl(sbio, ssl, BIO_CLOSE);
+        if (BIO_set_ssl(sbio, ssl, BIO_CLOSE) <= 0) {
+            SSL_free(ssl);
+            goto err;
+        }
+        if (!host_is_ip_address(info->server)) {
+            if (!SSL_set_tlsext_host_name(ssl, info->server)) /* set SNI */
+                goto err;
+        }

         bio = BIO_push(sbio, bio);
     } else { /* disconnect from TLS */
         bio = http_tls_shutdown(bio);
     }
     return bio;
+
+err:
+    BIO_free(sbio);
+    return NULL;
 }

 void APP_HTTP_TLS_INFO_free(APP_HTTP_TLS_INFO *info)
 {
     if (info != NULL) {
+        OPENSSL_free((char *)info->server);
+        OPENSSL_free((char *)info->port);
         SSL_CTX_free(info->ssl_ctx);
         OPENSSL_free(info);
     }
diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in
index d62afe61aa..c393929264 100644
--- a/doc/man1/openssl-cmp.pod.in
+++ b/doc/man1/openssl-cmp.pod.in
@@ -116,7 +116,7 @@ TLS connection options:
 [B<-tls_keypass> I<arg>]
 [B<-tls_extra> I<filenames>|I<uris>]
 [B<-tls_trusted> I<filenames>|I<uris>]
-[B<-tls_host> I<name>]
+[B<-tls_host> I<host>]

 Client-side debugging options:

@@ -527,9 +527,14 @@ Reason numbers defined in RFC 5280 are:
 =item B<-server> I<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]>

 The I<host> domain name or IP address and optionally I<port>
-of the CMP server to connect to using HTTP(S).
-IP address may be for v4 or v6, such as C<127.0.0.1> or C<[::1]> for localhost.
-If the host string is an IPv6 address, it must be enclosed in C<[> and C<]>.
+of the CMP server to connect to via HTTP(S).
+An IP address may be for v4 or v6, such as C<127.0.0.1> or C<[::1]> for C<localhost>.
+If the I<host> string is an IPv6 address, it must be enclosed in C<[> and C<]>.
+
+For TLS connections, the name verified in server certificates is I<host>
+unless the B<-tls_host> option is used to specify a different name.
+If I<host> is a DNS name, it is also used for
+TLS Server Name Indication (SNI) according to RFC 3546 section 3.1.

 This option excludes I<-port> and I<-use_mock_srv>.
 It is ignored if I<-rspin> is given with enough filename arguments.
@@ -1038,11 +1043,10 @@ The certificate verification options
 B<-verify_hostname>, B<-verify_ip>, and B<-verify_email>
 have no effect on the certificate verification enabled via this option.

-=item B<-tls_host> I<name>
+=item B<-tls_host> I<host>

-Address to be checked during hostname validation.
-This may be a DNS name or an IP address.
-If not given it defaults to the B<-server> address.
+Hostname or IP address to be checked in the TLS server certificate.
+If not given it defaults to the host part of the B<-server> option URL argument.

 =back