/* * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include #include #ifdef OPENSSL_IS_BORINGSSL #include #include #endif /* OPENSSL_IS_BORINGSSL */ #include "common.h" #include "tls_openssl.h" #ifdef OPENSSL_IS_BORINGSSL static void tls_show_errors(int level, const char *func, const char *txt) { unsigned long err; wpa_printf(level, "OpenSSL: %s - %s %s", func, txt, ERR_error_string(ERR_get_error(), NULL)); while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", ERR_error_string(err, NULL)); } } /* * CertID ::= SEQUENCE { * hashAlgorithm AlgorithmIdentifier, * issuerNameHash OCTET STRING, -- Hash of Issuer's DN * issuerKeyHash OCTET STRING, -- Hash of Issuer's public key * serialNumber CertificateSerialNumber } */ typedef struct { X509_ALGOR *hashAlgorithm; ASN1_OCTET_STRING *issuerNameHash; ASN1_OCTET_STRING *issuerKeyHash; ASN1_INTEGER *serialNumber; } CertID; /* * ResponseBytes ::= SEQUENCE { * responseType OBJECT IDENTIFIER, * response OCTET STRING } */ typedef struct { ASN1_OBJECT *responseType; ASN1_OCTET_STRING *response; } ResponseBytes; /* * OCSPResponse ::= SEQUENCE { * responseStatus OCSPResponseStatus, * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } */ typedef struct { ASN1_ENUMERATED *responseStatus; ResponseBytes *responseBytes; } OCSPResponse; ASN1_SEQUENCE(ResponseBytes) = { ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT), ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING) } ASN1_SEQUENCE_END(ResponseBytes); ASN1_SEQUENCE(OCSPResponse) = { ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED), ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0) } ASN1_SEQUENCE_END(OCSPResponse); IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse); /* * ResponderID ::= CHOICE { * byName [1] Name, * byKey [2] KeyHash } */ typedef struct { int type; union { X509_NAME *byName; ASN1_OCTET_STRING *byKey; } value; } ResponderID; /* * RevokedInfo ::= SEQUENCE { * revocationTime GeneralizedTime, * revocationReason [0] EXPLICIT CRLReason OPTIONAL } */ typedef struct { ASN1_GENERALIZEDTIME *revocationTime; ASN1_ENUMERATED *revocationReason; } RevokedInfo; /* * CertStatus ::= CHOICE { * good [0] IMPLICIT NULL, * revoked [1] IMPLICIT RevokedInfo, * unknown [2] IMPLICIT UnknownInfo } */ typedef struct { int type; union { ASN1_NULL *good; RevokedInfo *revoked; ASN1_NULL *unknown; } value; } CertStatus; /* * SingleResponse ::= SEQUENCE { * certID CertID, * certStatus CertStatus, * thisUpdate GeneralizedTime, * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, * singleExtensions [1] EXPLICIT Extensions OPTIONAL } */ typedef struct { CertID *certID; CertStatus *certStatus; ASN1_GENERALIZEDTIME *thisUpdate; ASN1_GENERALIZEDTIME *nextUpdate; STACK_OF(X509_EXTENSION) *singleExtensions; } SingleResponse; /* * ResponseData ::= SEQUENCE { * version [0] EXPLICIT Version DEFAULT v1, * responderID ResponderID, * producedAt GeneralizedTime, * responses SEQUENCE OF SingleResponse, * responseExtensions [1] EXPLICIT Extensions OPTIONAL } */ typedef struct { ASN1_INTEGER *version; ResponderID *responderID; ASN1_GENERALIZEDTIME *producedAt; STACK_OF(SingleResponse) *responses; STACK_OF(X509_EXTENSION) *responseExtensions; } ResponseData; /* * BasicOCSPResponse ::= SEQUENCE { * tbsResponseData ResponseData, * signatureAlgorithm AlgorithmIdentifier, * signature BIT STRING, * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } */ typedef struct { ResponseData *tbsResponseData; X509_ALGOR *signatureAlgorithm; ASN1_BIT_STRING *signature; STACK_OF(X509) *certs; } BasicOCSPResponse; ASN1_SEQUENCE(CertID) = { ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR), ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING), ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING), ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER) } ASN1_SEQUENCE_END(CertID); ASN1_CHOICE(ResponderID) = { ASN1_EXP(ResponderID, value.byName, X509_NAME, 1), ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2) } ASN1_CHOICE_END(ResponderID); ASN1_SEQUENCE(RevokedInfo) = { ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME), ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0) } ASN1_SEQUENCE_END(RevokedInfo); ASN1_CHOICE(CertStatus) = { ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0), ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1), ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2) } ASN1_CHOICE_END(CertStatus); ASN1_SEQUENCE(SingleResponse) = { ASN1_SIMPLE(SingleResponse, certID, CertID), ASN1_SIMPLE(SingleResponse, certStatus, CertStatus), ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME), ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0), ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions, X509_EXTENSION, 1) } ASN1_SEQUENCE_END(SingleResponse); ASN1_SEQUENCE(ResponseData) = { ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0), ASN1_SIMPLE(ResponseData, responderID, ResponderID), ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME), ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse), ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions, X509_EXTENSION, 1) } ASN1_SEQUENCE_END(ResponseData); ASN1_SEQUENCE(BasicOCSPResponse) = { ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData), ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR), ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING), ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0) } ASN1_SEQUENCE_END(BasicOCSPResponse); IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse); DEFINE_STACK_OF(SingleResponse) static char * mem_bio_to_str(BIO *out) { char *txt; size_t rlen; int res; rlen = BIO_ctrl_pending(out); txt = os_malloc(rlen + 1); if (!txt) { BIO_free(out); return NULL; } res = BIO_read(out, txt, rlen); BIO_free(out); if (res < 0) { os_free(txt); return NULL; } txt[res] = '\0'; return txt; } static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t) { BIO *out; out = BIO_new(BIO_s_mem()); if (!out) return NULL; if (!ASN1_GENERALIZEDTIME_print(out, t)) { BIO_free(out); return NULL; } return mem_bio_to_str(out); } static char * responderid_str(ResponderID *rid) { BIO *out; out = BIO_new(BIO_s_mem()); if (!out) return NULL; switch (rid->type) { case 0: X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE); break; case 1: i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING); break; default: BIO_free(out); return NULL; } return mem_bio_to_str(out); } static char * octet_string_str(ASN1_OCTET_STRING *o) { BIO *out; out = BIO_new(BIO_s_mem()); if (!out) return NULL; i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING); return mem_bio_to_str(out); } static char * integer_str(ASN1_INTEGER *i) { BIO *out; out = BIO_new(BIO_s_mem()); if (!out) return NULL; i2a_ASN1_INTEGER(out, i); return mem_bio_to_str(out); } static char * algor_str(X509_ALGOR *alg) { BIO *out; out = BIO_new(BIO_s_mem()); if (!out) return NULL; i2a_ASN1_OBJECT(out, alg->algorithm); return mem_bio_to_str(out); } static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext) { BIO *out; if (!ext) return NULL; out = BIO_new(BIO_s_mem()); if (!out) return NULL; if (!X509V3_extensions_print(out, title, ext, 0, 0)) { BIO_free(out); return NULL; } return mem_bio_to_str(out); } static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd) { time_t now, tmp; if (!ASN1_GENERALIZEDTIME_check(thisupd)) { wpa_printf(MSG_DEBUG, "OpenSSL: Invalid OCSP response thisUpdate"); return 0; } time(&now); tmp = now + 5 * 60; /* allow five minute clock difference */ if (X509_cmp_time(thisupd, &tmp) > 0) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid"); return 0; } if (!nextupd) return 1; /* OK - no limit on response age */ if (!ASN1_GENERALIZEDTIME_check(nextupd)) { wpa_printf(MSG_DEBUG, "OpenSSL: Invalid OCSP response nextUpdate"); return 0; } tmp = now - 5 * 60; /* allow five minute clock difference */ if (X509_cmp_time(nextupd, &tmp) < 0) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired"); return 0; } if (ASN1_STRING_cmp(nextupd, thisupd) < 0) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response nextUpdate before thisUpdate"); return 0; } /* Both thisUpdate and nextUpdate are valid */ return -1; } static int issuer_match(X509 *cert, X509 *issuer, CertID *certid) { X509_NAME *iname; ASN1_BIT_STRING *ikey; const EVP_MD *dgst; unsigned int len; unsigned char md[EVP_MAX_MD_SIZE]; ASN1_OCTET_STRING *hash; char *txt; dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm); if (!dgst) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not find matching hash algorithm for OCSP"); return -1; } iname = X509_get_issuer_name(cert); if (!X509_NAME_digest(iname, dgst, md, &len)) return -1; hash = ASN1_OCTET_STRING_new(); if (!hash) return -1; if (!ASN1_OCTET_STRING_set(hash, md, len)) { ASN1_OCTET_STRING_free(hash); return -1; } txt = octet_string_str(hash); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s", txt); os_free(txt); } if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) { ASN1_OCTET_STRING_free(hash); return -1; } ikey = X509_get0_pubkey_bitstr(issuer); if (!ikey || !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) || !ASN1_OCTET_STRING_set(hash, md, len)) { ASN1_OCTET_STRING_free(hash); return -1; } txt = octet_string_str(hash); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s", txt); os_free(txt); } if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) { ASN1_OCTET_STRING_free(hash); return -1; } ASN1_OCTET_STRING_free(hash); return 0; } static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid) { unsigned int i; unsigned char hash[SHA_DIGEST_LENGTH]; if (rid->type == 0) { /* byName */ return X509_find_by_subject(certs, rid->value.byName); } /* byKey */ if (rid->value.byKey->length != SHA_DIGEST_LENGTH) return NULL; for (i = 0; i < sk_X509_num(certs); i++) { X509 *x = sk_X509_value(certs, i); X509_pubkey_digest(x, EVP_sha1(), hash, NULL); if (os_memcmp(rid->value.byKey->data, hash, SHA_DIGEST_LENGTH) == 0) return x; } return NULL; } enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert, X509 *issuer, X509 *issuer_issuer) { const uint8_t *resp_data; size_t resp_len; OCSPResponse *resp; int status; ResponseBytes *bytes; const u8 *basic_data; size_t basic_len; BasicOCSPResponse *basic; ResponseData *rd; char *txt; int i, num; unsigned int j, num_resp; SingleResponse *matching_resp = NULL, *cmp_sresp; enum ocsp_result result = OCSP_INVALID; X509_STORE *store; STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL; X509_STORE_CTX *ctx = NULL; X509 *signer, *tmp_cert; int signer_trusted = 0; EVP_PKEY *skey; int ret; char buf[256]; txt = integer_str(X509_get_serialNumber(cert)); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt); os_free(txt); } SSL_get0_ocsp_response(ssl, &resp_data, &resp_len); if (resp_data == NULL || resp_len == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); return OCSP_NO_RESPONSE; } wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len); resp = d2i_OCSPResponse(NULL, &resp_data, resp_len); if (!resp) { wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse"); return OCSP_INVALID; } status = ASN1_ENUMERATED_get(resp->responseStatus); if (status != 0) { wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d", status); return OCSP_INVALID; } bytes = resp->responseBytes; if (!bytes || OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) { wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); return OCSP_INVALID; } basic_data = ASN1_STRING_data(bytes->response); basic_len = ASN1_STRING_length(bytes->response); wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse", basic_data, basic_len); basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len); if (!basic) { wpa_printf(MSG_INFO, "OpenSSL: Could not parse BasicOCSPResponse"); OCSPResponse_free(resp); return OCSP_INVALID; } rd = basic->tbsResponseData; if (basic->certs) { untrusted = sk_X509_dup(basic->certs); if (!untrusted) goto fail; num = sk_X509_num(basic->certs); for (i = 0; i < num; i++) { X509 *extra_cert; extra_cert = sk_X509_value(basic->certs, i); X509_NAME_oneline(X509_get_subject_name(extra_cert), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "OpenSSL: BasicOCSPResponse cert %s", buf); if (!sk_X509_push(untrusted, extra_cert)) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not add certificate to the untrusted stack"); } } } store = SSL_CTX_get_cert_store(ssl_ctx); if (issuer) { if (X509_STORE_add_cert(store, issuer) != 1) { tls_show_errors(MSG_INFO, __func__, "OpenSSL: Could not add issuer to certificate store"); } certs = sk_X509_new_null(); if (certs) { tmp_cert = X509_dup(issuer); if (tmp_cert && !sk_X509_push(certs, tmp_cert)) { tls_show_errors( MSG_INFO, __func__, "OpenSSL: Could not add issuer to OCSP responder trust store"); X509_free(tmp_cert); sk_X509_free(certs); certs = NULL; } if (certs && issuer_issuer) { tmp_cert = X509_dup(issuer_issuer); if (tmp_cert && !sk_X509_push(certs, tmp_cert)) { tls_show_errors( MSG_INFO, __func__, "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); X509_free(tmp_cert); } } } } signer = ocsp_find_signer(certs, rd->responderID); if (!signer) signer = ocsp_find_signer(untrusted, rd->responderID); else signer_trusted = 1; if (!signer) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not find OCSP signer certificate"); goto fail; } skey = X509_get_pubkey(signer); if (!skey) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not get OCSP signer public key"); goto fail; } if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData), basic->signatureAlgorithm, basic->signature, basic->tbsResponseData, skey) <= 0) { wpa_printf(MSG_DEBUG, "OpenSSL: BasicOCSPResponse signature is invalid"); goto fail; } X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature", buf); ctx = X509_STORE_CTX_new(); if (!ctx || !X509_STORE_CTX_init(ctx, store, signer, untrusted)) goto fail; X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER); ret = X509_verify_cert(ctx); chain = X509_STORE_CTX_get1_chain(ctx); X509_STORE_CTX_cleanup(ctx); if (ret <= 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not validate OCSP signer certificate"); goto fail; } if (!chain || sk_X509_num(chain) <= 0) { wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found"); goto fail; } if (!signer_trusted) { X509_check_purpose(signer, -1, 0); if ((X509_get_extension_flags(signer) & EXFLAG_XKUSAGE) && (X509_get_extended_key_usage(signer) & XKU_OCSP_SIGN)) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP signer certificate delegation OK"); } else { tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1); if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) != X509_TRUST_TRUSTED) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP signer certificate not trusted"); result = OCSP_NO_RESPONSE; goto fail; } } } wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu", ASN1_INTEGER_get(rd->version)); txt = responderid_str(rd->responderID); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s", txt); os_free(txt); } txt = generalizedtime_str(rd->producedAt); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s", txt); os_free(txt); } num_resp = sk_SingleResponse_num(rd->responses); if (num_resp == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse"); result = OCSP_NO_RESPONSE; goto fail; } cmp_sresp = sk_SingleResponse_value(rd->responses, 0); for (j = 0; j < num_resp; j++) { SingleResponse *sresp; CertID *cid1, *cid2; sresp = sk_SingleResponse_value(rd->responses, j); wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u", j + 1, num_resp); txt = algor_str(sresp->certID->hashAlgorithm); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: certID hashAlgorithm: %s", txt); os_free(txt); } txt = octet_string_str(sresp->certID->issuerNameHash); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: certID issuerNameHash: %s", txt); os_free(txt); } txt = octet_string_str(sresp->certID->issuerKeyHash); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: certID issuerKeyHash: %s", txt); os_free(txt); } txt = integer_str(sresp->certID->serialNumber); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: certID serialNumber: %s", txt); os_free(txt); } switch (sresp->certStatus->type) { case 0: wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good"); break; case 1: wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked"); break; default: wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown"); break; } txt = generalizedtime_str(sresp->thisUpdate); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt); os_free(txt); } if (sresp->nextUpdate) { txt = generalizedtime_str(sresp->nextUpdate); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s", txt); os_free(txt); } } txt = extensions_str("singleExtensions", sresp->singleExtensions); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); os_free(txt); } cid1 = cmp_sresp->certID; cid2 = sresp->certID; if (j > 0 && (OBJ_cmp(cid1->hashAlgorithm->algorithm, cid2->hashAlgorithm->algorithm) != 0 || ASN1_OCTET_STRING_cmp(cid1->issuerNameHash, cid2->issuerNameHash) != 0 || ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash, cid2->issuerKeyHash) != 0)) { wpa_printf(MSG_DEBUG, "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse"); goto fail; } if (!matching_resp && issuer && ASN1_INTEGER_cmp(sresp->certID->serialNumber, X509_get_serialNumber(cert)) == 0 && issuer_match(cert, issuer, sresp->certID) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: This response matches peer certificate"); matching_resp = sresp; } } txt = extensions_str("responseExtensions", rd->responseExtensions); if (txt) { wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); os_free(txt); } if (!matching_resp) { wpa_printf(MSG_DEBUG, "OpenSSL: Could not find OCSP response that matches the peer certificate"); result = OCSP_NO_RESPONSE; goto fail; } if (!ocsp_resp_valid(matching_resp->thisUpdate, matching_resp->nextUpdate)) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not valid at this time"); goto fail; } if (matching_resp->certStatus->type == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response indicated that the peer certificate has been revoked"); result = OCSP_REVOKED; goto fail; } if (matching_resp->certStatus->type != 0) { wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response did not indicate good status"); result = OCSP_NO_RESPONSE; goto fail; } /* OCSP response indicated the certificate is good. */ result = OCSP_GOOD; fail: sk_X509_pop_free(chain, X509_free); sk_X509_free(untrusted); sk_X509_pop_free(certs, X509_free); BasicOCSPResponse_free(basic); OCSPResponse_free(resp); X509_STORE_CTX_free(ctx); return result; } #endif /* OPENSSL_IS_BORINGSSL */