Commit 57e38e59a3 for openssl.org

commit 57e38e59a30a92e2ffba37607b771a5ee12980bd
Author: Bob Beck <beck@openssl.org>
Date:   Sat Sep 5 13:17:08 2026 -0600

    Mark the encoding stale when adding a CRL extension

    X509_CRL_add_ext() and X509_CRL_add1_ext_i2d() left crl.enc.modified
    untouched, so a CRL given a new extension kept, and re-emitted, its old
    cached encoding. X509_CRL_delete_ext() already marks it.

    Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Merge-date: Thu Sep 17 16:44:28 2026
    Merged-from: https://github.com/openssl/openssl/pull/32686

diff --git a/crypto/x509/x509_ext.c b/crypto/x509/x509_ext.c
index 8c9c5d96bc..261eccc51d 100644
--- a/crypto/x509/x509_ext.c
+++ b/crypto/x509/x509_ext.c
@@ -59,12 +59,20 @@ void *X509_CRL_get_ext_d2i(const X509_CRL *x, int nid, int *crit, int *idx)
 int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit,
     unsigned long flags)
 {
+    /*
+     * Assume modified, sadly the underlying function does not tell us whether
+     * changes were made, or not.
+     */
+    x->crl.enc.modified = 1;
     return X509V3_add1_i2d(&x->crl.extensions, nid, value, crit, flags);
 }

 int X509_CRL_add_ext(X509_CRL *x, const X509_EXTENSION *ex, int loc)
 {
-    return (X509v3_add_ext(&(x->crl.extensions), ex, loc) != NULL);
+    if (X509v3_add_ext(&x->crl.extensions, ex, loc) == NULL)
+        return 0;
+    x->crl.enc.modified = 1;
+    return 1;
 }

 int X509_get_ext_count(const X509 *x)
diff --git a/test/x509_internal_test.c b/test/x509_internal_test.c
index 84254697d6..31b2bdd9eb 100644
--- a/test/x509_internal_test.c
+++ b/test/x509_internal_test.c
@@ -232,6 +232,59 @@ static int test_ipaddr_to_asc(int idx)
     return good;
 }

+/* Adding an extension to a CRL marks its cached encoding stale */
+static int test_crl_add_ext_modifies(void)
+{
+    EVP_PKEY *pkey = NULL;
+    X509_NAME *name = NULL;
+    X509_CRL *crl = NULL, *copy = NULL;
+    ASN1_TIME *tm = NULL;
+    ASN1_INTEGER *num = NULL;
+    X509_EXTENSION *ext = NULL;
+    int ret = 0;
+
+    if (!TEST_ptr(pkey = EVP_PKEY_Q_keygen(NULL, NULL, "RSA", (size_t)2048))
+        || !TEST_ptr(name = X509_NAME_new())
+        || !TEST_true(X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+            (const unsigned char *)"crl ext test", -1, -1, 0))
+        || !TEST_ptr(tm = ASN1_TIME_set(NULL, 0))
+        || !TEST_ptr(num = ASN1_INTEGER_new())
+        || !TEST_true(ASN1_INTEGER_set(num, 1))
+        || !TEST_ptr(crl = X509_CRL_new())
+        || !TEST_true(X509_CRL_set_issuer_name(crl, name))
+        || !TEST_true(X509_CRL_set1_lastUpdate(crl, tm))
+        || !TEST_int_gt(X509_CRL_sign(crl, pkey, EVP_sha256()), 0))
+        goto err;
+
+    /* X509_CRL_add1_ext_i2d() on a decoded copy */
+    if (!TEST_ptr(copy = X509_CRL_dup(crl))
+        || !TEST_false(copy->crl.enc.modified)
+        || !TEST_true(X509_CRL_add1_ext_i2d(copy, NID_crl_number, num, 0, 0))
+        || !TEST_true(copy->crl.enc.modified))
+        goto err;
+    X509_CRL_free(copy);
+    copy = NULL;
+
+    /* X509_CRL_add_ext() on a decoded copy */
+    if (!TEST_ptr(ext = X509V3_EXT_i2d(NID_crl_number, 0, num))
+        || !TEST_ptr(copy = X509_CRL_dup(crl))
+        || !TEST_false(copy->crl.enc.modified)
+        || !TEST_true(X509_CRL_add_ext(copy, ext, -1))
+        || !TEST_true(copy->crl.enc.modified))
+        goto err;
+
+    ret = 1;
+err:
+    X509_EXTENSION_free(ext);
+    X509_CRL_free(copy);
+    X509_CRL_free(crl);
+    ASN1_INTEGER_free(num);
+    ASN1_TIME_free(tm);
+    X509_NAME_free(name);
+    EVP_PKEY_free(pkey);
+    return ret;
+}
+
 static int ck_purp(ossl_unused const X509_PURPOSE *purpose,
     ossl_unused const X509 *x, int ca)
 {
@@ -1169,6 +1222,7 @@ int setup_tests(void)
     ADD_TEST(test_standard_exts);
     ADD_ALL_TESTS(test_a2i_ipaddress, OSSL_NELEM(a2i_ipaddress_tests));
     ADD_ALL_TESTS(test_ipaddr_to_asc, OSSL_NELEM(ipaddr_to_asc_tests));
+    ADD_TEST(test_crl_add_ext_modifies);
     ADD_TEST(tests_X509_PURPOSE);
     ADD_TEST(tests_X509_check_time);
     ADD_TEST(tests_X509_check_crypto);