Skip to content

Commit

Permalink
CMP client: fix checking new cert enrolled with oldcert and without p…
Browse files Browse the repository at this point in the history
…rivate key

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Todd Short <todd.short@me.com>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from #20832)

(cherry picked from commit e0f1ec3)
  • Loading branch information
DDvO committed May 12, 2023
1 parent 6e4783d commit 09382af
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 36 deletions.
6 changes: 2 additions & 4 deletions crypto/cmp/cmp_client.c
Expand Up @@ -412,12 +412,10 @@ static X509 *get1_cert_status(OSSL_CMP_CTX *ctx, int bodytype,
{
char buf[OSSL_CMP_PKISI_BUFLEN];
X509 *crt = NULL;
EVP_PKEY *privkey;

if (!ossl_assert(ctx != NULL && crep != NULL))
return NULL;

privkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1);
switch (ossl_cmp_pkisi_get_status(crep->status)) {
case OSSL_CMP_PKISTATUS_waiting:
ossl_cmp_err(ctx,
Expand Down Expand Up @@ -455,7 +453,7 @@ static X509 *get1_cert_status(OSSL_CMP_CTX *ctx, int bodytype,
ERR_raise(ERR_LIB_CMP, CMP_R_UNKNOWN_PKISTATUS);
goto err;
}
crt = ossl_cmp_certresponse_get1_cert(crep, ctx, privkey);
crt = ossl_cmp_certresponse_get1_cert(ctx, crep);
if (crt == NULL) /* according to PKIStatus, we can expect a cert */
ERR_raise(ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND);

Expand Down Expand Up @@ -560,7 +558,7 @@ static int cert_response(OSSL_CMP_CTX *ctx, int sleep, int rid,
OSSL_CMP_MSG **resp, int *checkAfter,
int req_type, int expected_type)
{
EVP_PKEY *rkey = OSSL_CMP_CTX_get0_newPkey(ctx /* may be NULL */, 0);
EVP_PKEY *rkey = ossl_cmp_ctx_get0_newPubkey(ctx);
int fail_info = 0; /* no failure */
const char *txt = NULL;
OSSL_CMP_CERTREPMESSAGE *crepmsg;
Expand Down
16 changes: 16 additions & 0 deletions crypto/cmp/cmp_ctx.c
Expand Up @@ -834,6 +834,7 @@ int OSSL_CMP_CTX_set0_newPkey(OSSL_CMP_CTX *ctx, int priv, EVP_PKEY *pkey)
}

/* Get the private/public key to use for cert enrollment, or NULL on error */
/* In case |priv| == 0, better use ossl_cmp_ctx_get0_newPubkey() below */
EVP_PKEY *OSSL_CMP_CTX_get0_newPkey(const OSSL_CMP_CTX *ctx, int priv)
{
if (ctx == NULL) {
Expand All @@ -848,6 +849,21 @@ EVP_PKEY *OSSL_CMP_CTX_get0_newPkey(const OSSL_CMP_CTX *ctx, int priv)
return ctx->pkey; /* may be NULL */
}

EVP_PKEY *ossl_cmp_ctx_get0_newPubkey(const OSSL_CMP_CTX *ctx)
{
if (!ossl_assert(ctx != NULL))
return NULL;
if (ctx->newPkey != NULL)
return ctx->newPkey;
if (ctx->p10CSR != NULL)
return X509_REQ_get0_pubkey(ctx->p10CSR);
if (ctx->oldCert != NULL)
return X509_get0_pubkey(ctx->oldCert);
if (ctx->cert != NULL)
return X509_get0_pubkey(ctx->cert);
return ctx->pkey;
}

/* Set the given transactionID to the context */
int OSSL_CMP_CTX_set1_transactionID(OSSL_CMP_CTX *ctx,
const ASN1_OCTET_STRING *id)
Expand Down
5 changes: 3 additions & 2 deletions crypto/cmp/cmp_local.h
Expand Up @@ -789,6 +789,7 @@ int ossl_cmp_ctx_set1_extraCertsIn(OSSL_CMP_CTX *ctx,
STACK_OF(X509) *extraCertsIn);
int ossl_cmp_ctx_set1_recipNonce(OSSL_CMP_CTX *ctx,
const ASN1_OCTET_STRING *nonce);
EVP_PKEY *ossl_cmp_ctx_get0_newPubkey(const OSSL_CMP_CTX *ctx);

/* from cmp_status.c */
int ossl_cmp_pkisi_get_status(const OSSL_CMP_PKISI *si);
Expand Down Expand Up @@ -902,8 +903,8 @@ ossl_cmp_pollrepcontent_get0_pollrep(const OSSL_CMP_POLLREPCONTENT *prc,
OSSL_CMP_CERTRESPONSE *
ossl_cmp_certrepmessage_get0_certresponse(const OSSL_CMP_CERTREPMESSAGE *crm,
int rid);
X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CERTRESPONSE *crep,
const OSSL_CMP_CTX *ctx, EVP_PKEY *pkey);
X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx,
const OSSL_CMP_CERTRESPONSE *crep);
OSSL_CMP_MSG *ossl_cmp_msg_load(const char *file);

/* from cmp_protect.c */
Expand Down
25 changes: 8 additions & 17 deletions crypto/cmp/cmp_msg.c
Expand Up @@ -274,7 +274,7 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid)
OSSL_CRMF_MSG *crm = NULL;
X509 *refcert = ctx->oldCert != NULL ? ctx->oldCert : ctx->cert;
/* refcert defaults to current client cert */
EVP_PKEY *rkey = OSSL_CMP_CTX_get0_newPkey(ctx, 0);
EVP_PKEY *rkey = ossl_cmp_ctx_get0_newPubkey(ctx);
STACK_OF(GENERAL_NAME) *default_sans = NULL;
const X509_NAME *ref_subj =
refcert != NULL ? X509_get_subject_name(refcert) : NULL;
Expand All @@ -286,12 +286,6 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid)
/* RFC5280: subjectAltName MUST be critical if subject is null */
X509_EXTENSIONS *exts = NULL;

if (rkey == NULL && ctx->p10CSR != NULL)
rkey = X509_REQ_get0_pubkey(ctx->p10CSR);
if (rkey == NULL && refcert != NULL)
rkey = X509_get0_pubkey(refcert);
if (rkey == NULL)
rkey = ctx->pkey; /* default is independent of ctx->oldCert */
if (rkey == NULL) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PUBLIC_KEY);
Expand Down Expand Up @@ -411,13 +405,7 @@ OSSL_CMP_MSG *ossl_cmp_certreq_new(OSSL_CMP_CTX *ctx, int type,
if (type != OSSL_CMP_PKIBODY_P10CR) {
EVP_PKEY *privkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1);

/*
* privkey is NULL in case ctx->newPkey does not include a private key.
* We then may try to use ctx->pkey as fallback/default, but only
* if ctx-> newPkey does not include a (non-matching) public key:
*/
if (privkey == NULL && OSSL_CMP_CTX_get0_newPkey(ctx, 0) == NULL)
privkey = ctx->pkey; /* default is independent of ctx->oldCert */
/* privkey is ctx->newPkey (if private, else NULL) or ctx->pkey */
if (ctx->popoMethod >= OSSL_CRMF_POPO_SIGNATURE && privkey == NULL) {
ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY_FOR_POPO);
goto err;
Expand Down Expand Up @@ -1036,14 +1024,15 @@ ossl_cmp_certrepmessage_get0_certresponse(const OSSL_CMP_CERTREPMESSAGE *crm,

/*-
* Retrieve the newly enrolled certificate from the given certResponse crep.
* In case of indirect POPO uses the libctx and propq from ctx and private key.
* Uses libctx and propq from ctx, in case of indirect POPO also private key.
* Returns a pointer to a copy of the found certificate, or NULL if not found.
*/
X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CERTRESPONSE *crep,
const OSSL_CMP_CTX *ctx, EVP_PKEY *pkey)
X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx,
const OSSL_CMP_CERTRESPONSE *crep)
{
OSSL_CMP_CERTORENCCERT *coec;
X509 *crt = NULL;
EVP_PKEY *pkey;

if (!ossl_assert(crep != NULL && ctx != NULL))
return NULL;
Expand All @@ -1056,6 +1045,8 @@ X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CERTRESPONSE *crep,
break;
case OSSL_CMP_CERTORENCCERT_ENCRYPTEDCERT:
/* cert encrypted for indirect PoP; RFC 4210, 5.2.8.2 */
pkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1);
/* pkey is ctx->newPkey (if private, else NULL) or ctx->pkey */
if (pkey == NULL) {
ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY);
return NULL;
Expand Down
4 changes: 2 additions & 2 deletions crypto/cmp/cmp_vfy.c
Expand Up @@ -323,11 +323,11 @@ static int check_cert_path_3gpp(const OSSL_CMP_CTX *ctx,
* verify that the newly enrolled certificate (which assumed rid ==
* OSSL_CMP_CERTREQID) can also be validated with the same trusted store
*/
EVP_PKEY *pkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1);
OSSL_CMP_CERTRESPONSE *crep =
ossl_cmp_certrepmessage_get0_certresponse(msg->body->value.ip,
OSSL_CMP_CERTREQID);
X509 *newcrt = ossl_cmp_certresponse_get1_cert(crep, ctx, pkey);
X509 *newcrt = ossl_cmp_certresponse_get1_cert(ctx, crep);

/*
* maybe better use get_cert_status() from cmp_client.c, which catches
* errors
Expand Down
6 changes: 3 additions & 3 deletions doc/internal/man3/ossl_cmp_pkisi_get_status.pod
Expand Up @@ -43,8 +43,8 @@ ossl_cmp_pkisi_check_pkifailureinfo
# define OSSL_CMP_PKIFAILUREINFO_duplicateCertReq 26
# define OSSL_CMP_PKIFAILUREINFO_MAX 26

X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CERTRESPONSE *crep,
const OSSL_CMP_CTX *ctx, EVP_PKEY *pkey);
X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx,
const OSSL_CMP_CERTRESPONSE *crep);
int ossl_cmp_pkisi_get_status(const OSSL_CMP_PKISI *si);
const char *ossl_cmp_PKIStatus_to_string(int status);
OSSL_CMP_PKIFREETEXT *ossl_cmp_pkisi_get0_statusString(const OSSL_CMP_PKISI *si);
Expand All @@ -55,7 +55,7 @@ ossl_cmp_pkisi_check_pkifailureinfo

ossl_cmp_certresponse_get1_cert() returns a pointer to a copy of the newly
enrolled certificate from the given certResponse I<crep>, or NULL on error.
In case of indirect POPO uses data from the I<ctx> and the private key I<pkey>.
Uses data from I<ctx>, which in case of indirect POPO includes the private key.

ossl_cmp_pkisi_get_status() returns the PKIStatus of I<si>, or -1 on error.

Expand Down
45 changes: 40 additions & 5 deletions test/cmp_client_test.c
Expand Up @@ -78,6 +78,7 @@ static CMP_SES_TEST_FIXTURE *set_up(const char *const test_case_name)
|| !OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_UNPROTECTED_ERRORS, 1)
|| !OSSL_CMP_CTX_set1_oldCert(ctx, client_cert)
|| !OSSL_CMP_CTX_set1_pkey(ctx, client_key)
/* client_key is by default used also for newPkey */
|| !OSSL_CMP_CTX_set1_srvCert(ctx, server_cert)
|| !OSSL_CMP_CTX_set1_referenceValue(ctx, ref, sizeof(ref)))
goto err;
Expand Down Expand Up @@ -253,26 +254,57 @@ static int test_exec_CR_ses_implicit_confirm(void)
&& test_exec_CR_ses(1, 1 /* granted */, 0);
}

static int test_exec_KUR_ses(int transfer_error)
static int test_exec_KUR_ses(int transfer_error, int pubkey, int raverified)
{
SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
fixture->req_type = OSSL_CMP_KUR;
/* ctx->oldCert has already been set */

if (transfer_error)
OSSL_CMP_CTX_set_transfer_cb_arg(fixture->cmp_ctx, NULL);
fixture->expected = transfer_error ? OSSL_CMP_PKISTATUS_trans
: OSSL_CMP_PKISTATUS_accepted;
if (pubkey) {
EVP_PKEY *key = raverified /* wrong key */ ? server_key : client_key;

EVP_PKEY_up_ref(key);
OSSL_CMP_CTX_set0_newPkey(fixture->cmp_ctx, 0 /* not priv */, key);
OSSL_CMP_SRV_CTX_set_accept_raverified(fixture->srv_ctx, 1);
}
if (pubkey || raverified)
OSSL_CMP_CTX_set_option(fixture->cmp_ctx, OSSL_CMP_OPT_POPO_METHOD,
OSSL_CRMF_POPO_RAVERIFIED);
fixture->expected = transfer_error ? OSSL_CMP_PKISTATUS_trans :
raverified ? OSSL_CMP_PKISTATUS_rejection : OSSL_CMP_PKISTATUS_accepted;
EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
return result;
}

static int test_exec_KUR_ses_ok(void)
{
return test_exec_KUR_ses(0);
return test_exec_KUR_ses(0, 0, 0);
}

static int test_exec_KUR_ses_transfer_error(void)
{
return test_exec_KUR_ses(1);
return test_exec_KUR_ses(1, 0, 0);
}

static int test_exec_KUR_ses_wrong_popo(void)
{
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* cf ossl_cmp_verify_popo() */
return test_exec_KUR_ses(0, 0, 1);
#else
return 1;
#endif
}

static int test_exec_KUR_ses_pub(void)
{
return test_exec_KUR_ses(0, 1, 0);
}

static int test_exec_KUR_ses_wrong_pub(void)
{
return test_exec_KUR_ses(0, 1, 1);
}

static int test_certConf_cb(OSSL_CMP_CTX *ctx, X509 *cert, int fail_info,
Expand Down Expand Up @@ -497,6 +529,9 @@ int setup_tests(void)
ADD_TEST(test_exec_IR_ses_poll_total_timeout);
ADD_TEST(test_exec_KUR_ses_ok);
ADD_TEST(test_exec_KUR_ses_transfer_error);
ADD_TEST(test_exec_KUR_ses_wrong_popo);
ADD_TEST(test_exec_KUR_ses_pub);
ADD_TEST(test_exec_KUR_ses_wrong_pub);
ADD_TEST(test_exec_P10CR_ses_ok);
ADD_TEST(test_exec_P10CR_ses_reject);
ADD_TEST(test_try_certreq_poll);
Expand Down
4 changes: 1 addition & 3 deletions test/cmp_msg_test.c
Expand Up @@ -383,7 +383,6 @@ static int execute_certrep_create(CMP_MSG_TEST_FIXTURE *fixture)
OSSL_CMP_CTX *ctx = fixture->cmp_ctx;
OSSL_CMP_CERTREPMESSAGE *crepmsg = OSSL_CMP_CERTREPMESSAGE_new();
OSSL_CMP_CERTRESPONSE *read_cresp, *cresp = OSSL_CMP_CERTRESPONSE_new();
EVP_PKEY *privkey;
X509 *certfromresp = NULL;
int res = 0;

Expand All @@ -405,8 +404,7 @@ static int execute_certrep_create(CMP_MSG_TEST_FIXTURE *fixture)
goto err;
if (!TEST_ptr_null(ossl_cmp_certrepmessage_get0_certresponse(crepmsg, 88)))
goto err;
privkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1); /* may be NULL */
certfromresp = ossl_cmp_certresponse_get1_cert(read_cresp, ctx, privkey);
certfromresp = ossl_cmp_certresponse_get1_cert(ctx, read_cresp);
if (certfromresp == NULL || !TEST_int_eq(X509_cmp(cert, certfromresp), 0))
goto err;

Expand Down

0 comments on commit 09382af

Please sign in to comment.