Add OSSL_CMP_CTX_get1_newChain() and related CLI option -chainout

Also simplify certificate saving in apps/cmp.c

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/12790)
This commit is contained in:
Dr. David von Oheimb 2020-09-04 09:29:01 +02:00
parent 09e76c5dd3
commit 39082af2fa
8 changed files with 84 additions and 37 deletions

View File

@ -129,6 +129,7 @@ static char *opt_out_trusted = NULL;
static int opt_implicit_confirm = 0;
static int opt_disable_confirm = 0;
static char *opt_certout = NULL;
static char *opt_chainout = NULL;
/* certificate enrollment and revocation */
static char *opt_oldcert = NULL;
@ -205,7 +206,7 @@ typedef enum OPTION_choice {
OPT_POLICIES, OPT_POLICY_OIDS, OPT_POLICY_OIDS_CRITICAL,
OPT_POPO, OPT_CSR,
OPT_OUT_TRUSTED, OPT_IMPLICIT_CONFIRM, OPT_DISABLE_CONFIRM,
OPT_CERTOUT,
OPT_CERTOUT, OPT_CHAINOUT,
OPT_OLDCERT, OPT_REVREASON,
@ -314,6 +315,8 @@ const OPTIONS cmp_options[] = {
"confirmation. WARNING: This leads to behavior violating RFC 4210"},
{"certout", OPT_CERTOUT, 's',
"File to save newly enrolled certificate"},
{"chainout", OPT_CHAINOUT, 's',
"File to save the chain of newly enrolled certificate"},
OPT_SECTION("Certificate enrollment and revocation"),
@ -521,7 +524,7 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
{(char **)&opt_popo}, {&opt_csr},
{&opt_out_trusted},
{(char **)&opt_implicit_confirm}, {(char **)&opt_disable_confirm},
{&opt_certout},
{&opt_certout}, {&opt_chainout},
{&opt_oldcert}, {(char **)&opt_revreason},
@ -2066,18 +2069,21 @@ static int write_cert(BIO *bio, X509 *cert)
}
/*
* writes out a stack of certs to the given file.
* If destFile != NULL writes out a stack of certs to the given file.
* In any case frees the certs.
* Depending on options use either PEM or DER format,
* where DER does not make much sense for writing more than one cert!
* Returns number of written certificates on success, 0 on error.
* Returns number of written certificates on success, -1 on error.
*/
static int save_certs(OSSL_CMP_CTX *ctx,
STACK_OF(X509) *certs, char *destFile, char *desc)
static int save_free_certs(OSSL_CMP_CTX *ctx,
STACK_OF(X509) *certs, char *destFile, char *desc)
{
BIO *bio = NULL;
int i;
int n = sk_X509_num(certs);
if (destFile == NULL)
goto end;
CMP_info3("received %d %s certificate(s), saving to file '%s'",
n, desc, destFile);
if (n > 1 && opt_certform != FORMAT_PEM)
@ -2087,19 +2093,20 @@ static int save_certs(OSSL_CMP_CTX *ctx,
|| !BIO_write_filename(bio, (char *)destFile)) {
CMP_err1("could not open file '%s' for writing", destFile);
n = -1;
goto err;
goto end;
}
for (i = 0; i < n; i++) {
if (!write_cert(bio, sk_X509_value(certs, i))) {
CMP_err1("cannot write certificate to file '%s'", destFile);
n = -1;
goto err;
goto end;
}
}
err:
end:
BIO_free(bio);
sk_X509_pop_free(certs, X509_free);
return n;
}
@ -2511,6 +2518,9 @@ static int get_opts(int argc, char **argv)
case OPT_CERTOUT:
opt_certout = opt_str("certout");
break;
case OPT_CHAINOUT:
opt_chainout = opt_str("chainout");
break;
case OPT_OLDCERT:
opt_oldcert = opt_str("oldcert");
break;
@ -2935,39 +2945,26 @@ int cmp_main(int argc, char **argv)
OPENSSL_free(buf);
}
if (opt_cacertsout != NULL) {
STACK_OF(X509) *certs = OSSL_CMP_CTX_get1_caPubs(cmp_ctx);
if (sk_X509_num(certs) > 0
&& save_certs(cmp_ctx, certs, opt_cacertsout, "CA") < 0) {
sk_X509_pop_free(certs, X509_free);
goto err;
}
sk_X509_pop_free(certs, X509_free);
}
if (opt_extracertsout != NULL) {
STACK_OF(X509) *certs = OSSL_CMP_CTX_get1_extraCertsIn(cmp_ctx);
if (sk_X509_num(certs) > 0
&& save_certs(cmp_ctx, certs, opt_extracertsout,
"extra") < 0) {
sk_X509_pop_free(certs, X509_free);
goto err;
}
sk_X509_pop_free(certs, X509_free);
}
if (opt_certout != NULL && newcert != NULL) {
if (save_free_certs(cmp_ctx, OSSL_CMP_CTX_get1_caPubs(cmp_ctx),
opt_cacertsout, "CA") < 0)
goto err;
if (save_free_certs(cmp_ctx, OSSL_CMP_CTX_get1_extraCertsIn(cmp_ctx),
opt_extracertsout, "extra") < 0)
goto err;
if (newcert != NULL) {
STACK_OF(X509) *certs = sk_X509_new_null();
if (certs == NULL || !sk_X509_push(certs, newcert)
|| save_certs(cmp_ctx, certs, opt_certout,
"enrolled") < 0) {
if (!X509_add_cert(certs, newcert, X509_ADD_FLAG_UP_REF)) {
sk_X509_free(certs);
goto err;
}
sk_X509_free(certs);
if (save_free_certs(cmp_ctx, certs, opt_certout, "enrolled") < 0)
goto err;
}
if (save_free_certs(cmp_ctx, OSSL_CMP_CTX_get1_newChain(cmp_ctx),
opt_chainout, "chain") < 0)
goto err;
if (!OSSL_CMP_CTX_reinit(cmp_ctx))
goto err;
}

View File

@ -162,6 +162,7 @@ int OSSL_CMP_CTX_reinit(OSSL_CMP_CTX *ctx)
return ossl_cmp_ctx_set0_statusString(ctx, NULL)
&& ossl_cmp_ctx_set0_newCert(ctx, NULL)
&& ossl_cmp_ctx_set1_newChain(ctx, NULL)
&& ossl_cmp_ctx_set1_caPubs(ctx, NULL)
&& ossl_cmp_ctx_set1_extraCertsIn(ctx, NULL)
&& ossl_cmp_ctx_set0_validatedSrvCert(ctx, NULL)
@ -216,6 +217,7 @@ void OSSL_CMP_CTX_free(OSSL_CMP_CTX *ctx)
sk_ASN1_UTF8STRING_pop_free(ctx->statusString, ASN1_UTF8STRING_free);
X509_free(ctx->newCert);
sk_X509_pop_free(ctx->newChain, X509_free);
sk_X509_pop_free(ctx->caPubs, X509_free);
sk_X509_pop_free(ctx->extraCertsIn, X509_free);
@ -459,6 +461,34 @@ int OSSL_CMP_CTX_set1_secretValue(OSSL_CMP_CTX *ctx, const unsigned char *sec,
return 1;
}
/* Returns the cert chain computed by OSSL_CMP_certConf_cb(), NULL on error */
STACK_OF(X509) *OSSL_CMP_CTX_get1_newChain(const OSSL_CMP_CTX *ctx)
{
if (ctx == NULL) {
CMPerr(0, CMP_R_NULL_ARGUMENT);
return NULL;
}
if (ctx->newChain == NULL)
return sk_X509_new_null();
return X509_chain_up_ref(ctx->newChain);
}
/*
* Copies any given stack of inbound X509 certificates to newChain
* of the OSSL_CMP_CTX structure so that they may be retrieved later.
*/
int ossl_cmp_ctx_set1_newChain(OSSL_CMP_CTX *ctx, STACK_OF(X509) *newChain)
{
if (!ossl_assert(ctx != NULL))
return 0;
sk_X509_pop_free(ctx->newChain, X509_free);
ctx->newChain= NULL;
if (newChain == NULL)
return 1;
return (ctx->newChain = X509_chain_up_ref(newChain)) != NULL;
}
/*
* Returns the stack of certificates received in a response message.
* The stack is duplicated so the caller must handle freeing it!

View File

@ -120,6 +120,7 @@ struct ossl_cmp_ctx_st {
/* TODO: this should be a stack since there could be more than one */
X509 *newCert; /* newly enrolled cert received from the CA */
/* TODO: this should be a stack since there could be more than one */
STACK_OF(X509) *newChain; /* chain of newly enrolled cert received */
STACK_OF(X509) *caPubs; /* CA certs received from server (in IP message) */
STACK_OF(X509) *extraCertsIn; /* extraCerts received from server */
@ -780,6 +781,7 @@ int ossl_cmp_ctx_set0_statusString(OSSL_CMP_CTX *ctx,
OSSL_CMP_PKIFREETEXT *text);
int ossl_cmp_ctx_set_failInfoCode(OSSL_CMP_CTX *ctx, int fail_info);
int ossl_cmp_ctx_set0_newCert(OSSL_CMP_CTX *ctx, X509 *cert);
int ossl_cmp_ctx_set1_newChain(OSSL_CMP_CTX *ctx, STACK_OF(X509) *newChain);
int ossl_cmp_ctx_set1_caPubs(OSSL_CMP_CTX *ctx, STACK_OF(X509) *caPubs);
int ossl_cmp_ctx_set1_extraCertsIn(OSSL_CMP_CTX *ctx,
STACK_OF(X509) *extraCertsIn);

View File

@ -63,6 +63,7 @@ B<openssl> B<cmp>
[B<-implicit_confirm>]
[B<-disable_confirm>]
[B<-certout> I<filename>]
[B<-chainout> I<filename>]
[B<-oldcert> I<filename>]
[B<-revreason> I<number>]
@ -378,6 +379,10 @@ B<WARNING:> This leads to behavior violating RFC 4210.
The file where the newly enrolled certificate should be saved.
=item B<-chainout> I<filename>
The file where the chain of the newly enrolled certificate should be saved.
=back

View File

@ -54,6 +54,7 @@ OSSL_CMP_CTX_get_status,
OSSL_CMP_CTX_get0_statusString,
OSSL_CMP_CTX_get_failInfoCode,
OSSL_CMP_CTX_get0_newCert,
OSSL_CMP_CTX_get1_newChain,
OSSL_CMP_CTX_get1_caPubs,
OSSL_CMP_CTX_get1_extraCertsIn,
OSSL_CMP_CTX_set1_transactionID,
@ -144,6 +145,7 @@ OSSL_CMP_CTX_set1_senderNonce
int OSSL_CMP_CTX_get_failInfoCode(const OSSL_CMP_CTX *ctx);
X509 *OSSL_CMP_CTX_get0_newCert(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_newChain(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_caPubs(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_extraCertsIn(const OSSL_CMP_CTX *ctx);
@ -170,7 +172,7 @@ OSSL_CMP_CTX_free() deallocates an OSSL_CMP_CTX structure.
OSSL_CMP_CTX_reinit() prepares the given B<ctx> for a further transaction by
clearing the internal CMP transaction (aka session) status, PKIStatusInfo,
and any previous results (newCert, caPubs, and extraCertsIn)
and any previous results (newCert, newChain, caPubs, and extraCertsIn)
from the last executed transaction.
All other field values (i.e., CMP options) are retained for potential re-use.
@ -579,6 +581,10 @@ OSSL_CMP_CTX_FAILINFO_badAlg. Returns -1 if the failInfoCode field is unset.
OSSL_CMP_CTX_get0_newCert() returns the pointer to the newly obtained
certificate in case it is available, else NULL.
OSSL_CMP_CTX_get1_newChain() returns a pointer to a duplicate of the stack of
X.509 certificates computed by OSSL_CMP_certConf_cb() (if this function has
been called) on the last received certificate response message IP/CP/KUP.
OSSL_CMP_CTX_get1_caPubs() returns a pointer to a duplicate of the stack of
X.509 certificates received in the caPubs field of last received certificate
response message IP/CP/KUP.
@ -611,6 +617,7 @@ OSSL_CMP_CTX_get0_newPkey(),
OSSL_CMP_CTX_get_certConf_cb_arg(),
OSSL_CMP_CTX_get0_statusString(),
OSSL_CMP_CTX_get0_newCert(),
OSSL_CMP_CTX_get0_newChain(),
OSSL_CMP_CTX_get1_caPubs(), and
OSSL_CMP_CTX_get1_extraCertsIn()
return the intended pointer value as described above or NULL on error.

View File

@ -331,6 +331,7 @@ OSSL_CMP_PKIFREETEXT *OSSL_CMP_CTX_get0_statusString(const OSSL_CMP_CTX *ctx);
int OSSL_CMP_CTX_get_failInfoCode(const OSSL_CMP_CTX *ctx);
# define OSSL_CMP_PKISI_BUFLEN 1024
X509 *OSSL_CMP_CTX_get0_newCert(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_newChain(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_caPubs(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_extraCertsIn(const OSSL_CMP_CTX *ctx);
int OSSL_CMP_CTX_set1_transactionID(OSSL_CMP_CTX *ctx,

View File

@ -76,6 +76,7 @@ static int execute_CTX_reinit_test(OSSL_CMP_CTX_TEST_FIXTURE *fixture)
if (!ossl_cmp_ctx_set0_statusString(ctx, sk_ASN1_UTF8STRING_new_null())
|| !ossl_cmp_ctx_set0_newCert(ctx, X509_new())
|| !TEST_ptr(certs = sk_X509_new_1())
|| !ossl_cmp_ctx_set1_newChain(ctx, certs)
|| !ossl_cmp_ctx_set1_caPubs(ctx, certs)
|| !ossl_cmp_ctx_set1_extraCertsIn(ctx, certs)
|| !ossl_cmp_ctx_set0_validatedSrvCert(ctx, X509_new())
@ -93,6 +94,7 @@ static int execute_CTX_reinit_test(OSSL_CMP_CTX_TEST_FIXTURE *fixture)
&& ctx->failInfoCode == -1
&& ctx->statusString == NULL
&& ctx->newCert == NULL
&& ctx->newChain == NULL
&& ctx->caPubs == NULL
&& ctx->extraCertsIn == NULL
&& ctx->validatedSrvCert == NULL
@ -780,6 +782,7 @@ DEFINE_SET_GET_INT_TEST(ossl_cmp, ctx, status)
DEFINE_SET_GET_SK_TEST(ossl_cmp, ctx, 0, 0, statusString, ASN1_UTF8STRING)
DEFINE_SET_GET_INT_TEST(ossl_cmp, ctx, failInfoCode)
DEFINE_SET_GET_TEST(ossl_cmp, ctx, 0, 0, 0, newCert, X509)
DEFINE_SET_GET_SK_X509_TEST(ossl_cmp, ctx, 1, 1, newChain)
DEFINE_SET_GET_SK_X509_TEST(ossl_cmp, ctx, 1, 1, caPubs)
DEFINE_SET_GET_SK_X509_TEST(ossl_cmp, ctx, 1, 1, extraCertsIn)
@ -869,6 +872,7 @@ int setup_tests(void)
ADD_TEST(test_CTX_set0_get0_statusString);
ADD_TEST(test_CTX_set_get_failInfoCode);
ADD_TEST(test_CTX_set0_get0_newCert);
ADD_TEST(test_CTX_set1_get1_newChain);
ADD_TEST(test_CTX_set1_get1_caPubs);
ADD_TEST(test_CTX_set1_get1_extraCertsIn);
/* exported for testing and debugging purposes: */

View File

@ -4797,6 +4797,7 @@ OSSL_CMP_CTX_get_status ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_get0_statusString ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_get_failInfoCode ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_get0_newCert ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_get1_newChain ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_get1_caPubs ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_get1_extraCertsIn ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_set1_transactionID ? 3_0_0 EXIST::FUNCTION:CMP