summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/cmp.c94
-rw-r--r--apps/lib/cmp_mock_srv.c18
-rw-r--r--crypto/cmp/build.info2
-rw-r--r--crypto/cmp/cmp_asn.c34
-rw-r--r--crypto/cmp/cmp_err.c3
-rw-r--r--crypto/cmp/cmp_genm.c179
-rw-r--r--crypto/cmp/cmp_local.h2
-rw-r--r--crypto/cmp/cmp_msg.c2
-rw-r--r--crypto/err/openssl.txt3
-rw-r--r--doc/build.info6
-rw-r--r--doc/internal/man3/ossl_cmp_mock_srv_new.pod7
-rw-r--r--doc/man1/openssl-cmp.pod.in4
-rw-r--r--doc/man3/OSSL_CMP_ITAV_new_caCerts.pod59
-rw-r--r--doc/man3/OSSL_CMP_exec_certreq.pod15
-rw-r--r--include/openssl/cmp.h.in14
-rw-r--r--include/openssl/cmperr.h3
-rw-r--r--test/recipes/80-test_cmp_http_data/test_commands.csv4
-rw-r--r--util/libcrypto.num3
18 files changed, 409 insertions, 43 deletions
diff --git a/apps/cmp.c b/apps/cmp.c
index a3d0c19dd3..aec56b4425 100644
--- a/apps/cmp.c
+++ b/apps/cmp.c
@@ -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;
}
diff --git a/apps/lib/cmp_mock_srv.c b/apps/lib/cmp_mock_srv.c
index c19651221b..fac396bd2e 100644
--- a/apps/lib/cmp_mock_srv.c
+++ b/apps/lib/cmp_mock_srv.c
@@ -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);
diff --git a/crypto/cmp/build.info b/crypto/cmp/build.info
index a2a57c14ec..ad67c434cc 100644
--- a/crypto/cmp/build.info
+++ b/crypto/cmp/build.info
@@ -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
diff --git a/crypto/cmp/cmp_asn.c b/crypto/cmp/cmp_asn.c
index e1ecc40838..b97c8323ff 100644
--- a/crypto/cmp/cmp_asn.c
+++ b/crypto/cmp/cmp_asn.c
@@ -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)
{
diff --git a/crypto/cmp/cmp_err.c b/crypto/cmp/cmp_err.c
index dfc6dfbede..9d0e968032 100644
--- a/crypto/cmp/cmp_err.c
+++ b/crypto/cmp/cmp_err.c
@@ -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
index 0000000000..f4be997eb2
--- /dev/null
+++ b/crypto/cmp/cmp_genm.c
@@ -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;
+}
diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h
index 507496e149..1a27d39abf 100644
--- a/crypto/cmp/cmp_local.h
+++ b/crypto/cmp/cmp_local.h
@@ -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;
diff --git a/crypto/cmp/cmp_msg.c b/crypto/cmp/cmp_msg.c
index 689a31a767..806abe599d 100644
--- a/crypto/cmp/cmp_msg.c
+++ b/crypto/cmp/cmp_msg.c
@@ -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,
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 753fb6557d..a3fac68232 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -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
diff --git a/doc/build.info b/doc/build.info
index 52db908985..6031a85d4d 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -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 \
diff --git a/doc/internal/man3/ossl_cmp_mock_srv_new.pod b/doc/internal/man3/ossl_cmp_mock_srv_new.pod
index 119077ea7c..59568c65fc 100644
--- a/doc/internal/man3/ossl_cmp_mock_srv_new.pod
+++ b/doc/internal/man3/ossl_cmp_mock_srv_new.pod
@@ -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.
diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in
index 76c4313bd5..2c8a8b2540 100644
--- a/doc/man1/openssl-cmp.pod.in
+++ b/doc/man1/openssl-cmp.pod.in
@@ -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
index 0000000000..eb397388aa
--- /dev/null
+++ b/doc/man3/OSSL_CMP_ITAV_new_caCerts.pod
@@ -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
diff --git a/doc/man3/OSSL_CMP_exec_certreq.pod b/doc/man3/OSSL_CMP_exec_certreq.pod
index b0d81c7c41..a418657e3c 100644
--- a/doc/man3/OSSL_CMP_exec_certreq.pod
+++ b/doc/man3/OSSL_CMP_exec_certreq.pod
@@ -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.
diff --git a/include/openssl/cmp.h.in b/include/openssl/cmp.h.in
index 4e14200d82..c986674be2 100644
--- a/include/openssl/cmp.h.in
+++ b/include/openssl/cmp.h.in
@@ -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
diff --git a/include/openssl/cmperr.h b/include/openssl/cmperr.h
index 49fd5e392a..f4435d825d 100644
--- a/include/openssl/cmperr.h
+++ b/include/openssl/cmperr.h
@@ -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