aboutsummaryrefslogtreecommitdiffstats
path: root/src/crypto
diff options
context:
space:
mode:
authorJouni Malinen <jouni@qca.qualcomm.com>2015-12-04 12:04:31 (GMT)
committerJouni Malinen <j@w1.fi>2015-12-04 18:08:31 (GMT)
commit213e158ca8d466c5bf1403eec83b9af56cf28dff (patch)
treeee642c3ef38e1ff5129a03d09945ecc96530750e /src/crypto
parentf846211e3ec43dc1638ce1e4cae80e79c07129c7 (diff)
downloadhostap-213e158ca8d466c5bf1403eec83b9af56cf28dff.zip
hostap-213e158ca8d466c5bf1403eec83b9af56cf28dff.tar.gz
hostap-213e158ca8d466c5bf1403eec83b9af56cf28dff.tar.bz2
BoringSSL: Move OCSP implementation into a separate file
This makes it easier to share the OCSP implementation needed for BoringSSL outside tls_openssl.c. For now, this is mainly for http_curl.c. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Diffstat (limited to 'src/crypto')
-rw-r--r--src/crypto/tls_openssl.c826
-rw-r--r--src/crypto/tls_openssl.h19
-rw-r--r--src/crypto/tls_openssl_ocsp.c843
3 files changed, 868 insertions, 820 deletions
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 030e1e2..471ae2b 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -30,16 +30,12 @@
#include <openssl/dh.h>
#endif
-#ifdef OPENSSL_IS_BORINGSSL
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
-#endif /* OPENSSL_IS_BORINGSSL */
-
#include "common.h"
#include "crypto.h"
#include "sha1.h"
#include "sha256.h"
#include "tls.h"
+#include "tls_openssl.h"
#if OPENSSL_VERSION_NUMBER < 0x10000000L
/* ERR_remove_thread_state replaces ERR_remove_state and the latter is
@@ -1654,819 +1650,6 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
}
-#ifdef OPENSSL_IS_BORINGSSL
-
-/*
- * 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 sk_SingleResponse_num(sk) \
-sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk))
-
-#define sk_SingleResponse_value(sk, i) \
- ((SingleResponse *) \
- sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i)))
-
-
-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 (!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 {
- OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
-};
-
-static enum ocsp_result check_ocsp_resp(struct tls_connection *conn,
- X509 *cert, X509 *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;
- 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(conn->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);
-
- 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(conn->ssl_ctx);
- if (conn->peer_issuer) {
- if (X509_STORE_add_cert(store, conn->peer_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(conn->peer_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 && conn->peer_issuer_issuer) {
- tmp_cert = X509_dup(conn->peer_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);
-
- if (!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 ((signer->ex_flags & EXFLAG_XKUSAGE) &&
- (signer->ex_xkusage & 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);
-
- return result;
-}
-
-#endif /* OPENSSL_IS_BORINGSSL */
-
-
static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
char buf[256];
@@ -2613,10 +1796,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
}
#ifdef OPENSSL_IS_BORINGSSL
- if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP)) {
+ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+ preverify_ok) {
enum ocsp_result res;
- res = check_ocsp_resp(conn, err_cert, conn->peer_issuer);
+ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+ conn->peer_issuer,
+ conn->peer_issuer_issuer);
if (res == OCSP_REVOKED) {
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
diff --git a/src/crypto/tls_openssl.h b/src/crypto/tls_openssl.h
new file mode 100644
index 0000000..2a62d5c
--- /dev/null
+++ b/src/crypto/tls_openssl.h
@@ -0,0 +1,19 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLS_OPENSSL_H
+#define TLS_OPENSSL_H
+
+enum ocsp_result {
+ OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
+};
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+ X509 *issuer, X509 *issuer_issuer);
+
+#endif /* TLS_OPENSSL_H */
diff --git a/src/crypto/tls_openssl_ocsp.c b/src/crypto/tls_openssl_ocsp.c
new file mode 100644
index 0000000..37c87f4
--- /dev/null
+++ b/src/crypto/tls_openssl_ocsp.c
@@ -0,0 +1,843 @@
+/*
+ * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#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 sk_SingleResponse_num(sk) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk))
+
+#define sk_SingleResponse_value(sk, i) \
+ ((SingleResponse *) \
+ sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i)))
+
+
+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 (!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;
+ 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);
+
+ 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);
+
+ if (!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 ((signer->ex_flags & EXFLAG_XKUSAGE) &&
+ (signer->ex_xkusage & 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);
+
+ return result;
+}
+
+#endif /* OPENSSL_IS_BORINGSSL */