/*
* Copyright 2007-2022 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
*/
#include "cmp_local.h"
#include "internal/cryptlib.h"
/* explicit #includes not strictly needed since implied by the above: */
#include <openssl/bio.h>
#include <openssl/cmp.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/x509v3.h>
#include <openssl/cmp_util.h>
#define IS_CREP(t) ((t) == OSSL_CMP_PKIBODY_IP || (t) == OSSL_CMP_PKIBODY_CP \
|| (t) == OSSL_CMP_PKIBODY_KUP)
/*-
* Evaluate whether there's an exception (violating the standard) configured for
* handling negative responses without protection or with invalid protection.
* Returns 1 on acceptance, 0 on rejection, or -1 on (internal) error.
*/
static int unprotected_exception(const OSSL_CMP_CTX *ctx,
const OSSL_CMP_MSG *rep,
int invalid_protection,
ossl_unused int expected_type)
{
int rcvd_type = OSSL_CMP_MSG_get_bodytype(rep /* may be NULL */);
const char *msg_type = NULL;
if (!ossl_assert(ctx != NULL && rep != NULL))
return -1;
if (!ctx->unprotectedErrors)
return 0;
switch (rcvd_type) {
case OSSL_CMP_PKIBODY_ERROR:
msg_type = "error response";
break;
case OSSL_CMP_PKIBODY_RP:
{
OSSL_CMP_PKISI *si =
ossl_cmp_revrepcontent_get_pkisi(rep->body->value.rp,
OSSL_CMP_REVREQSID);
if (si == NULL)
return -1;
if (ossl_cmp_pkisi_get_status(si) == OSSL_CMP_PKISTATUS_rejection)
msg_type = "revocation response message with rejection status";
break;
}
case OSSL_CMP_PKIBODY_PKICONF:
msg_type = "PKI Confirmation message";
break;
default:
if (IS_CREP(rcvd_type)) {
int any_rid = OSSL_CMP_CERTREQID_NONE;
OSSL_CMP_CERTREPMESSAGE *crepmsg = rep->body->value.ip;
OSSL_CMP_CERTRESPONSE *crep =
ossl_cmp_certrepmessage_get0_certresponse(crepmsg, any_rid);
if (sk_OSSL_CMP_CERTRESPONSE_num(crepmsg->response) > 1)
return -1;
if (crep == NULL)
return -1;
if (ossl_cmp_pkisi_get_status(crep->status)
== OSSL_CMP_PKISTATUS_rejection)
msg_type = "CertRepMessage with rejection status";
}
}
if (msg_type == NULL)
return 0;
ossl_cmp_log2(WARN, ctx, "ignoring %s protection of %s",
invalid_protection ? "invalid" : "missing", msg_type);
return 1;
}
/* Save error info from PKIStatusInfo field of a certresponse into ctx */
static int save_statusInfo(OSSL_CMP_CTX *ctx, OSSL_CMP_PKISI *si)
{
int i;
OSSL_CMP_PKIFREETEXT *ss;
if (!ossl_assert(ctx != NULL && si != NULL))
return 0;
ctx->status = ossl_cmp_pkisi_get_status(si);
if (ctx->status < OSSL_CMP_PKISTATUS_accepted)
return 0;
ctx->failInfoCode = ossl_cmp_pkisi_get_pkifailureinfo(si);
if (!ossl_cmp_ctx_set0_statusString(ctx, sk_ASN1_UTF8STRING_new_null())
|| (ctx->statusString == NULL))
return 0;
ss = si->statusString; /* may be NULL */
for (i = 0; i < sk_ASN1_UTF8STRING_num(ss); i++) {
ASN1_UTF8STRING *str = sk_ASN1_UTF8STRING_value(ss, i);
if (!sk_ASN1_UTF8STRING_push(ctx->statusString, ASN1_STRING_dup(str)))
return 0;
}
return 1;
}
/*-
* Perform the generic aspects of sending a request and receiving a response.
* Returns 1 on success and provides the received PKIMESSAGE in *rep.
* Returns 0 on error.
* Regardless of success, caller is responsible for freeing *rep (unless NULL).
*/
static int send_receive_check(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *req,
OSSL_CMP_MSG **rep, int expected_type)
{
int begin_transaction =
expected_type != OSSL_CMP_PKIBODY_POLLREP
&& expected_type != OSSL_CMP_PKIBODY_PKICONF;
const char *req_type_str =
ossl_cmp_bodytype_to_string(OSSL_CMP_MSG_get_bodytype(req));
const char *expected_type_str = ossl_cmp_bodytype_to_string(expected_type);
int bak_msg_timeout = ctx->msg_timeout;
int bt;
time_t now = time(NULL);
int time_left;
OSSL_CMP_transfer_cb_t transfer_cb = ctx->transfer_cb;
#ifndef OPENSSL_NO_HTTP
if (transfer_cb == NULL)
transfer_cb = OSSL_CMP_MSG_http_perform;
#endif
*rep = NULL;
if (ctx->total_timeout != 0 /* not waiting indefinitely */) {
if (begin_transaction)