From f62548245d1ecfd6b14a6b3ed27e13e7352e13fb Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Fri, 11 Apr 2025 19:53:00 +0200 Subject: [PATCH 1/7] SSL_CTX_set_tlsext_servername_callback.pod: improve doc of SSL_set_tlsext_host_name(); update format Add recommendation to use it for TLS clients, together with X509_VERIFY_PARAM_{set1,add1}_host() --- ...SSL_CTX_set_tlsext_servername_callback.pod | 35 +++++++++++-------- ssl/s3_lib.c | 3 +- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/doc/man3/SSL_CTX_set_tlsext_servername_callback.pod b/doc/man3/SSL_CTX_set_tlsext_servername_callback.pod index a0a4bd6367..3067d10791 100644 --- a/doc/man3/SSL_CTX_set_tlsext_servername_callback.pod +++ b/doc/man3/SSL_CTX_set_tlsext_servername_callback.pod @@ -26,9 +26,9 @@ the ClientHello callback, which can be set using SSL_CTX_set_client_hello_cb(). However, even where the ClientHello callback is used, the servername callback is still necessary in order to acknowledge the servername requested by the client. -SSL_CTX_set_tlsext_servername_callback() sets the application callback B +SSL_CTX_set_tlsext_servername_callback() sets the application callback I used by a server to perform any actions or configuration required based on -the servername extension received in the incoming connection. When B +the servername extension received in the incoming connection. When I is NULL, SNI is not used. The servername callback should return one of the following values: @@ -45,14 +45,14 @@ up a different configuration for the selected servername in this case. In this case the servername requested by the client is not accepted and the handshake will be aborted. The value of the alert to be used should be stored in -the location pointed to by the B parameter to the callback. By default this +the location pointed to by the I parameter to the callback. By default this value is initialised to SSL_AD_UNRECOGNIZED_NAME. =item SSL_TLSEXT_ERR_ALERT_WARNING If this value is returned then the servername is not accepted by the server. However, the handshake will continue and send a warning alert instead. The value -of the alert should be stored in the location pointed to by the B parameter +of the alert should be stored in the location pointed to by the I parameter as for SSL_TLSEXT_ERR_ALERT_FATAL above. Note that TLSv1.3 does not support warning alerts, so if TLSv1.3 has been negotiated then this return value is treated the same way as SSL_TLSEXT_ERR_NOACK. @@ -65,7 +65,7 @@ No alerts are sent and the server will not acknowledge the requested servername. =back SSL_CTX_set_tlsext_servername_arg() sets a context-specific argument to be -passed into the callback (via the B parameter) for this B. +passed into the callback (via the I parameter) for this B. The behaviour of SSL_get_servername() depends on a number of different factors. In particular note that in TLSv1.3 the servername is negotiated in every @@ -123,12 +123,21 @@ client is processed. The servername, certificate and ALPN callbacks occur after a servername extension from the client is processed. SSL_get_servername_type() returns the servername type or -1 if no servername -is present. Currently the only supported type (defined in RFC3546) is -B. +is present. +The only type defined in RFC 3546 and supported here is B. -SSL_set_tlsext_host_name() sets the server name indication ClientHello extension -to contain the value B. The type of server name indication extension is set -to B (defined in RFC3546). +SSL_set_tlsext_host_name() sets the Server Name Indication (SNI) +ClientHello extension defined in RFC 3546 section 3.1 +to contain the value I of type B. +If I is NULL it clears the SNI extension. + +Using this function may be crucial also for correct routing of connection requests. +TLS clients are recommended to use this function as well as L +or L for validation of server hostname(s) and/or IP address. + +The SSL_set_tlsext_host_name() function should only be called on SSL objects +that will act as clients; otherwise the configured I will be ignored. +In the future, calling this function on the server side may result in an error. =head1 NOTES @@ -136,9 +145,6 @@ Several callbacks are executed during ClientHello processing, including the ClientHello, ALPN, and servername callbacks. The ClientHello callback is executed first, then the servername callback, followed by the ALPN callback. -The SSL_set_tlsext_host_name() function should only be called on SSL objects -that will act as clients; otherwise the configured B will be ignored. - =head1 RETURN VALUES SSL_CTX_set_tlsext_servername_callback() and @@ -148,7 +154,8 @@ SSL_set_tlsext_host_name() returns 1 on success, 0 in case of error. =head1 SEE ALSO L, L, -L, L +L, L, +L, L =head1 HISTORY diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index e82d921ae6..a10c1133bf 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3605,8 +3605,7 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) * from the server, but we currently allow it to be used on servers * as well, which is a programming error. Currently we just clear * the field in SSL_do_handshake() for server SSLs, but when we can - * make ABI-breaking changes, we may want to make use of this API - * an error on server SSLs. + * make ABI-breaking changes, we may want to return an error in this case. */ if (larg == TLSEXT_NAMETYPE_host_name) { size_t len; From f387b0fedd9eeb8cad86334299e1ab50033b00fa Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Fri, 11 Apr 2025 20:00:21 +0200 Subject: [PATCH 2/7] X509_VERIFY_PARAM_set_flags.pod: add hint on joint use of X509_VERIFY_PARAM_set1_host() and SSL_set_tlsext_host_name() --- doc/man3/X509_VERIFY_PARAM_set_flags.pod | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/man3/X509_VERIFY_PARAM_set_flags.pod b/doc/man3/X509_VERIFY_PARAM_set_flags.pod index 267975778b..f765e09123 100644 --- a/doc/man3/X509_VERIFY_PARAM_set_flags.pod +++ b/doc/man3/X509_VERIFY_PARAM_set_flags.pod @@ -367,6 +367,12 @@ instead of functions which work in specific structures such as X509_STORE_CTX_set_flags() which are likely to be deprecated in a future release. +TLS clients are recommended to set up validation of server hostname(s) and/or +IP address (directly using the above functions +or more conveniently using L or L) +and to use L for Server Name Indication (SNI), +which may be crucial also for correct routing of the connection request. + =head1 BUGS Delta CRL checking is currently primitive. Only a single delta can be used and @@ -395,6 +401,7 @@ L, L, L, L, +L, L =head1 HISTORY From 405f49af6e42255e124617ab4a86931ea61e0e09 Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Fri, 11 Apr 2025 20:01:38 +0200 Subject: [PATCH 3/7] X509_VERIFY_PARAM_set_flags.pod: remove heavily outdated texts on X509_V_FLAG_NO_ALT_CHAINS; other small fixes --- doc/man3/X509_VERIFY_PARAM_set_flags.pod | 89 +++++++++++++----------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/doc/man3/X509_VERIFY_PARAM_set_flags.pod b/doc/man3/X509_VERIFY_PARAM_set_flags.pod index f765e09123..263b38ed54 100644 --- a/doc/man3/X509_VERIFY_PARAM_set_flags.pod +++ b/doc/man3/X509_VERIFY_PARAM_set_flags.pod @@ -76,54 +76,54 @@ X509_VERIFY_PARAM_set1_ip_asc These functions manipulate the B structure associated with a certificate verification operation. -The X509_VERIFY_PARAM_set_flags() function sets the flags in B by oring -it with B. See L for a complete -description of values the B parameter can take. +The X509_VERIFY_PARAM_set_flags() function sets the flags in I by oring +it with I. See L for a complete +description of values the I parameter can take. -X509_VERIFY_PARAM_get_flags() returns the flags in B. +X509_VERIFY_PARAM_get_flags() returns the flags in I. -X509_VERIFY_PARAM_get_inh_flags() returns the inheritance flags in B +X509_VERIFY_PARAM_get_inh_flags() returns the inheritance flags in I which specifies how verification flags are copied from one structure to another. X509_VERIFY_PARAM_set_inh_flags() sets the inheritance flags. -See the B section for a description of these bits. +See the L section for a description of these bits. -X509_VERIFY_PARAM_clear_flags() clears the flags B in B. +X509_VERIFY_PARAM_clear_flags() clears the flags I in I. -X509_VERIFY_PARAM_set_purpose() sets the verification purpose in B +X509_VERIFY_PARAM_set_purpose() sets the verification purpose in I to B. This determines the acceptable purpose of the certificate chain, for example B. The purpose requirement is cleared if B is X509_PURPOSE_DEFAULT_ANY. X509_VERIFY_PARAM_get_purpose() returns the purpose in B. -X509_VERIFY_PARAM_set_trust() sets the trust setting in B to -B. +X509_VERIFY_PARAM_set_trust() sets the trust setting in I to +I. -X509_VERIFY_PARAM_set_time() sets the verification time in B to -B. Normally the current time is used. +X509_VERIFY_PARAM_set_time() sets the verification time in I to +I. Normally the current time is used. -X509_VERIFY_PARAM_add0_policy() adds B to the acceptable policy set. +X509_VERIFY_PARAM_add0_policy() adds I to the acceptable policy set. Contrary to preexisting documentation of this function it does not enable policy checking. X509_VERIFY_PARAM_set1_policies() enables policy checking (it is disabled -by default) and sets the acceptable policy set to B. Any existing -policy set is cleared. The B parameter can be B to clear +by default) and sets the acceptable policy set to I. Any existing +policy set is cleared. The I parameter can be NULL to clear an existing policy set. -X509_VERIFY_PARAM_set_depth() sets the maximum verification depth to B. +X509_VERIFY_PARAM_set_depth() sets the maximum verification depth to I. That is the maximum number of intermediate CA certificates that can appear in a chain. A maximal depth chain contains 2 more certificates than the limit, since neither the end-entity certificate nor the trust-anchor count against this limit. -Thus a B limit of 0 only allows the end-entity certificate to be signed -directly by the trust anchor, while with a B limit of 1 there can be one +Thus a I limit of 0 only allows the end-entity certificate to be signed +directly by the trust anchor, while with a I limit of 1 there can be one intermediate CA certificate between the trust anchor and the end-entity certificate. X509_VERIFY_PARAM_set_auth_level() sets the authentication security level to -B. +I. The authentication security level determines the acceptable signature and public key strength when verifying certificate chains. For a certificate chain to validate, the public keys of all the certificates @@ -139,21 +139,21 @@ Security level 1 requires at least 80-bit-equivalent security and is broadly interoperable, though it will, for example, reject MD5 signatures or RSA keys shorter than 1024 bits. -X509_VERIFY_PARAM_get0_host() returns the Bth expected DNS hostname that has +X509_VERIFY_PARAM_get0_host() returns the Ith expected DNS hostname that has been set using X509_VERIFY_PARAM_set1_host() or X509_VERIFY_PARAM_add1_host(). -To obtain all names start with B = 0 and increment B as long as no NULL +To obtain all names start with I = 0 and increment I as long as no NULL pointer is returned. -X509_VERIFY_PARAM_set1_host() sets the expected DNS hostname to -B clearing any previously specified hostname. If -B is NULL, or empty the list of hostnames is cleared, and -name checks are not performed on the peer certificate. If B -is NUL-terminated, B may be zero, otherwise B -must be set to the length of B. +X509_VERIFY_PARAM_set1_host() sets in I the expected +DNS hostname to I, clearing any previously specified hostname. +If I is NULL or the empty string, the list of hostnames is cleared +and hostname checks are not performed on the peer certificate. +If I is NUL-terminated, I may be zero, +otherwise I must be set to the length of I. When a hostname is specified, certificate verification automatically invokes L -with flags equal to the B argument given to +with flags equal to the I argument given to X509_VERIFY_PARAM_set_hostflags() (default zero). Applications are strongly advised to use this interface in preference to explicitly calling L, hostname checks may be out of scope @@ -176,10 +176,10 @@ flag takes precedence over the B flag. X509_VERIFY_PARAM_get_hostflags() returns any host flags previously set via a call to X509_VERIFY_PARAM_set_hostflags(). -X509_VERIFY_PARAM_add1_host() adds B as an additional reference +X509_VERIFY_PARAM_add1_host() adds I as an additional reference identifier that can match the peer's certificate. Any previous names set via X509_VERIFY_PARAM_set1_host() or X509_VERIFY_PARAM_add1_host() -are retained, no change is made if B is NULL or empty. When +are retained, no change is made if I is NULL or the empty string. When multiple names are configured, the peer is considered verified when any name matches. @@ -190,28 +190,28 @@ reference identifier specifies a parent domain (starts with ".") rather than a hostname, the peer name may be a wildcard name or a sub-domain of the reference identifier respectively. The return string is allocated by the library and is no longer valid once the -associated B argument is freed. Applications must not free +associated I argument is freed. Applications must not free the return value. X509_VERIFY_PARAM_get0_email() returns the expected RFC822 email address. X509_VERIFY_PARAM_set1_email() sets the expected RFC822 email address to -B. If B is NUL-terminated, B may be zero, otherwise -B must be set to the length of B. When an email address +I. If I is NUL-terminated, I may be zero, otherwise +I must be set to the length of I. When an email address is specified, certificate verification automatically invokes L. X509_VERIFY_PARAM_get1_ip_asc() returns the expected IP address as a string. The caller is responsible for freeing it. -X509_VERIFY_PARAM_set1_ip() sets the expected IP address to B. -The B argument is in binary format, in network byte-order and -B must be set to 4 for IPv4 and 16 for IPv6. When an IP +X509_VERIFY_PARAM_set1_ip() sets the expected IP address to I. +The I argument is in binary format, in network byte-order and +I must be set to 4 for IPv4 and 16 for IPv6. When an IP address is specified, certificate verification automatically invokes L. X509_VERIFY_PARAM_set1_ip_asc() sets the expected IP address to -B. The B argument is a NUL-terminal ASCII string: +I. The I argument is a NUL-terminal ASCII string: dotted decimal quad for IPv4 and colon-separated hexadecimal for IPv6. The condensed "::" notation is supported for IPv6 addresses. @@ -275,9 +275,9 @@ no policy checking is performed. Additional information is sent to the verification callback relating to policy checking. B, B and -B set the B, B and B flags respectively as defined in -B. Policy checking is automatically enabled if any of these flags +B set the C, C and C flags respectively as defined in +RFC 5280. Policy checking is automatically enabled if any of these flags are set. If B is set and the policy checking is successful @@ -311,6 +311,8 @@ requirements and lead to a locally trusted root. This is especially important when some certificates in the trust store have explicit trust settings (see "TRUST SETTINGS" in L). +=begin comment + The B flag could have been used before OpenSSL 1.1.0 to suppress checking for alternative chains. By default, unless B is set, when building a @@ -321,12 +323,14 @@ found that is trusted. As of OpenSSL 1.1.0, with B always set, this option has no effect. +=end comment + The B flag causes non-self-signed certificates in the trust store to be treated as trust anchors, in the same way as self-signed root CA certificates. This makes it possible to trust self-issued certificates as well as certificates issued by an intermediate CA without having to trust their ancestor root CA. -With OpenSSL 1.1.0 and later and B set, chain +With B set, chain construction stops as soon as the first certificate contained in the trust store is added to the chain, whether that certificate is a self-signed "root" certificate or a not self-signed "intermediate" or self-issued certificate. @@ -386,7 +390,7 @@ CRLs from the CRL distribution points extension. =head1 EXAMPLES Enable CRL checking when performing certificate verification during SSL -connections associated with an B structure B: +connections associated with an B structure I: X509_VERIFY_PARAM *param; @@ -397,6 +401,7 @@ connections associated with an B structure B: =head1 SEE ALSO +L, L, L, L, From 3dfa30bc5a6cc5d17b34645a6572cea093afac73 Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Fri, 11 Apr 2025 20:19:46 +0200 Subject: [PATCH 4/7] apps/cmp.c: fix use of SSL_set_tlsext_host_name() for SNI and X509_VERIFY_PARAM_set1_host() --- apps/cmp.c | 34 ++++++++++++++++++++++------------ apps/include/apps.h | 3 +++ apps/lib/apps.c | 26 ++++++++++++++------------ doc/man1/openssl-cmp.pod.in | 12 +++++++----- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/apps/cmp.c b/apps/cmp.c index 6f2fea4f55..b8deffb79a 100644 --- a/apps/cmp.c +++ b/apps/cmp.c @@ -528,7 +528,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"}, + "Address to be used for SNI and to be checked during TLS hostname validation"}, #endif OPT_SECTION("Client-side debugging"), @@ -787,7 +787,11 @@ static X509 *load_cert_pwd(const char *uri, const char *pass, 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. + * Otherwise, it is interpreted as an IP address if 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); @@ -797,10 +801,13 @@ 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)) + return X509_VERIFY_PARAM_set1_ip_asc(ts_vpm, host) || X509_VERIFY_PARAM_set1_host(ts_vpm, host, 0); } @@ -1510,8 +1517,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; @@ -2283,6 +2289,7 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine) #if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP) if (opt_tls_used) { APP_HTTP_TLS_INFO *info; + const char *hostaddr; if (opt_tls_cert != NULL || opt_tls_key != NULL || opt_tls_keypass != NULL) { @@ -2299,9 +2306,16 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine) 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, engine); + hostaddr = opt_tls_host != NULL ? opt_tls_host : host; + info->ssl_ctx = setup_ssl_ctx(ctx, hostaddr, engine); + if (!IS_IP_ADDR(hostaddr)) { + if (hostaddr == NULL) + info->sni_hostname = NULL; + else if ((info->sni_hostname = OPENSSL_strdup(hostaddr)) == NULL) + goto err; + } 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: */ @@ -3919,11 +3933,7 @@ int cmp_main(int argc, char **argv) /* 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 1ea1c13fde..25134c0b73 100644 --- a/apps/include/apps.h +++ b/apps/include/apps.h @@ -45,6 +45,8 @@ */ # define _UC(c) ((unsigned char)(c)) +# define IS_IP_ADDR(host) ((host) != NULL && ((*(host) >= '0' && *(host) <= '9') || *(host) == '[')) + void app_RAND_load_conf(CONF *c, const char *section); int app_RAND_write(void); int app_RAND_load(void); @@ -285,6 +287,7 @@ int check_cert_attributes(BIO *bio, X509 *x, void store_setup_crl_download(X509_STORE *st); typedef struct app_http_tls_info_st { + const char *sni_hostname; const char *server; const char *port; int use_proxy; diff --git a/apps/lib/apps.c b/apps/lib/apps.c index a6278f6745..ddedef1fcd 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -2600,16 +2600,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); - const 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 @@ -2619,27 +2615,31 @@ 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 (!SSL_set_tlsext_host_name(ssl, info->sni_hostname /* may be NULL */)) + 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->sni_hostname); + OPENSSL_free((char *)info->server); + OPENSSL_free((char *)info->port); SSL_CTX_free(info->ssl_ctx); OPENSSL_free(info); } @@ -2677,6 +2677,7 @@ ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy, goto end; } + info.sni_hostname = server; info.server = server; info.port = port; info.use_proxy = /* workaround for callback design flaw, see #17088 */ @@ -2714,6 +2715,7 @@ ASN1_VALUE *app_http_post_asn1(const char *host, const char *port, if (req_mem == NULL) return NULL; + info.sni_hostname = host; info.server = host; info.port = port; info.use_proxy = /* workaround for callback design flaw, see #17088 */ diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in index f44615d1fc..0a796a79e0 100644 --- a/doc/man1/openssl-cmp.pod.in +++ b/doc/man1/openssl-cmp.pod.in @@ -115,7 +115,7 @@ TLS connection options: [B<-tls_keypass> I] [B<-tls_extra> I|I] [B<-tls_trusted> I|I] -[B<-tls_host> I] +[B<-tls_host> I
] Client-side debugging options: @@ -528,6 +528,7 @@ The I domain name or IP address and optionally I 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<]>. +For TLS connections the host part is taken as fallback for the B<-tls_host> option. This option excludes I<-port> and I<-use_mock_srv>. It is ignored if I<-rspin> is given with enough filename arguments. @@ -1029,11 +1030,12 @@ 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 +=item B<-tls_host> I
-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. +Address to be used for Server Name Indication (SNI) according to RFC 3546 section 3.1 +and to be checked during TLS hostname validation. +This may be a DNS name (for SNI, only this is used) or an IPv4 or IPv6 address. +If not given it defaults to the host part of the B<-server> option URL argument. =back From 3eb703db5661d03b8821387a2121fe57bbfec8fe Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Fri, 11 Apr 2025 20:58:48 +0200 Subject: [PATCH 5/7] fix OSSL_parse_url() going too far when scanning for end of userinfo '@' --- crypto/http/http_lib.c | 4 +-- test/http_test.c | 26 ++++++++++------- util/platform_symbols/windows-symbols.txt | 34 ++++++++++------------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/crypto/http/http_lib.c b/crypto/http/http_lib.c index fcf8a69e07..4267ecda6e 100644 --- a/crypto/http/http_lib.c +++ b/crypto/http/http_lib.c @@ -92,8 +92,8 @@ int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost, /* parse optional "userinfo@" */ user = user_end = host = p; - host = strchr(p, '@'); - if (host != NULL) + host = strpbrk(p, "@[]/?#"); /* [ and ] delimit IPv6 addresses */ + if (host != NULL && *host == '@') user_end = host++; else host = p; diff --git a/test/http_test.c b/test/http_test.c index 66463002ac..d4aca967b9 100644 --- a/test/http_test.c +++ b/test/http_test.c @@ -283,7 +283,7 @@ static int test_http_keep_alive(char version, int keep_alive, int kept_alive) } static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host, - const char *exp_port, const char *exp_path) + const char *exp_port, const char *exp_path, const char *exp_query) { char *user, *host, *port, *path, *query, *frag; int exp_num, num, ssl; @@ -302,8 +302,8 @@ static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host, res = TEST_str_eq(user, "user:pass"); if (res && *frag != '\0') res = TEST_str_eq(frag, "fr"); - if (res && *query != '\0') - res = TEST_str_eq(query, "q"); + if (res) + res = TEST_str_eq(query, exp_query); OPENSSL_free(user); OPENSSL_free(host); OPENSSL_free(port); @@ -329,17 +329,17 @@ static int test_http_url_path_query_ok(const char *url, const char *exp_path_qu) static int test_http_url_dns(void) { - return test_http_url_ok("host:65535/path", 0, "host", "65535", "/path"); + return test_http_url_ok("host:65535/path", 0, "host", "65535", "/path", ""); } static int test_http_url_timestamp(void) { return test_http_url_ok("host/p/2017-01-03T00:00:00", 0, "host", "80", - "/p/2017-01-03T00:00:00") + "/p/2017-01-03T00:00:00", "") && test_http_url_ok("http://host/p/2017-01-03T00:00:00", 0, "host", - "80", "/p/2017-01-03T00:00:00") + "80", "/p/2017-01-03T00:00:00", "") && test_http_url_ok("https://host/p/2017-01-03T00:00:00", 1, "host", - "443", "/p/2017-01-03T00:00:00"); + "443", "/p/2017-01-03T00:00:00", ""); } static int test_http_url_path_query(void) @@ -351,17 +351,22 @@ static int test_http_url_path_query(void) static int test_http_url_userinfo_query_fragment(void) { - return test_http_url_ok("user:pass@host/p?q#fr", 0, "host", "80", "/p"); + return test_http_url_ok("user:pass@host/p?q#fr", 0, "host", "80", "/p", "q"); +} + +static int test_http_url_query_including_at(void) +{ + return test_http_url_ok("https://host/p?q@z#fr", 1, "host", "443", "/p", "q@z"); } static int test_http_url_ipv4(void) { - return test_http_url_ok("https://1.2.3.4/p/q", 1, "1.2.3.4", "443", "/p/q"); + return test_http_url_ok("https://1.2.3.4/p/q", 1, "1.2.3.4", "443", "/p/q", ""); } static int test_http_url_ipv6(void) { - return test_http_url_ok("http://[FF01::101]:6", 0, "[FF01::101]", "6", "/"); + return test_http_url_ok("http://[FF01::101]:6", 0, "[FF01::101]", "6", "/", ""); } static int test_http_url_invalid(const char *url) @@ -572,6 +577,7 @@ int setup_tests(void) ADD_TEST(test_http_url_timestamp); ADD_TEST(test_http_url_path_query); ADD_TEST(test_http_url_userinfo_query_fragment); + ADD_TEST(test_http_url_query_including_at); ADD_TEST(test_http_url_ipv4); ADD_TEST(test_http_url_ipv6); ADD_TEST(test_http_url_invalid_prefix); diff --git a/util/platform_symbols/windows-symbols.txt b/util/platform_symbols/windows-symbols.txt index 69fb23bfc1..c4c68a99c2 100644 --- a/util/platform_symbols/windows-symbols.txt +++ b/util/platform_symbols/windows-symbols.txt @@ -86,11 +86,7 @@ __current_exception __C_specific_handler wcsstr __current_exception_context -strlen -strstr -strchr memmove -strrchr memcmp memset memcpy @@ -119,22 +115,27 @@ __stdio_common_vsprintf_s fwrite fgets _setmode -strtoul atoi -strtol tolower -strspn -strcspn -strncpy -strpbrk -strncmp -strcmp strcat_s +strchr +strcmp +strcpy_s +strcspn +strerror_s +strlen +strncmp +strncpy +strncpy_s +strpbrk +strrchr +strspn +strstr +strtol +strtoul isspace _strdup isdigit -strncpy_s -strcpy_s _gmtime64_s __timezone _mktime64 @@ -150,7 +151,6 @@ _initialize_narrow_environment _beginthreadex _endthreadex _register_onexit_function -strerror_s _execute_onexit_table raise _crt_atexit @@ -202,10 +202,8 @@ GetModuleHandleW memcpy memset __current_exception_context -strchr memcmp memchr -strstr memmove __std_type_info_destroy_list __current_exception @@ -226,8 +224,6 @@ _crt_at_quick_exit _cexit _beginthreadex _time64 -strncmp -strcmp qsort _stat64i32 atoi From cb285102e0a7b28a03f984e79bc59b72a21651a7 Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Mon, 21 Apr 2025 12:04:41 +0200 Subject: [PATCH 6/7] X509_VERIFY_PARAM_set_flags.pod: fix doc of NULL param to X509_VERIFY_PARAM_set1_email() and X509_VERIFY_PARAM_set1{,_ip}() --- doc/man3/X509_VERIFY_PARAM_set_flags.pod | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/man3/X509_VERIFY_PARAM_set_flags.pod b/doc/man3/X509_VERIFY_PARAM_set_flags.pod index 263b38ed54..1c6dbcce12 100644 --- a/doc/man3/X509_VERIFY_PARAM_set_flags.pod +++ b/doc/man3/X509_VERIFY_PARAM_set_flags.pod @@ -196,7 +196,9 @@ the return value. X509_VERIFY_PARAM_get0_email() returns the expected RFC822 email address. X509_VERIFY_PARAM_set1_email() sets the expected RFC822 email address to -I. If I is NUL-terminated, I may be zero, otherwise +I. +If I is NULL or the empty string, email checking is disabled. +If I is NUL-terminated, I may be zero, otherwise I must be set to the length of I. When an email address is specified, certificate verification automatically invokes L. @@ -205,13 +207,15 @@ X509_VERIFY_PARAM_get1_ip_asc() returns the expected IP address as a string. The caller is responsible for freeing it. X509_VERIFY_PARAM_set1_ip() sets the expected IP address to I. -The I argument is in binary format, in network byte-order and +If I is NULL or the empty string, IP address checking is disabled. Otherwise, +the I argument must be in binary format, in network byte-order and I must be set to 4 for IPv4 and 16 for IPv6. When an IP address is specified, certificate verification automatically invokes L. -X509_VERIFY_PARAM_set1_ip_asc() sets the expected IP address to -I. The I argument is a NUL-terminal ASCII string: +X509_VERIFY_PARAM_set1_ip_asc() sets the expected IP address to I. +If I is NULL or the empty string, IP address checking is disabled. Otherwise, +the I argument must be a NUL-terminated ASCII string: dotted decimal quad for IPv4 and colon-separated hexadecimal for IPv6. The condensed "::" notation is supported for IPv6 addresses. From 6350ca4bb668979acdfa3c280231a41fde867ef3 Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Sat, 19 Jul 2025 09:12:11 +0200 Subject: [PATCH 7/7] fixup! apps/cmp.c: fix use of SSL_set_tlsext_host_name() for SNI and X509_VERIFY_PARAM_set1_host() --- apps/cmp.c | 2 +- apps/include/apps.h | 7 ++++--- apps/lib/apps.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/apps/cmp.c b/apps/cmp.c index b8deffb79a..c27f397358 100644 --- a/apps/cmp.c +++ b/apps/cmp.c @@ -2308,7 +2308,7 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine) (void)OSSL_CMP_CTX_set_http_cb_arg(ctx, info); hostaddr = opt_tls_host != NULL ? opt_tls_host : host; info->ssl_ctx = setup_ssl_ctx(ctx, hostaddr, engine); - if (!IS_IP_ADDR(hostaddr)) { + if (!APP_is_IP_address(hostaddr)) { if (hostaddr == NULL) info->sni_hostname = NULL; else if ((info->sni_hostname = OPENSSL_strdup(hostaddr)) == NULL) diff --git a/apps/include/apps.h b/apps/include/apps.h index 25134c0b73..b8be5d5632 100644 --- a/apps/include/apps.h +++ b/apps/include/apps.h @@ -45,12 +45,9 @@ */ # define _UC(c) ((unsigned char)(c)) -# define IS_IP_ADDR(host) ((host) != NULL && ((*(host) >= '0' && *(host) <= '9') || *(host) == '[')) - void app_RAND_load_conf(CONF *c, const char *section); int app_RAND_write(void); int app_RAND_load(void); - extern char *default_config_file; /* may be "" */ extern BIO *bio_in; extern BIO *bio_out; @@ -286,6 +283,10 @@ int check_cert_attributes(BIO *bio, X509 *x, void store_setup_crl_download(X509_STORE *st); +# ifndef OPENSSL_NO_SOCK +int APP_is_IP_address(const char *host); +# endif + typedef struct app_http_tls_info_st { const char *sni_hostname; const char *server; diff --git a/apps/lib/apps.c b/apps/lib/apps.c index ddedef1fcd..f9eac5f269 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -54,6 +54,9 @@ #ifdef _WIN32 static int WIN32_rename(const char *from, const char *to); # define rename(from, to) WIN32_rename((from), (to)) +/* for getaddrinfo() and freeaddrinfo(): */ +# include +# include #endif #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) @@ -2551,6 +2554,31 @@ void store_setup_crl_download(X509_STORE *st) X509_STORE_set_lookup_crls_cb(st, crls_http_cb); } +#ifndef OPENSSL_NO_SOCK +int APP_is_IP_address(const char *host) +{ + size_t len; + struct addrinfo hints, *res; + int ret; + + if (host == NULL) + return 0; + + /* presume IPv6 address literal if host has the form "[]" */ + len = strlen(host); + if (len > 2 && *host == '[' && strchr(host + 1, '[') == NULL + && strchr(host + 1, ']') == host + len - 1) + return 1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + ret = getaddrinfo(host, NULL, &hints, &res); + if (res != NULL) + freeaddrinfo(res); + return ret == 0; +} +#endif + #if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP) static const char *tls_error_hint(void) {