Commit 70c89a2c33 for openssl.org

commit 70c89a2c33b3524fdb32ee9609e526e555f11078
Author: Dr. David von Oheimb <dev@ddvo.net>
Date:   Thu Oct 23 21:44:37 2025 +0200

    OSSL_CMP_SRV_process_request(): complete transaction detection and context re-initialization

    This motivated factoring out new functions: assuming_new_transaction()
    and transaction_reinit().  It also includes a minor fix: so far, receiving
    an error message from the client was taken as an indication for a new
    transaction, which does not make much sense;  so, this case has been dropped
    in assuming_new_transaction().

    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    MergeDate: Thu Aug 27 07:25:01 2026
    (Merged from https://github.com/openssl/openssl/pull/28988)

diff --git a/crypto/cmp/cmp_server.c b/crypto/cmp/cmp_server.c
index 8a321ecbd9..41de2790ae 100644
--- a/crypto/cmp/cmp_server.c
+++ b/crypto/cmp/cmp_server.c
@@ -55,7 +55,6 @@ OSSL_CMP_SRV_CTX *OSSL_CMP_SRV_CTX_new(OSSL_LIB_CTX *libctx, const char *propq)
     if ((ctx->ctx = OSSL_CMP_CTX_new(libctx, propq)) == NULL)
         goto err;
     ctx->certReqId = OSSL_CMP_CERTREQID_INVALID;
-    ctx->polling = 0;

     /* all other elements are initialized to 0 or NULL, respectively */
     return ctx;
@@ -571,6 +570,60 @@ static int unprotected_exception(const OSSL_CMP_CTX *ctx,
     return 0;
 }

+static int assuming_new_transaction(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *req)
+{
+    int body_type = OSSL_CMP_MSG_get_bodytype(req);
+    ASN1_OCTET_STRING *tid;
+
+    if (ctx->transactionID == NULL) /* no currently active transaction */
+        return 1;
+
+    tid = OSSL_CMP_HDR_get0_transactionID(OSSL_CMP_MSG_get0_header(req));
+    if (tid != NULL && ASN1_OCTET_STRING_cmp(tid, ctx->transactionID) != 0) {
+        char *ctx_str = i2s_ASN1_OCTET_STRING(NULL, ctx->transactionID);
+        char *tid_str = i2s_ASN1_OCTET_STRING(NULL, tid);
+
+        ossl_cmp_log2(WARN, ctx, "Assuming that last transaction with ID=%s got aborted, new ID=%s",
+            ctx_str != NULL ? ctx_str : "(null)",
+            tid_str != NULL ? tid_str : "(null)");
+        OPENSSL_free(tid_str);
+        OPENSSL_free(ctx_str);
+        return 1;
+    }
+
+    switch (body_type) {
+    case OSSL_CMP_PKIBODY_IR:
+    case OSSL_CMP_PKIBODY_CR:
+    case OSSL_CMP_PKIBODY_P10CR:
+    case OSSL_CMP_PKIBODY_KUR:
+    case OSSL_CMP_PKIBODY_RR:
+    case OSSL_CMP_PKIBODY_GENM:
+        ossl_cmp_log1(WARN, ctx, "Assuming new transaction due to received body type %s",
+            ossl_cmp_bodytype_to_string(body_type));
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+/* Prepare for next transaction */
+static int transaction_reinit(OSSL_CMP_SRV_CTX *srv_ctx)
+{
+    int ret = 1;
+
+    srv_ctx->ctx->status = OSSL_CMP_PKISTATUS_unspecified; /* transaction closed */
+    srv_ctx->certReqId = OSSL_CMP_CERTREQID_INVALID;
+    srv_ctx->polling = 0;
+
+    if (srv_ctx->clean_transaction != NULL)
+        ret = srv_ctx->clean_transaction(srv_ctx, srv_ctx->ctx->transactionID);
+    if (!OSSL_CMP_CTX_set1_transactionID(srv_ctx->ctx, NULL))
+        ret = 0;
+    if (!OSSL_CMP_CTX_set1_senderNonce(srv_ctx->ctx, NULL))
+        ret = 0;
+    return ret;
+}
+
 /*
  * returns created message and NULL on internal error
  */
@@ -613,36 +666,18 @@ OSSL_CMP_MSG *OSSL_CMP_SRV_process_request(OSSL_CMP_SRV_CTX *srv_ctx,
         goto err;
     }

-    switch (req_type) {
-    case OSSL_CMP_PKIBODY_IR:
-    case OSSL_CMP_PKIBODY_CR:
-    case OSSL_CMP_PKIBODY_P10CR:
-    case OSSL_CMP_PKIBODY_KUR:
-    case OSSL_CMP_PKIBODY_RR:
-    case OSSL_CMP_PKIBODY_GENM:
-    case OSSL_CMP_PKIBODY_ERROR:
-        if (ctx->transactionID != NULL) {
-            char *tid = i2s_ASN1_OCTET_STRING(NULL, ctx->transactionID);
-
-            if (tid != NULL)
-                ossl_cmp_log1(WARN, ctx,
-                    "Assuming that last transaction with ID=%s got aborted",
-                    tid);
-            OPENSSL_free(tid);
-        }
-        /* start of a new transaction, reset transactionID and senderNonce */
-        if (!OSSL_CMP_CTX_set1_transactionID(ctx, NULL)
-            || !OSSL_CMP_CTX_set1_senderNonce(ctx, NULL))
-            goto err;
-
-        if (srv_ctx->clean_transaction != NULL
-            && !srv_ctx->clean_transaction(srv_ctx, NULL)) {
+    if (assuming_new_transaction(ctx, req)) {
+        /*
+         * Start of a new transaction, resetting transactionID and senderNonce.
+         * Must in this case reset transactionID beforehand such that the clean()
+         * callback function gets a NULL transactionID argument, as documented.
+         */
+        (void)OSSL_CMP_CTX_set1_transactionID(ctx, NULL);
+        if (!transaction_reinit(srv_ctx)) {
             ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE);
             goto err;
         }
-
-        break;
-    default:
+    } else {
         /* transactionID should be already initialized */
         if (ctx->transactionID == NULL) {
 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
@@ -735,13 +770,9 @@ err:
     case OSSL_CMP_PKIBODY_PKICONF:
     case OSSL_CMP_PKIBODY_GENP:
         /* Other terminating response message types are not supported */
-        srv_ctx->certReqId = OSSL_CMP_CERTREQID_INVALID;
-        /* Prepare for next transaction, ignoring any errors here: */
-        if (srv_ctx->clean_transaction != NULL)
-            (void)srv_ctx->clean_transaction(srv_ctx, ctx->transactionID);
-        (void)OSSL_CMP_CTX_set1_transactionID(ctx, NULL);
-        (void)OSSL_CMP_CTX_set1_senderNonce(ctx, NULL);
-        ctx->status = OSSL_CMP_PKISTATUS_unspecified; /* transaction closed */
+
+        /* Prepare for next transaction, ignoring any errors here */
+        (void)transaction_reinit(srv_ctx);

     default: /* not closing transaction in other cases */
         break;
diff --git a/doc/man3/OSSL_CMP_SRV_CTX_new.pod b/doc/man3/OSSL_CMP_SRV_CTX_new.pod
index e82c46b800..2052e2963a 100644
--- a/doc/man3/OSSL_CMP_SRV_CTX_new.pod
+++ b/doc/man3/OSSL_CMP_SRV_CTX_new.pod
@@ -112,6 +112,9 @@ transaction (which can be a certificate confirmation or a poll request message).
 The function should not be called by multiple threads sharing
 the same I<srv_ctx> without explicitly serialising the calls;
 calling it in parallel leads to undefined behavior.
+When an earlier call did not yet finish the respective transaction,
+calling it in an interleaved way with a new request belonging to a different
+transaction will abort the earlier transaction and begin the new one.

 OSSL_CMP_CTX_server_perform() is an interface to
 OSSL_CMP_SRV_process_request() that can be used by a CMP client