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);