WvStreams
wvcrl.cc
1 /* -*- Mode: C++ -*-
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2005 Net Integration Technologies, Inc.
4  *
5  * X.509v3 CRL management classes.
6  */
7 
8 #include <openssl/x509v3.h>
9 #include <openssl/pem.h>
10 
11 #include "wvcrl.h"
12 #include "wvx509mgr.h"
13 #include "wvbase64.h"
14 
15 
16 static const char * warning_str_get = "Tried to determine %s, but CRL is blank!\n";
17 #define CHECK_CRL_EXISTS_GET(x, y) \
18  if (!crl) { \
19  debug(WvLog::Warning, warning_str_get, x); \
20  return y; \
21  }
22 
23 
24 static ASN1_INTEGER * serial_to_int(WvStringParm serial)
25 {
26  if (!!serial)
27  {
28  BIGNUM *bn = NULL;
29  BN_dec2bn(&bn, serial);
30  ASN1_INTEGER *retval = ASN1_INTEGER_new();
31  retval = BN_to_ASN1_INTEGER(bn, retval);
32  BN_free(bn);
33  return retval;
34  }
35 
36  return NULL;
37 }
38 
39 
41  : debug("X509 CRL", WvLog::Debug5)
42 {
43  crl = NULL;
44 }
45 
46 
48  : debug("X509 CRL", WvLog::Debug5)
49 {
50  assert(crl = X509_CRL_new());
51 
52  // Use Version 2 CRLs - Of COURSE that means
53  // to set it to 1 here... grumble..
54  X509_CRL_set_version(crl, 1);
55  X509_CRL_set_issuer_name(crl, X509_get_issuer_name(ca.cert));
56 
57  // most of this copied from wvx509.cc, sigh
58  ASN1_OCTET_STRING *ikeyid = NULL;
59  X509_EXTENSION *ext;
60  int i = X509_get_ext_by_NID(ca.cert, NID_subject_key_identifier, -1);
61  if ((i >= 0) && (ext = X509_get_ext(ca.cert, i)))
62  ikeyid = static_cast<ASN1_OCTET_STRING *>(X509V3_EXT_d2i(ext));
63 
64  if (ikeyid)
65  {
66  AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
67  akeyid->issuer = NULL;
68  akeyid->serial = NULL;
69  akeyid->keyid = ikeyid;
70  ext = X509V3_EXT_i2d(NID_authority_key_identifier, 0, akeyid);
71  X509_CRL_add_ext(crl, ext, -1);
72  X509_EXTENSION_free(ext);
73  AUTHORITY_KEYID_free(akeyid);
74  }
75 
76  // Sign the CRL and set some expiration params
77  ca.signcrl(*this);
78 }
79 
80 
82 {
83  debug("Deleting.\n");
84  if (crl)
85  X509_CRL_free(crl);
86 }
87 
88 
89 bool WvCRL::isok() const
90 {
91  return crl;
92 }
93 
94 
95 bool WvCRL::signedbyca(const WvX509 &cacert) const
96 {
97  CHECK_CRL_EXISTS_GET("if CRL is signed by CA", false);
98 
99  EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
100  int result = X509_CRL_verify(crl, pkey);
101  EVP_PKEY_free(pkey);
102  if (result < 0)
103  {
104  debug("There was an error (%s) determining whether or not we were "
105  "signed by CA '%s'\n", wvssl_errstr(), cacert.get_subject());
106  return false;
107  }
108  bool issigned = (result > 0);
109 
110  debug("CRL was%s signed by CA %s\n", issigned ? "" : " NOT",
111  cacert.get_subject());
112 
113  return issigned;
114 }
115 
116 
117 bool WvCRL::issuedbyca(const WvX509 &cacert) const
118 {
119  CHECK_CRL_EXISTS_GET("if CRL is issued by CA", false);
120 
121  WvString name = get_issuer();
122  bool issued = (cacert.get_subject() == name);
123  if (issued)
124  debug("CRL issuer '%s' matches subject '%s' of cert. We can say "
125  "that it appears to be issued by this CA.\n",
126  name, cacert.get_subject());
127  else
128  debug("CRL issuer '%s' doesn't match subject '%s' of cert. Doesn't "
129  "appear to be issued by this CA.\n", name,
130  cacert.get_subject());
131 
132  return issued;
133 }
134 
135 
136 bool WvCRL::expired() const
137 {
138  CHECK_CRL_EXISTS_GET("if CRL has expired", false);
139 
140  if (X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)) < 0)
141  {
142  debug("CRL appears to be expired.\n");
143  return true;
144  }
145 
146  debug("CRL appears not to be expired.\n");
147  return false;
148 }
149 
150 
151 bool WvCRL::has_critical_extensions() const
152 {
153  CHECK_CRL_EXISTS_GET("if CRL has critical extensions", false);
154 
155  int critical = X509_CRL_get_ext_by_critical(crl, 1, 0);
156  return (critical > 0);
157 }
158 
159 
161 {
162  CHECK_CRL_EXISTS_GET("CRL's AKI", WvString::null);
163 
164  AUTHORITY_KEYID *aki = NULL;
165  int i;
166 
167  aki = static_cast<AUTHORITY_KEYID *>(
168  X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier,
169  &i, NULL));
170  if (aki)
171  {
172  char *tmp = hex_to_string(aki->keyid->data, aki->keyid->length);
173  WvString str(tmp);
174 
175  OPENSSL_free(tmp);
176  AUTHORITY_KEYID_free(aki);
177 
178  return str;
179  }
180 
181  return WvString::null;
182 }
183 
184 
186 {
187  CHECK_CRL_EXISTS_GET("CRL's issuer", WvString::null);
188 
189  char *name = X509_NAME_oneline(X509_CRL_get_issuer(crl), 0, 0);
190  WvString retval(name);
191  OPENSSL_free(name);
192 
193  return retval;
194 }
195 
196 
197 WvString WvCRL::encode(const DumpMode mode) const
198 {
199  WvDynBuf retval;
200  encode(mode, retval);
201 
202  return retval.getstr();
203 }
204 
205 
206 void WvCRL::encode(const DumpMode mode, WvBuf &buf) const
207 {
208  if (mode == CRLFileDER || mode == CRLFilePEM)
209  return; // file modes are no ops with encode
210 
211  if (!crl)
212  {
213  debug(WvLog::Warning, "Tried to encode CRL, but CRL is blank!\n");
214  return;
215  }
216 
217  BIO *bufbio = BIO_new(BIO_s_mem());
218  BUF_MEM *bm;
219  switch (mode)
220  {
221  case CRLPEM:
222  debug("Dumping CRL in PEM format.\n");
223  PEM_write_bio_X509_CRL(bufbio, crl);
224  break;
225  case CRLDER:
226  debug("Dumping CRL in DER format.\n");
227  i2d_X509_CRL_bio(bufbio, crl);
228  break;
229  default:
230  debug("Tried to dump CRL in unknown format!\n");
231  break;
232  }
233 
234  BIO_get_mem_ptr(bufbio, &bm);
235  buf.put(bm->data, bm->length);
236  BIO_free(bufbio);
237 }
238 
239 
240 void WvCRL::decode(const DumpMode mode, WvStringParm str)
241 {
242  if (crl)
243  {
244  debug("Replacing already existant CRL.\n");
245  X509_CRL_free(crl);
246  crl = NULL;
247  }
248 
249  if (mode == CRLFileDER)
250  {
251  BIO *bio = BIO_new(BIO_s_file());
252 
253  if (BIO_read_filename(bio, str.cstr()) <= 0)
254  {
255  debug(WvLog::Warning, "Import CRL from '%s': %s\n",
256  str, wvssl_errstr());
257  BIO_free(bio);
258  return;
259  }
260 
261  if (!(crl = d2i_X509_CRL_bio(bio, NULL)))
262  debug(WvLog::Warning, "Read CRL from '%s': %s\n",
263  str, wvssl_errstr());
264 
265  BIO_free(bio);
266  return;
267  }
268  else if (mode == CRLFilePEM)
269  {
270  FILE * fp = fopen(str, "rb");
271  if (!fp)
272  {
273  int errnum = errno;
274  debug(WvLog::Warning,
275  "open '%s': %s\n",
276  str, strerror(errnum));
277  return;
278  }
279 
280  if (!(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)))
281  debug(WvLog::Warning, "Import CRL from '%s': %s\n",
282  str, wvssl_errstr());
283 
284  fclose(fp);
285  return;
286  }
287 
288  // we use the buffer decode functions for everything else
289  WvDynBuf buf;
290  buf.putstr(str);
291  decode(mode, buf);
292 }
293 
294 
295 void WvCRL::decode(const DumpMode mode, WvBuf &buf)
296 {
297  if (crl)
298  {
299  debug("Replacing already existant CRL.\n");
300  X509_CRL_free(crl);
301  crl = NULL;
302  }
303 
304  if (mode == CRLFileDER || mode == CRLFilePEM)
305  {
306  decode(mode, buf.getstr());
307  return;
308  }
309 
310  BIO *bufbio = BIO_new(BIO_s_mem());
311  BIO_write(bufbio, buf.get(buf.used()), buf.used());
312 
313  if (mode == CRLPEM)
314  {
315  debug("Decoding CRL from PEM format.\n");
316  crl = PEM_read_bio_X509_CRL(bufbio, NULL, NULL, NULL);
317  }
318  else if (mode == CRLDER)
319  {
320  debug("Decoding CRL from DER format.\n");
321  crl = d2i_X509_CRL_bio(bufbio, NULL);
322  }
323  else
324  debug(WvLog::Warning, "Attempted to decode unknown format.\n");
325 
326  if (!crl)
327  debug(WvLog::Warning, "Couldn't decode CRL.\n");
328 
329  BIO_free(bufbio);
330 }
331 
332 
333 bool WvCRL::isrevoked(const WvX509 &cert) const
334 {
335  if (cert.cert)
336  {
337  debug("Checking to see if certificate with name '%s' and serial "
338  "number '%s' is revoked.\n", cert.get_subject(),
339  cert.get_serial());
340  return isrevoked(cert.get_serial());
341  }
342  else
343  {
344  debug(WvLog::Error, "Given certificate to check revocation status, "
345  "but certificate is blank. Declining.\n");
346  return true;
347  }
348 }
349 
350 
351 bool WvCRL::isrevoked(WvStringParm serial_number) const
352 {
353  CHECK_CRL_EXISTS_GET("if certificate is revoked in CRL", false);
354 
355  if (!!serial_number)
356  {
357  ASN1_INTEGER *serial = serial_to_int(serial_number);
358  if (serial)
359  {
360  X509_REVOKED *revoked_entry = NULL;
361  int idx = X509_CRL_get0_by_serial(crl, &revoked_entry, serial);
362  ASN1_INTEGER_free(serial);
363  if (idx >= 1 || revoked_entry)
364  {
365  debug("Certificate is revoked.\n");
366  return true;
367  }
368  else
369  {
370  debug("Certificate is not revoked.\n");
371  return false;
372  }
373  }
374  else
375  debug(WvLog::Warning, "Can't convert serial number to ASN1 format. "
376  "Saying it's not revoked.\n");
377  }
378  else
379  debug(WvLog::Warning, "Serial number for certificate is blank.\n");
380 
381  debug("Certificate is not revoked (or could not determine whether it "
382  "was).\n");
383  return false;
384 }
385 
386 
387 WvCRL::Valid WvCRL::validate(const WvX509 &cacert) const
388 {
389  if (!issuedbyca(cacert))
390  return NOT_THIS_CA;
391 
392  if (!signedbyca(cacert))
393  return NO_VALID_SIGNATURE;
394 
395  if (expired())
396  return EXPIRED;
397 
398  // neither we or openssl handles any critical extensions yet
399  if (has_critical_extensions())
400  {
401  debug("CRL has unhandled critical extensions.\n");
402  return UNHANDLED_CRITICAL_EXTENSIONS;
403  }
404 
405  return VALID;
406 }
407 
408 
409 int WvCRL::numcerts() const
410 {
411  CHECK_CRL_EXISTS_GET("number of certificates in CRL", 0);
412 
413  STACK_OF(X509_REVOKED) *rev;
414  rev = X509_CRL_get_REVOKED(crl);
415  int certcount = sk_X509_REVOKED_num(rev);
416 
417  if (certcount < 0)
418  certcount = 0;
419 
420  return certcount;
421 }
422 
423 
424 void WvCRL::addcert(const WvX509 &cert)
425 {
426  if (!crl)
427  {
428  debug(WvLog::Warning, "Tried to add certificate to CRL, but CRL is "
429  "blank!\n");
430  return;
431  }
432 
433  if (cert.isok())
434  {
435  ASN1_INTEGER *serial = serial_to_int(cert.get_serial());
436  X509_REVOKED *revoked = X509_REVOKED_new();
437  ASN1_GENERALIZEDTIME *now = ASN1_GENERALIZEDTIME_new();
438  X509_REVOKED_set_serialNumber(revoked, serial);
439  X509_gmtime_adj(now, 0);
440  X509_REVOKED_set_revocationDate(revoked, now);
441  // FIXME: We don't deal with the reason here...
442  X509_CRL_add0_revoked(crl, revoked);
443  ASN1_GENERALIZEDTIME_free(now);
444  ASN1_INTEGER_free(serial);
445  }
446  else
447  {
448  debug(WvLog::Warning, "Tried to add a certificate to the CRL, but "
449  "certificate is either bad or broken.\n");
450  }
451 }
452 
WvBufBaseCommonImpl::get
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
WvCRL::Valid
Valid
Type for validate() method: ERROR = there was an error that happened.
Definition: wvcrl.h:91
WvCRL::signedbyca
bool signedbyca(const WvX509 &cacert) const
Check the CRL in crl against the CA certificate in cert.
Definition: wvcrl.cc:95
WvCRL::DumpMode
DumpMode
Type for the encode() and decode() methods: CRLPEM = PEM Encoded X.509 CRL CRLDER = DER Encoded X....
Definition: wvcrl.h:38
WvX509
X509 Class to handle certificates and their related functions.
Definition: wvx509.h:41
WvCRL::issuedbyca
bool issuedbyca(const WvX509 &cacert) const
Check the issuer name of the CRL in crl against the CA certificate in cert.
Definition: wvcrl.cc:117
WvBufBase< unsigned char >::getstr
WvString getstr()
Returns the entire buffer as a null-terminated WvString.
Definition: wvbuffer.cc:17
WvBufBase< unsigned char >::putstr
void putstr(WvStringParm str)
Copies a WvString into the buffer, excluding the null-terminator.
Definition: wvbuffer.cc:11
WvCRL::~WvCRL
virtual ~WvCRL()
Destructor.
Definition: wvcrl.cc:81
WvCRL::encode
WvString encode(const DumpMode mode) const
Return the information requested by mode as a WvString.
Definition: wvcrl.cc:197
WvX509::isok
virtual bool isok() const
Is the certificate object valid?
Definition: wvx509.cc:1285
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvCRL::validate
Valid validate(const WvX509 &cacert) const
Checks to see that a CRL is signed and issued by a CA certificate, and that it has not expired.
Definition: wvcrl.cc:387
WvLog
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition: wvlog.h:56
WvCRL::isrevoked
bool isrevoked(const WvX509 &cert) const
Is the certificate in cert revoked?
Definition: wvcrl.cc:333
WvCRL::addcert
void addcert(const WvX509 &cert)
Add the certificate specified by cert to the CRL.
Definition: wvcrl.cc:424
WvBufBase< unsigned char >
Specialization of WvBufBase for unsigned char type buffers intended for use with raw memory buffers.
Definition: wvbuf.h:22
WvX509::get_serial
WvString get_serial(bool hex=false) const
get and set the serialNumber field of the certificate
Definition: wvx509.cc:704
WvX509Mgr
Definition: wvx509mgr.h:14
WvCRL::get_aki
WvString get_aki() const
Get the Authority key Info.
Definition: wvcrl.cc:160
WvCRL::get_issuer
WvString get_issuer() const
Get the CRL Issuer.
Definition: wvcrl.cc:185
WvDynBufBase< unsigned char >
WvX509Mgr::signcrl
bool signcrl(WvCRL &unsignedcrl) const
Sign the CRL with the rsa key associated with this class.
Definition: wvx509mgr.cc:393
WvCRL::decode
void decode(const DumpMode mode, WvStringParm encoded)
Load the information from the format requested by mode into the class - this overwrites the CRL.
Definition: wvcrl.cc:240
WvBufBaseCommonImpl::used
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92
WvCRL::WvCRL
WvCRL()
Initialize a blank (null) CRL object.
Definition: wvcrl.cc:40
WvX509::get_subject
WvString get_subject() const
get and set the Subject field of the certificate
Definition: wvx509.cc:624
WvCRL::numcerts
int numcerts() const
Counts the number of certificates in this CRL.
Definition: wvcrl.cc:409
WvCRL::expired
bool expired() const
Checks to see if the CRL is expired (i.e.
Definition: wvcrl.cc:136
WvCRL::isok
bool isok() const
Do we have any errors...
Definition: wvcrl.cc:89