From: Dr. David von Oheimb Date: Mon, 21 Apr 2025 13:11:01 +0000 (+0200) Subject: Fix SSL_{set1,add1}_host() handling of host name/IP address and related documentation X-Git-Tag: openssl-3.4.2~76 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0080dd66e0fa820abd23ecb18d92b2899fff425d;p=thirdparty%2Fopenssl.git Fix SSL_{set1,add1}_host() handling of host name/IP address and related documentation Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/27457) (cherry picked from commit 1eee02d3e710e01d864c37708f64e83511627e28) --- diff --git a/doc/man3/SSL_set1_host.pod b/doc/man3/SSL_set1_host.pod index fd16a943198..65689224fb9 100644 --- a/doc/man3/SSL_set1_host.pod +++ b/doc/man3/SSL_set1_host.pod @@ -9,8 +9,8 @@ SSL server verification parameters #include - int SSL_set1_host(SSL *s, const char *hostname); - int SSL_add1_host(SSL *s, const char *hostname); + int SSL_set1_host(SSL *s, const char *host); + int SSL_add1_host(SSL *s, const char *host); void SSL_set_hostflags(SSL *s, unsigned int flags); const char *SSL_get0_peername(SSL *s); @@ -18,34 +18,36 @@ SSL server verification parameters These functions configure server hostname checks in the SSL client. -SSL_set1_host() sets the expected DNS hostname to B clearing -any previously specified hostname. If B is NULL -or the empty string, the list of hostnames is cleared and name -checks are not performed on the peer certificate. When a nonempty -B is specified, certificate verification automatically checks -the peer hostname via L with B as specified +SSL_set1_host() sets in the verification parameters of I +the expected DNS hostname or IP address to I, +clearing any previously specified IP address and hostnames. +If I is NULL or the empty string, IP address +and hostname checks are not performed on the peer certificate. +When a nonempty I is specified, certificate verification automatically +checks the peer hostname via L with I as specified via SSL_set_hostflags(). Clients that enable DANE TLSA authentication via L should leave it to that function to set the primary reference identifier of the peer, and should not call SSL_set1_host(). -SSL_add1_host() adds B as an additional reference identifier -that can match the peer's certificate. Any previous names set via -SSL_set1_host() or SSL_add1_host() are retained, no change is made -if B is NULL or empty. When multiple names are configured, -the peer is considered verified when any name matches. This function -is required for DANE TLSA in the presence of service name indirection -via CNAME, MX or SRV records as specified in RFC7671, RFC7672 or -RFC7673. +SSL_add1_host() adds I as an additional reference identifier +that can match the peer's certificate. Any previous hostnames +set via SSL_set1_host() or SSL_add1_host() are retained. +Adding an IP address is allowed only if no IP address has been set before. +No change is made if I is NULL or empty. +When an IP address and/or multiple hostnames are configured, +the peer is considered verified when any of these matches. +This function is required for DANE TLSA in the presence of service name indirection +via CNAME, MX or SRV records as specified in RFCs 7671, 7672, and 7673. TLS clients are recommended to use SSL_set1_host() or SSL_add1_host() for server hostname or IP address validation, as well as L for Server Name Indication (SNI), which may be crucial also for correct routing of the connection request. -SSL_set_hostflags() sets the B that will be passed to +SSL_set_hostflags() sets the I that will be passed to L when name checks are applicable, by default -the B value is 0. See L for the list +the I value is 0. See L for the list of available flags and their meaning. SSL_get0_peername() returns the DNS hostname or subject CommonName @@ -56,13 +58,13 @@ of the reference identifiers configured via SSL_set1_host() or SSL_add1_host() starts with ".", which indicates a parent domain prefix rather than a fixed name, the matched peer name may be a sub-domain of the reference identifier. The returned string is allocated by -the library and is no longer valid once the associated B handle +the library and is no longer valid once the associated I handle is cleared or freed, or a renegotiation takes place. Applications must not free the return value. SSL clients are advised to use these functions in preference to explicitly calling L. Hostname checks may be out -of scope with the RFC7671 DANE-EE(3) certificate usage, and the +of scope with the RFC 7671 DANE-EE(3) certificate usage, and the internal check will be suppressed as appropriate when DANE is enabled. @@ -71,8 +73,10 @@ enabled. SSL_set1_host() and SSL_add1_host() return 1 for success and 0 for failure. +SSL_set_hostflags() returns nothing at all. + SSL_get0_peername() returns NULL if peername verification is not -applicable (as with RFC7671 DANE-EE(3)), or no trusted peername was +applicable (as with RFC 7671 DANE-EE(3)), or no trusted peername was matched. Otherwise, it returns the matched peername. To determine whether verification succeeded call L. @@ -105,8 +109,7 @@ the lifetime of the SSL connection. L, L, L, -L. -L. +L, L =head1 HISTORY diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index 4bab2ac767f..0530a19b588 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -1821,8 +1821,8 @@ __owur int SSL_set_purpose(SSL *ssl, int purpose); __owur int SSL_CTX_set_trust(SSL_CTX *ctx, int trust); __owur int SSL_set_trust(SSL *ssl, int trust); -__owur int SSL_set1_host(SSL *s, const char *hostname); -__owur int SSL_add1_host(SSL *s, const char *hostname); +__owur int SSL_set1_host(SSL *s, const char *host); +__owur int SSL_add1_host(SSL *s, const char *host); __owur const char *SSL_get0_peername(SSL *s); void SSL_set_hostflags(SSL *s, unsigned int flags); diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 2c0d57e04a6..2430b5e7ae0 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1095,52 +1095,55 @@ int SSL_set_trust(SSL *s, int trust) return X509_VERIFY_PARAM_set_trust(sc->param, trust); } -int SSL_set1_host(SSL *s, const char *hostname) +int SSL_set1_host(SSL *s, const char *host) { SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s); if (sc == NULL) return 0; - /* If a hostname is provided and parses as an IP address, - * treat it as such. */ - if (hostname != NULL - && X509_VERIFY_PARAM_set1_ip_asc(sc->param, hostname) == 1) + /* clear hostname(s) and IP address in any case, also if host parses as an IP address */ + (void)X509_VERIFY_PARAM_set1_host(sc->param, NULL, 0); + (void)X509_VERIFY_PARAM_set1_ip(sc->param, NULL, 0); + if (host == NULL) return 1; - return X509_VERIFY_PARAM_set1_host(sc->param, hostname, 0); + /* If a host is provided and parses as an IP address, treat it as such. */ + return X509_VERIFY_PARAM_set1_ip_asc(sc->param, host) + || X509_VERIFY_PARAM_set1_host(sc->param, host, 0); } -int SSL_add1_host(SSL *s, const char *hostname) +int SSL_add1_host(SSL *s, const char *host) { SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s); if (sc == NULL) return 0; - /* If a hostname is provided and parses as an IP address, - * treat it as such. */ - if (hostname) { + /* If a host is provided and parses as an IP address, treat it as such. */ + if (host != NULL) { ASN1_OCTET_STRING *ip; char *old_ip; - ip = a2i_IPADDRESS(hostname); - if (ip) { + ip = a2i_IPADDRESS(host); + if (ip != NULL) { /* We didn't want it; only to check if it *is* an IP address */ ASN1_OCTET_STRING_free(ip); old_ip = X509_VERIFY_PARAM_get1_ip_asc(sc->param); - if (old_ip) { + if (old_ip != NULL) { OPENSSL_free(old_ip); /* There can be only one IP address */ + ERR_raise_data(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT, + "IP address was already set"); return 0; } - return X509_VERIFY_PARAM_set1_ip_asc(sc->param, hostname); + return X509_VERIFY_PARAM_set1_ip_asc(sc->param, host); } } - return X509_VERIFY_PARAM_add1_host(sc->param, hostname, 0); + return X509_VERIFY_PARAM_add1_host(sc->param, host, 0); } void SSL_set_hostflags(SSL *s, unsigned int flags)