From 4dde554c6ae2375ce53b24cc535124355c339462 Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Fri, 27 Sep 2019 10:22:23 +0200 Subject: chunk 5 of CMP contribution to OpenSSL Reviewed-by: Bernd Edlinger Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/10036) --- crypto/cmp/build.info | 3 +- crypto/cmp/cmp_asn.c | 9 +- crypto/cmp/cmp_ctx.c | 2 +- crypto/cmp/cmp_err.c | 6 + crypto/cmp/cmp_hdr.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++ crypto/cmp/cmp_local.h | 32 +++- crypto/cmp/cmp_status.c | 302 ++++++++++++++++++++++++++++++++++++++ crypto/err/openssl.txt | 3 + 8 files changed, 726 insertions(+), 8 deletions(-) create mode 100644 crypto/cmp/cmp_hdr.c create mode 100644 crypto/cmp/cmp_status.c (limited to 'crypto') diff --git a/crypto/cmp/build.info b/crypto/cmp/build.info index d5ce60e040..154022762a 100644 --- a/crypto/cmp/build.info +++ b/crypto/cmp/build.info @@ -1,2 +1,3 @@ LIBS=../../libcrypto -SOURCE[../../libcrypto]= cmp_asn.c cmp_ctx.c cmp_err.c cmp_util.c +SOURCE[../../libcrypto]= cmp_asn.c cmp_ctx.c cmp_err.c cmp_util.c \ + cmp_status.c cmp_hdr.c diff --git a/crypto/cmp/cmp_asn.c b/crypto/cmp/cmp_asn.c index fa7c26d78e..ca121b068a 100644 --- a/crypto/cmp/cmp_asn.c +++ b/crypto/cmp/cmp_asn.c @@ -164,7 +164,7 @@ int OSSL_CMP_ITAV_push0_stack_item(STACK_OF(OSSL_CMP_ITAV) **itav_sk_p, { int created = 0; - if (itav_sk_p == NULL) { + if (itav_sk_p == NULL || itav == NULL) { CMPerr(0, CMP_R_NULL_ARGUMENT); goto err; } @@ -174,11 +174,10 @@ int OSSL_CMP_ITAV_push0_stack_item(STACK_OF(OSSL_CMP_ITAV) **itav_sk_p, goto err; created = 1; } - if (itav != NULL) { - if (!sk_OSSL_CMP_ITAV_push(*itav_sk_p, itav)) - goto err; - } + if (!sk_OSSL_CMP_ITAV_push(*itav_sk_p, itav)) + goto err; return 1; + err: if (created != 0) { sk_OSSL_CMP_ITAV_free(*itav_sk_p); diff --git a/crypto/cmp/cmp_ctx.c b/crypto/cmp/cmp_ctx.c index 6ec23ad877..4a70b33ee7 100644 --- a/crypto/cmp/cmp_ctx.c +++ b/crypto/cmp/cmp_ctx.c @@ -195,7 +195,7 @@ void OSSL_CMP_CTX_free(OSSL_CMP_CTX *ctx) int ossl_cmp_ctx_set_status(OSSL_CMP_CTX *ctx, int status) { if (!ossl_assert(ctx != NULL)) - return 0; + return 0; ctx->status = status; return 1; } diff --git a/crypto/cmp/cmp_err.c b/crypto/cmp/cmp_err.c index 4086d5220b..683b8472d7 100644 --- a/crypto/cmp/cmp_err.c +++ b/crypto/cmp/cmp_err.c @@ -15,9 +15,15 @@ #ifndef OPENSSL_NO_ERR static const ERR_STRING_DATA CMP_str_reasons[] = { + {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_PARSING_PKISTATUS), + "error parsing pkistatus"}, + {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILURE_OBTAINING_RANDOM), + "failure obtaining random"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_ARGS), "invalid args"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MULTIPLE_SAN_SOURCES), "multiple san sources"}, + {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_SENDER_IDENTIFICATION), + "missing sender identification"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_NO_STDIO), "no stdio"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_NULL_ARGUMENT), "null argument"}, {0, NULL} diff --git a/crypto/cmp/cmp_hdr.c b/crypto/cmp/cmp_hdr.c new file mode 100644 index 0000000000..29f477f1b5 --- /dev/null +++ b/crypto/cmp/cmp_hdr.c @@ -0,0 +1,377 @@ +/* + * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright Nokia 2007-2019 + * Copyright Siemens AG 2015-2019 + * + * 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 + */ + +/* CMP functions for PKIHeader handling */ + +#include "cmp_local.h" + +#include + +/* explicit #includes not strictly needed since implied by the above: */ +#include +#include +#include + +int ossl_cmp_hdr_set_pvno(OSSL_CMP_PKIHEADER *hdr, int pvno) +{ + if (!ossl_assert(hdr != NULL)) + return 0; + return ASN1_INTEGER_set(hdr->pvno, pvno); +} + +int ossl_cmp_hdr_get_pvno(const OSSL_CMP_PKIHEADER *hdr) +{ + int64_t pvno; + + if (!ossl_assert(hdr != NULL)) + return -1; + if (!ASN1_INTEGER_get_int64(&pvno, hdr->pvno) || pvno < 0 || pvno > INT_MAX) + return -1; + return (int)pvno; +} + +ASN1_OCTET_STRING *OSSL_CMP_HDR_get0_transactionID(const OSSL_CMP_PKIHEADER *hdr) +{ + if (hdr == NULL) { + CMPerr(0, CMP_R_NULL_ARGUMENT); + return NULL; + } + return hdr->transactionID; +} + +ASN1_OCTET_STRING *ossl_cmp_hdr_get0_senderNonce(const OSSL_CMP_PKIHEADER *hdr) +{ + if (!ossl_assert(hdr != NULL)) + return NULL; + return hdr->senderNonce; +} + +ASN1_OCTET_STRING *OSSL_CMP_HDR_get0_recipNonce(const OSSL_CMP_PKIHEADER *hdr) +{ + if (hdr == NULL) { + CMPerr(0, CMP_R_NULL_ARGUMENT); + return NULL; + } + return hdr->recipNonce; +} + +/* assign to *tgt a copy of src (which may be NULL to indicate an empty DN) */ +static int set1_general_name(GENERAL_NAME **tgt, const X509_NAME *src) +{ + GENERAL_NAME *gen; + + if (!ossl_assert(tgt != NULL)) + return 0; + if ((gen = GENERAL_NAME_new()) == NULL) + goto err; + gen->type = GEN_DIRNAME; + + if (src == NULL) { /* NULL-DN */ + if ((gen->d.directoryName = X509_NAME_new()) == NULL) + goto err; + } else if (!X509_NAME_set(&gen->d.directoryName, src)) { + goto err; + } + + GENERAL_NAME_free(*tgt); + *tgt = gen; + + return 1; + + err: + GENERAL_NAME_free(gen); + return 0; +} + +/* + * Set the sender name in PKIHeader. + * when nm is NULL, sender is set to an empty string + * returns 1 on success, 0 on error + */ +int ossl_cmp_hdr_set1_sender(OSSL_CMP_PKIHEADER *hdr, const X509_NAME *nm) +{ + if (!ossl_assert(hdr != NULL)) + return 0; + return set1_general_name(&hdr->sender, nm); +} + +int ossl_cmp_hdr_set1_recipient(OSSL_CMP_PKIHEADER *hdr, const X509_NAME *nm) +{ + if (!ossl_assert(hdr != NULL)) + return 0; + return set1_general_name(&hdr->recipient, nm); +} + +int ossl_cmp_hdr_update_messageTime(OSSL_CMP_PKIHEADER *hdr) +{ + if (!ossl_assert(hdr != NULL)) + return 0; + if (hdr->messageTime == NULL + && (hdr->messageTime = ASN1_GENERALIZEDTIME_new()) == NULL) + return 0; + return ASN1_GENERALIZEDTIME_set(hdr->messageTime, time(NULL)) != NULL; +} + +/* assign to *tgt a copy of src (or if NULL a random byte array of given len) */ +static int set1_aostr_else_random(ASN1_OCTET_STRING **tgt, + const ASN1_OCTET_STRING *src, size_t len) +{ + unsigned char *bytes = NULL; + int res = 0; + + if (src == NULL) { /* generate a random value if src == NULL */ + if ((bytes = OPENSSL_malloc(len)) == NULL) + goto err; + if (RAND_bytes(bytes, len) <= 0) { + CMPerr(0, CMP_R_FAILURE_OBTAINING_RANDOM); + goto err; + } + res = ossl_cmp_asn1_octet_string_set1_bytes(tgt, bytes, len); + } else { + res = ossl_cmp_asn1_octet_string_set1(tgt, src); + } + + err: + OPENSSL_free(bytes); + return res; +} + +int ossl_cmp_hdr_set1_senderKID(OSSL_CMP_PKIHEADER *hdr, + const ASN1_OCTET_STRING *senderKID) +{ + if (!ossl_assert(hdr != NULL)) + return 0; + return ossl_cmp_asn1_octet_string_set1(&hdr->senderKID, senderKID); +} + +/* push the given text string to the given PKIFREETEXT ft */ +int ossl_cmp_pkifreetext_push_str(OSSL_CMP_PKIFREETEXT *ft, const char *text) +{ + ASN1_UTF8STRING *utf8string; + + if (!ossl_assert(ft != NULL && text != NULL)) + return 0; + if ((utf8string = ASN1_UTF8STRING_new()) == NULL) + return 0; + if (!ASN1_STRING_set(utf8string, text, -1)) + goto err; + if (!sk_ASN1_UTF8STRING_push(ft, utf8string)) + goto err; + return 1; + + err: + ASN1_UTF8STRING_free(utf8string); + return 0; +} + +int ossl_cmp_hdr_push0_freeText(OSSL_CMP_PKIHEADER *hdr, ASN1_UTF8STRING *text) +{ + if (!ossl_assert(hdr != NULL && text != NULL)) + return 0; + + if (hdr->freeText == NULL + && (hdr->freeText = sk_ASN1_UTF8STRING_new_null()) == NULL) + return 0; + + return sk_ASN1_UTF8STRING_push(hdr->freeText, text); +} + +int ossl_cmp_hdr_push1_freeText(OSSL_CMP_PKIHEADER *hdr, ASN1_UTF8STRING *text) +{ + if (!ossl_assert(hdr != NULL && text != NULL)) + return 0; + + if (hdr->freeText == NULL + && (hdr->freeText = sk_ASN1_UTF8STRING_new_null()) == NULL) + return 0; + + return ossl_cmp_pkifreetext_push_str(hdr->freeText, (char *)text->data); +} + +int ossl_cmp_hdr_generalInfo_push0_item(OSSL_CMP_PKIHEADER *hdr, + OSSL_CMP_ITAV *itav) +{ + if (!ossl_assert(hdr != NULL && itav != NULL)) + return 0; + return OSSL_CMP_ITAV_push0_stack_item(&hdr->generalInfo, itav); +} + +int ossl_cmp_hdr_generalInfo_push1_items(OSSL_CMP_PKIHEADER *hdr, + STACK_OF(OSSL_CMP_ITAV) *itavs) +{ + int i; + OSSL_CMP_ITAV *itav; + + if (!ossl_assert(hdr != NULL)) + return 0; + + for (i = 0; i < sk_OSSL_CMP_ITAV_num(itavs); i++) { + itav = OSSL_CMP_ITAV_dup(sk_OSSL_CMP_ITAV_value(itavs, i)); + if (itav == NULL) + return 0; + + if (!ossl_cmp_hdr_generalInfo_push0_item(hdr, itav)) { + OSSL_CMP_ITAV_free(itav); + return 0; + } + } + return 1; +} + +int ossl_cmp_hdr_set_implicitConfirm(OSSL_CMP_PKIHEADER *hdr) +{ + OSSL_CMP_ITAV *itav; + ASN1_TYPE *asn1null; + + if (!ossl_assert(hdr != NULL)) + return 0; + asn1null = (ASN1_TYPE *)ASN1_NULL_new(); + if (asn1null == NULL) + return 0; + if ((itav = OSSL_CMP_ITAV_create(OBJ_nid2obj(NID_id_it_implicitConfirm), + asn1null)) == NULL) + goto err; + if (!ossl_cmp_hdr_generalInfo_push0_item(hdr, itav)) + goto err; + return 1; + + err: + ASN1_TYPE_free(asn1null); + OSSL_CMP_ITAV_free(itav); + return 0; +} + +/* return 1 if implicitConfirm in the generalInfo field of the header is set */ +int ossl_cmp_hdr_check_implicitConfirm(const OSSL_CMP_PKIHEADER *hdr) +{ + int itavCount; + int i; + OSSL_CMP_ITAV *itav; + + if (!ossl_assert(hdr != NULL)) + return 0; + + itavCount = sk_OSSL_CMP_ITAV_num(hdr->generalInfo); + for (i = 0; i < itavCount; i++) { + itav = sk_OSSL_CMP_ITAV_value(hdr->generalInfo, i); + if (itav != NULL + && OBJ_obj2nid(itav->infoType) == NID_id_it_implicitConfirm) + return 1; + } + + return 0; +} + +/* fill in all fields of the hdr according to the info given in ctx */ +int ossl_cmp_hdr_init(OSSL_CMP_CTX *ctx, OSSL_CMP_PKIHEADER *hdr) +{ + X509_NAME *sender; + X509_NAME *rcp = NULL; + + if (!ossl_assert(ctx != NULL && hdr != NULL)) + return 0; + + /* set the CMP version */ + if (!ossl_cmp_hdr_set_pvno(hdr, OSSL_CMP_PVNO)) + return 0; + + sender = ctx->clCert != NULL ? + X509_get_subject_name(ctx->clCert) : ctx->subjectName; + /* + * The sender name is copied from the subject of the client cert, if any, + * or else from the the subject name provided for certification requests. + * As required by RFC 4210 section 5.1.1., if the sender name is not known + * to the client it set to NULL-DN. In this case for identification at least + * the senderKID must be set, which we take from any referenceValue given. + */ + if (sender == NULL && ctx->referenceValue == NULL) { + CMPerr(0, CMP_R_MISSING_SENDER_IDENTIFICATION); + return 0; + } + if (!ossl_cmp_hdr_set1_sender(hdr, sender)) + return 0; + + /* determine recipient entry in PKIHeader */ + if (ctx->srvCert != NULL) { + rcp = X509_get_subject_name(ctx->srvCert); + /* set also as expected_sender of responses unless set explicitly */ + if (ctx->expected_sender == NULL && rcp != NULL + && !OSSL_CMP_CTX_set1_expected_sender(ctx, rcp)) + return 0; + } else if (ctx->recipient != NULL) { + rcp = ctx->recipient; + } else if (ctx->issuer != NULL) { + rcp = ctx->issuer; + } else if (ctx->oldCert != NULL) { + rcp = X509_get_issuer_name(ctx->oldCert); + } else if (ctx->clCert != NULL) { + rcp = X509_get_issuer_name(ctx->clCert); + } + if (!ossl_cmp_hdr_set1_recipient(hdr, rcp)) + return 0; + + /* set current time as message time */ + if (!ossl_cmp_hdr_update_messageTime(hdr)) + return 0; + + if (ctx->recipNonce != NULL + && !ossl_cmp_asn1_octet_string_set1(&hdr->recipNonce, + ctx->recipNonce)) + return 0; + + /* + * set ctx->transactionID in CMP header + * if ctx->transactionID is NULL, a random one is created with 128 bit + * according to section 5.1.1: + * + * It is RECOMMENDED that the clients fill the transactionID field with + * 128 bits of (pseudo-) random data for the start of a transaction to + * reduce the probability of having the transactionID in use at the server. + */ + if (ctx->transactionID == NULL + && !set1_aostr_else_random(&ctx->transactionID, NULL, + OSSL_CMP_TRANSACTIONID_LENGTH)) + return 0; + if (!ossl_cmp_asn1_octet_string_set1(&hdr->transactionID, + ctx->transactionID)) + return 0; + + /*- + * set random senderNonce + * according to section 5.1.1: + * + * senderNonce present + * -- 128 (pseudo-)random bits + * The senderNonce and recipNonce fields protect the PKIMessage against + * replay attacks. The senderNonce will typically be 128 bits of + * (pseudo-) random data generated by the sender, whereas the recipNonce + * is copied from the senderNonce of the previous message in the + * transaction. + */ + if (!set1_aostr_else_random(&hdr->senderNonce, NULL, + OSSL_CMP_SENDERNONCE_LENGTH)) + return 0; + + /* store senderNonce - for cmp with recipNonce in next outgoing msg */ + if (!OSSL_CMP_CTX_set1_senderNonce(ctx, hdr->senderNonce)) + return 0; + + /*- + * freeText [7] PKIFreeText OPTIONAL, + * -- this may be used to indicate context-specific instructions + * -- (this field is intended for human consumption) + */ + if (ctx->freeText != NULL + && !ossl_cmp_hdr_push1_freeText(hdr, ctx->freeText)) + return 0; + + return 1; +} diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h index 1a7dcca3bd..b49ead62df 100644 --- a/crypto/cmp/cmp_local.h +++ b/crypto/cmp/cmp_local.h @@ -77,6 +77,7 @@ struct ossl_cmp_ctx_st { ASN1_OCTET_STRING *transactionID; /* the current transaction ID */ ASN1_OCTET_STRING *senderNonce; /* last nonce sent */ ASN1_OCTET_STRING *recipNonce; /* last nonce received */ + ASN1_UTF8STRING *freeText; /* optional string to include each msg */ STACK_OF(OSSL_CMP_ITAV) *geninfo_ITAVs; int implicitConfirm; /* set implicitConfirm in IR/KUR/CR messages */ int disableConfirm; /* disable certConf in IR/KUR/CR for broken servers */ @@ -720,6 +721,35 @@ int ossl_cmp_ctx_set1_extraCertsIn(OSSL_CMP_CTX *ctx, int ossl_cmp_ctx_set1_recipNonce(OSSL_CMP_CTX *ctx, const ASN1_OCTET_STRING *nonce); -# define OSSL_CMP_TRANSACTIONID_LENGTH 16 +/* from cmp_status.c */ +OSSL_CMP_PKISI * +ossl_cmp_statusinfo_new(int status, int fail_info, const char *text); +int ossl_cmp_pkisi_get_pkistatus(const OSSL_CMP_PKISI *statusInfo); +const char *ossl_cmp_PKIStatus_to_string(int status); +OSSL_CMP_PKIFREETEXT *ossl_cmp_pkisi_get0_statusstring(const OSSL_CMP_PKISI *si); +int ossl_cmp_pkisi_get_pkifailureinfo(const OSSL_CMP_PKISI *si); +int ossl_cmp_pkisi_pkifailureinfo_check(const OSSL_CMP_PKISI *si, int bit_index); + +/* from cmp_hdr.c */ +int ossl_cmp_hdr_set_pvno(OSSL_CMP_PKIHEADER *hdr, int pvno); +int ossl_cmp_hdr_get_pvno(const OSSL_CMP_PKIHEADER *hdr); +ASN1_OCTET_STRING *ossl_cmp_hdr_get0_senderNonce(const OSSL_CMP_PKIHEADER *hdr); +int ossl_cmp_hdr_set1_sender(OSSL_CMP_PKIHEADER *hdr, const X509_NAME *nm); +int ossl_cmp_hdr_set1_recipient(OSSL_CMP_PKIHEADER *hdr, const X509_NAME *nm); +int ossl_cmp_hdr_update_messageTime(OSSL_CMP_PKIHEADER *hdr); +int ossl_cmp_hdr_set1_senderKID(OSSL_CMP_PKIHEADER *hdr, + const ASN1_OCTET_STRING *senderKID); +int ossl_cmp_pkifreetext_push_str(OSSL_CMP_PKIFREETEXT *ft, const char *text); +int ossl_cmp_hdr_push0_freeText(OSSL_CMP_PKIHEADER *hdr, ASN1_UTF8STRING *text); +int ossl_cmp_hdr_push1_freeText(OSSL_CMP_PKIHEADER *hdr, ASN1_UTF8STRING *text); +int ossl_cmp_hdr_generalInfo_push0_item(OSSL_CMP_PKIHEADER *hdr, + OSSL_CMP_ITAV *itav); +int ossl_cmp_hdr_generalInfo_push1_items(OSSL_CMP_PKIHEADER *hdr, + STACK_OF(OSSL_CMP_ITAV) *itavs); +int ossl_cmp_hdr_set_implicitConfirm(OSSL_CMP_PKIHEADER *hdr); +int ossl_cmp_hdr_check_implicitConfirm(const OSSL_CMP_PKIHEADER *hdr); +# define OSSL_CMP_TRANSACTIONID_LENGTH 16 +# define OSSL_CMP_SENDERNONCE_LENGTH 16 +int ossl_cmp_hdr_init(OSSL_CMP_CTX *ctx, OSSL_CMP_PKIHEADER *hdr); #endif /* !defined OSSL_CRYPTO_CMP_LOCAL_H */ diff --git a/crypto/cmp/cmp_status.c b/crypto/cmp/cmp_status.c new file mode 100644 index 0000000000..f6b7978a4f --- /dev/null +++ b/crypto/cmp/cmp_status.c @@ -0,0 +1,302 @@ +/* + * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright Nokia 2007-2019 + * Copyright Siemens AG 2015-2019 + * + * 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 + */ + +/* CMP functions for PKIStatusInfo handling and PKIMessage decomposition */ + +#include + +#include "cmp_local.h" + +/* explicit #includes not strictly needed since implied by the above: */ +#include +#include +#include +#include /* needed in case config no-deprecated */ +#include +#include +#include +#include +#include /* for ASN1_R_TOO_SMALL and ASN1_R_TOO_LARGE */ + +/* CMP functions related to PKIStatus */ + +int ossl_cmp_pkisi_get_pkistatus(const OSSL_CMP_PKISI *si) +{ + if (!ossl_assert(si != NULL && si->status != NULL)) + return -1; + return ossl_cmp_asn1_get_int(si->status); +} + +/* + * return the declared identifier and a short explanation for the PKIStatus + * value as specified in RFC4210, Appendix F. + */ +const char *ossl_cmp_PKIStatus_to_string(int status) +{ + switch (status) { + case OSSL_CMP_PKISTATUS_accepted: + return "PKIStatus: accepted"; + case OSSL_CMP_PKISTATUS_grantedWithMods: + return "PKIStatus: granted with modifications"; + case OSSL_CMP_PKISTATUS_rejection: + return "PKIStatus: rejection"; + case OSSL_CMP_PKISTATUS_waiting: + return "PKIStatus: waiting"; + case OSSL_CMP_PKISTATUS_revocationWarning: + return "PKIStatus: revocation warning - a revocation of the cert is imminent"; + case OSSL_CMP_PKISTATUS_revocationNotification: + return "PKIStatus: revocation notification - a revocation of the cert has occurred"; + case OSSL_CMP_PKISTATUS_keyUpdateWarning: + return "PKIStatus: key update warning - update already done for the cert"; + default: + { + char buf[40]; + BIO_snprintf(buf, sizeof(buf), "PKIStatus: invalid=%d", status); + CMPerr(0, CMP_R_ERROR_PARSING_PKISTATUS); + ossl_cmp_add_error_data(buf); + return NULL; + } + } +} + +/* + * returns a pointer to the statusString contained in a PKIStatusInfo + * returns NULL on error + */ +OSSL_CMP_PKIFREETEXT *ossl_cmp_pkisi_get0_statusstring(const OSSL_CMP_PKISI *si) +{ + if (!ossl_assert(si != NULL)) + return NULL; + return si->statusString; +} + +/* + * returns the FailureInfo bits of the given PKIStatusInfo + * returns -1 on error + */ +int ossl_cmp_pkisi_get_pkifailureinfo(const OSSL_CMP_PKISI *si) +{ + int i; + int res = 0; + + if (!ossl_assert(si != NULL && si->failInfo != NULL)) + return -1; + for (i = 0; i <= OSSL_CMP_PKIFAILUREINFO_MAX; i++) + if (ASN1_BIT_STRING_get_bit(si->failInfo, i)) + res |= 1 << i; + return res; +} + +/* + * internal function + * convert PKIFailureInfo number to human-readable string + * + * returns pointer to static string + * returns NULL on error + */ +static const char *CMP_PKIFAILUREINFO_to_string(int number) +{ + switch (number) { + case OSSL_CMP_PKIFAILUREINFO_badAlg: + return "badAlg"; + case OSSL_CMP_PKIFAILUREINFO_badMessageCheck: + return "badMessageCheck"; + case OSSL_CMP_PKIFAILUREINFO_badRequest: + return "badRequest"; + case OSSL_CMP_PKIFAILUREINFO_badTime: + return "badTime"; + case OSSL_CMP_PKIFAILUREINFO_badCertId: + return "badCertId"; + case OSSL_CMP_PKIFAILUREINFO_badDataFormat: + return "badDataFormat"; + case OSSL_CMP_PKIFAILUREINFO_wrongAuthority: + return "wrongAuthority"; + case OSSL_CMP_PKIFAILUREINFO_incorrectData: + return "incorrectData"; + case OSSL_CMP_PKIFAILUREINFO_missingTimeStamp: + return "missingTimeStamp"; + case OSSL_CMP_PKIFAILUREINFO_badPOP: + return "badPOP"; + case OSSL_CMP_PKIFAILUREINFO_certRevoked: + return "certRevoked"; + case OSSL_CMP_PKIFAILUREINFO_certConfirmed: + return "certConfirmed"; + case OSSL_CMP_PKIFAILUREINFO_wrongIntegrity: + return "wrongIntegrity"; + case OSSL_CMP_PKIFAILUREINFO_badRecipientNonce: + return "badRecipientNonce"; + case OSSL_CMP_PKIFAILUREINFO_timeNotAvailable: + return "timeNotAvailable"; + case OSSL_CMP_PKIFAILUREINFO_unacceptedPolicy: + return "unacceptedPolicy"; + case OSSL_CMP_PKIFAILUREINFO_unacceptedExtension: + return "unacceptedExtension"; + case OSSL_CMP_PKIFAILUREINFO_addInfoNotAvailable: + return "addInfoNotAvailable"; + case OSSL_CMP_PKIFAILUREINFO_badSenderNonce: + return "badSenderNonce"; + case OSSL_CMP_PKIFAILUREINFO_badCertTemplate: + return "badCertTemplate"; + case OSSL_CMP_PKIFAILUREINFO_signerNotTrusted: + return "signerNotTrusted"; + case OSSL_CMP_PKIFAILUREINFO_transactionIdInUse: + return "transactionIdInUse"; + case OSSL_CMP_PKIFAILUREINFO_unsupportedVersion: + return "unsupportedVersion"; + case OSSL_CMP_PKIFAILUREINFO_notAuthorized: + return "notAuthorized"; + case OSSL_CMP_PKIFAILUREINFO_systemUnavail: + return "systemUnavail"; + case OSSL_CMP_PKIFAILUREINFO_systemFailure: + return "systemFailure"; + case OSSL_CMP_PKIFAILUREINFO_duplicateCertReq: + return "duplicateCertReq"; + default: + return NULL; /* illegal failure number */ + } +} + +/* + * checks PKIFailureInfo bits in a given PKIStatusInfo + * returns 1 if a given bit is set, 0 if not, -1 on error + */ +int ossl_cmp_pkisi_pkifailureinfo_check(const OSSL_CMP_PKISI *si, int bit_index) +{ + if (!ossl_assert(si != NULL && si->failInfo != NULL)) + return -1; + if (bit_index < 0 || bit_index > OSSL_CMP_PKIFAILUREINFO_MAX) { + CMPerr(0, CMP_R_INVALID_ARGS); + return -1; + } + + return ASN1_BIT_STRING_get_bit(si->failInfo, bit_index); +} + +/* + * place human-readable error string created from PKIStatusInfo in given buffer + * returns pointer to the same buffer containing the string, or NULL on error + */ +char *OSSL_CMP_CTX_snprint_PKIStatus(OSSL_CMP_CTX *ctx, char *buf, + size_t bufsize) +{ + int status, failure, fail_info; + const char *status_string, *failure_string; + OSSL_CMP_PKIFREETEXT *status_strings; + ASN1_UTF8STRING *text; + int i; + int printed_chars; + int failinfo_found = 0; + int n_status_strings; + char* write_ptr = buf; + +#define ADVANCE_BUFFER \ + if (printed_chars < 0 || (size_t)printed_chars >= bufsize) \ + return NULL; \ + write_ptr += printed_chars; \ + bufsize -= printed_chars; + + if (ctx == NULL + || buf == NULL + || (status = OSSL_CMP_CTX_get_status(ctx)) < 0 + || (status_string = ossl_cmp_PKIStatus_to_string(status)) == NULL) + return NULL; + printed_chars = BIO_snprintf(write_ptr, bufsize, "%s", status_string); + ADVANCE_BUFFER; + + /* failInfo is optional and may be empty */ + if ((fail_info = OSSL_CMP_CTX_get_failInfoCode(ctx)) > 0) { + printed_chars = BIO_snprintf(write_ptr, bufsize, "; PKIFailureInfo: "); + ADVANCE_BUFFER; + for (failure = 0; failure <= OSSL_CMP_PKIFAILUREINFO_MAX; failure++) { + if ((fail_info & (1 << failure)) != 0) { + failure_string = CMP_PKIFAILUREINFO_to_string(failure); + if (failure_string != NULL) { + printed_chars = BIO_snprintf(write_ptr, bufsize, "%s%s", + failure > 0 ? ", " : "", + failure_string); + ADVANCE_BUFFER; + failinfo_found = 1; + } + } + } + } + if (!failinfo_found && status != OSSL_CMP_PKISTATUS_accepted + && status != OSSL_CMP_PKISTATUS_grantedWithMods) { + printed_chars = BIO_snprintf(write_ptr, bufsize, "; "); + ADVANCE_BUFFER; + } + + /* statusString sequence is optional and may be empty */ + status_strings = OSSL_CMP_CTX_get0_statusString(ctx); + n_status_strings = sk_ASN1_UTF8STRING_num(status_strings); + if (n_status_strings > 0) { + printed_chars = BIO_snprintf(write_ptr, bufsize, "; StatusString%s: ", + n_status_strings > 1 ? "s" : ""); + ADVANCE_BUFFER; + for (i = 0; i < n_status_strings; i++) { + text = sk_ASN1_UTF8STRING_value(status_strings, i); + printed_chars = BIO_snprintf(write_ptr, bufsize, "\"%s\"%s", + ASN1_STRING_get0_data(text), + i < n_status_strings - 1 ? ", " : ""); + ADVANCE_BUFFER; + } + } +#undef ADVANCE_BUFFER + return buf; +} + +/* + * Creates a new PKIStatusInfo structure and fills it in + * returns a pointer to the structure on success, NULL on error + * note: strongly overlaps with TS_RESP_CTX_set_status_info() + * and TS_RESP_CTX_add_failure_info() in ../ts/ts_rsp_sign.c + */ +OSSL_CMP_PKISI *ossl_cmp_statusinfo_new(int status, int fail_info, + const char *text) +{ + OSSL_CMP_PKISI *si = OSSL_CMP_PKISI_new(); + ASN1_UTF8STRING *utf8_text = NULL; + int failure; + + if (si == NULL) + goto err; + if (!ASN1_INTEGER_set(si->status, status)) + goto err; + + if (text != NULL) { + if ((utf8_text = ASN1_UTF8STRING_new()) == NULL + || !ASN1_STRING_set(utf8_text, text, -1)) + goto err; + if ((si->statusString = sk_ASN1_UTF8STRING_new_null()) == NULL) + goto err; + if (!sk_ASN1_UTF8STRING_push(si->statusString, utf8_text)) + goto err; + /* Ownership is lost. */ + utf8_text = NULL; + } + + for (failure = 0; failure <= OSSL_CMP_PKIFAILUREINFO_MAX; failure++) { + if ((fail_info & (1 << failure)) != 0) { + if (si->failInfo == NULL + && (si->failInfo = ASN1_BIT_STRING_new()) == NULL) + goto err; + if (!ASN1_BIT_STRING_set_bit(si->failInfo, failure, 1)) + goto err; + } + } + return si; + + err: + OSSL_CMP_PKISI_free(si); + ASN1_UTF8STRING_free(utf8_text); + return NULL; +} + diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 9f405018c4..27aad6a6ce 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -2063,8 +2063,11 @@ BN_R_PRIVATE_KEY_TOO_LARGE:117:private key too large BN_R_P_IS_NOT_PRIME:112:p is not prime BN_R_TOO_MANY_ITERATIONS:113:too many iterations BN_R_TOO_MANY_TEMPORARY_VARIABLES:109:too many temporary variables +CMP_R_ERROR_PARSING_PKISTATUS:107:error parsing pkistatus +CMP_R_FAILURE_OBTAINING_RANDOM:110:failure obtaining random CMP_R_INVALID_ARGS:100:invalid args CMP_R_MULTIPLE_SAN_SOURCES:102:multiple san sources +CMP_R_MISSING_SENDER_IDENTIFICATION:111:missing sender identification CMP_R_NO_STDIO:194:no stdio CMP_R_NULL_ARGUMENT:103:null argument CMS_R_ADD_SIGNER_ERROR:99:add signer error -- cgit v1.2.3