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 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
89bool WvCRL::isok() const
90{
91 return crl;
92}
93
94
95bool 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
117bool 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
136bool 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
151bool 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
198{
199 WvDynBuf retval;
200 encode(mode, retval);
201
202 return retval.getstr();
203}
204
205
206void 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
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
295void 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
333bool 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
351bool 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
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
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
424void 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
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
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92
Specialization of WvBufBase for unsigned char type buffers intended for use with raw memory buffers.
Definition: wvbuf.h:24
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:240
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:387
bool expired() const
Checks to see if the CRL is expired (i.e.
Definition: wvcrl.cc:136
bool signedbyca(const WvX509 &cacert) const
Check the CRL in crl against the CA certificate in cert.
Definition: wvcrl.cc:95
bool isok() const
Do we have any errors... convenience function.
Definition: wvcrl.cc:89
void addcert(const WvX509 &cert)
Add the certificate specified by cert to the CRL.
Definition: wvcrl.cc:424
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
WvString encode(const DumpMode mode) const
Return the information requested by mode as a WvString.
Definition: wvcrl.cc:197
Valid
Type for validate() method: ERROR = there was an error that happened.
Definition: wvcrl.h:91
int numcerts() const
Counts the number of certificates in this CRL.
Definition: wvcrl.cc:409
bool isrevoked(const WvX509 &cert) const
Is the certificate in cert revoked?
Definition: wvcrl.cc:333
WvString get_issuer() const
Get the CRL Issuer.
Definition: wvcrl.cc:185
DumpMode
Type for the encode() and decode() methods: CRLPEM = PEM Encoded X.509 CRL CRLDER = DER Encoded X....
Definition: wvcrl.h:38
WvString get_aki() const
Get the Authority key Info.
Definition: wvcrl.cc:160
virtual ~WvCRL()
Destructor.
Definition: wvcrl.cc:81
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition: wvstring.h:94
const char * cstr() const
return a (const char *) for this string.
Definition: wvstring.h:267
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition: wvlog.h:57
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:330
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.
Definition: wvx509.h:42
WvString get_serial(bool hex=false) const
get and set the serialNumber field of the certificate
Definition: wvx509.cc:704
WvString get_subject() const
get and set the Subject field of the certificate
Definition: wvx509.cc:624
virtual bool isok() const
Is the certificate object valid?
Definition: wvx509.cc:1285