2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2005 Net Integration Technologies, Inc.
5 * X.509v3 CRL management classes.
8 #include <openssl/x509v3.h>
9 #include <openssl/pem.h>
12 #include "wvx509mgr.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) \
18 debug(WvLog::Warning, warning_str_get, x); \
22 static ASN1_INTEGER
* serial_to_int(WvStringParm serial
)
27 BN_dec2bn(&bn
, serial
);
28 ASN1_INTEGER
*retval
= ASN1_INTEGER_new();
29 retval
= BN_to_ASN1_INTEGER(bn
, retval
);
39 : debug("X509 CRL", WvLog::Debug5
)
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
;
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
));
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);
84 bool WvCRL::isok() const
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
);
99 debug("There was an error determining whether or not we were signed by "
100 "CA '%s'\n", cacert
.get_subject());
103 bool issigned
= (result
> 0);
105 debug("CRL was%s signed by CA %s\n", issigned
? "" : " NOT",
106 cacert
.get_subject());
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
);
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());
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());
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");
141 debug("CRL appears not to be expired.\n");
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
;
162 aki
= static_cast<AUTHORITY_KEYID
*>(
163 X509_CRL_get_ext_d2i(crl
, NID_authority_key_identifier
,
167 char *tmp
= hex_to_string(aki
->keyid
->data
, aki
->keyid
->length
);
171 AUTHORITY_KEYID_free(aki
);
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
);
192 WvString
WvCRL::encode(const DumpMode mode
) const
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
208 debug(WvLog::Warning
, "Tried to encode CRL, but CRL is blank!\n");
212 BIO
*bufbio
= BIO_new(BIO_s_mem());
217 debug("Dumping CRL in PEM format.\n");
218 PEM_write_bio_X509_CRL(bufbio
, crl
);
221 debug("Dumping CRL in DER format.\n");
222 i2d_X509_CRL_bio(bufbio
, crl
);
225 debug("Tried to dump CRL in unknown format!\n");
229 BIO_get_mem_ptr(bufbio
, &bm
);
230 buf
.put(bm
->data
, bm
->length
);
235 void WvCRL::decode(const DumpMode mode
, WvStringParm str
)
239 debug("Replacing already existant CRL.\n");
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());
256 if (!(crl
= d2i_X509_CRL_bio(bio
, NULL
)))
257 debug(WvLog::Warning
, "Read CRL from '%s': %s\n",
258 str
, wvssl_errstr());
263 else if (mode
== CRLFilePEM
)
265 FILE * fp
= fopen(str
, "rb");
269 debug(WvLog::Warning
,
271 str
, strerror(errnum
));
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());
283 // we use the buffer decode functions for everything else
290 void WvCRL::decode(const DumpMode mode
, WvBuf
&buf
)
294 debug("Replacing already existant CRL.\n");
299 if (mode
== CRLFileDER
|| mode
== CRLFilePEM
)
301 decode(mode
, buf
.getstr());
305 BIO
*bufbio
= BIO_new(BIO_s_mem());
306 BIO_write(bufbio
, buf
.get(buf
.used()), buf
.used());
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
);
319 debug(WvLog::Warning
, "Attempted to decode unknown format.\n");
322 debug(WvLog::Warning
, "Couldn't decode CRL.\n");
328 bool WvCRL::isrevoked(const WvX509
&cert
) const
332 debug("Checking to see if certificate with name '%s' and serial "
333 "number '%s' is revoked.\n", cert
.get_subject(),
335 return isrevoked(cert
.get_serial());
339 debug(WvLog::Error
, "Given certificate to check revocation status, "
340 "but certificate is blank. Declining.\n");
346 bool WvCRL::isrevoked(WvStringParm serial_number
) const
348 CHECK_CRL_EXISTS_GET("if certificate is revoked in CRL", false);
352 ASN1_INTEGER
*serial
= serial_to_int(serial_number
);
355 X509_REVOKED mayberevoked
;
356 mayberevoked
.serialNumber
= serial
;
357 if (crl
->crl
->revoked
)
359 int idx
= sk_X509_REVOKED_find(crl
->crl
->revoked
,
361 ASN1_INTEGER_free(serial
);
364 debug("Certificate is revoked.\n");
369 debug("Certificate is not revoked.\n");
375 ASN1_INTEGER_free(serial
);
376 debug("CRL does not have revoked list.\n");
382 debug(WvLog::Warning
, "Can't convert serial number to ASN1 format. "
383 "Saying it's not revoked.\n");
386 debug(WvLog::Warning
, "Serial number for certificate is blank.\n");
388 debug("Certificate is not revoked (or could not determine whether it "
394 WvCRL::Valid
WvCRL::validate(const WvX509
&cacert
) const
396 if (!issuedbyca(cacert
))
399 if (!signedbyca(cacert
))
400 return NO_VALID_SIGNATURE
;
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
;
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
);
431 void WvCRL::addcert(const WvX509
&cert
)
435 debug(WvLog::Warning
, "Tried to add certificate to CRL, but CRL is "
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
);
455 debug(WvLog::Warning
, "Tried to add a certificate to the CRL, but "
456 "certificate is either bad or broken.\n");