[rs-commit] r546 - in /mod_ca/trunk: ChangeLog mod_ca_disk.c

rs-commit at redwax.eu rs-commit at redwax.eu
Thu Mar 12 16:52:29 CET 2026


Author: minfrin at redwax.eu
Date: Thu Mar 12 16:52:28 2026
New Revision: 546

Log:
mod_ca_disk: When the ca_certstore hook is fired to
save the certificate, but the index has not yet been 
updated because the ca_makeserial hook was not used,
update the index while make sure this happens just
once.

Modified:
    mod_ca/trunk/ChangeLog
    mod_ca/trunk/mod_ca_disk.c

Modified: mod_ca/trunk/ChangeLog
==============================================================================
--- mod_ca/trunk/ChangeLog	(original)
+++ mod_ca/trunk/ChangeLog	Thu Mar 12 16:52:28 2026
@@ -1,5 +1,11 @@
 
 Changes with v1.0.0
+
+ *) mod_ca_disk: When the ca_certstore hook is fired to
+    save the certificate, but the index has not yet been
+    updated because the ca_makeserial hook was not used,
+    update the index while make sure this happens just
+    once. [Graham Leggett]
 
  *) Add ca_reqauthz hook to mod_ca_disk to verify the case
     where the renewal or reissue was signed by a

Modified: mod_ca/trunk/mod_ca_disk.c
==============================================================================
--- mod_ca/trunk/mod_ca_disk.c	(original)
+++ mod_ca/trunk/mod_ca_disk.c	Thu Mar 12 16:52:28 2026
@@ -70,6 +70,7 @@
 
 typedef struct
 {
+    TXT_DB *db;
     const char *csr_path;
     const char *serial_path;
     const char *serial_path_suffix;
@@ -519,6 +520,10 @@
     char buf[HUGE_STRING_LEN];
     X509 *cert = NULL;
     PKCS7 *p7 = NULL;
+    ASN1_INTEGER *si;
+    BIGNUM *bn = NULL;
+    ASN1_TIME *tm = NULL;
+    X509_NAME *subject = NULL;
     apr_status_t status;
 
     ca_config_rec *conf = ap_get_module_config(r->per_dir_config,
@@ -559,18 +564,22 @@
         return HTTP_BAD_REQUEST;
     }
 
+    /* sanity check the serial number */
+    si = X509_get_serialNumber(cert);
+    if (!si) {
+        log_message(r, APR_SUCCESS,
+                "certificate had no serial number, could not be stored");
+
+        return HTTP_BAD_REQUEST;
+    }
+    bn = ASN1_INTEGER_to_BN(si, NULL);
+
+    apr_pool_cleanup_register(r->pool, bn, ca_BIGNUM_cleanup,
+            apr_pool_cleanup_null);
+
     /* extract the serial if requested */
     if (conf->serial_path) {
         const char *key;
-        BIGNUM *bn = NULL;
-        ASN1_INTEGER *si = X509_get_serialNumber(cert);
-        if (!si) {
-            log_message(r, APR_SUCCESS,
-                    "certificate had no serial number, could not be stored");
-
-            return HTTP_BAD_REQUEST;
-        }
-        bn = ASN1_INTEGER_to_BN(si, NULL);
         if (BN_is_zero(bn)) {
             key = apr_pstrcat(r->pool, "00.", conf->serial_path_suffix, NULL);
         }
@@ -580,7 +589,6 @@
                     NULL);
             OPENSSL_free(tmp);
         }
-        BN_free(bn);
 
         /* set up the path */
         status = apr_filepath_merge(&path, conf->serial_path, key,
@@ -649,7 +657,7 @@
         return DECLINED;
     }
 
-    /* create a bio for our CSR */
+    /* create a bio for our certificate */
     out = BIO_new(BIO_s_mem());
     apr_pool_cleanup_register(r->pool, out, ca_BIO_cleanup,
             apr_pool_cleanup_null);
@@ -719,6 +727,146 @@
                     "Could not link the certificate file to the CADiskCertificateByTransactionPath");
 
             apr_file_remove(path, r->pool);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    /* did we already index the serial in the ca_makeserial hook? */
+    if (conf->db) {
+        return OK;
+    }
+
+    /* get the not after time and subject, if set */
+    tm = parse_ASN1_TIME(r->pool,
+            apr_hash_get(params, "notAfter", APR_HASH_KEY_STRING));
+    subject = parse_X509_NAME(r->pool,
+            apr_hash_get(params, "subject", APR_HASH_KEY_STRING));
+
+    /* must we update the openssl database? */
+    if (conf->index_file && tm && subject) {
+
+        char *tname;
+        BIO *in;
+        BIO *out;
+        OPENSSL_STRING *row;
+
+        in = BIO_new(BIO_s_file());
+
+        if (BIO_read_filename(in, conf->index_file) <= 0) {
+            /* on read error, we create a database */
+            BIO_free(in);
+            in = BIO_new(BIO_s_mem());
+        }
+        apr_pool_cleanup_register(r->pool, in, ca_BIO_cleanup,
+                apr_pool_cleanup_null);
+
+        conf->db = TXT_DB_read(in, DB_NUMBER);
+        if (!conf->db) {
+            log_message(r, APR_SUCCESS, "Could not parse the index file");
+
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        apr_pool_cleanup_register(r->pool, conf->db, ca_TXT_DB_cleanup,
+                apr_pool_cleanup_null);
+
+        if (!TXT_DB_create_index(conf->db, DB_serial, NULL,
+                LHASH_HASH_FN(index_serial), LHASH_COMP_FN(index_serial))) {
+            log_message(r, APR_SUCCESS,
+                    apr_psprintf(r->pool,
+                            "Could not hash the index file (%ld,%ld,%ld)",
+                            conf->db->error, conf->db->arg1, conf->db->arg2));
+
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        if (conf->index_unique
+                && !TXT_DB_create_index(conf->db, DB_name, index_name_qual,
+                        LHASH_HASH_FN(index_name), LHASH_COMP_FN(index_name))) {
+            log_message(r, APR_SUCCESS,
+                    apr_psprintf(r->pool,
+                            "Could not hash the index file (%ld,%ld,%ld)",
+                            conf->db->error, conf->db->arg1, conf->db->arg2));
+
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        row = (char **) OPENSSL_malloc(sizeof(char *)*(DB_NUMBER+1));
+        if (!row) {
+            log_message(r, APR_SUCCESS,
+                    "Could not allocate new row into index file");
+
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        row[DB_type] = (char *) OPENSSL_malloc(2);
+        row[DB_exp_date] = (char *) OPENSSL_malloc(tm->length+1);
+        if (BN_is_zero(bn)) {
+            row[DB_serial] = BUF_strdup("00");
+        }
+        else {
+            row[DB_serial] = BN_bn2hex(bn);
+        }
+        row[DB_rev_date] = NULL;
+        row[DB_file] = (char *) OPENSSL_malloc(8);
+        row[DB_name] = X509_NAME_oneline(subject, NULL, 0);
+        if ((!row[DB_type]) || (!row[DB_exp_date]) || (!row[DB_serial])
+                || (!row[DB_file]) || (!row[DB_name])) {
+            log_message(r, APR_SUCCESS,
+                    "Could not allocate new row into index file");
+
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        row[DB_type][0] = 'V';
+        row[DB_type][1] = '\0';
+        BUF_strlcpy(row[DB_file], "unknown", 8);
+        memcpy(row[DB_exp_date], tm->data, tm->length);
+        row[DB_exp_date][tm->length] = '\0';
+
+        if (!TXT_DB_insert(conf->db, row)) {
+            log_message(r, APR_SUCCESS,
+                    conf->db->error == DB_ERROR_INDEX_CLASH ?
+                            conf->index_unique ? "Index file clash: serial / subject already used" :
+                                    "Index file clash: serial already used"
+                            : "Could not insert new row into index file");
+
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        out = BIO_new(BIO_s_file());
+
+        tname = apr_pstrcat(r->pool, conf->index_file, ".XXXXXX", NULL);
+        if (BIO_write_filename(out, tname) <= 0) {
+            log_message(r, APR_SUCCESS, "Index file could not be created");
+
+            BIO_free(out);
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        if (conf->db && !TXT_DB_write(out, conf->db)) {
+            log_message(r, APR_SUCCESS, "Index could not generated");
+
+            BIO_free(out);
+            apr_global_mutex_unlock(ca_disk_mutex);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        BIO_free(out);
+
+        /* rename the file into place */
+        status = apr_file_rename(tname, conf->index_file, r->pool);
+        if (APR_SUCCESS != status) {
+            log_message(r, status,
+                    "Could not rename the index temporary file");
+
+            apr_file_remove(tname, r->pool);
+            apr_global_mutex_unlock(ca_disk_mutex);
             return HTTP_INTERNAL_SERVER_ERROR;
         }
     }
@@ -1000,7 +1148,6 @@
     apr_file_t *tfile;
     ASN1_INTEGER *ai;
     BIGNUM *bn = NULL;
-    TXT_DB *db = NULL;
     ASN1_TIME *tm = NULL;
     X509_NAME *subject = NULL;
     char *serial_file_new;
@@ -1040,34 +1187,34 @@
         apr_pool_cleanup_register(r->pool, in, ca_BIO_cleanup,
                 apr_pool_cleanup_null);
 
-        db = TXT_DB_read(in, DB_NUMBER);
-        if (!db) {
+        conf->db = TXT_DB_read(in, DB_NUMBER);
+        if (!conf->db) {
             log_message(r, APR_SUCCESS, "Could not parse the index file");
 
             apr_global_mutex_unlock(ca_disk_mutex);
             return HTTP_INTERNAL_SERVER_ERROR;
         }
-        apr_pool_cleanup_register(r->pool, db, ca_TXT_DB_cleanup,
+        apr_pool_cleanup_register(r->pool, conf->db, ca_TXT_DB_cleanup,
                 apr_pool_cleanup_null);
 
-        if (!TXT_DB_create_index(db, DB_serial, NULL,
+        if (!TXT_DB_create_index(conf->db, DB_serial, NULL,
                 LHASH_HASH_FN(index_serial), LHASH_COMP_FN(index_serial))) {
             log_message(r, APR_SUCCESS,
                     apr_psprintf(r->pool,
                             "Could not hash the index file (%ld,%ld,%ld)",
-                            db->error, db->arg1, db->arg2));
+                            conf->db->error, conf->db->arg1, conf->db->arg2));
 
             apr_global_mutex_unlock(ca_disk_mutex);
             return HTTP_INTERNAL_SERVER_ERROR;
         }
 
         if (conf->index_unique
-                && !TXT_DB_create_index(db, DB_name, index_name_qual,
+                && !TXT_DB_create_index(conf->db, DB_name, index_name_qual,
                         LHASH_HASH_FN(index_name), LHASH_COMP_FN(index_name))) {
             log_message(r, APR_SUCCESS,
                     apr_psprintf(r->pool,
                             "Could not hash the index file (%ld,%ld,%ld)",
-                            db->error, db->arg1, db->arg2));
+                            conf->db->error, conf->db->arg1, conf->db->arg2));
 
             apr_global_mutex_unlock(ca_disk_mutex);
             return HTTP_INTERNAL_SERVER_ERROR;
@@ -1157,7 +1304,7 @@
         BIO_read(out, buf, size);
         BIO_free(out);
 
-        if (db) {
+        if (conf->db) {
             OPENSSL_STRING *row =
                     (char **) OPENSSL_malloc(sizeof(char *)*(DB_NUMBER+1));
             if (!row) {
@@ -1198,9 +1345,9 @@
             memcpy(row[DB_exp_date], tm->data, tm->length);
             row[DB_exp_date][tm->length] = '\0';
 
-            if (!TXT_DB_insert(db, row)) {
+            if (!TXT_DB_insert(conf->db, row)) {
                 log_message(r, APR_SUCCESS,
-                        db->error == DB_ERROR_INDEX_CLASH ?
+                        conf->db->error == DB_ERROR_INDEX_CLASH ?
                                 conf->index_unique ? "Index file clash: serial / subject already used" :
                                         "Index file clash: serial already used"
                                 : "Could not insert new row into index file");
@@ -1256,7 +1403,7 @@
                 return HTTP_INTERNAL_SERVER_ERROR;
             }
 
-            if (db && !TXT_DB_write(out, db)) {
+            if (conf->db && !TXT_DB_write(out, conf->db)) {
                 log_message(r, APR_SUCCESS, "Index could not generated");
 
                 BIO_free(out);



More information about the rs-commit mailing list