#include "wvocsp.h" #include "wvsslhacks.h" static const int OCSP_MAX_VALIDITY_PERIOD = (5 * 60); // 5 min: openssl default WvOCSPReq::WvOCSPReq(const WvX509 &cert, const WvX509 &issuer) { wvssl_init(); req = OCSP_REQUEST_new(); assert(req); if (cert.isok() && issuer.isok()) { id = OCSP_cert_to_id(NULL, cert.cert, issuer.cert); OCSP_request_add0_id(req, id); } } WvOCSPReq::~WvOCSPReq() { if (req) OCSP_REQUEST_free(req); wvssl_free(); } void WvOCSPReq::encode(WvBuf &buf) { BIO *bufbio = BIO_new(BIO_s_mem()); assert(bufbio); BUF_MEM *bm; // there is no reason why the following should fail, except for OOM assert(wv_i2d_OCSP_REQUEST_bio(bufbio, req) > 0); BIO_get_mem_ptr(bufbio, &bm); buf.put(bm->data, bm->length); BIO_free(bufbio); } WvOCSPResp::WvOCSPResp() : resp(NULL), bs(NULL), log("OCSP Response", WvLog::Debug5) { wvssl_init(); } WvOCSPResp::~WvOCSPResp() { if (bs) OCSP_BASICRESP_free(bs); if (resp) OCSP_RESPONSE_free(resp); wvssl_free(); } void WvOCSPResp::decode(WvBuf &encoded) { BIO *membuf = BIO_new(BIO_s_mem()); BIO_write(membuf, encoded.get(encoded.used()), encoded.used()); resp = d2i_OCSP_RESPONSE_bio(membuf, NULL); if (resp) bs = OCSP_response_get1_basic(resp); else log("Failed to decode response.\n"); BIO_free_all(membuf); } bool WvOCSPResp::isok() const { if (!resp) return false; int i = OCSP_response_status(resp); if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) { log("Status not successful: %s\n", wvssl_errstr()); return false; } return true; } bool WvOCSPResp::check_nonce(const WvOCSPReq &req) const { if (!bs) return false; int i; if ((i = OCSP_check_nonce(req.req, bs)) <= 0) { if (i == -1) log("No nonce in response\n"); else log("Nonce verify error\n"); return false; } return true; } bool WvOCSPResp::signedbycert(const WvX509 &cert) const { EVP_PKEY *skey = X509_get_pubkey(cert.cert); int i = OCSP_BASICRESP_verify(bs, skey, 0); EVP_PKEY_free(skey); if(i > 0) return true; return false; } WvX509 WvOCSPResp::get_signing_cert() const { if (!bs || !sk_X509_num(bs->certs)) return WvX509(); // note: the following bit of code is taken almost verbatim from // ocsp_vfy.c in OpenSSL 0.9.8. Copyright and attribution should // properly belong to them OCSP_RESPID *id = bs->tbsResponseData->responderId; if (id->type == V_OCSP_RESPID_NAME) { X509 *x = X509_find_by_subject(bs->certs, id->value.byName); if (x) return WvX509(X509_dup(x)); } if (id->value.byKey->length != SHA_DIGEST_LENGTH) return NULL; unsigned char tmphash[SHA_DIGEST_LENGTH]; unsigned char *keyhash = id->value.byKey->data; for (int i = 0; i < sk_X509_num(bs->certs); i++) { X509 *x = sk_X509_value(bs->certs, i); X509_pubkey_digest(x, EVP_sha1(), tmphash, NULL); if(!memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH)) return WvX509(X509_dup(x)); } return WvX509(); } WvOCSPResp::Status WvOCSPResp::get_status(const WvX509 &cert, const WvX509 &issuer) const { if (!isok()) return Error; if (!cert.isok() && !issuer.isok()) return Error; int status, reason; ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; OCSP_CERTID *id = OCSP_cert_to_id(NULL, cert.cert, issuer.cert); assert(id); // only fails in case of OOM if(!OCSP_resp_find_status(bs, id, &status, &reason, &rev, &thisupd, &nextupd)) { log("OCSP Find Status Error: %s\n", wvssl_errstr()); OCSP_CERTID_free(id); return Error; } OCSP_CERTID_free(id); if (!OCSP_check_validity(thisupd, nextupd, OCSP_MAX_VALIDITY_PERIOD, -1)) { log("Error checking for OCSP validity: %s\n", wvssl_errstr()); return Error; } if (status == V_OCSP_CERTSTATUS_GOOD) return Good; else if (status == V_OCSP_CERTSTATUS_REVOKED) return Revoked; log("OCSP cert status is %s, marking as 'Unknown'.\n", OCSP_cert_status_str(status)); return Unknown; } WvString WvOCSPResp::status_str(WvOCSPResp::Status status) { if (status == Good) return "good"; else if (status == Error) return "error"; else if (status == Revoked) return "revoked"; return "unknown"; }