Saner handling if config.mk doesn't exist: use a default config.defaults.mk.
[wvstreams.git] / crypto / wvcrl.cc
blob03ad3032d2aa56b7d66f9416eac16b09772b8e96
1 /* -*- Mode: C++ -*-
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2005 Net Integration Technologies, Inc.
5 * X.509v3 CRL management classes.
6 */
8 #include <openssl/x509v3.h>
9 #include <openssl/pem.h>
11 #include "wvcrl.h"
12 #include "wvx509mgr.h"
13 #include "wvbase64.h"
15 static const char * warning_str_get = "Tried to determine %s, but CRL is blank!\n";
16 #define CHECK_CRL_EXISTS_GET(x, y) \
17 if (!crl) { \
18 debug(WvLog::Warning, warning_str_get, x); \
19 return y; \
22 static ASN1_INTEGER * serial_to_int(WvStringParm serial)
24 if (!!serial)
26 BIGNUM *bn = NULL;
27 BN_dec2bn(&bn, serial);
28 ASN1_INTEGER *retval = ASN1_INTEGER_new();
29 retval = BN_to_ASN1_INTEGER(bn, retval);
30 BN_free(bn);
31 return retval;
34 return NULL;
38 WvCRL::WvCRL()
39 : debug("X509 CRL", WvLog::Debug5)
41 crl = NULL;
45 WvCRL::WvCRL(const WvX509Mgr &cacert)
46 : debug("X509 CRL", WvLog::Debug5)
48 assert(crl = X509_CRL_new());
49 cacert.signcrl(*this);
53 WvCRL::~WvCRL()
55 debug("Deleting.\n");
56 if (crl)
57 X509_CRL_free(crl);
61 bool WvCRL::isok() const
63 return crl;
67 bool WvCRL::signedbyca(const WvX509 &cacert) const
69 CHECK_CRL_EXISTS_GET("if CRL is signed by CA", false);
71 EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
72 int result = X509_CRL_verify(crl, pkey);
73 EVP_PKEY_free(pkey);
74 if (result < 0)
76 debug("There was an error determining whether or not we were signed by "
77 "CA '%s'\n", cacert.get_subject());
78 return false;
80 bool issigned = (result > 0);
82 debug("CRL was%s signed by CA %s\n", issigned ? "" : " NOT",
83 cacert.get_subject());
85 return issigned;
89 bool WvCRL::issuedbyca(const WvX509 &cacert) const
91 CHECK_CRL_EXISTS_GET("if CRL is issued by CA", false);
93 WvString name = get_issuer();
94 bool issued = (cacert.get_subject() == name);
95 if (issued)
96 debug("CRL issuer '%s' matches subject '%s' of cert. We can say "
97 "that it appears to be issued by this CA.\n",
98 name, cacert.get_subject());
99 else
100 debug("CRL issuer '%s' doesn't match subject '%s' of cert. Doesn't "
101 "appear to be issued by this CA.\n", name,
102 cacert.get_subject());
104 return issued;
108 bool WvCRL::expired() const
110 CHECK_CRL_EXISTS_GET("if CRL has expired", false);
112 if (X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)) < 0)
114 debug("CRL appears to be expired.\n");
115 return true;
118 debug("CRL appears not to be expired.\n");
119 return false;
123 bool WvCRL::has_critical_extensions() const
125 CHECK_CRL_EXISTS_GET("if CRL has critical extensions", false);
127 int critical = X509_CRL_get_ext_by_critical(crl, 1, 0);
128 return (critical > 0);
132 WvString WvCRL::get_aki() const
134 CHECK_CRL_EXISTS_GET("CRL's AKI", WvString::null);
136 AUTHORITY_KEYID *aki = NULL;
137 int i;
139 aki = static_cast<AUTHORITY_KEYID *>(
140 X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier,
141 &i, NULL));
142 if (aki)
144 char *tmp = hex_to_string(aki->keyid->data, aki->keyid->length);
145 WvString str(tmp);
147 OPENSSL_free(tmp);
148 AUTHORITY_KEYID_free(aki);
150 return str;
153 return WvString::null;
157 WvString WvCRL::get_issuer() const
159 CHECK_CRL_EXISTS_GET("CRL's issuer", WvString::null);
161 char *name = X509_NAME_oneline(X509_CRL_get_issuer(crl), 0, 0);
162 WvString retval(name);
163 OPENSSL_free(name);
165 return retval;
169 WvString WvCRL::encode(const DumpMode mode) const
171 WvDynBuf retval;
172 encode(mode, retval);
174 return retval.getstr();
178 void WvCRL::encode(const DumpMode mode, WvBuf &buf) const
180 if (mode == CRLFileDER || mode == CRLFilePEM)
181 return; // file modes are no ops with encode
183 if (!crl)
185 debug(WvLog::Warning, "Tried to encode CRL, but CRL is blank!\n");
186 return;
189 BIO *bufbio = BIO_new(BIO_s_mem());
190 BUF_MEM *bm;
191 switch (mode)
193 case CRLPEM:
194 debug("Dumping CRL in PEM format.\n");
195 PEM_write_bio_X509_CRL(bufbio, crl);
196 break;
197 case CRLDER:
198 debug("Dumping CRL in DER format.\n");
199 i2d_X509_CRL_bio(bufbio, crl);
200 break;
201 default:
202 debug("Tried to dump CRL in unknown format!\n");
203 break;
206 BIO_get_mem_ptr(bufbio, &bm);
207 buf.put(bm->data, bm->length);
208 BIO_free(bufbio);
212 void WvCRL::decode(const DumpMode mode, WvStringParm str)
214 if (crl)
216 debug("Replacing already existant CRL.\n");
217 X509_CRL_free(crl);
218 crl = NULL;
221 if (mode == CRLFileDER)
223 BIO *bio = BIO_new(BIO_s_file());
225 if (BIO_read_filename(bio, str.cstr()) <= 0)
227 debug(WvLog::Warning, "Import CRL from '%s': %s\n",
228 str, wvssl_errstr());
229 BIO_free(bio);
230 return;
233 if (!(crl = d2i_X509_CRL_bio(bio, NULL)))
234 debug(WvLog::Warning, "Read CRL from '%s': %s\n",
235 str, wvssl_errstr());
237 BIO_free(bio);
238 return;
240 else if (mode == CRLFilePEM)
242 FILE * fp = fopen(str, "rb");
243 if (!fp)
245 int errnum = errno;
246 debug(WvLog::Warning,
247 "Import CRL from '%s': %s\n",
248 str, strerror(errnum));
249 return;
252 if (!(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)))
253 debug(WvLog::Warning, "Can't read CRL from file");
255 fclose(fp);
256 return;
259 // we use the buffer decode functions for everything else
260 WvDynBuf buf;
261 buf.putstr(str);
262 decode(mode, buf);
266 void WvCRL::decode(const DumpMode mode, WvBuf &buf)
268 if (crl)
270 debug("Replacing already existant CRL.\n");
271 X509_CRL_free(crl);
272 crl = NULL;
275 if (mode == CRLFileDER || mode == CRLFilePEM)
277 decode(mode, buf.getstr());
278 return;
281 BIO *bufbio = BIO_new(BIO_s_mem());
282 BIO_write(bufbio, buf.get(buf.used()), buf.used());
284 if (mode == CRLPEM)
286 debug("Decoding CRL from PEM format.\n");
287 crl = PEM_read_bio_X509_CRL(bufbio, NULL, NULL, NULL);
289 else if (mode == CRLDER)
291 debug("Decoding CRL from DER format.\n");
292 crl = d2i_X509_CRL_bio(bufbio, NULL);
294 else
295 debug(WvLog::Warning, "Attempted to decode unknown format.\n");
297 if (!crl)
298 debug(WvLog::Warning, "Couldn't decode CRL.\n");
300 BIO_free(bufbio);
304 bool WvCRL::isrevoked(const WvX509 &cert) const
306 if (cert.cert)
308 debug("Checking to see if certificate with name '%s' and serial "
309 "number '%s' is revoked.\n", cert.get_subject(),
310 cert.get_serial());
311 return isrevoked(cert.get_serial());
313 else
315 debug(WvLog::Error, "Given certificate to check revocation status, "
316 "but certificate is blank. Declining.\n");
317 return true;
322 bool WvCRL::isrevoked(WvStringParm serial_number) const
324 CHECK_CRL_EXISTS_GET("if certificate is revoked in CRL", false);
326 if (!!serial_number)
328 ASN1_INTEGER *serial = serial_to_int(serial_number);
329 if (serial)
331 X509_REVOKED mayberevoked;
332 mayberevoked.serialNumber = serial;
333 if (crl->crl->revoked)
335 int idx = sk_X509_REVOKED_find(crl->crl->revoked,
336 &mayberevoked);
337 ASN1_INTEGER_free(serial);
338 if (idx >= 0)
340 debug("Certificate is revoked.\n");
341 return true;
343 else
345 debug("Certificate is not revoked.\n");
346 return false;
349 else
351 ASN1_INTEGER_free(serial);
352 debug("CRL does not have revoked list.\n");
353 return false;
357 else
358 debug(WvLog::Warning, "Can't convert serial number to ASN1 format. "
359 "Saying it's not revoked.\n");
361 else
362 debug(WvLog::Warning, "Serial number for certificate is blank.\n");
364 debug("Certificate is not revoked (or could not determine whether it "
365 "was).\n");
366 return false;
370 WvCRL::Valid WvCRL::validate(const WvX509 &cacert) const
372 if (!issuedbyca(cacert))
373 return NOT_THIS_CA;
375 if (!signedbyca(cacert))
376 return NO_VALID_SIGNATURE;
378 if (expired())
379 return EXPIRED;
381 // neither we or openssl handles any critical extensions yet
382 if (has_critical_extensions())
384 debug("CRL has unhandled critical extensions.\n");
385 return UNHANDLED_CRITICAL_EXTENSIONS;
388 return VALID;
392 int WvCRL::numcerts() const
394 CHECK_CRL_EXISTS_GET("number of certificates in CRL", 0);
396 STACK_OF(X509_REVOKED) *rev;
397 rev = X509_CRL_get_REVOKED(crl);
398 int certcount = sk_X509_REVOKED_num(rev);
400 if (certcount < 0)
401 certcount = 0;
403 return certcount;
407 void WvCRL::addcert(const WvX509 &cert)
409 if (!crl)
411 debug(WvLog::Warning, "Tried to add certificate to CRL, but CRL is "
412 "blank!\n");
413 return;
416 if (cert.isok())
418 ASN1_INTEGER *serial = serial_to_int(cert.get_serial());
419 X509_REVOKED *revoked = X509_REVOKED_new();
420 ASN1_GENERALIZEDTIME *now = ASN1_GENERALIZEDTIME_new();
421 X509_REVOKED_set_serialNumber(revoked, serial);
422 X509_gmtime_adj(now, 0);
423 X509_REVOKED_set_revocationDate(revoked, now);
424 // FIXME: We don't deal with the reason here...
425 X509_CRL_add0_revoked(crl, revoked);
426 ASN1_GENERALIZEDTIME_free(now);
427 ASN1_INTEGER_free(serial);
429 else
431 debug(WvLog::Warning, "Tried to add a certificate to the CRL, but "
432 "certificate is either bad or broken.\n");