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