Add OSSL_CMP_CTX_get1_newChain() and related CLI option -chainout
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>
Fri, 4 Sep 2020 07:29:01 +0000 (09:29 +0200)
committerDr. David von Oheimb <David.von.Oheimb@siemens.com>
Sat, 5 Sep 2020 16:10:03 +0000 (18:10 +0200)
Also simplify certificate saving in apps/cmp.c

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/12790)

apps/cmp.c
crypto/cmp/cmp_ctx.c
crypto/cmp/cmp_local.h
doc/man1/openssl-cmp.pod.in
doc/man3/OSSL_CMP_CTX_new.pod
include/openssl/cmp.h
test/cmp_ctx_test.c
util/libcrypto.num

index 4d6acdd4997470d2c7f6d351994abab30cae94fb..1af27f7881ef1019465ed2f021b8f788eba1e75c 100644 (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;
     }
index 57878a8f8df9a6ffc6446c4b0ceaa66b302c766b..50c5d0e061f4e54a769a99d2b5337ea800627128 100644 (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!
index 41c10b22c128486a5379a4dd75b94545ebfff66c..a6e55cfd1b9578b9662746130cf14213ca59b43b 100644 (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);
index 760e21ccbeccc3b06c71360b25035f596cf1075f..d91bd3168426ac3281e96f020f1cb21a146aad37 100644 (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
 
 
index 62e1a562c9c5b4fe2088e99f4609c6900e1f932d..fda51504345e7cbc9a67637f2d7a163e6f3c807a 100644 (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.
index cf79a4c71f5bb05746436e71695866219cb04467..d12d48ba4f13e3d8e32a2dc6a604d9ab6e8ae7c3 100644 (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,
index 184e5bf498cf1aebefe140b3547273bb518c8b9e..9b2a53df5fd1f0d5c699e17b3a413ecace911cc7 100644 (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: */
index d5e7ab423ed0b08aab1e6dac316ff0f5ff1576bf..cfe1cb8bc6c9b0284529c9098d70a2bf6f206cee 100644 (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