WvStreams
wvocsp.cc
1 #include "wvocsp.h"
2 #include "wvsslhacks.h"
3 
4 static const int OCSP_MAX_VALIDITY_PERIOD = (5 * 60); // 5 min: openssl default
5 
6 
7 WvOCSPReq::WvOCSPReq(const WvX509 &cert, const WvX509 &issuer)
8 {
9  wvssl_init();
10 
11  req = OCSP_REQUEST_new();
12  assert(req);
13 
14  if (cert.isok() && issuer.isok())
15  {
16  id = OCSP_cert_to_id(NULL, cert.cert, issuer.cert);
17  OCSP_request_add0_id(req, id);
18  }
19 }
20 
21 
22 WvOCSPReq::~WvOCSPReq()
23 {
24  if (req)
25  OCSP_REQUEST_free(req);
26 
27  wvssl_free();
28 }
29 
30 
31 void WvOCSPReq::encode(WvBuf &buf)
32 {
33  BIO *bufbio = BIO_new(BIO_s_mem());
34  assert(bufbio);
35  BUF_MEM *bm;
36 
37  // there is no reason why the following should fail, except for OOM
38  assert(wv_i2d_OCSP_REQUEST_bio(bufbio, req) > 0);
39 
40  BIO_get_mem_ptr(bufbio, &bm);
41  buf.put(bm->data, bm->length);
42  BIO_free(bufbio);
43 }
44 
45 
46 WvOCSPResp::WvOCSPResp() :
47  resp(NULL),
48  bs(NULL),
49  log("OCSP Response", WvLog::Debug5)
50 {
51  wvssl_init();
52 }
53 
54 
55 WvOCSPResp::~WvOCSPResp()
56 {
57  if (bs)
58  OCSP_BASICRESP_free(bs);
59 
60  if (resp)
61  OCSP_RESPONSE_free(resp);
62 
63  wvssl_free();
64 }
65 
66 
67 void WvOCSPResp::decode(WvBuf &encoded)
68 {
69  BIO *membuf = BIO_new(BIO_s_mem());
70  BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
71 
72  resp = d2i_OCSP_RESPONSE_bio(membuf, NULL);
73 
74  if (resp)
75  bs = OCSP_response_get1_basic(resp);
76  else
77  log("Failed to decode response.\n");
78 
79  BIO_free_all(membuf);
80 }
81 
82 
83 bool WvOCSPResp::isok() const
84 {
85  if (!resp)
86  return false;
87 
88  int i = OCSP_response_status(resp);
89  if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL)
90  {
91  log("Status not successful: %s\n", wvssl_errstr());
92  return false;
93  }
94 
95  return true;
96 }
97 
98 
99 bool WvOCSPResp::check_nonce(const WvOCSPReq &req) const
100 {
101  if (!bs)
102  return false;
103 
104  int i;
105  if ((i = OCSP_check_nonce(req.req, bs)) <= 0)
106  {
107  if (i == -1)
108  log("No nonce in response\n");
109  else
110  log("Nonce verify error\n");
111 
112  return false;
113  }
114 
115  return true;
116 }
117 
118 
119 bool WvOCSPResp::signedbycert(const WvX509 &cert) const
120 {
121  EVP_PKEY *skey = X509_get_pubkey(cert.cert);
122  int i = OCSP_BASICRESP_verify(bs, skey, 0);
123  EVP_PKEY_free(skey);
124 
125  if(i > 0)
126  return true;
127 
128  return false;
129 }
130 
131 
132 WvX509 WvOCSPResp::get_signing_cert() const
133 {
134  if (!bs || !sk_X509_num(bs->certs))
135  return WvX509();
136 
137  // note: the following bit of code is taken almost verbatim from
138  // ocsp_vfy.c in OpenSSL 0.9.8. Copyright and attribution should
139  // properly belong to them
140 
141  OCSP_RESPID *id = bs->tbsResponseData->responderId;
142 
143  if (id->type == V_OCSP_RESPID_NAME)
144  {
145  X509 *x = X509_find_by_subject(bs->certs, id->value.byName);
146  if (x)
147  return WvX509(X509_dup(x));
148  }
149 
150  if (id->value.byKey->length != SHA_DIGEST_LENGTH) return NULL;
151  unsigned char tmphash[SHA_DIGEST_LENGTH];
152  unsigned char *keyhash = id->value.byKey->data;
153  for (int i = 0; i < sk_X509_num(bs->certs); i++)
154  {
155  X509 *x = sk_X509_value(bs->certs, i);
156  X509_pubkey_digest(x, EVP_sha1(), tmphash, NULL);
157  if(!memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH))
158  return WvX509(X509_dup(x));
159  }
160 
161  return WvX509();
162 }
163 
164 
165 WvOCSPResp::Status WvOCSPResp::get_status(const WvX509 &cert,
166  const WvX509 &issuer) const
167 {
168  if (!isok())
169  return Error;
170 
171  if (!cert.isok() && !issuer.isok())
172  return Error;
173 
174  int status, reason;
175  ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
176 
177  OCSP_CERTID *id = OCSP_cert_to_id(NULL, cert.cert, issuer.cert);
178  assert(id); // only fails in case of OOM
179 
180  if(!OCSP_resp_find_status(bs, id, &status, &reason,
181  &rev, &thisupd, &nextupd))
182  {
183  log("OCSP Find Status Error: %s\n", wvssl_errstr());
184  OCSP_CERTID_free(id);
185  return Error;
186  }
187  OCSP_CERTID_free(id);
188 
189  if (!OCSP_check_validity(thisupd, nextupd, OCSP_MAX_VALIDITY_PERIOD, -1))
190  {
191  log("Error checking for OCSP validity: %s\n", wvssl_errstr());
192  return Error;
193  }
194 
195  if (status == V_OCSP_CERTSTATUS_GOOD)
196  return Good;
197  else if (status == V_OCSP_CERTSTATUS_REVOKED)
198  return Revoked;
199 
200  log("OCSP cert status is %s, marking as 'Unknown'.\n",
201  OCSP_cert_status_str(status));
202 
203  return Unknown;
204 }
205 
206 WvString WvOCSPResp::status_str(WvOCSPResp::Status status)
207 {
208  if (status == Good)
209  return "good";
210  else if (status == Error)
211  return "error";
212  else if (status == Revoked)
213  return "revoked";
214 
215  return "unknown";
216 }
X509 Class to handle certificates and their related functions.
Definition: wvx509.h:41
virtual bool isok() const
Is the certificate object valid?
Definition: wvx509.cc:1288
Specialization of WvBufBase for unsigned char type buffers intended for use with raw memory buffers...
Definition: wvbuf.h:22
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
const T * get(size_t count)
Reads exactly the specified number of elements and returns a pointer to a storage location owned by t...
Definition: wvbufbase.h:114
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's...
Definition: wvlog.h:56