Commit 34340878d0 for openssl.org
commit 34340878d0f8c76cb2750ff71afece56c73bf483
Author: Bob Beck <beck@openssl.org>
Date: Tue Feb 17 14:28:01 2026 -0700
Constify various functions that were non const due to extension cache
for https://github.com/openssl/openssl/issues/30052
This is a blatent cheat. While I can get pretty close to getting
around cheating by cacheing extensions as X509 objects are created it's
too fragile at the moment. In a future with a better not-copying all
the things X509, we would endeavour to not need this.
In the meantime, in the interest of getting the public API ready to
do that, we instead make a blatent cheat in the internal function of
int ossl_x509v3_cache_extensions(const X509 *x);
Which in a future world we can work to make go away.
And then the public API all changes to const.
long X509_get_pathlen(const X509 *x);
int X509_check_ca(const X509 *x);
int X509_check_purpose(const X509 *x, int id, int ca);
long X509_get_proxy_pathlen(const X509 *x);
uint32_t X509_get_extension_flags(const X509 *x);
uint32_t X509_get_key_usage(const X509 *x);
uint32_t X509_get_extended_key_usage(const X509 *x);
onst ASN1_OCTET_STRING *X509_get0_subject_key_id(const X509 *x);
const ASN1_OCTET_STRING *X509_get0_authority_key_id(const X509 *x);
const GENERAL_NAMES *X509_get0_authority_issuer(const X509 *x);
const ASN1_INTEGER *X509_get0_authority_serial(const X509 *x);
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
MergeDate: Mon Feb 23 16:34:29 2026
(Merged from https://github.com/openssl/openssl/pull/30055)
diff --git a/CHANGES.md b/CHANGES.md
index 147e13149d..7d1ec71221 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -252,6 +252,14 @@ OpenSSL 4.0
*Ryan Hooper*
+ * Constify Various X509 functions:
+ X509_get_pathlen X509_check_ca X509_check_purpose X509_get_proxy_pathlen
+ X509_get_extension_flags X509_get_key_usage X509_get_extended_key_usage
+ X509_get0_subject_key_id X509_get0_authority_key_id X509_get0_authority_issuer
+ X509_get0_authority_serial.
+
+ * Bob Beck *
+
* Fixed CRLs with invalid `ASN1_TIME` in invalidityDate extensions,
where verification incorrectly succeeded. Enforced proper
handling of `ASN1_TIME` validation results so that any CRL
diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c
index a1eb581936..016fd5a606 100644
--- a/crypto/x509/v3_purp.c
+++ b/crypto/x509/v3_purp.c
@@ -79,12 +79,6 @@ static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b)
return (*a)->purpose - (*b)->purpose;
}
-/*
- * As much as I'd like to make X509_check_purpose use a "const" X509* I really
- * can't because it does recalculate hashes and do other non-const things.
- * If id == -1 it just calls x509v3_cache_extensions() for its side-effect.
- * Returns 1 on success, 0 if x does not allow purpose, -1 on (internal) error.
- */
int X509_check_purpose(const X509 *x, int id, int non_leaf)
{
int idx;
@@ -440,8 +434,13 @@ static int check_sig_alg_match(const EVP_PKEY *issuer_key, const X509 *subject)
* x->sha1_hash is filled in, or else EXFLAG_NO_FINGERPRINT is set in x->flags.
* X509_SIG_INFO_VALID is set in x->flags if x->siginf was filled successfully.
* Set EXFLAG_INVALID and return 0 in case the certificate is invalid.
+ *
+ * This is usually called by side-effect on objects, and forces us to keep
+ * mutable X509 objects around. We should really make this go away.
+ * In the interest of being able to do so, this function explicitly takes
+ * a const argument and casts away const.
*/
-int ossl_x509v3_cache_extensions(X509 *x)
+int ossl_x509v3_cache_extensions(const X509 *const_x)
{
BASIC_CONSTRAINTS *bs;
PROXY_CERT_INFO_EXTENSION *pci;
@@ -450,6 +449,16 @@ int ossl_x509v3_cache_extensions(X509 *x)
EXTENDED_KEY_USAGE *extusage;
int i;
int res;
+ X509 *x;
+
+ /*
+ * XXX deliberately cast away const - this is so the
+ * public API may be made const even though we are lying
+ * about it for the moment. This will enable us
+ * to move to where we do not have to cast this away
+ * in the future
+ */
+ x = (X509 *)const_x;
#ifdef tsan_ld_acq
/* Fast lock-free check, see end of the function for details. */
@@ -733,7 +742,7 @@ void X509_set_proxy_pathlen(X509 *x, long l)
x->ex_pcpathlen = l;
}
-int X509_check_ca(X509 *x)
+int X509_check_ca(const X509 *x)
{
/* Note 0 normally means "not a CA" - but in this case means error. */
if (!ossl_x509v3_cache_extensions(x))
@@ -1077,14 +1086,14 @@ int X509_check_akid(const X509 *issuer, const AUTHORITY_KEYID *akid)
return X509_V_OK;
}
-uint32_t X509_get_extension_flags(X509 *x)
+uint32_t X509_get_extension_flags(const X509 *x)
{
/* Call for side-effect of computing hash and caching extensions */
X509_check_purpose(x, -1, 0);
return x->ex_flags;
}
-uint32_t X509_get_key_usage(X509 *x)
+uint32_t X509_get_key_usage(const X509 *x)
{
/* Call for side-effect of computing hash and caching extensions */
if (X509_check_purpose(x, -1, 0) != 1)
@@ -1092,7 +1101,7 @@ uint32_t X509_get_key_usage(X509 *x)
return (x->ex_flags & EXFLAG_KUSAGE) != 0 ? x->ex_kusage : UINT32_MAX;
}
-uint32_t X509_get_extended_key_usage(X509 *x)
+uint32_t X509_get_extended_key_usage(const X509 *x)
{
/* Call for side-effect of computing hash and caching extensions */
if (X509_check_purpose(x, -1, 0) != 1)
@@ -1100,7 +1109,7 @@ uint32_t X509_get_extended_key_usage(X509 *x)
return (x->ex_flags & EXFLAG_XKUSAGE) != 0 ? x->ex_xkusage : UINT32_MAX;
}
-const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x)
+const ASN1_OCTET_STRING *X509_get0_subject_key_id(const X509 *x)
{
/* Call for side-effect of computing hash and caching extensions */
if (X509_check_purpose(x, -1, 0) != 1)
@@ -1108,7 +1117,7 @@ const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x)
return x->skid;
}
-const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x)
+const ASN1_OCTET_STRING *X509_get0_authority_key_id(const X509 *x)
{
/* Call for side-effect of computing hash and caching extensions */
if (X509_check_purpose(x, -1, 0) != 1)
@@ -1116,7 +1125,7 @@ const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x)
return (x->akid != NULL ? x->akid->keyid : NULL);
}
-const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x)
+const GENERAL_NAMES *X509_get0_authority_issuer(const X509 *x)
{
/* Call for side-effect of computing hash and caching extensions */
if (X509_check_purpose(x, -1, 0) != 1)
@@ -1124,7 +1133,7 @@ const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x)
return (x->akid != NULL ? x->akid->issuer : NULL);
}
-const ASN1_INTEGER *X509_get0_authority_serial(X509 *x)
+const ASN1_INTEGER *X509_get0_authority_serial(const X509 *x)
{
/* Call for side-effect of computing hash and caching extensions */
if (X509_check_purpose(x, -1, 0) != 1)
@@ -1132,7 +1141,7 @@ const ASN1_INTEGER *X509_get0_authority_serial(X509 *x)
return (x->akid != NULL ? x->akid->serial : NULL);
}
-long X509_get_pathlen(X509 *x)
+long X509_get_pathlen(const X509 *x)
{
/* Called for side effect of caching extensions */
if (X509_check_purpose(x, -1, 0) != 1
@@ -1141,7 +1150,7 @@ long X509_get_pathlen(X509 *x)
return x->ex_pathlen;
}
-long X509_get_proxy_pathlen(X509 *x)
+long X509_get_proxy_pathlen(const X509 *x)
{
/* Called for side effect of caching extensions */
if (X509_check_purpose(x, -1, 0) != 1
diff --git a/doc/man3/X509_check_ca.pod b/doc/man3/X509_check_ca.pod
index 91eba44f41..496eff2cb7 100644
--- a/doc/man3/X509_check_ca.pod
+++ b/doc/man3/X509_check_ca.pod
@@ -8,7 +8,7 @@ X509_check_ca - check if given certificate is CA certificate
#include <openssl/x509v3.h>
- int X509_check_ca(X509 *cert);
+ int X509_check_ca(const X509 *cert);
=head1 DESCRIPTION
diff --git a/doc/man3/X509_get_extension_flags.pod b/doc/man3/X509_get_extension_flags.pod
index 054eab5a26..c2a5ac0e7f 100644
--- a/doc/man3/X509_get_extension_flags.pod
+++ b/doc/man3/X509_get_extension_flags.pod
@@ -18,17 +18,17 @@ X509_get_proxy_pathlen - retrieve certificate extension data
#include <openssl/x509v3.h>
- long X509_get_pathlen(X509 *x);
- uint32_t X509_get_extension_flags(X509 *x);
- uint32_t X509_get_key_usage(X509 *x);
- uint32_t X509_get_extended_key_usage(X509 *x);
- const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x);
- const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x);
- const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x);
- const ASN1_INTEGER *X509_get0_authority_serial(X509 *x);
+ long X509_get_pathlen(const X509 *x);
+ uint32_t X509_get_extension_flags(const X509 *x);
+ uint32_t X509_get_key_usage(const X509 *x);
+ uint32_t X509_get_extended_key_usage(const X509 *x);
+ const ASN1_OCTET_STRING *X509_get0_subject_key_id(const X509 *x);
+ const ASN1_OCTET_STRING *X509_get0_authority_key_id(const X509 *x);
+ const GENERAL_NAMES *X509_get0_authority_issuer(const X509 *x);
+ const ASN1_INTEGER *X509_get0_authority_serial(const X509 *x);
void X509_set_proxy_flag(X509 *x);
void X509_set_proxy_pathlen(int l);
- long X509_get_proxy_pathlen(X509 *x);
+ long X509_get_proxy_pathlen(const X509 *x);
=head1 DESCRIPTION
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index ff073e2479..a2f3a24391 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -313,7 +313,7 @@ struct x509_object_st {
int ossl_a2i_ipadd(unsigned char *ipout, const char *ipasc);
int ossl_x509_set1_time(int *modified, ASN1_TIME **ptm, const ASN1_TIME *tm);
int ossl_x509_print_ex_brief(BIO *bio, X509 *cert, unsigned long neg_cflags);
-int ossl_x509v3_cache_extensions(X509 *x);
+int ossl_x509v3_cache_extensions(const X509 *x);
int ossl_x509_init_sig_info(X509 *x);
int ossl_x509_set0_libctx(X509 *x, OSSL_LIB_CTX *libctx, const char *propq);
diff --git a/include/openssl/x509.h.in b/include/openssl/x509.h.in
index 2f13b025bf..1846c9b688 100644
--- a/include/openssl/x509.h.in
+++ b/include/openssl/x509.h.in
@@ -530,7 +530,7 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey);
EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key);
EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key);
int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain);
-long X509_get_pathlen(X509 *x);
+long X509_get_pathlen(const X509 *x);
DECLARE_ASN1_ENCODE_FUNCTIONS_only(EVP_PKEY, PUBKEY)
EVP_PKEY *d2i_PUBKEY_ex(EVP_PKEY **a, const unsigned char **pp, long length,
OSSL_LIB_CTX *libctx, const char *propq);
diff --git a/include/openssl/x509v3.h.in b/include/openssl/x509v3.h.in
index de75440060..1cc66047dd 100644
--- a/include/openssl/x509v3.h.in
+++ b/include/openssl/x509v3.h.in
@@ -744,22 +744,22 @@ int X509V3_extensions_print(BIO *out, const char *title,
const STACK_OF(X509_EXTENSION) *exts,
unsigned long flag, int indent);
-int X509_check_ca(X509 *x);
+int X509_check_ca(const X509 *x);
int X509_check_purpose(const X509 *x, int id, int ca);
int X509_supported_extension(X509_EXTENSION *ex);
int X509_check_issued(X509 *issuer, X509 *subject);
int X509_check_akid(const X509 *issuer, const AUTHORITY_KEYID *akid);
void X509_set_proxy_flag(X509 *x);
void X509_set_proxy_pathlen(X509 *x, long l);
-long X509_get_proxy_pathlen(X509 *x);
-
-uint32_t X509_get_extension_flags(X509 *x);
-uint32_t X509_get_key_usage(X509 *x);
-uint32_t X509_get_extended_key_usage(X509 *x);
-const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x);
-const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x);
-const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x);
-const ASN1_INTEGER *X509_get0_authority_serial(X509 *x);
+long X509_get_proxy_pathlen(const X509 *x);
+
+uint32_t X509_get_extension_flags(const X509 *x);
+uint32_t X509_get_key_usage(const X509 *x);
+uint32_t X509_get_extended_key_usage(const X509 *x);
+const ASN1_OCTET_STRING *X509_get0_subject_key_id(const X509 *x);
+const ASN1_OCTET_STRING *X509_get0_authority_key_id(const X509 *x);
+const GENERAL_NAMES *X509_get0_authority_issuer(const X509 *x);
+const ASN1_INTEGER *X509_get0_authority_serial(const X509 *x);
int X509_PURPOSE_get_count(void);
int X509_PURPOSE_get_unused_id(OSSL_LIB_CTX *libctx);