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 + +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, ""); + 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 Bs is printed to stdout. Set InfoType name to use for requesting specific info in B, e.g., C. +So far, there is specific support for C. =item B<-geninfo> I @@ -655,7 +656,8 @@ field of the last received response message that is not a pollRep nor PKIConf. =item B<-cacertsout> I 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. =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 + + 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 structure of type +B and fills it with a copy of the provided list of certificates. +The I argument may be NULL or contain any number of certificates. + +OSSL_CMP_ITAV_get0_caCerts() requires that I has type B. +It assigns NULL to I<*out> if there are no CA certificates in I, otherwise +the internal pointer of type B 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 and L + +=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. + +=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 Bs 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. +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 yields NULL and the output parameter I has been used to assign the received value unless I 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 received, which may be empty. @@ -161,6 +170,8 @@ L 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 191 # define CMP_R_UNEXPECTED_PKIBODY 133 # define CMP_R_UNEXPECTED_PKISTATUS 185 # define CMP_R_UNEXPECTED_PVNO 153 diff --git a/test/recipes/80-test_cmp_http_data/test_commands.csv b/test/recipes/80-test_cmp_http_data/test_commands.csv index 15d24c3bc0..03b20fcc74 100644 --- a/test/recipes/80-test_cmp_http_data/test_commands.csv +++ b/test/recipes/80-test_cmp_http_data/test_commands.csv @@ -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,,,,, diff --git a/util/libcrypto.num b/util/libcrypto.num index 753d1348ba..aedb8789bc 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -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