WvStreams
wvocsp.cc
00001 #include "wvocsp.h"
00002 #include "wvsslhacks.h"
00003 
00004 static const int OCSP_MAX_VALIDITY_PERIOD = (5 * 60); // 5 min: openssl default
00005 
00006 
00007 WvOCSPReq::WvOCSPReq(const WvX509 &cert, const WvX509 &issuer)
00008 {
00009     wvssl_init();
00010 
00011     req = OCSP_REQUEST_new();
00012     assert(req);
00013 
00014     if (cert.isok() && issuer.isok())
00015     {
00016         id = OCSP_cert_to_id(NULL, cert.cert, issuer.cert);
00017         OCSP_request_add0_id(req, id);
00018     }
00019 }
00020 
00021 
00022 WvOCSPReq::~WvOCSPReq()
00023 {
00024     if (req)
00025         OCSP_REQUEST_free(req);
00026 
00027     wvssl_free();
00028 }
00029 
00030 
00031 void WvOCSPReq::encode(WvBuf &buf)
00032 {
00033     BIO *bufbio = BIO_new(BIO_s_mem());
00034     assert(bufbio);
00035     BUF_MEM *bm;
00036 
00037     // there is no reason why the following should fail, except for OOM
00038     assert(wv_i2d_OCSP_REQUEST_bio(bufbio, req) > 0);
00039 
00040     BIO_get_mem_ptr(bufbio, &bm);
00041     buf.put(bm->data, bm->length);
00042     BIO_free(bufbio);
00043 }
00044 
00045 
00046 WvOCSPResp::WvOCSPResp() :
00047     resp(NULL),
00048     bs(NULL),
00049     log("OCSP Response", WvLog::Debug5)
00050 {
00051     wvssl_init();
00052 }
00053 
00054 
00055 WvOCSPResp::~WvOCSPResp()
00056 {
00057     if (bs)
00058         OCSP_BASICRESP_free(bs);
00059         
00060     if (resp)
00061         OCSP_RESPONSE_free(resp);
00062 
00063     wvssl_free();
00064 }
00065 
00066 
00067 void WvOCSPResp::decode(WvBuf &encoded)
00068 {
00069     BIO *membuf = BIO_new(BIO_s_mem());
00070     BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
00071 
00072     resp = d2i_OCSP_RESPONSE_bio(membuf, NULL);
00073     
00074     if (resp)
00075         bs = OCSP_response_get1_basic(resp);
00076     else
00077         log("Failed to decode response.\n");
00078 
00079     BIO_free_all(membuf);
00080 }
00081 
00082 
00083 bool WvOCSPResp::isok() const
00084 {
00085     if (!resp)
00086         return false;
00087 
00088     int i = OCSP_response_status(resp);
00089     if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL)
00090     {
00091         log("Status not successful: %s\n", wvssl_errstr());
00092         return false;
00093     }
00094 
00095     return true;
00096 }
00097 
00098 
00099 bool WvOCSPResp::check_nonce(const WvOCSPReq &req) const
00100 {
00101     if (!bs)
00102         return false;
00103 
00104     int i;
00105     if ((i = OCSP_check_nonce(req.req, bs)) <= 0)
00106     {
00107         if (i == -1)
00108             log("No nonce in response\n");
00109         else
00110             log("Nonce verify error\n");
00111         
00112         return false;
00113     }
00114 
00115     return true;
00116 }
00117 
00118 
00119 bool WvOCSPResp::signedbycert(const WvX509 &cert) const
00120 {
00121     EVP_PKEY *skey = X509_get_pubkey(cert.cert);
00122     int i = OCSP_BASICRESP_verify(bs, skey, 0);
00123     EVP_PKEY_free(skey);
00124 
00125     if(i > 0)
00126         return true;
00127 
00128     return false;    
00129 }
00130 
00131 
00132 WvX509 WvOCSPResp::get_signing_cert() const
00133 {
00134     if (!bs || !sk_X509_num(bs->certs))
00135         return WvX509();
00136 
00137     // note: the following bit of code is taken almost verbatim from
00138     // ocsp_vfy.c in OpenSSL 0.9.8. Copyright and attribution should 
00139     // properly belong to them
00140 
00141     OCSP_RESPID *id = bs->tbsResponseData->responderId;
00142 
00143     if (id->type == V_OCSP_RESPID_NAME)
00144     {
00145         X509 *x = X509_find_by_subject(bs->certs, id->value.byName);
00146         if (x)
00147             return WvX509(X509_dup(x));
00148     }
00149 
00150     if (id->value.byKey->length != SHA_DIGEST_LENGTH) return NULL;
00151     unsigned char tmphash[SHA_DIGEST_LENGTH];
00152     unsigned char *keyhash = id->value.byKey->data;
00153     for (int i = 0; i < sk_X509_num(bs->certs); i++)
00154     {
00155         X509 *x = sk_X509_value(bs->certs, i);
00156         X509_pubkey_digest(x, EVP_sha1(), tmphash, NULL);
00157         if(!memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH))
00158             return WvX509(X509_dup(x));
00159     }
00160     
00161     return WvX509();
00162 }
00163 
00164 
00165 WvOCSPResp::Status WvOCSPResp::get_status(const WvX509 &cert, 
00166                                           const WvX509 &issuer) const
00167 {
00168     if (!isok())
00169         return Error;
00170 
00171     if (!cert.isok() && !issuer.isok())
00172         return Error;
00173 
00174     int status, reason;
00175     ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
00176 
00177     OCSP_CERTID *id = OCSP_cert_to_id(NULL, cert.cert, issuer.cert);
00178     assert(id); // only fails in case of OOM
00179 
00180     if(!OCSP_resp_find_status(bs, id, &status, &reason,
00181                               &rev, &thisupd, &nextupd))
00182     {
00183         log("OCSP Find Status Error: %s\n", wvssl_errstr());
00184         OCSP_CERTID_free(id);
00185         return Error;
00186     }
00187     OCSP_CERTID_free(id);
00188 
00189     if (!OCSP_check_validity(thisupd, nextupd, OCSP_MAX_VALIDITY_PERIOD, -1))
00190     {
00191         log("Error checking for OCSP validity: %s\n", wvssl_errstr());
00192         return Error;
00193     }
00194 
00195     if (status == V_OCSP_CERTSTATUS_GOOD)
00196         return Good;
00197     else if (status == V_OCSP_CERTSTATUS_REVOKED)
00198         return Revoked;
00199 
00200     log("OCSP cert status is %s, marking as 'Unknown'.\n", 
00201         OCSP_cert_status_str(status));
00202     
00203     return Unknown;
00204 }
00205 
00206 WvString WvOCSPResp::status_str(WvOCSPResp::Status status)
00207 {
00208     if (status == Good)
00209         return "good";
00210     else if (status == Error)
00211         return "error";
00212     else if (status == Revoked)
00213         return "revoked";
00214 
00215     return "unknown";
00216 }