diff --git a/apps/cmp.c b/apps/cmp.c index 5167446cde..2e4867b0db 100644 --- a/apps/cmp.c +++ b/apps/cmp.c @@ -174,6 +174,7 @@ static char *opt_srv_keypass = NULL; static char *opt_srv_trusted = NULL; static char *opt_srv_untrusted = NULL; +static char *opt_ref_cert = NULL; static char *opt_rsp_cert = NULL; static char *opt_rsp_extracerts = NULL; static char *opt_rsp_capubs = NULL; @@ -249,7 +250,7 @@ typedef enum OPTION_choice { OPT_SRV_REF, OPT_SRV_SECRET, OPT_SRV_CERT, OPT_SRV_KEY, OPT_SRV_KEYPASS, OPT_SRV_TRUSTED, OPT_SRV_UNTRUSTED, - OPT_RSP_CERT, OPT_RSP_EXTRACERTS, OPT_RSP_CAPUBS, + OPT_REF_CERT, OPT_RSP_CERT, OPT_RSP_EXTRACERTS, OPT_RSP_CAPUBS, OPT_POLL_COUNT, OPT_CHECK_AFTER, OPT_GRANT_IMPLICITCONF, OPT_PKISTATUS, OPT_FAILURE, @@ -498,6 +499,8 @@ const OPTIONS cmp_options[] = { "Trusted certificates for client authentication"}, {"srv_untrusted", OPT_SRV_UNTRUSTED, 's', "Intermediate certs that may be useful for verifying CMP protection"}, + {"ref_cert", OPT_RSP_CERT, 's', + "Certificate to be expected for rr and any oldCertID in kur messages"}, {"rsp_cert", OPT_RSP_CERT, 's', "Certificate to be returned as mock enrollment result"}, {"rsp_extracerts", OPT_RSP_EXTRACERTS, 's', @@ -600,7 +603,7 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */ {&opt_srv_ref}, {&opt_srv_secret}, {&opt_srv_cert}, {&opt_srv_key}, {&opt_srv_keypass}, {&opt_srv_trusted}, {&opt_srv_untrusted}, - {&opt_rsp_cert}, {&opt_rsp_extracerts}, {&opt_rsp_capubs}, + {&opt_ref_cert}, {&opt_rsp_cert}, {&opt_rsp_extracerts}, {&opt_rsp_capubs}, {(char **)&opt_poll_count}, {(char **)&opt_check_after}, {(char **)&opt_grant_implicitconf}, {(char **)&opt_pkistatus}, {(char **)&opt_failure}, @@ -1074,6 +1077,18 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine) (add_X509_stack_fn_t)OSSL_CMP_CTX_set1_untrusted)) goto err; + if (opt_ref_cert != NULL) { + X509 *cert = load_cert_pwd(opt_ref_cert, opt_keypass, + "reference cert to be expected by the mock server"); + + if (cert == NULL) + goto err; + if (!ossl_cmp_mock_srv_set1_refCert(srv_ctx, cert)) { + X509_free(cert); + goto err; + } + X509_free(cert); + } if (opt_rsp_cert == NULL) { CMP_warn("no -rsp_cert given for mock server"); } else { @@ -1082,7 +1097,6 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine) if (cert == NULL) goto err; - /* from server perspective the server is the client */ if (!ossl_cmp_mock_srv_set1_certOut(srv_ctx, cert)) { X509_free(cert); goto err; @@ -2573,6 +2587,9 @@ static int get_opts(int argc, char **argv) case OPT_SRV_UNTRUSTED: opt_srv_untrusted = opt_str(); break; + case OPT_REF_CERT: + opt_ref_cert = opt_str(); + break; case OPT_RSP_CERT: opt_rsp_cert = opt_str(); break; diff --git a/apps/include/cmp_mock_srv.h b/apps/include/cmp_mock_srv.h index 6beba14735..f3edc1b01b 100644 --- a/apps/include/cmp_mock_srv.h +++ b/apps/include/cmp_mock_srv.h @@ -20,6 +20,7 @@ OSSL_CMP_SRV_CTX *ossl_cmp_mock_srv_new(OSSL_LIB_CTX *libctx, const char *propq); void ossl_cmp_mock_srv_free(OSSL_CMP_SRV_CTX *srv_ctx); +int ossl_cmp_mock_srv_set1_refCert(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert); int ossl_cmp_mock_srv_set1_certOut(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert); int ossl_cmp_mock_srv_set1_chainOut(OSSL_CMP_SRV_CTX *srv_ctx, STACK_OF(X509) *chain); diff --git a/apps/lib/cmp_mock_srv.c b/apps/lib/cmp_mock_srv.c index 5cd2b23737..dcd3123733 100644 --- a/apps/lib/cmp_mock_srv.c +++ b/apps/lib/cmp_mock_srv.c @@ -18,6 +18,7 @@ /* the context for the CMP mock server */ typedef struct { + X509 *refCert; /* cert to expect for oldCertID in kur/rr msg */ X509 *certOut; /* certificate to be returned in cp/ip/kup msg */ STACK_OF(X509) *chainOut; /* chain of certOut to add to extraCerts field */ STACK_OF(X509) *caPubsOut; /* certs to return in caPubs field of ip msg */ @@ -37,6 +38,7 @@ static void mock_srv_ctx_free(mock_srv_ctx *ctx) return; OSSL_CMP_PKISI_free(ctx->statusOut); + X509_free(ctx->refCert); X509_free(ctx->certOut); OSSL_STACK_OF_X509_free(ctx->chainOut); OSSL_STACK_OF_X509_free(ctx->caPubsOut); @@ -63,6 +65,22 @@ static mock_srv_ctx *mock_srv_ctx_new(void) return NULL; } +int ossl_cmp_mock_srv_set1_refCert(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert) +{ + mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); + + if (ctx == NULL) { + ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT); + return 0; + } + if (cert == NULL || X509_up_ref(cert)) { + X509_free(ctx->refCert); + ctx->refCert = cert; + return 1; + } + return 0; +} + int ossl_cmp_mock_srv_set1_certOut(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert) { mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); @@ -170,6 +188,21 @@ int ossl_cmp_mock_srv_set_checkAfterTime(OSSL_CMP_SRV_CTX *srv_ctx, int sec) return 1; } +/* check for matching reference cert components, as far as given */ +static int refcert_cmp(const X509 *refcert, + const X509_NAME *issuer, const ASN1_INTEGER *serial) +{ + const X509_NAME *ref_issuer; + const ASN1_INTEGER *ref_serial; + + if (refcert == NULL) + return 1; + ref_issuer = X509_get_issuer_name(refcert); + ref_serial = X509_get0_serialNumber(refcert); + return (ref_issuer == NULL || X509_NAME_cmp(issuer, ref_issuer) == 0) + && (ref_serial == NULL || ASN1_INTEGER_cmp(serial, ref_serial) == 0); +} + static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, const OSSL_CMP_MSG *cert_req, int certReqId, @@ -212,24 +245,18 @@ static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, /* give final response after polling */ ctx->curr_pollCount = 0; + /* accept cert update request only for the reference cert, if given */ if (OSSL_CMP_MSG_get_bodytype(cert_req) == OSSL_CMP_KUR - && crm != NULL && ctx->certOut != NULL) { + && crm != NULL /* thus not p10cr */ && ctx->refCert != NULL) { const OSSL_CRMF_CERTID *cid = OSSL_CRMF_MSG_get0_regCtrl_oldCertID(crm); - const X509_NAME *issuer = X509_get_issuer_name(ctx->certOut); - const ASN1_INTEGER *serial = X509_get0_serialNumber(ctx->certOut); if (cid == NULL) { ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_CERTID); return NULL; } - if (issuer != NULL - && X509_NAME_cmp(issuer, OSSL_CRMF_CERTID_get0_issuer(cid)) != 0) { - ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_CERTID); - return NULL; - } - if (serial != NULL - && ASN1_INTEGER_cmp(serial, - OSSL_CRMF_CERTID_get0_serialNumber(cid)) != 0) { + if (!refcert_cmp(ctx->refCert, + OSSL_CRMF_CERTID_get0_issuer(cid), + OSSL_CRMF_CERTID_get0_serialNumber(cid))) { ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_CERTID); return NULL; } @@ -270,19 +297,15 @@ static OSSL_CMP_PKISI *process_rr(OSSL_CMP_SRV_CTX *srv_ctx, ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT); return NULL; } - if (ctx->sendError || ctx->certOut == NULL) { + if (ctx->sendError) { ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE); return NULL; } - /* Allow any RR derived from CSR, which may include subject and serial */ - if (issuer == NULL || serial == NULL) - return OSSL_CMP_PKISI_dup(ctx->statusOut); - - /* accept revocation only for the certificate we sent in ir/cr/kur */ - if (X509_NAME_cmp(issuer, X509_get_issuer_name(ctx->certOut)) != 0 - || ASN1_INTEGER_cmp(serial, - X509_get0_serialNumber(ctx->certOut)) != 0) { + /* allow any RR derived from CSR which does not include issuer and serial */ + if ((issuer != NULL || serial != NULL) + /* accept revocation only for the reference cert, if given */ + && !refcert_cmp(ctx->refCert, issuer, serial)) { ERR_raise_data(ERR_LIB_CMP, CMP_R_REQUEST_NOT_ACCEPTED, "wrong certificate to revoke"); return NULL; diff --git a/doc/internal/man3/ossl_cmp_mock_srv_new.pod b/doc/internal/man3/ossl_cmp_mock_srv_new.pod index 837ca06bb3..cf85139e0a 100644 --- a/doc/internal/man3/ossl_cmp_mock_srv_new.pod +++ b/doc/internal/man3/ossl_cmp_mock_srv_new.pod @@ -4,6 +4,7 @@ ossl_cmp_mock_srv_new, ossl_cmp_mock_srv_free, +ossl_cmp_mock_srv_set1_refCert, ossl_cmp_mock_srv_set1_certOut, ossl_cmp_mock_srv_set1_chainOut, ossl_cmp_mock_srv_set1_caPubsOut, @@ -20,6 +21,7 @@ ossl_cmp_mock_srv_set_checkAfterTime OSSL_CMP_SRV_CTX *ossl_cmp_mock_srv_new(OSSL_LIB_CTX *libctx, const char *propq); void ossl_cmp_mock_srv_free(OSSL_CMP_SRV_CTX *srv_ctx); + int ossl_cmp_mock_srv_set1_refCert(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert); int ossl_cmp_mock_srv_set1_certOut(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert); int ossl_cmp_mock_srv_set1_chainOut(OSSL_CMP_SRV_CTX *srv_ctx, STACK_OF(X509) *chain); @@ -39,12 +41,18 @@ I, both of which may be NULL to select the defaults. ossl_cmp_mock_srv_free() deallocates the contexts for the CMP mock server. +OSSL_CMP_SRV_CTX_set1_refCert() sets the reference certificate to be expected +for rr messages and for any oldCertID included in kur messages. + OSSL_CMP_SRV_CTX_set1_certOut() sets the certificate to be returned in cp/ip/kup. +Note that on each certificate request the mock server does not produce +a fresh certificate but just returns the same pre-existing certificate. OSSL_CMP_SRV_CTX_set1_chainOut() sets the certificate chain to be added to the extraCerts in a cp/ip/kup. -It should to useful to validate B. +It should be useful for the validation of the certificate given via +OSSL_CMP_SRV_CTX_set1_certOut(). OSSL_CMP_SRV_CTX_set1_caPubsOut() sets the caPubs to be returned in an ip. diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in index 84241a3197..3bae43cc35 100644 --- a/doc/man1/openssl-cmp.pod.in +++ b/doc/man1/openssl-cmp.pod.in @@ -123,6 +123,7 @@ Mock server options: [B<-srv_keypass> I] [B<-srv_trusted> I|I] [B<-srv_untrusted> I|I] +[B<-ref_cert> I|I] [B<-rsp_cert> I|I] [B<-rsp_extracerts> I|I] [B<-rsp_capubs> I|I] @@ -959,6 +960,10 @@ have no effect on the certificate verification enabled via this option. Intermediate CA certs that may be useful when validating client certificates. +=item B<-ref_cert> I|I + +Certificate to be expected for RR messages and any oldCertID in KUR messages. + =item B<-rsp_cert> I|I Certificate to be returned as mock enrollment result. diff --git a/test/cmp_client_test.c b/test/cmp_client_test.c index 70543b3b01..4e7b26c220 100644 --- a/test/cmp_client_test.c +++ b/test/cmp_client_test.c @@ -62,6 +62,7 @@ static CMP_SES_TEST_FIXTURE *set_up(const char *const test_case_name) fixture->test_case_name = test_case_name; if (!TEST_ptr(fixture->srv_ctx = ossl_cmp_mock_srv_new(libctx, NULL)) || !OSSL_CMP_SRV_CTX_set_accept_unprotected(fixture->srv_ctx, 1) + || !ossl_cmp_mock_srv_set1_refCert(fixture->srv_ctx, client_cert) || !ossl_cmp_mock_srv_set1_certOut(fixture->srv_ctx, client_cert) || (srv_cmp_ctx = OSSL_CMP_SRV_CTX_get0_cmp_ctx(fixture->srv_ctx)) == NULL diff --git a/test/recipes/80-test_cmp_http_data/Mock/server.cnf b/test/recipes/80-test_cmp_http_data/Mock/server.cnf index 774b34a7f5..991c1cf525 100644 --- a/test/recipes/80-test_cmp_http_data/Mock/server.cnf +++ b/test/recipes/80-test_cmp_http_data/Mock/server.cnf @@ -9,6 +9,7 @@ srv_secret = pass:test no_check_time = 1 srv_trusted = signer_root.crt +ref_cert = signer_only.crt rsp_cert = signer_only.crt rsp_capubs = signer_root.crt rsp_extracerts = signer_issuing.crt