CMP: add support for genm/genp messages with id-it-caCerts
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>
Fri, 3 Dec 2021 17:17:50 +0000 (18:17 +0100)
committerDr. David von Oheimb <dev@ddvo.net>
Thu, 1 Jun 2023 07:39:12 +0000 (09:39 +0200)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/19231)

18 files changed:
apps/cmp.c
apps/lib/cmp_mock_srv.c
crypto/cmp/build.info
crypto/cmp/cmp_asn.c
crypto/cmp/cmp_err.c
crypto/cmp/cmp_genm.c [new file with mode: 0644]
crypto/cmp/cmp_local.h
crypto/cmp/cmp_msg.c
crypto/err/openssl.txt
doc/build.info
doc/internal/man3/ossl_cmp_mock_srv_new.pod
doc/man1/openssl-cmp.pod.in
doc/man3/OSSL_CMP_ITAV_new_caCerts.pod [new file with mode: 0644]
doc/man3/OSSL_CMP_exec_certreq.pod
include/openssl/cmp.h.in
include/openssl/cmperr.h
test/recipes/80-test_cmp_http_data/test_commands.csv
util/libcrypto.num

index a3d0c19dd3d783f340f95bb4d89f9fab6cea23fb..aec56b442509060d36f4a638b0d146a0c9c4a6b0 100644 (file)
@@ -277,7 +277,7 @@ const OPTIONS cmp_options[] = {
     OPT_SECTION("Generic message"),
     {"cmd", OPT_CMD, 's', "CMP request to send: ir/cr/kur/p10cr/rr/genm"},
     {"infotype", OPT_INFOTYPE, 's',
-     "InfoType name for requesting specific info in genm, e.g. 'signKeyPairTypes'"},
+     "InfoType name for requesting specific info in genm, e.g. 'caCerts'"},
     {"geninfo", OPT_GENINFO, 's',
      "generalInfo integer values to place in request PKIHeader with given OID"},
     {OPT_MORE_STR, 0, 0,
@@ -395,7 +395,7 @@ const OPTIONS cmp_options[] = {
     {"extracertsout", OPT_EXTRACERTSOUT, 's',
      "File to save extra certificates received in the extraCerts field"},
     {"cacertsout", OPT_CACERTSOUT, 's',
-     "File to save CA certificates received in the caPubs field of 'ip' messages"},
+     "File to save CA certs received in caPubs field or genp with id-it-caCerts"},
 
     OPT_SECTION("Client authentication"),
     {"ref", OPT_REF, 's',
@@ -1615,6 +1615,8 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
             CMP_warn1("-days %s", msg);
         if (opt_popo != OSSL_CRMF_POPO_NONE - 1)
             CMP_warn1("-popo %s", msg);
+        if (opt_out_trusted != NULL)
+            CMP_warn1("-out_trusted %s", msg);
     } else if (opt_newkey != NULL) {
         const char *file = opt_newkey;
         const int format = opt_keyform;
@@ -1873,8 +1875,9 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
         }
         goto set_path;
     }
-    if (!OSSL_HTTP_parse_url(opt_server, &use_ssl, NULL /* user */, &host, &port,
-                             &portnum, &path, NULL /* q */, NULL /* frag */)) {
+    if (!OSSL_HTTP_parse_url(opt_server, &use_ssl, NULL /* user */,
+                             &host, &port, &portnum,
+                             &path, NULL /* q */, NULL /* frag */)) {
         CMP_err1("cannot parse -server URL: %s", opt_server);
         goto err;
     }
@@ -1909,7 +1912,12 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
     if (!transform_opts())
         goto err;
 
-    if (opt_infotype_s != NULL) {
+    if (opt_infotype_s == NULL) {
+        if (opt_cmd == CMP_GENM)
+            CMP_warn("no -infotype option given for genm");
+    } else if (opt_cmd != CMP_GENM) {
+        CMP_warn("-infotype option is ignored for commands other than 'genm'");
+    } else {
         char id_buf[100] = "id-it-";
 
         strncat(id_buf, opt_infotype_s, sizeof(id_buf) - strlen(id_buf) - 1);
@@ -2136,9 +2144,8 @@ static int print_itavs(const STACK_OF(OSSL_CMP_ITAV) *itavs)
         if (i2t_ASN1_OBJECT(name, sizeof(name), type) <= 0) {
             CMP_err1("error parsing type of ITAV #%d from genp", i);
             ret = 0;
-        }
-        else {
-            CMP_info2("ITAV #%d from genp type=%s", i, name);
+        } else {
+            CMP_info2("ITAV #%d from genp infoType=%s", i, name);
         }
     }
     return ret;
@@ -2829,6 +2836,56 @@ static void print_status(void)
     OPENSSL_free(buf);
 }
 
+static int do_genm(OSSL_CMP_CTX *ctx)
+{
+    if (opt_infotype == NID_id_it_caCerts) {
+        STACK_OF(X509) *cacerts = NULL;
+
+        if (opt_cacertsout == NULL) {
+            CMP_err("Missing -cacertsout option for -infotype caCerts");
+            return 0;
+        }
+
+        if (!OSSL_CMP_get_caCerts(ctx, &cacerts))
+            return 0;
+
+        /* could check authorization of sender/origin at this point */
+        if (cacerts == NULL) {
+            CMP_warn("no CA certificates provided by server");
+        } else if (save_free_certs(cacerts, opt_cacertsout, "CA") < 0) {
+            CMP_err1("Failed to store CA certficates from genp in %s",
+                     opt_cacertsout);
+            return 0;
+        }
+        return 1;
+    } else {
+        OSSL_CMP_ITAV *req;
+        STACK_OF(OSSL_CMP_ITAV) *itavs;
+
+        if (opt_infotype != NID_undef) {
+            CMP_warn1("No specific support for -infotype %s available",
+                      opt_infotype_s);
+
+            req = OSSL_CMP_ITAV_create(OBJ_nid2obj(opt_infotype), NULL);
+            if (req == NULL || !OSSL_CMP_CTX_push0_genm_ITAV(ctx, req)) {
+                CMP_err1("Failed to create genm for -infotype %s",
+                         opt_infotype_s);
+                return 0;
+            }
+        }
+
+        if ((itavs = OSSL_CMP_exec_GENM_ses(ctx)) != NULL) {
+            int res = print_itavs(itavs);
+
+            sk_OSSL_CMP_ITAV_pop_free(itavs, OSSL_CMP_ITAV_free);
+            return res;
+        }
+        if (OSSL_CMP_CTX_get_status(ctx) != OSSL_CMP_PKISTATUS_request)
+            CMP_err("Did not receive response on genm or genp is not valid");
+        return 0;
+    }
+}
+
 int cmp_main(int argc, char **argv)
 {
     char *configfile = NULL;
@@ -3041,26 +3098,7 @@ int cmp_main(int argc, char **argv)
             ret = OSSL_CMP_exec_RR_ses(cmp_ctx);
             break;
         case CMP_GENM:
-            {
-                STACK_OF(OSSL_CMP_ITAV) *itavs;
-
-                if (opt_infotype != NID_undef) {
-                    OSSL_CMP_ITAV *itav =
-                        OSSL_CMP_ITAV_create(OBJ_nid2obj(opt_infotype), NULL);
-
-                    if (itav == NULL)
-                        goto err;
-                    OSSL_CMP_CTX_push0_genm_ITAV(cmp_ctx, itav);
-                }
-
-                if ((itavs = OSSL_CMP_exec_GENM_ses(cmp_ctx)) != NULL) {
-                    ret = print_itavs(itavs);
-                    sk_OSSL_CMP_ITAV_pop_free(itavs, OSSL_CMP_ITAV_free);
-                } else {
-                    CMP_err("could not obtain ITAVs from genp");
-                }
-                break;
-            }
+            ret = do_genm(cmp_ctx);
         default:
             break;
         }
index c19651221b10d5c6329da603235ac9aa3d5de56c..fac396bd2e99a51d9faccd3e66ae86f0120ba3d6 100644 (file)
@@ -21,7 +21,7 @@ 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 */
+    STACK_OF(X509) *caPubsOut; /* used in caPubs of ip and in caCerts of genp */
     OSSL_CMP_PKISI *statusOut; /* status for ip/cp/kup/rp msg unless polling */
     int sendError;             /* send error response on given request type */
     OSSL_CMP_MSG *certReq;     /* ir/cr/p10cr/kur remembered while polling */
@@ -30,7 +30,6 @@ typedef struct
     int checkAfterTime;        /* time the client should wait between polling */
 } mock_srv_ctx;
 
-
 static void mock_srv_ctx_free(mock_srv_ctx *ctx)
 {
     if (ctx == NULL)
@@ -332,6 +331,21 @@ static int process_genm(OSSL_CMP_SRV_CTX *srv_ctx,
         ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE);
         return 0;
     }
+    if (sk_OSSL_CMP_ITAV_num(in) == 1) {
+        OSSL_CMP_ITAV *req = sk_OSSL_CMP_ITAV_value(in, 0), *rsp;
+        ASN1_OBJECT *obj = OSSL_CMP_ITAV_get0_type(req);
+
+        if (OBJ_obj2nid(obj) == NID_id_it_caCerts) {
+            if ((*out = sk_OSSL_CMP_ITAV_new_reserve(NULL, 1)) == NULL)
+                return 0;
+            if ((rsp = OSSL_CMP_ITAV_new_caCerts(ctx->caPubsOut)) == NULL) {
+                sk_OSSL_CMP_ITAV_free(*out);
+                return 0;
+            }
+            (void)sk_OSSL_CMP_ITAV_push(*out, rsp);
+            return 1;
+        }
+    }
 
     *out = sk_OSSL_CMP_ITAV_deep_copy(in, OSSL_CMP_ITAV_dup,
                                       OSSL_CMP_ITAV_free);
index a2a57c14ec10061234ee3e992a2f6e834cffcabc..ad67c434cce9e36abedd4c4d99ecab0b98dd703a 100644 (file)
@@ -1,4 +1,4 @@
 LIBS=../../libcrypto
 SOURCE[../../libcrypto]= cmp_asn.c cmp_ctx.c cmp_err.c cmp_util.c \
         cmp_status.c cmp_hdr.c cmp_protect.c cmp_msg.c cmp_vfy.c \
-        cmp_server.c cmp_client.c cmp_http.c
+        cmp_server.c cmp_client.c cmp_genm.c cmp_http.c
index e1ecc40838cd5d2b918afe2652a350f405801678..b97c8323ff6f52ca58567edfa332e36e3bb0d7f0 100644 (file)
@@ -114,10 +114,11 @@ ASN1_ADB(OSSL_CMP_ITAV) = {
     ADB_ENTRY(NID_id_it_suppLangTags,
               ASN1_SEQUENCE_OF_OPT(OSSL_CMP_ITAV, infoValue.suppLangTagsValue,
                                    ASN1_UTF8STRING)),
+    ADB_ENTRY(NID_id_it_caCerts,
+              ASN1_SEQUENCE_OF_OPT(OSSL_CMP_ITAV, infoValue.caCerts, X509)),
 } ASN1_ADB_END(OSSL_CMP_ITAV, 0, infoType, 0,
                &infotypeandvalue_default_tt, NULL);
 
-
 ASN1_SEQUENCE(OSSL_CMP_ITAV) = {
     ASN1_SIMPLE(OSSL_CMP_ITAV, infoType, ASN1_OBJECT),
     ASN1_ADB_OBJECT(OSSL_CMP_ITAV)
@@ -183,6 +184,37 @@ int OSSL_CMP_ITAV_push0_stack_item(STACK_OF(OSSL_CMP_ITAV) **itav_sk_p,
     return 0;
 }
 
+OSSL_CMP_ITAV *OSSL_CMP_ITAV_new_caCerts(const STACK_OF(X509) *caCerts)
+{
+    OSSL_CMP_ITAV *itav = OSSL_CMP_ITAV_new();
+
+    if (itav == NULL)
+        return NULL;
+    if (sk_X509_num(caCerts) > 0
+        && (itav->infoValue.caCerts =
+            sk_X509_deep_copy(caCerts, X509_dup, X509_free)) == NULL) {
+        OSSL_CMP_ITAV_free(itav);
+        return NULL;
+    }
+    itav->infoType = OBJ_nid2obj(NID_id_it_caCerts);
+    return itav;
+}
+
+int OSSL_CMP_ITAV_get0_caCerts(const OSSL_CMP_ITAV *itav, STACK_OF(X509) **out)
+{
+    if (itav == NULL || out == NULL) {
+        ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (OBJ_obj2nid(itav->infoType) != NID_id_it_caCerts) {
+        ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    *out = sk_X509_num(itav->infoValue.caCerts) > 0
+        ? itav->infoValue.caCerts : NULL;
+    return 1;
+}
+
 /* get ASN.1 encoded integer, return -1 on error */
 int ossl_cmp_asn1_get_int(const ASN1_INTEGER *a)
 {
index dfc6dfbedee908ca5d813a4a47fb2f6ef6ce1740..9d0e9680329f01e46c987007e94940a08f18756c 100644 (file)
@@ -84,7 +84,9 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
     "failure obtaining random"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAIL_INFO_OUT_OF_RANGE),
     "fail info out of range"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_GETTING_GENP), "getting genp"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_ARGS), "invalid args"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_GENP), "invalid genp"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_OPTION), "invalid option"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_CERTID), "missing certid"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION),
@@ -139,6 +141,7 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_TRANSACTIONID_UNMATCHED),
     "transactionid unmatched"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_TRANSFER_ERROR), "transfer error"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNCLEAN_CTX), "unclean ctx"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_PKIBODY), "unexpected pkibody"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_PKISTATUS),
     "unexpected pkistatus"},
diff --git a/crypto/cmp/cmp_genm.c b/crypto/cmp/cmp_genm.c
new file mode 100644 (file)
index 0000000..f4be997
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright Siemens AG 2022
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "cmp_local.h"
+#include <openssl/cmp_util.h>
+
+static const X509_VERIFY_PARAM *get0_trustedStore_vpm(const OSSL_CMP_CTX *ctx)
+{
+    const X509_STORE *ts = OSSL_CMP_CTX_get0_trustedStore(ctx);
+
+    return ts == NULL ? NULL : X509_STORE_get0_param(ts);
+}
+
+static void cert_msg(const char *func, const char *file, int lineno,
+                     OSSL_CMP_severity level, OSSL_CMP_CTX *ctx,
+                     const char *source, X509 *cert, const char *msg)
+{
+    char *subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+
+    ossl_cmp_print_log(level, ctx, func, file, lineno,
+                       level == OSSL_CMP_LOG_WARNING ? "WARN" : "ERR",
+                       "certificate from '%s' with subject '%s' %s",
+                       source, subj, msg);
+    OPENSSL_free(subj);
+}
+
+/* use |type_CA| -1 (no CA type check) or 0 (must be EE) or 1 (must be CA) */
+static int ossl_X509_check(OSSL_CMP_CTX *ctx, const char *source, X509 *cert,
+                           int type_CA, const X509_VERIFY_PARAM *vpm)
+{
+    uint32_t ex_flags = X509_get_extension_flags(cert);
+    int res = X509_cmp_timeframe(vpm, X509_get0_notBefore(cert),
+                                 X509_get0_notAfter(cert));
+    int ret = res == 0;
+    OSSL_CMP_severity level =
+        vpm == NULL ? OSSL_CMP_LOG_WARNING : OSSL_CMP_LOG_ERR;
+
+    if (!ret)
+        cert_msg(OPENSSL_FUNC, OPENSSL_FILE, OPENSSL_LINE, level, ctx,
+                 source, cert, res > 0 ? "has expired" : "not yet valid");
+    if (type_CA >= 0 && (ex_flags & EXFLAG_V1) == 0) {
+        int is_CA = (ex_flags & EXFLAG_CA) != 0;
+
+        if ((type_CA != 0) != is_CA) {
+            cert_msg(OPENSSL_FUNC, OPENSSL_FILE, OPENSSL_LINE, level, ctx,
+                     source, cert,
+                     is_CA ? "is not an EE cert" : "is not a CA cert");
+            ret = 0;
+        }
+    }
+    return ret;
+}
+
+static int ossl_X509_check_all(OSSL_CMP_CTX *ctx, const char *source,
+                               STACK_OF(X509) *certs,
+                               int type_CA, const X509_VERIFY_PARAM *vpm)
+{
+    int i;
+    int ret = 1;
+
+    for (i = 0; i < sk_X509_num(certs /* may be NULL */); i++)
+        ret = ossl_X509_check(ctx, source,
+                              sk_X509_value(certs, i), type_CA, vpm)
+            && ret; /* Having 'ret' after the '&&', all certs are checked. */
+    return ret;
+}
+
+static OSSL_CMP_ITAV *get_genm_itav(OSSL_CMP_CTX *ctx,
+                                    OSSL_CMP_ITAV *req, /* gets consumed */
+                                    int expected, const char *desc)
+{
+    STACK_OF(OSSL_CMP_ITAV) *itavs = NULL;
+    int i, n;
+
+    if (ctx == NULL) {
+        ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+        goto err;
+    }
+    if (OSSL_CMP_CTX_get_status(ctx) != OSSL_CMP_PKISTATUS_unspecified) {
+        ERR_raise_data(ERR_LIB_CMP, CMP_R_UNCLEAN_CTX,
+                       "client context in unsuitable state; should call CMPclient_reinit() before");
+        goto err;
+    }
+
+    if (!OSSL_CMP_CTX_push0_genm_ITAV(ctx, req))
+        goto err;
+    req = NULL;
+    itavs = OSSL_CMP_exec_GENM_ses(ctx);
+    if (itavs == NULL) {
+        if (OSSL_CMP_CTX_get_status(ctx) != OSSL_CMP_PKISTATUS_request)
+            ERR_raise_data(ERR_LIB_CMP, CMP_R_GETTING_GENP,
+                           "with infoType %s", desc);
+        return NULL;
+    }
+
+    if ((n = sk_OSSL_CMP_ITAV_num(itavs)) <= 0) {
+        ERR_raise_data(ERR_LIB_CMP, CMP_R_INVALID_GENP,
+                       "response on genm requesting infoType %s does not include suitable value", desc);
+        sk_OSSL_CMP_ITAV_free(itavs);
+        return NULL;
+    }
+
+    if (n > 1)
+        ossl_cmp_log2(WARN, ctx,
+                      "response on genm contains %d ITAVs; will use the first ITAV with infoType id-it-%s",
+                      n, desc);
+    for (i = 0; i < n; i++) {
+        OSSL_CMP_ITAV *itav = sk_OSSL_CMP_ITAV_shift(itavs);
+        ASN1_OBJECT *obj = OSSL_CMP_ITAV_get0_type(itav);
+        char name[128] = "genp contains InfoType '";
+        size_t offset = strlen(name);
+
+        if (OBJ_obj2nid(obj) == expected) {
+            for (i++; i < n; i++)
+                OSSL_CMP_ITAV_free(sk_OSSL_CMP_ITAV_shift(itavs));
+            sk_OSSL_CMP_ITAV_free(itavs);
+            return itav;
+        }
+
+        if (OBJ_obj2txt(name + offset, sizeof(name) - offset, obj, 0) < 0)
+            strcat(name, "<unknown>");
+        ossl_cmp_log2(WARN, ctx, "%s' while expecting 'id-it-%s'", name, desc);
+        OSSL_CMP_ITAV_free(itav);
+    }
+    ERR_raise_data(ERR_LIB_CMP, CMP_R_INVALID_GENP,
+                   "could not find any ITAV for %s", desc);
+
+ err:
+    sk_OSSL_CMP_ITAV_free(itavs);
+    OSSL_CMP_ITAV_free(req);
+    return NULL;
+}
+
+int OSSL_CMP_get_caCerts(OSSL_CMP_CTX *ctx, STACK_OF(X509) **out)
+{
+    OSSL_CMP_ITAV *req, *itav;
+    STACK_OF(X509) *certs = NULL;
+    int ret = 0;
+
+    if (out == NULL) {
+        ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+        return 0;
+    }
+    *out = NULL;
+
+    if ((req = OSSL_CMP_ITAV_new_caCerts(NULL)) == NULL)
+        return 0;
+    if ((itav = get_genm_itav(ctx, req, NID_id_it_caCerts, "caCerts")) == NULL)
+        return 0;
+    if (!OSSL_CMP_ITAV_get0_caCerts(itav, &certs))
+        goto end;
+    ret = 1;
+    if (certs == NULL) /* no CA certificate available */
+        goto end;
+
+    if (!ossl_X509_check_all(ctx, "genp", certs, 1 /* CA */,
+                             get0_trustedStore_vpm(ctx))) {
+        ret = 0;
+        goto end;
+    }
+    *out = sk_X509_new_reserve(NULL, sk_X509_num(certs));
+    if (!X509_add_certs(*out, certs,
+                        X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP)) {
+        sk_X509_pop_free(*out, X509_free);
+        *out = NULL;
+        ret = 0;
+    }
+
+ end:
+    OSSL_CMP_ITAV_free(itav);
+    return ret;
+}
index 507496e1499d763542fe2c6a1a6f98e3d409b7a2..1a27d39abf131768da90c95d3463c67eaa5a972c 100644 (file)
@@ -247,6 +247,8 @@ struct ossl_cmp_itav_st {
         OSSL_CMP_MSGS *origPKIMessage;
         /* NID_id_it_suppLangTags - Supported Language Tags */
         STACK_OF(ASN1_UTF8STRING) *suppLangTagsValue;
+        /* NID_id_it_caCerts - CA Certificates */
+        STACK_OF(X509) *caCerts;
         /* this is to be used for so far undeclared objects */
         ASN1_TYPE *other;
     } infoValue;
index 689a31a7673206c0875fe8bc250b76c864fde3e1..806abe599d6b4d69954172511fdfe2faad4aaf81 100644 (file)
@@ -672,7 +672,7 @@ int ossl_cmp_msg_gen_push1_ITAVs(OSSL_CMP_MSG *msg,
 }
 
 /*
- * Creates a new General Message/Response with an empty itav stack
+ * Creates a new General Message/Response with a copy of the given itav stack
  * returns a pointer to the PKIMessage on success, NULL on error
  */
 static OSSL_CMP_MSG *gen_new(OSSL_CMP_CTX *ctx,
index 753fb6557dcb7cdd0b15611122bc31aacdd0ca65..a3fac682322567e04a9119d3c28df47f0375d203 100644 (file)
@@ -231,7 +231,9 @@ CMP_R_FAILED_BUILDING_OWN_CHAIN:164:failed building own chain
 CMP_R_FAILED_EXTRACTING_PUBKEY:141:failed extracting pubkey
 CMP_R_FAILURE_OBTAINING_RANDOM:110:failure obtaining random
 CMP_R_FAIL_INFO_OUT_OF_RANGE:129:fail info out of range
+CMP_R_GETTING_GENP:192:getting genp
 CMP_R_INVALID_ARGS:100:invalid args
+CMP_R_INVALID_GENP:193:invalid genp
 CMP_R_INVALID_OPTION:174:invalid option
 CMP_R_MISSING_CERTID:165:missing certid
 CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION:130:\
@@ -268,6 +270,7 @@ CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG:151:srvcert does not validate msg
 CMP_R_TOTAL_TIMEOUT:184:total timeout
 CMP_R_TRANSACTIONID_UNMATCHED:152:transactionid unmatched
 CMP_R_TRANSFER_ERROR:159:transfer error
+CMP_R_UNCLEAN_CTX:191:unclean ctx
 CMP_R_UNEXPECTED_PKIBODY:133:unexpected pkibody
 CMP_R_UNEXPECTED_PKISTATUS:185:unexpected pkistatus
 CMP_R_UNEXPECTED_PVNO:153:unexpected pvno
index 52db908985a25d9e7777e8c2498e27163a7a733e..6031a85d4d3b18b5069c34e4e7f2191ce6799cbf 100644 (file)
@@ -1591,6 +1591,10 @@ DEPEND[html/man3/OSSL_CMP_HDR_get0_transactionID.html]=man3/OSSL_CMP_HDR_get0_tr
 GENERATE[html/man3/OSSL_CMP_HDR_get0_transactionID.html]=man3/OSSL_CMP_HDR_get0_transactionID.pod
 DEPEND[man/man3/OSSL_CMP_HDR_get0_transactionID.3]=man3/OSSL_CMP_HDR_get0_transactionID.pod
 GENERATE[man/man3/OSSL_CMP_HDR_get0_transactionID.3]=man3/OSSL_CMP_HDR_get0_transactionID.pod
+DEPEND[html/man3/OSSL_CMP_ITAV_new_caCerts.html]=man3/OSSL_CMP_ITAV_new_caCerts.pod
+GENERATE[html/man3/OSSL_CMP_ITAV_new_caCerts.html]=man3/OSSL_CMP_ITAV_new_caCerts.pod
+DEPEND[man/man3/OSSL_CMP_ITAV_new_caCerts.3]=man3/OSSL_CMP_ITAV_new_caCerts.pod
+GENERATE[man/man3/OSSL_CMP_ITAV_new_caCerts.3]=man3/OSSL_CMP_ITAV_new_caCerts.pod
 DEPEND[html/man3/OSSL_CMP_ITAV_set0.html]=man3/OSSL_CMP_ITAV_set0.pod
 GENERATE[html/man3/OSSL_CMP_ITAV_set0.html]=man3/OSSL_CMP_ITAV_set0.pod
 DEPEND[man/man3/OSSL_CMP_ITAV_set0.3]=man3/OSSL_CMP_ITAV_set0.pod
@@ -3289,6 +3293,7 @@ html/man3/OSSL_ALGORITHM.html \
 html/man3/OSSL_CALLBACK.html \
 html/man3/OSSL_CMP_CTX_new.html \
 html/man3/OSSL_CMP_HDR_get0_transactionID.html \
+html/man3/OSSL_CMP_ITAV_new_caCerts.html \
 html/man3/OSSL_CMP_ITAV_set0.html \
 html/man3/OSSL_CMP_MSG_get0_header.html \
 html/man3/OSSL_CMP_MSG_http_perform.html \
@@ -3924,6 +3929,7 @@ man/man3/OSSL_ALGORITHM.3 \
 man/man3/OSSL_CALLBACK.3 \
 man/man3/OSSL_CMP_CTX_new.3 \
 man/man3/OSSL_CMP_HDR_get0_transactionID.3 \
+man/man3/OSSL_CMP_ITAV_new_caCerts.3 \
 man/man3/OSSL_CMP_ITAV_set0.3 \
 man/man3/OSSL_CMP_MSG_get0_header.3 \
 man/man3/OSSL_CMP_MSG_http_perform.3 \
index 119077ea7cd210cf47d3eaabaadba3ae767c58bb..59568c65fcf9f823275641dd2cc51c881514932f 100644 (file)
@@ -45,16 +45,17 @@ ossl_cmp_mock_srv_set1_refCert() sets the reference certificate to be expected
 for rr messages and for any oldCertID included in kur messages.
 
 ossl_cmp_mock_srv_set1_certOut() sets the certificate to be returned in
-cp/ip/kup.
+cp/ip/kup messages.
 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_mock_srv_set1_chainOut() sets the certificate chain to be added to
-the extraCerts in a cp/ip/kup.
+the extraCerts in a cp/ip/kup message.
 It should be useful for the validation of the certificate given via
 ossl_cmp_mock_srv_set1_certOut().
 
-ossl_cmp_mock_srv_set1_caPubsOut() sets the caPubs to be returned in an ip.
+ossl_cmp_mock_srv_set1_caPubsOut() sets the caPubs to be returned in an ip msg
+and the list of certificates to be returned in a genp of infoType caCerts.
 
 ossl_cmp_mock_srv_set_statusInfo() sets the status info to be returned.
 
index 76c4313bd5cf9a31b3c5357ddc363e77c501579e..2c8a8b2540a05368b1e4e613e7ffa91ebc21410f 100644 (file)
@@ -236,6 +236,7 @@ ITAV B<infoType>s is printed to stdout.
 
 Set InfoType name to use for requesting specific info in B<genm>,
 e.g., C<signKeyPairTypes>.
+So far, there is specific support for C<caCerts>.
 
 =item B<-geninfo> I<OID:int:N>
 
@@ -655,7 +656,8 @@ field of the last received response message that is not a pollRep nor PKIConf.
 =item B<-cacertsout> I<filename>
 
 The file where to save the list of CA certificates contained in the caPubs field
-if a positive certificate response (i.e., IP, CP, or KUP) message was received.
+if a positive certificate response (i.e., IP, CP, or KUP) message was received
+or contained in a general response (genp) message with infoType C<caCerts>.
 
 =back
 
diff --git a/doc/man3/OSSL_CMP_ITAV_new_caCerts.pod b/doc/man3/OSSL_CMP_ITAV_new_caCerts.pod
new file mode 100644 (file)
index 0000000..eb39738
--- /dev/null
@@ -0,0 +1,59 @@
+=pod
+
+=head1 NAME
+
+OSSL_CMP_ITAV_new_caCerts,
+OSSL_CMP_ITAV_get0_caCerts
+- CMP utility functions for handling specific genm and genp messages
+
+=head1 SYNOPSIS
+
+ #include <openssl/cmp.h>
+
+ OSSL_CMP_ITAV *OSSL_CMP_ITAV_new_caCerts(const STACK_OF(X509) *caCerts);
+ int OSSL_CMP_ITAV_get0_caCerts(const OSSL_CMP_ITAV *itav, STACK_OF(X509) **out);
+
+=head1 DESCRIPTION
+
+ITAV is short for InfoTypeAndValue.
+
+OSSL_CMP_ITAV_new_caCerts() creates an B<OSSL_CMP_ITAV> structure of type
+B<caCerts> and fills it with a copy of the provided list of certificates.
+The I<caCerts> argument may be NULL or contain any number of certificates.
+
+OSSL_CMP_ITAV_get0_caCerts() requires that I<itav> has type B<caCerts>.
+It assigns NULL to I<*out> if there are no CA certificates in I<itav>, otherwise
+the internal pointer of type B<STACK_OF(X509)> with the certificates present.
+
+=head1 NOTES
+
+CMP is defined in RFC 4210.
+
+=head1 RETURN VALUES
+
+OSSL_CMP_ITAV_new_caCerts()
+returns a pointer to the new ITAV structure on success, or NULL on error.
+
+OSSL_CMP_ITAV_get0_caCerts()
+returns 1 on success, 0 on error.
+
+=head1 SEE ALSO
+
+L<OSSL_CMP_ITAV_create(3)> and L<OSSL_CMP_ITAV_get0_type(3)>
+
+=head1 HISTORY
+
+OSSL_CMP_ITAV_new_caCerts() and
+OSSL_CMP_ITAV_get0_rootCaCert()
+were added in OpenSSL 3.2.
+
+=head1 COPYRIGHT
+
+Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
index b0d81c7c41a96894d96f91e72c3119c2b4b889ca..a418657e3c4a7bb0bcfb8135560e26e0797f4e63 100644 (file)
@@ -13,7 +13,8 @@ OSSL_CMP_P10CR,
 OSSL_CMP_KUR,
 OSSL_CMP_try_certreq,
 OSSL_CMP_exec_RR_ses,
-OSSL_CMP_exec_GENM_ses
+OSSL_CMP_exec_GENM_ses,
+OSSL_CMP_get_caCerts
 - functions implementing CMP client transactions
 
 =head1 SYNOPSIS
@@ -34,6 +35,7 @@ OSSL_CMP_exec_GENM_ses
                           const OSSL_CRMF_MSG *crm, int *checkAfter);
  int OSSL_CMP_exec_RR_ses(OSSL_CMP_CTX *ctx);
  STACK_OF(OSSL_CMP_ITAV) *OSSL_CMP_exec_GENM_ses(OSSL_CMP_CTX *ctx);
+ int OSSL_CMP_get_caCerts(OSSL_CMP_CTX *ctx, STACK_OF(X509) **out);
 
 =head1 DESCRIPTION
 
@@ -115,6 +117,12 @@ and returns the list of B<ITAV>s received in the GENP message.
 This can be used, for instance, to poll for CRLs or CA Key Updates.
 See RFC 4210 section 5.3.19 and appendix E.5 for details.
 
+OSSL_CMP_get_caCerts() uses a genm/gemp message exchange with infoType caCerts
+to obtain a list of CA certificates from the CMP server referenced by I<ctx>.
+On success it assigns to I<*out> the list of certificates received,
+which must be freed by the caller.
+NULL means that no CA certificate is available at the server.
+
 =head1 NOTES
 
 CMP is defined in RFC 4210 (and CRMF in RFC 4211).
@@ -138,7 +146,8 @@ In the latter case L<OSSL_CMP_CTX_get0_newCert(3)> yields NULL
 and the output parameter I<checkAfter> has been used to
 assign the received value unless I<checkAfter> is NULL.
 
-OSSL_CMP_exec_RR_ses() returns 1 on success, 0 on error.
+OSSL_CMP_exec_RR_ses() and OSSL_CMP_get_caCerts()
+return 1 on success, 0 on error.
 
 OSSL_CMP_exec_GENM_ses() returns NULL on error,
 otherwise a pointer to the sequence of B<ITAV> received, which may be empty.
@@ -161,6 +170,8 @@ L<OSSL_CMP_MSG_http_perform(3)>
 
 The OpenSSL CMP support was added in OpenSSL 3.0.
 
+OSSL_CMP_get_caCerts() was added in OpenSSL 3.2.
+
 =head1 COPYRIGHT
 
 Copyright 2007-2021 The OpenSSL Project Authors. All Rights Reserved.
index 4e14200d82d9e1f3a0ffc4279e5fae85d9b4132f..c986674be2dc476d43a809deaa5adcbe42d2272e 100644 (file)
@@ -140,7 +140,6 @@ extern "C" {
 #  if OSSL_CMP_PKIFAILUREINFO_MAX_BIT_PATTERN > INT_MAX
 #   error CMP_PKIFAILUREINFO_MAX bit pattern does not fit in type int
 #  endif
-
 typedef ASN1_BIT_STRING OSSL_CMP_PKIFAILUREINFO;
 
 #  define OSSL_CMP_CTX_FAILINFO_badAlg (1 << 0)
@@ -206,8 +205,8 @@ typedef ASN1_BIT_STRING OSSL_CMP_PKIFAILUREINFO;
 #  define OSSL_CMP_PKISTATUS_revocationWarning      4
 #  define OSSL_CMP_PKISTATUS_revocationNotification 5
 #  define OSSL_CMP_PKISTATUS_keyUpdateWarning       6
-
 typedef ASN1_INTEGER OSSL_CMP_PKISTATUS;
+
 DECLARE_ASN1_ITEM(OSSL_CMP_PKISTATUS)
 
 #  define OSSL_CMP_CERTORENCCERT_CERTIFICATE 0
@@ -261,6 +260,10 @@ ASN1_TYPE *OSSL_CMP_ITAV_get0_value(const OSSL_CMP_ITAV *itav);
 int OSSL_CMP_ITAV_push0_stack_item(STACK_OF(OSSL_CMP_ITAV) **itav_sk_p,
                                    OSSL_CMP_ITAV *itav);
 void OSSL_CMP_ITAV_free(OSSL_CMP_ITAV *itav);
+
+OSSL_CMP_ITAV *OSSL_CMP_ITAV_new_caCerts(const STACK_OF(X509) *caCerts);
+int OSSL_CMP_ITAV_get0_caCerts(const OSSL_CMP_ITAV *itav, STACK_OF(X509) **out);
+
 void OSSL_CMP_MSG_free(OSSL_CMP_MSG *msg);
 
 /* from cmp_ctx.c */
@@ -317,9 +320,9 @@ void *OSSL_CMP_CTX_get_transfer_cb_arg(const OSSL_CMP_CTX *ctx);
 int OSSL_CMP_CTX_set1_srvCert(OSSL_CMP_CTX *ctx, X509 *cert);
 int OSSL_CMP_CTX_set1_expected_sender(OSSL_CMP_CTX *ctx, const X509_NAME *name);
 int OSSL_CMP_CTX_set0_trustedStore(OSSL_CMP_CTX *ctx, X509_STORE *store);
-#define OSSL_CMP_CTX_set0_trusted OSSL_CMP_CTX_set0_trustedStore
+#  define OSSL_CMP_CTX_set0_trusted OSSL_CMP_CTX_set0_trustedStore
 X509_STORE *OSSL_CMP_CTX_get0_trustedStore(const OSSL_CMP_CTX *ctx);
-#define OSSL_CMP_CTX_get0_trusted OSSL_CMP_CTX_get0_trustedStore
+#  define OSSL_CMP_CTX_get0_trusted OSSL_CMP_CTX_get0_trustedStore
 int OSSL_CMP_CTX_set1_untrusted(OSSL_CMP_CTX *ctx, STACK_OF(X509) *certs);
 STACK_OF(X509) *OSSL_CMP_CTX_get0_untrusted(const OSSL_CMP_CTX *ctx);
 /* client authentication: */
@@ -478,6 +481,9 @@ int OSSL_CMP_try_certreq(OSSL_CMP_CTX *ctx, int req_type,
 int OSSL_CMP_exec_RR_ses(OSSL_CMP_CTX *ctx);
 STACK_OF(OSSL_CMP_ITAV) *OSSL_CMP_exec_GENM_ses(OSSL_CMP_CTX *ctx);
 
+/* from cmp_genm.c */
+int OSSL_CMP_get_caCerts(OSSL_CMP_CTX *ctx, STACK_OF(X509) **out);
+
 #  ifdef  __cplusplus
 }
 #  endif
index 49fd5e392a17d73d561aed745d2b82a9b642e3e4..f4435d825d08b41f9c229b247e1c19542f09f9bf 100644 (file)
@@ -59,7 +59,9 @@
 #  define CMP_R_FAILED_EXTRACTING_PUBKEY                   141
 #  define CMP_R_FAILURE_OBTAINING_RANDOM                   110
 #  define CMP_R_FAIL_INFO_OUT_OF_RANGE                     129
+#  define CMP_R_GETTING_GENP                               192
 #  define CMP_R_INVALID_ARGS                               100
+#  define CMP_R_INVALID_GENP                               193
 #  define CMP_R_INVALID_OPTION                             174
 #  define CMP_R_MISSING_CERTID                             165
 #  define CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION  130
@@ -94,6 +96,7 @@
 #  define CMP_R_TOTAL_TIMEOUT                              184
 #  define CMP_R_TRANSACTIONID_UNMATCHED                    152
 #  define CMP_R_TRANSFER_ERROR                             159
+#  define CMP_R_UNCLEAN_CTX                                191
 #  define CMP_R_UNEXPECTED_PKIBODY                         133
 #  define CMP_R_UNEXPECTED_PKISTATUS                       185
 #  define CMP_R_UNEXPECTED_PVNO                            153
index 15d24c3bc0f2109c667021a976094c746be92183..03b20fcc749b7b59c0e371ec72abfa2860ff1959 100644 (file)
@@ -44,8 +44,12 @@ expected,description, -section,val, -cmd,val,val2, -cacertsout,val,val2, -infoty
 0,revreason out of integer range, -section,, -cmd,rr,,BLANK,,,BLANK,,, -oldcert,_RESULT_DIR/test.cert.pem, -revreason,010000000000000000000
 ,,,,,,,,,,,,,,,,,
 1,ir + infotype, -section,, -cmd,ir,,BLANK,,, -infotype,signKeyPairTypes,,BLANK,,BLANK,
+1,genm without -infotype, -section,, -cmd,genm,,BLANK,,, BLANK,,,BLANK,,BLANK,
 0,genm with missing infotype value, -section,, -cmd,genm,,BLANK,,, -infotype,,,BLANK,,BLANK,
 0,genm with invalid infotype value, -section,, -cmd,genm,,BLANK,,, -infotype,asdf,,BLANK,,BLANK,
+1,genm with infotype signKeyPairTypes, -section,, -cmd,genm,,BLANK,,, -infotype,signKeyPairTypes,,BLANK,,BLANK,
+0,genm with infotype caCerts but missing -cacertsout, -section,, -cmd,genm,,BLANK,,, -infotype,caCerts,,BLANK,,BLANK,
+1,genm with infotype caCerts, -section,, -cmd,genm,, -cacertsout,_RESULT_DIR/test.cacerts.pem,, -infotype,caCerts,,BLANK,,BLANK,
 ,,,,,,,,,,,,,,,,,,,,,,
 1,geninfo, -section,, -cmd,cr,, -cert,signer.crt, -key,signer.p12, -keypass,pass:12345,BLANK,, -geninfo,1.2.3:int:987,BLANK,,BLANK,
 0,geninfo missing argument, -section,, -cmd,cr,, -cert,signer.crt, -key,signer.p12, -keypass,pass:12345,BLANK,, -geninfo,,,,,
index 753d1348ba6df07f1d685a106f5a9ba428c5dfe0..aedb8789bce9cb20cde4dc43cfc75ff34e2c7e19 100644 (file)
@@ -5445,6 +5445,9 @@ BN_signed_bn2native                     ? 3_2_0   EXIST::FUNCTION:
 ASYNC_set_mem_functions                 ?      3_2_0   EXIST::FUNCTION:
 ASYNC_get_mem_functions                 ?      3_2_0   EXIST::FUNCTION:
 BIO_ADDR_dup                            ?      3_2_0   EXIST::FUNCTION:SOCK
+OSSL_CMP_ITAV_new_caCerts               ?      3_2_0   EXIST::FUNCTION:CMP
+OSSL_CMP_ITAV_get0_caCerts              ?      3_2_0   EXIST::FUNCTION:CMP
+OSSL_CMP_get_caCerts                    ?      3_2_0   EXIST::FUNCTION:CMP
 OSSL_CMP_CTX_get0_libctx                ?      3_2_0   EXIST::FUNCTION:CMP
 OSSL_CMP_CTX_get0_propq                 ?      3_2_0   EXIST::FUNCTION:CMP
 OSSL_CMP_CTX_reset_geninfo_ITAVs        ?      3_0_8   EXIST::FUNCTION:CMP