/* Provenance: openssl/openssl ossl_cmp_msg_check_update (fixed). * repo: openssl/openssl * commit: 21a5d9658b0c66daace60e10ea18ff32a448de9f * parent: 9f2b8522d39ff869fac2ff7c3f20609b54843188 * commit_url: https://github.com/openssl/openssl/commit/21a5d9658b0c66daace60e10ea18ff32a448de9f * cve: CVE-2026-63074 * license: Apache-2.0 * function: ossl_cmp_msg_check_update * relpath: crypto/cmp/cmp_vfy.c */ int ossl_cmp_msg_check_update(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg, ossl_cmp_allow_unprotected_cb_t cb, int cb_arg) { OSSL_CMP_PKIHEADER *hdr; const X509_NAME *expected_sender; int num_extra_before, num_extra_after, num_added; if (!ossl_assert(ctx != NULL && msg != NULL && msg->header != NULL)) return 0; hdr = OSSL_CMP_MSG_get0_header(msg); /* validate sender name of received msg */ if (hdr->sender->type != GEN_DIRNAME) { ERR_raise(ERR_LIB_CMP, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED); return 0; } /* * Compare actual sender name of response with expected sender name. * Mitigates risk to accept misused PBM secret * or misused certificate of an unauthorized entity of a trusted hierarchy. */ expected_sender = ctx->expected_sender; if (expected_sender == NULL && ctx->srvCert != NULL) expected_sender = X509_get_subject_name(ctx->srvCert); if (!check_name(ctx, 0, "sender DN field", hdr->sender->d.directoryName, "expected sender", expected_sender)) return 0; /* Note: if recipient was NULL-DN it could be learned here if needed */ if (sk_X509_num(msg->extraCerts) > 10) ossl_cmp_warn(ctx, "received CMP message contains more than 10 extraCerts"); /* * Store any provided extraCerts in ctx for use in OSSL_CMP_validate_msg() * and for future use, such that they are available to ctx->certConf_cb and * the peer does not need to send them again in the same transaction. * Note that it does not help validating the message before storing the * extraCerts because they do not belong to the protected msg part anyway. * For efficiency, the extraCerts are prepended so they get used first. */ num_extra_before = sk_X509_num(ctx->untrusted); if (!X509_add_certs(ctx->untrusted, msg->extraCerts, /* this allows self-signed certs */ X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP | X509_ADD_FLAG_PREPEND)) return 0; num_extra_after = sk_X509_num(ctx->untrusted); num_added = num_extra_after - num_extra_before; /* validate message protection */ if (hdr->protectionAlg != NULL) { /* detect explicitly permitted exceptions for invalid protection */ if (!OSSL_CMP_validate_msg(ctx, msg) && (cb == NULL || (*cb)(ctx, msg, 1, cb_arg) <= 0)) { /* * remove extraCerts again if not caching * or if we failed validation above, lest a remote user * starts sending us lots of certificate in invalid messages * leading to a DOS from unbounded certificate stack growth */ while (num_added-- > 0) X509_free(sk_X509_shift(ctx->untrusted)); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_PROTECTION); return 0; #endif } } else { /* detect explicitly permitted exceptions for missing protection */ if (cb == NULL || (*cb)(ctx, msg, 0, cb_arg) <= 0) { while (num_added-- > 0) X509_free(sk_X509_shift(ctx->untrusted)); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PROTECTION); return 0; #endif } } /* check CMP version number in header */ if (ossl_cmp_hdr_get_pvno(hdr) != OSSL_CMP_PVNO) { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ERR_raise(ERR_LIB_CMP, CMP_R_UNEXPECTED_PVNO); return 0; #endif } if (OSSL_CMP_MSG_get_bodytype(msg) < 0) { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ERR_raise(ERR_LIB_CMP, CMP_R_PKIBODY_ERROR); return 0; #endif } /* compare received transactionID with the expected one in previous msg */ if (ctx->transactionID != NULL && (hdr->transactionID == NULL || ASN1_OCTET_STRING_cmp(ctx->transactionID, hdr->transactionID) != 0)) { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ERR_raise(ERR_LIB_CMP, CMP_R_TRANSACTIONID_UNMATCHED); return 0; #endif } /* compare received nonce with the one we sent */ if (ctx->senderNonce != NULL && (msg->header->recipNonce == NULL || ASN1_OCTET_STRING_cmp(ctx->senderNonce, hdr->recipNonce) != 0)) { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ERR_raise(ERR_LIB_CMP, CMP_R_RECIPNONCE_UNMATCHED); return 0; #endif } /* if not yet present, learn transactionID */ if (ctx->transactionID == NULL && !OSSL_CMP_CTX_set1_transactionID(ctx, hdr->transactionID)) return 0; /* * RFC 4210 section 5.1.1 states: the recipNonce is copied from * the senderNonce of the previous message in the transaction. * --> Store for setting in next message */ if (!ossl_cmp_ctx_set1_recipNonce(ctx, hdr->senderNonce)) return 0; /* * Store any provided extraCerts in ctx for future use, * such that they are available to ctx->certConf_cb and * the peer does not need to send them again in the same transaction. * For efficiency, the extraCerts are prepended so they get used first. */ if (!X509_add_certs(ctx->untrusted, msg->extraCerts, /* this allows self-signed certs */ X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP | X509_ADD_FLAG_PREPEND)) return 0; if (ossl_cmp_hdr_get_protection_nid(hdr) == NID_id_PasswordBasedMAC) { /* * RFC 4210, 5.3.2: 'Note that if the PKI Message Protection is * "shared secret information", then any certificate transported in * the caPubs field may be directly trusted as a root CA * certificate by the initiator.' */ switch (OSSL_CMP_MSG_get_bodytype(msg)) { case OSSL_CMP_PKIBODY_IP: case OSSL_CMP_PKIBODY_CP: case OSSL_CMP_PKIBODY_KUP: case OSSL_CMP_PKIBODY_CCP: if (ctx->trusted != NULL) { STACK_OF(X509) *certs = msg->body->value.ip->caPubs; /* value.ip is same for cp, kup, and ccp */ if (!ossl_cmp_X509_STORE_add1_certs(ctx->trusted, certs, 0)) /* adds both self-issued and not self-issued certs */ return 0; } break; default: break; } } return 1; }