Commit c7c8dea229 for openssl.org
commit c7c8dea22984bab394bc227fabfe1b9416df952f
Author: Bob Beck <beck@openssl.org>
Date: Thu Jan 29 11:31:40 2026 -0700
Deprecate the X509_check_{email,host,ip,ip_asc} family of functions
Our own documentation for quite some time has indicated
that you should call X509_verify_cert() instead of using these.
Actually deprecate them and make apps not use the now deprecated
functions.
Resolves: https://github.com/openssl/project/issues/1899
References: https://github.com/openssl/project/issues/1897
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
MergeDate: Mon May 11 00:08:33 2026
(Merged from https://github.com/openssl/openssl/pull/30403)
diff --git a/CHANGES.md b/CHANGES.md
index 94953f18bd..1dd27df4ba 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -80,6 +80,13 @@ OpenSSL Releases
*John Claus*
+* 'X509_check_host()', 'X509_check_email()', 'X509_check_ip()', and 'X509_check_ip_asc()'
+ have been deprecated. Applications should migrate to setting a reference identifier
+ to check using 'X509_VERIFY_PARAM_set1_host()', 'X509_VERIFY_PARAM_set1_email()', or
+ X509_VERIFY_PARAM_set1_ip_asc()', and using 'X509_verify_cert()'.
+
+ *Bob Beck*
+
* Added AVX2 optimized ML-DSA NTT operations on `x86_64`.
*Marcel Cornu and Tomasz Kantecki*
diff --git a/apps/include/apps.h b/apps/include/apps.h
index 43097c013e..97c6669e3a 100644
--- a/apps/include/apps.h
+++ b/apps/include/apps.h
@@ -317,9 +317,9 @@ extern char *psk_key;
unsigned char *next_protos_parse(size_t *outlen, const char *in);
-int check_cert_attributes(BIO *bio, X509 *x,
+int check_cert_might_be_valid(BIO *bio, BIO *bio_err, X509 *x,
const char *checkhost, const char *checkemail,
- const char *checkip, int print);
+ const char *checkip);
void store_setup_crl_download(X509_STORE *st);
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
index fe1bc7e812..467f36ae1f 100644
--- a/apps/lib/apps.c
+++ b/apps/lib/apps.c
@@ -86,6 +86,22 @@ int app_init(long mesgwin)
}
#endif
+static int maybe_printf(BIO *bio, const char *format, ...)
+{
+ va_list args;
+ int ret = 0;
+
+ if (bio != NULL) {
+ va_start(args, format);
+
+ ret = BIO_vprintf(bio, format, args);
+
+ va_end(args);
+ }
+
+ return ret;
+}
+
int ctx_set_verify_locations(SSL_CTX *ctx,
const char *CAfile, int noCAfile,
const char *CApath, int noCApath,
@@ -2447,42 +2463,132 @@ unsigned char *next_protos_parse(size_t *outlen, const char *in)
return out;
}
-int check_cert_attributes(BIO *bio, X509 *x, const char *checkhost,
- const char *checkemail, const char *checkip,
- int print)
+int check_cert_might_be_valid(BIO *bio, BIO *b_err, X509 *x, const char *checkhost,
+ const char *checkemail, const char *checkip)
{
- int valid_host = 0;
- int valid_mail = 0;
- int valid_ip = 0;
- int ret = 1;
+ int ret = 0;
+ int error;
+ X509_STORE_CTX *ctx = NULL;
+ X509_STORE *store = NULL;
+ X509_VERIFY_PARAM *vpm = NULL;
- if (x == NULL)
- return 0;
+ if (x == NULL) {
+ maybe_printf(b_err, "Internal error, NULL certificate\n");
+ goto err;
+ }
- if (checkhost != NULL) {
- valid_host = X509_check_host(x, checkhost, 0, 0, NULL);
- if (print)
- BIO_printf(bio, "Hostname %s does%s match certificate\n",
- checkhost, valid_host == 1 ? "" : " NOT");
- ret = ret && valid_host > 0;
+ if ((store = X509_STORE_new()) == NULL) {
+ maybe_printf(b_err, "Malloc failed or internal error\n");
+ goto err;
}
- if (checkemail != NULL) {
- valid_mail = X509_check_email(x, checkemail, 0, 0);
- if (print)
- BIO_printf(bio, "Email %s does%s match certificate\n",
- checkemail, valid_mail ? "" : " NOT");
- ret = ret && valid_mail > 0;
+ if (!X509_STORE_add_cert(store, x)) {
+ maybe_printf(b_err, "Malloc failed or internal error\n");
+ goto err;
}
- if (checkip != NULL) {
- valid_ip = X509_check_ip_asc(x, checkip, 0);
- if (print)
- BIO_printf(bio, "IP %s does%s match certificate\n",
- checkip, valid_ip ? "" : " NOT");
- ret = ret && valid_ip > 0;
+ if ((vpm = X509_STORE_get0_param(store)) == NULL) {
+ maybe_printf(b_err, "Malloc failed or internal error\n");
+ goto err;
+ }
+
+ if ((ctx = X509_STORE_CTX_new()) == NULL) {
+ maybe_printf(b_err, "Malloc failed or internal error\n");
+ goto err;
}
+ /*
+ * As this is "might verify":
+ *
+ * We don't care about the verification time.
+ * We are trusting ourselves.
+ * We are very liberal in what we allow.
+ *
+ * Needless to say these flags should normally not be used in a
+ * for real verification.
+ */
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_CHECK_TIME);
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_PARTIAL_CHAIN);
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_IGNORE_CRITICAL);
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_ALLOW_PROXY_CERTS);
+ X509_VERIFY_PARAM_set_trust(vpm, X509_TRUST_OK_ANY_EKU);
+
+ if (!X509_VERIFY_PARAM_set1_ip_asc(vpm, checkip)) {
+ maybe_printf(b_err, "Invalid IP address: %s\n", checkip);
+ goto err;
+ }
+
+ if (!X509_VERIFY_PARAM_set1_host(vpm, checkhost, 0)) {
+ maybe_printf(b_err, "Invalid host name: %s\n", checkhost);
+ goto err;
+ }
+
+ if (!X509_VERIFY_PARAM_set1_email(vpm, checkemail, 0)) {
+ maybe_printf(b_err, "Invalid email address: %s\n", checkemail);
+ goto err;
+ }
+
+ if (!X509_VERIFY_PARAM_set1_ip_asc(vpm, checkip)) {
+ maybe_printf(b_err, "Invalid IP address: %s\n", checkip);
+ goto err;
+ }
+
+ if (!X509_STORE_CTX_init(ctx, store, x, NULL)) {
+ maybe_printf(b_err, "Malloc failed or internal error\n");
+ goto err;
+ }
+
+ /* We might be verifying for ANY purpose ... */
+ if (!X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_ANY)) {
+ maybe_printf(b_err, "Malloc failed or internal error\n");
+ goto err;
+ }
+
+ ret = X509_verify_cert(ctx);
+ error = X509_STORE_CTX_get_error(ctx);
+ if (!ret) {
+
+ maybe_printf(bio, "Certificate may not verify: error %s\n",
+ X509_verify_cert_error_string(error));
+
+ if (checkhost != NULL && error == X509_V_ERR_HOSTNAME_MISMATCH)
+ maybe_printf(bio, "Hostname %s does NOT match certificate\n",
+ checkhost);
+ else if (checkemail != NULL && error == X509_V_ERR_EMAIL_MISMATCH)
+ maybe_printf(bio, "Email %s does NOT match certificate\n",
+ checkemail);
+ else if (checkip != NULL && error == X509_V_ERR_IP_ADDRESS_MISMATCH)
+ maybe_printf(bio, "IP %s does NOT match certificate\n",
+ checkip);
+ else {
+ /* Originally, we only cared about the above failures */
+ /*
+ * Suppress trust rejection errors, we don't care, because
+ * we don't really know what this might be used for if
+ * this was for real.
+ */
+ if (error == X509_V_ERR_CERT_REJECTED) {
+ maybe_printf(bio, "Ignoring certificate rejection error\n");
+ ret = 1;
+ }
+ }
+ } else {
+ if (checkhost != NULL)
+ maybe_printf(bio, "Hostname %s does match certificate\n",
+ checkhost);
+ if (checkemail != NULL)
+ maybe_printf(bio, "Email %s does match certificate\n",
+ checkemail);
+ if (checkip != NULL)
+ maybe_printf(bio, "IP %s does match certificate\n",
+ checkip);
+ }
+ X509_STORE_CTX_cleanup(ctx);
+
+err:
+ ERR_print_errors(b_err);
+ X509_STORE_free(store);
+ X509_STORE_CTX_free(ctx);
return ret;
}
diff --git a/apps/s_server.c b/apps/s_server.c
index bcb26cb285..e8f431cd0a 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -617,7 +617,8 @@ static int ssl_ech_servername_cb(SSL *s, int *ad, void *arg)
return SSL_TLSEXT_ERR_NOACK;
if (echrv == SSL_ECH_STATUS_SUCCESS && servername != NULL) {
if (ctx2 != NULL) {
- int check_host = X509_check_host(p->scert, servername, 0, 0, NULL);
+ int check_host = check_cert_might_be_valid(p->biodebug,
+ p->biodebug, p->scert, servername, NULL, NULL);
if (check_host == 1) {
if (p->biodebug != NULL)
diff --git a/apps/x509.c b/apps/x509.c
index 824daa1ef5..867961e61d 100644
--- a/apps/x509.c
+++ b/apps/x509.c
@@ -1233,7 +1233,8 @@ cert_loop:
goto end;
}
- if (!check_cert_attributes(out, x, checkhost, checkemail, checkip, 1))
+ if (!check_cert_might_be_valid(out, bio_err, x, checkhost, checkemail,
+ checkip))
goto err;
if (noout || nocert) {
diff --git a/crypto/x509/v3_utl.c b/crypto/x509/v3_utl.c
index 774d3790e2..4ccfcacadb 100644
--- a/crypto/x509/v3_utl.c
+++ b/crypto/x509/v3_utl.c
@@ -1001,7 +1001,7 @@ static int do_x509_check(const X509 *x, const char *chk, size_t chklen,
return 0;
}
-int X509_check_host(const X509 *x, const char *chk, size_t chklen,
+int ossl_x509_check_host(const X509 *x, const char *chk, size_t chklen,
unsigned int flags, char **peername)
{
if (chk == NULL)
@@ -1020,6 +1020,14 @@ int X509_check_host(const X509 *x, const char *chk, size_t chklen,
return do_x509_check(x, chk, chklen, flags, GEN_DNS, 0, peername);
}
+#if !defined(OPENSSL_NO_DEPRECATED_4_1)
+int X509_check_host(const X509 *x, const char *chk, size_t chklen,
+ unsigned int flags, char **peername)
+{
+ return ossl_x509_check_host(x, chk, chklen, flags, peername);
+}
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */
+
int ossl_x509_check_rfc822(X509 *x, const char *chk, size_t chklen,
unsigned int flags)
{
@@ -1034,6 +1042,15 @@ int ossl_x509_check_smtputf8(X509 *x, const char *chk, size_t chklen,
== 1;
}
+int ossl_x509_check_ip(const X509 *x, const unsigned char *chk, size_t chklen,
+ unsigned int flags)
+{
+ if (chk == NULL)
+ return -2;
+ return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, 0, NULL);
+}
+
+#if !defined(OPENSSL_NO_DEPRECATED_4_1)
int X509_check_email(const X509 *x, const char *chk, size_t chklen,
unsigned int flags)
{
@@ -1063,9 +1080,7 @@ int X509_check_email(const X509 *x, const char *chk, size_t chklen,
int X509_check_ip(const X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags)
{
- if (chk == NULL)
- return -2;
- return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, 0, NULL);
+ return ossl_x509_check_ip(x, chk, chklen, flags);
}
int X509_check_ip_asc(const X509 *x, const char *ipasc, unsigned int flags)
@@ -1080,6 +1095,7 @@ int X509_check_ip_asc(const X509 *x, const char *ipasc, unsigned int flags)
return -2;
return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, 0, NULL);
}
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */
char *ossl_ipaddr_to_asc(const unsigned char *p, int len)
{
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index 3ec7f69104..27ea9921cd 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -939,7 +939,9 @@ static int check_hosts(X509 *x, X509_VERIFY_PARAM *vpm)
for (int i = 0; i < n; ++i) {
size_t len = sk_X509_BUFFER_value(vpm->hosts, i)->len;
name = sk_X509_BUFFER_value(vpm->hosts, i)->data;
- if (X509_check_host(x, (const char *)name, len, vpm->hostflags, &vpm->peername) > 0)
+ if (ossl_x509_check_host(x, (const char *)name, len, vpm->hostflags,
+ &vpm->peername)
+ > 0)
return 1;
}
return n <= 0;
@@ -974,7 +976,7 @@ static int check_ips(X509 *x, X509_VERIFY_PARAM *vpm)
for (int i = 0; i < n; ++i) {
size_t len = sk_X509_BUFFER_value(vpm->ips, i)->len;
name = sk_X509_BUFFER_value(vpm->ips, i)->data;
- if (X509_check_ip(x, name, len, vpm->hostflags) > 0)
+ if (ossl_x509_check_ip(x, name, len, vpm->hostflags) > 0)
return 1;
}
return n <= 0;
diff --git a/doc/man1/openssl-x509.pod.in b/doc/man1/openssl-x509.pod.in
index 4309c763fa..cbb2ab29e2 100644
--- a/doc/man1/openssl-x509.pod.in
+++ b/doc/man1/openssl-x509.pod.in
@@ -379,6 +379,15 @@ Check that the certificate matches the specified email address.
Check that the certificate matches the specified IP address.
+Certificate checking is done with X.509 certificate verification,
+which will fail when encountering an error. As such, when combining
+the B<-checkhost>, B<-checkemail>, and B<-checkip> flags, verify will
+indicate success if all checks pass, and will indicate failure if they
+do not. A diagnostic message of only the first encountered failed
+check that stops certificate verification will be printed in the
+failing case. If a specific diagnostic message is needed for
+individual checks they should be tried individually.
+
=back
=head2 Certificate Output Options
diff --git a/doc/man3/X509_check_host.pod b/doc/man3/X509_check_host.pod
index 42d0baa777..6b095f9582 100644
--- a/doc/man3/X509_check_host.pod
+++ b/doc/man3/X509_check_host.pod
@@ -134,11 +134,11 @@ NULs.
=head1 NOTES
-Applications are encouraged to use X509_VERIFY_PARAM_set1_host()
-rather than explicitly calling L<X509_check_host(3)>. Hostname
-checks may be out of scope with the DANE-EE(3) certificate usage,
-and the internal checks will be suppressed as appropriate when
-DANE support is enabled.
+Applications should use X509_VERIFY_PARAM_set1_host() and
+X509_verify_cert() rather than explicitly calling
+L<X509_check_host(3)>. Hostname checks may be out of scope with the
+DANE-EE(3) certificate usage, and the internal checks will be
+suppressed as appropriate when DANE support is enabled.
=head1 SEE ALSO
@@ -146,12 +146,15 @@ L<SSL_get_verify_result(3)>,
L<X509_VERIFY_PARAM_set1_host(3)>,
L<X509_VERIFY_PARAM_add1_host(3)>,
L<X509_VERIFY_PARAM_set1_email(3)>,
-L<X509_VERIFY_PARAM_set1_ip(3)>
+L<X509_VERIFY_PARAM_set1_ip(3)>,
+L<X509_verify_cert(3)>
=head1 HISTORY
These functions were added in OpenSSL 1.0.2.
+These functions were deprecated in OpenSSL 4.1.0.
+
=head1 COPYRIGHT
Copyright 2012-2026 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index 06ec5ecf45..4925131180 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -408,5 +408,9 @@ int ossl_x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth);
int ossl_x509_check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify);
int ossl_posix_to_asn1_time(int64_t posix_time, ASN1_TIME **out_time);
void ossl_x509_verify_param_set_time_posix(X509_VERIFY_PARAM *param, int64_t t);
+int ossl_x509_check_host(const X509 *x, const char *chk, size_t chklen,
+ unsigned int flags, char **peername);
+int ossl_x509_check_ip(const X509 *x, const unsigned char *chk, size_t chklen,
+ unsigned int flags);
#endif /* OSSL_CRYPTO_X509_H */
diff --git a/include/openssl/x509v3.h.in b/include/openssl/x509v3.h.in
index 16e40f5379..8e50d31144 100644
--- a/include/openssl/x509v3.h.in
+++ b/include/openssl/x509v3.h.in
@@ -814,13 +814,15 @@ STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(const X509 *x);
*/
#define _X509_CHECK_FLAG_DOT_SUBDOMAINS 0x8000
-int X509_check_host(const X509 *x, const char *chk, size_t chklen,
+#if !defined(OPENSSL_NO_DEPRECATED_4_1)
+OSSL_DEPRECATEDIN_4_1 int X509_check_host(const X509 *x, const char *chk, size_t chklen,
unsigned int flags, char **peername);
-int X509_check_email(const X509 *x, const char *chk, size_t chklen,
+OSSL_DEPRECATEDIN_4_1 int X509_check_email(const X509 *x, const char *chk, size_t chklen,
unsigned int flags);
-int X509_check_ip(const X509 *x, const unsigned char *chk, size_t chklen,
+OSSL_DEPRECATEDIN_4_1 int X509_check_ip(const X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags);
-int X509_check_ip_asc(const X509 *x, const char *ipasc, unsigned int flags);
+OSSL_DEPRECATEDIN_4_1 int X509_check_ip_asc(const X509 *x, const char *ipasc, unsigned int flags);
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */
ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc);
ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc);
diff --git a/test/v3nametest.c b/test/v3nametest.c
index c73067d947..8ae7a9e54f 100644
--- a/test/v3nametest.c
+++ b/test/v3nametest.c
@@ -10,11 +10,14 @@
#include <string.h>
#include <openssl/e_os2.h>
+#include <openssl/macros.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include "internal/nelem.h"
#include "testutil.h"
+#if !defined(OPENSSL_NO_DEPRECATED_4_1)
+OSSL_BEGIN_ALLOW_DEPRECATED
static const char *const names[] = {
"a", "b", ".", "*", "@",
".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..",
@@ -227,6 +230,8 @@ static int set_altname_email(X509 *crt, const char *name)
{
return set_altname(crt, GEN_EMAIL, name, 0);
}
+OSSL_END_ALLOW_DEPRECATED
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */
struct set_name_fn {
int (*fn)(X509 *, const char *);
@@ -235,6 +240,8 @@ struct set_name_fn {
int email;
};
+#if !defined(OPENSSL_NO_DEPRECATED_4_1)
+OSSL_BEGIN_ALLOW_DEPRECATED
static const struct set_name_fn name_fns[] = {
{ set_cn1, "set CN", 1, 0 },
{ set_cn2, "set CN", 1, 0 },
@@ -358,6 +365,8 @@ static int call_run_cert(int i)
}
return failed == 0;
}
+OSSL_END_ALLOW_DEPRECATED
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */
static struct gennamedata {
const unsigned char der[22];
@@ -657,7 +666,9 @@ end:
int setup_tests(void)
{
+#if !defined(OPENSSL_NO_DEPRECATED_4_1)
ADD_ALL_TESTS(call_run_cert, OSSL_NELEM(name_fns));
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */
ADD_TEST(test_GENERAL_NAME_cmp);
return 1;
}
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 4e6ac0563c..b0d0e7266c 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5439,10 +5439,10 @@ X509_get1_email 5436 4_0_0 EXIST::FUNCTION:
X509_REQ_get1_email 5437 4_0_0 EXIST::FUNCTION:
X509_email_free 5438 4_0_0 EXIST::FUNCTION:
X509_get1_ocsp 5439 4_0_0 EXIST::FUNCTION:
-X509_check_host 5440 4_0_0 EXIST::FUNCTION:
-X509_check_email 5441 4_0_0 EXIST::FUNCTION:
-X509_check_ip 5442 4_0_0 EXIST::FUNCTION:
-X509_check_ip_asc 5443 4_0_0 EXIST::FUNCTION:
+X509_check_host 5440 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1
+X509_check_email 5441 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1
+X509_check_ip 5442 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1
+X509_check_ip_asc 5443 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1
a2i_IPADDRESS 5444 4_0_0 EXIST::FUNCTION:
a2i_IPADDRESS_NC 5445 4_0_0 EXIST::FUNCTION:
X509V3_NAME_from_section 5446 4_0_0 EXIST::FUNCTION: