wvdbusserver: implement NameHasOwner request.
[wvstreams.git] / crypto / wvcrl.cc
blob3e916ad3ee50783dcf6cef7e4f544a2d0572582f
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());
50 // now, set the aki (FIXME: Mostly blatantly copied from wvx509.cc)
52 // can't set a meaningful AKI for subordinate certification without the
53 // parent having an SKI
54 ASN1_OCTET_STRING *ikeyid = NULL;
55 X509_EXTENSION *ext;
56 int i = X509_get_ext_by_NID(cacert.cert, NID_subject_key_identifier, -1);
57 if ((i >= 0) && (ext = X509_get_ext(cacert.cert, i)))
58 ikeyid = static_cast<ASN1_OCTET_STRING *>(X509V3_EXT_d2i(ext));
60 if (!ikeyid)
61 return;
63 AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
64 akeyid->issuer = NULL;
65 akeyid->serial = NULL;
66 akeyid->keyid = ikeyid;
67 ext = X509V3_EXT_i2d(NID_authority_key_identifier, 0, akeyid);
68 X509_CRL_add_ext(crl, ext, -1);
69 X509_EXTENSION_free(ext);
70 AUTHORITY_KEYID_free(akeyid);
72 cacert.signcrl(*this);
76 WvCRL::~WvCRL()
78 debug("Deleting.\n");
79 if (crl)
80 X509_CRL_free(crl);
84 bool WvCRL::isok() const
86 return crl;
90 bool WvCRL::signedbyca(const WvX509 &cacert) const
92 CHECK_CRL_EXISTS_GET("if CRL is signed by CA", false);
94 EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
95 int result = X509_CRL_verify(crl, pkey);
96 EVP_PKEY_free(pkey);
97 if (result < 0)
99 debug("There was an error determining whether or not we were signed by "
100 "CA '%s'\n", cacert.get_subject());
101 return false;
103 bool issigned = (result > 0);
105 debug("CRL was%s signed by CA %s\n", issigned ? "" : " NOT",
106 cacert.get_subject());
108 return issigned;
112 bool WvCRL::issuedbyca(const WvX509 &cacert) const
114 CHECK_CRL_EXISTS_GET("if CRL is issued by CA", false);
116 WvString name = get_issuer();
117 bool issued = (cacert.get_subject() == name);
118 if (issued)
119 debug("CRL issuer '%s' matches subject '%s' of cert. We can say "
120 "that it appears to be issued by this CA.\n",
121 name, cacert.get_subject());
122 else
123 debug("CRL issuer '%s' doesn't match subject '%s' of cert. Doesn't "
124 "appear to be issued by this CA.\n", name,
125 cacert.get_subject());
127 return issued;
131 bool WvCRL::expired() const
133 CHECK_CRL_EXISTS_GET("if CRL has expired", false);
135 if (X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)) < 0)
137 debug("CRL appears to be expired.\n");
138 return true;
141 debug("CRL appears not to be expired.\n");
142 return false;
146 bool WvCRL::has_critical_extensions() const
148 CHECK_CRL_EXISTS_GET("if CRL has critical extensions", false);
150 int critical = X509_CRL_get_ext_by_critical(crl, 1, 0);
151 return (critical > 0);
155 WvString WvCRL::get_aki() const
157 CHECK_CRL_EXISTS_GET("CRL's AKI", WvString::null);
159 AUTHORITY_KEYID *aki = NULL;
160 int i;
162 aki = static_cast<AUTHORITY_KEYID *>(
163 X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier,
164 &i, NULL));
165 if (aki)
167 char *tmp = hex_to_string(aki->keyid->data, aki->keyid->length);
168 WvString str(tmp);
170 OPENSSL_free(tmp);
171 AUTHORITY_KEYID_free(aki);
173 return str;
176 return WvString::null;
180 WvString WvCRL::get_issuer() const
182 CHECK_CRL_EXISTS_GET("CRL's issuer", WvString::null);
184 char *name = X509_NAME_oneline(X509_CRL_get_issuer(crl), 0, 0);
185 WvString retval(name);
186 OPENSSL_free(name);
188 return retval;
192 WvString WvCRL::encode(const DumpMode mode) const
194 WvDynBuf retval;
195 encode(mode, retval);
197 return retval.getstr();
201 void WvCRL::encode(const DumpMode mode, WvBuf &buf) const
203 if (mode == CRLFileDER || mode == CRLFilePEM)
204 return; // file modes are no ops with encode
206 if (!crl)
208 debug(WvLog::Warning, "Tried to encode CRL, but CRL is blank!\n");
209 return;
212 BIO *bufbio = BIO_new(BIO_s_mem());
213 BUF_MEM *bm;
214 switch (mode)
216 case CRLPEM:
217 debug("Dumping CRL in PEM format.\n");
218 PEM_write_bio_X509_CRL(bufbio, crl);
219 break;
220 case CRLDER:
221 debug("Dumping CRL in DER format.\n");
222 i2d_X509_CRL_bio(bufbio, crl);
223 break;
224 default:
225 debug("Tried to dump CRL in unknown format!\n");
226 break;
229 BIO_get_mem_ptr(bufbio, &bm);
230 buf.put(bm->data, bm->length);
231 BIO_free(bufbio);
235 void WvCRL::decode(const DumpMode mode, WvStringParm str)
237 if (crl)
239 debug("Replacing already existant CRL.\n");
240 X509_CRL_free(crl);
241 crl = NULL;
244 if (mode == CRLFileDER)
246 BIO *bio = BIO_new(BIO_s_file());
248 if (BIO_read_filename(bio, str.cstr()) <= 0)
250 debug(WvLog::Warning, "Import CRL from '%s': %s\n",
251 str, wvssl_errstr());
252 BIO_free(bio);
253 return;
256 if (!(crl = d2i_X509_CRL_bio(bio, NULL)))
257 debug(WvLog::Warning, "Read CRL from '%s': %s\n",
258 str, wvssl_errstr());
260 BIO_free(bio);
261 return;
263 else if (mode == CRLFilePEM)
265 FILE * fp = fopen(str, "rb");
266 if (!fp)
268 int errnum = errno;
269 debug(WvLog::Warning,
270 "open '%s': %s\n",
271 str, strerror(errnum));
272 return;
275 if (!(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)))
276 debug(WvLog::Warning, "Import CRL from '%s': %s\n",
277 str, wvssl_errstr());
279 fclose(fp);
280 return;
283 // we use the buffer decode functions for everything else
284 WvDynBuf buf;
285 buf.putstr(str);
286 decode(mode, buf);
290 void WvCRL::decode(const DumpMode mode, WvBuf &buf)
292 if (crl)
294 debug("Replacing already existant CRL.\n");
295 X509_CRL_free(crl);
296 crl = NULL;
299 if (mode == CRLFileDER || mode == CRLFilePEM)
301 decode(mode, buf.getstr());
302 return;
305 BIO *bufbio = BIO_new(BIO_s_mem());
306 BIO_write(bufbio, buf.get(buf.used()), buf.used());
308 if (mode == CRLPEM)
310 debug("Decoding CRL from PEM format.\n");
311 crl = PEM_read_bio_X509_CRL(bufbio, NULL, NULL, NULL);
313 else if (mode == CRLDER)
315 debug("Decoding CRL from DER format.\n");
316 crl = d2i_X509_CRL_bio(bufbio, NULL);
318 else
319 debug(WvLog::Warning, "Attempted to decode unknown format.\n");
321 if (!crl)
322 debug(WvLog::Warning, "Couldn't decode CRL.\n");
324 BIO_free(bufbio);
328 bool WvCRL::isrevoked(const WvX509 &cert) const
330 if (cert.cert)
332 debug("Checking to see if certificate with name '%s' and serial "
333 "number '%s' is revoked.\n", cert.get_subject(),
334 cert.get_serial());
335 return isrevoked(cert.get_serial());
337 else
339 debug(WvLog::Error, "Given certificate to check revocation status, "
340 "but certificate is blank. Declining.\n");
341 return true;
346 bool WvCRL::isrevoked(WvStringParm serial_number) const
348 CHECK_CRL_EXISTS_GET("if certificate is revoked in CRL", false);
350 if (!!serial_number)
352 ASN1_INTEGER *serial = serial_to_int(serial_number);
353 if (serial)
355 X509_REVOKED mayberevoked;
356 mayberevoked.serialNumber = serial;
357 if (crl->crl->revoked)
359 int idx = sk_X509_REVOKED_find(crl->crl->revoked,
360 &mayberevoked);
361 ASN1_INTEGER_free(serial);
362 if (idx >= 0)
364 debug("Certificate is revoked.\n");
365 return true;
367 else
369 debug("Certificate is not revoked.\n");
370 return false;
373 else
375 ASN1_INTEGER_free(serial);
376 debug("CRL does not have revoked list.\n");
377 return false;
381 else
382 debug(WvLog::Warning, "Can't convert serial number to ASN1 format. "
383 "Saying it's not revoked.\n");
385 else
386 debug(WvLog::Warning, "Serial number for certificate is blank.\n");
388 debug("Certificate is not revoked (or could not determine whether it "
389 "was).\n");
390 return false;
394 WvCRL::Valid WvCRL::validate(const WvX509 &cacert) const
396 if (!issuedbyca(cacert))
397 return NOT_THIS_CA;
399 if (!signedbyca(cacert))
400 return NO_VALID_SIGNATURE;
402 if (expired())
403 return EXPIRED;
405 // neither we or openssl handles any critical extensions yet
406 if (has_critical_extensions())
408 debug("CRL has unhandled critical extensions.\n");
409 return UNHANDLED_CRITICAL_EXTENSIONS;
412 return VALID;
416 int WvCRL::numcerts() const
418 CHECK_CRL_EXISTS_GET("number of certificates in CRL", 0);
420 STACK_OF(X509_REVOKED) *rev;
421 rev = X509_CRL_get_REVOKED(crl);
422 int certcount = sk_X509_REVOKED_num(rev);
424 if (certcount < 0)
425 certcount = 0;
427 return certcount;
431 void WvCRL::addcert(const WvX509 &cert)
433 if (!crl)
435 debug(WvLog::Warning, "Tried to add certificate to CRL, but CRL is "
436 "blank!\n");
437 return;
440 if (cert.isok())
442 ASN1_INTEGER *serial = serial_to_int(cert.get_serial());
443 X509_REVOKED *revoked = X509_REVOKED_new();
444 ASN1_GENERALIZEDTIME *now = ASN1_GENERALIZEDTIME_new();
445 X509_REVOKED_set_serialNumber(revoked, serial);
446 X509_gmtime_adj(now, 0);
447 X509_REVOKED_set_revocationDate(revoked, now);
448 // FIXME: We don't deal with the reason here...
449 X509_CRL_add0_revoked(crl, revoked);
450 ASN1_GENERALIZEDTIME_free(now);
451 ASN1_INTEGER_free(serial);
453 else
455 debug(WvLog::Warning, "Tried to add a certificate to the CRL, but "
456 "certificate is either bad or broken.\n");