crypto/ocsp/ocsp_client.c (263 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include "../asn1/internal.h"
#include "internal.h"
OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) {
OCSP_ONEREQ *one = OCSP_ONEREQ_new();
if (one == NULL) {
return NULL;
}
// Reassign |OCSP_CERTID| allocated by OCSP_ONEREQ_new().
OCSP_CERTID_free(one->reqCert);
one->reqCert = cid;
if (req != NULL && !sk_OCSP_ONEREQ_push(req->tbsRequest->requestList, one)) {
one->reqCert = NULL; // do not free on error
OCSP_ONEREQ_free(one);
return NULL;
}
return one;
}
int OCSP_request_set1_name(OCSP_REQUEST *req, X509_NAME *nm) {
GENERAL_NAME *gen = GENERAL_NAME_new();
if (gen == NULL) {
return 0;
}
if (!X509_NAME_set(&gen->d.directoryName, nm)) {
GENERAL_NAME_free(gen);
return 0;
}
gen->type = GEN_DIRNAME;
GENERAL_NAME_free(req->tbsRequest->requestorName);
req->tbsRequest->requestorName = gen;
return 1;
}
int OCSP_request_add1_cert(OCSP_REQUEST *req, X509 *cert) {
if (req->optionalSignature == NULL) {
req->optionalSignature = OCSP_SIGNATURE_new();
}
OCSP_SIGNATURE *sig = req->optionalSignature;
if (sig == NULL) {
return 0;
}
if (cert == NULL) {
return 1;
}
if (sig->certs == NULL && (sig->certs = sk_X509_new_null()) == NULL) {
return 0;
}
if (!sk_X509_push(sig->certs, cert)) {
return 0;
}
// sk_X509_push takes ownership.
X509_up_ref(cert);
return 1;
}
int OCSP_request_sign(OCSP_REQUEST *req, X509 *signer, EVP_PKEY *key,
const EVP_MD *dgst, STACK_OF(X509) *certs,
unsigned long flags) {
if (req->optionalSignature != NULL) {
// OpenSSL lets an |OCSP_REQUEST| be signed twice, regardless of whether
// an |optionalSignature| exists. Signing an OCSP Request twice creates
// a dangling |OCSP_SIGNATURE| pointer with no clear way of recovering
// it. We disallow this behavior and only allow the |OCSP_REQUEST| to be
// signed once. There's no indication or use case detailed in the RFC
// that the OCSP request can or should be signed twice.
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.2
OPENSSL_PUT_ERROR(OCSP, OCSP_R_OCSP_REQUEST_DUPLICATE_SIGNATURE);
goto err;
}
if (!OCSP_request_set1_name(req, X509_get_subject_name(signer))) {
goto err;
}
req->optionalSignature = OCSP_SIGNATURE_new();
if (req->optionalSignature == NULL) {
goto err;
}
if (key != NULL) {
if (!X509_check_private_key(signer, key)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
goto err;
}
const EVP_MD *init_dgst = OCSP_get_default_digest(dgst, key);
if (init_dgst == NULL) {
OPENSSL_PUT_ERROR(OCSP, EVP_R_NO_DEFAULT_DIGEST);
goto err;
}
if (!ASN1_item_sign(ASN1_ITEM_rptr(OCSP_REQINFO),
req->optionalSignature->signatureAlgorithm, NULL,
req->optionalSignature->signature, req->tbsRequest, key,
init_dgst)) {
goto err;
}
}
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOCERTS)) {
if (!OCSP_request_add1_cert(req, signer)) {
goto err;
}
for (size_t i = 0; i < sk_X509_num(certs); i++) {
if (!OCSP_request_add1_cert(req, sk_X509_value(certs, i))) {
goto err;
}
}
}
return 1;
err:
OCSP_SIGNATURE_free(req->optionalSignature);
req->optionalSignature = NULL;
return 0;
}
int OCSP_response_status(OCSP_RESPONSE *resp) {
if (resp == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
return ASN1_ENUMERATED_get(resp->responseStatus);
}
OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) {
if (resp == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
OCSP_RESPBYTES *rb = resp->responseBytes;
if (rb == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_RESPONSE_DATA);
return NULL;
}
if (OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NOT_BASIC_RESPONSE);
return NULL;
}
return ASN1_item_unpack(rb->response, ASN1_ITEM_rptr(OCSP_BASICRESP));
}
OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, size_t idx) {
if (bs == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (bs->tbsResponseData == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_RESPONSE_DATA);
return NULL;
}
return sk_OCSP_SINGLERESP_value(bs->tbsResponseData->responses, idx);
}
int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) {
if (bs == NULL || id == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
if (bs->tbsResponseData == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_RESPONSE_DATA);
return -1;
}
// |OCSP_SINGLERESP| stack is in |OCSP_BASICRESP| responseData, we look for
// |OCSP_CERTID| in here.
STACK_OF(OCSP_SINGLERESP) *sresp = bs->tbsResponseData->responses;
OCSP_SINGLERESP *single;
if (last < 0) {
last = 0;
} else {
last++;
}
for (size_t i = last; i < sk_OCSP_SINGLERESP_num(sresp); i++) {
single = sk_OCSP_SINGLERESP_value(sresp, i);
if (!OCSP_id_cmp(id, single->certId)) {
return i;
}
}
return -1;
}
int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason,
ASN1_GENERALIZEDTIME **revtime,
ASN1_GENERALIZEDTIME **thisupd,
ASN1_GENERALIZEDTIME **nextupd) {
if (single == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
OCSP_CERTSTATUS *cst = single->certStatus;
if (cst == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
int status = cst->type;
// If certificate status is revoked, we look up certificate revocation time
// and reason.
if (status == V_OCSP_CERTSTATUS_REVOKED) {
OCSP_REVOKEDINFO *rev = cst->value.revoked;
if (rev != NULL) {
if (revtime != NULL) {
*revtime = rev->revocationTime;
}
if (reason != NULL) {
if (rev->revocationReason) {
*reason = ASN1_ENUMERATED_get(rev->revocationReason);
} else {
*reason = -1;
}
}
}
}
// Send back when certificate was last updated and when is the next update
// time.
if (thisupd != NULL) {
*thisupd = single->thisUpdate;
}
if (nextupd != NULL) {
*nextupd = single->nextUpdate;
}
return status;
}
int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status,
int *reason, ASN1_GENERALIZEDTIME **revtime,
ASN1_GENERALIZEDTIME **thisupd,
ASN1_GENERALIZEDTIME **nextupd) {
if (bs == NULL || id == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
// we look for index of equivalent |OCSP_CERTID| of issuer certificate in
// |OCSP_BASICRESP|
int single_idx = OCSP_resp_find(bs, id, -1);
if (single_idx < 0) {
return 0;
}
OCSP_SINGLERESP *single = OCSP_resp_get0(bs, single_idx);
// Extract the update time and revocation status of certificate sent back
// from OCSP responder
int single_status =
OCSP_single_get0_status(single, reason, revtime, thisupd, nextupd);
if (status != NULL) {
*status = single_status;
}
return 1;
}
int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisUpdate,
ASN1_GENERALIZEDTIME *nextUpdate,
long drift_num_seconds, long max_age_seconds) {
int ret = 1;
int64_t t_tmp;
int64_t t_now = time(NULL);
// Check |thisUpdate| is valid and not more than |drift_num_seconds| in the
// future.
if (!ASN1_GENERALIZEDTIME_check(thisUpdate)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_ERROR_IN_THISUPDATE_FIELD);
ret = 0;
} else {
t_tmp = t_now + drift_num_seconds;
if (X509_cmp_time_posix(thisUpdate, t_tmp) > 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_STATUS_NOT_YET_VALID);
ret = 0;
}
// If |max_num_seconds| is specified, check that |thisUpdate| is not more
// than |max_num_seconds| in the past.
if (max_age_seconds >= 0) {
t_tmp = t_now - max_age_seconds;
if (X509_cmp_time_posix(thisUpdate, t_tmp) < 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_STATUS_TOO_OLD);
ret = 0;
}
}
}
// If |nextUpdate| field is empty, we have validated everything we can at
// this point.
if (nextUpdate == NULL) {
return ret;
}
// Check |nextUpdate| is valid and not more than |drift_num_seconds| in the
// past.
if (!ASN1_GENERALIZEDTIME_check(nextUpdate)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD);
ret = 0;
} else {
t_tmp = t_now - drift_num_seconds;
if (X509_cmp_time_posix(nextUpdate, t_tmp) < 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_STATUS_EXPIRED);
ret = 0;
}
}
// Also don't allow |nextUpdate| to precede |thisUpdate|.
if (ASN1_STRING_cmp(nextUpdate, thisUpdate) < 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE);
ret = 0;
}
return ret;
}
int OCSP_resp_count(OCSP_BASICRESP *bs) {
if (bs == NULL) {
return -1;
}
return (int)sk_OCSP_SINGLERESP_num(bs->tbsResponseData->responses);
}
const OCSP_CERTID *OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *single) {
return single->certId;
}