00001
00002
00003
00004
00005
00006
00007
00008 #include <openssl/x509v3.h>
00009 #include <openssl/pem.h>
00010
00011 #include "wvcrl.h"
00012 #include "wvx509mgr.h"
00013 #include "wvbase64.h"
00014
00015 static const char * warning_str_get = "Tried to determine %s, but CRL is blank!\n";
00016 #define CHECK_CRL_EXISTS_GET(x, y) \
00017 if (!crl) { \
00018 debug(WvLog::Warning, warning_str_get, x); \
00019 return y; \
00020 }
00021
00022 static ASN1_INTEGER * serial_to_int(WvStringParm serial)
00023 {
00024 if (!!serial)
00025 {
00026 BIGNUM *bn = NULL;
00027 BN_dec2bn(&bn, serial);
00028 ASN1_INTEGER *retval = ASN1_INTEGER_new();
00029 retval = BN_to_ASN1_INTEGER(bn, retval);
00030 BN_free(bn);
00031 return retval;
00032 }
00033
00034 return NULL;
00035 }
00036
00037
00038 WvCRL::WvCRL()
00039 : debug("X509 CRL", WvLog::Debug5)
00040 {
00041 crl = NULL;
00042 }
00043
00044
00045 WvCRL::WvCRL(const WvX509Mgr &cacert)
00046 : debug("X509 CRL", WvLog::Debug5)
00047 {
00048 assert(crl = X509_CRL_new());
00049 cacert.signcrl(*this);
00050 }
00051
00052
00053 WvCRL::~WvCRL()
00054 {
00055 debug("Deleting.\n");
00056 if (crl)
00057 X509_CRL_free(crl);
00058 }
00059
00060
00061 bool WvCRL::isok() const
00062 {
00063 return crl;
00064 }
00065
00066
00067 bool WvCRL::signedbyca(const WvX509 &cacert) const
00068 {
00069 CHECK_CRL_EXISTS_GET("if CRL is signed by CA", false);
00070
00071 EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
00072 int result = X509_CRL_verify(crl, pkey);
00073 EVP_PKEY_free(pkey);
00074 if (result < 0)
00075 {
00076 debug("There was an error determining whether or not we were signed by "
00077 "CA '%s'\n", cacert.get_subject());
00078 return false;
00079 }
00080 bool issigned = (result > 0);
00081
00082 debug("CRL was%s signed by CA %s\n", issigned ? "" : " NOT",
00083 cacert.get_subject());
00084
00085 return issigned;
00086 }
00087
00088
00089 bool WvCRL::issuedbyca(const WvX509 &cacert) const
00090 {
00091 CHECK_CRL_EXISTS_GET("if CRL is issued by CA", false);
00092
00093 WvString name = get_issuer();
00094 bool issued = (cacert.get_subject() == name);
00095 if (issued)
00096 debug("CRL issuer '%s' matches subject '%s' of cert. We can say "
00097 "that it appears to be issued by this CA.\n",
00098 name, cacert.get_subject());
00099 else
00100 debug("CRL issuer '%s' doesn't match subject '%s' of cert. Doesn't "
00101 "appear to be issued by this CA.\n", name,
00102 cacert.get_subject());
00103
00104 return issued;
00105 }
00106
00107
00108 bool WvCRL::expired() const
00109 {
00110 CHECK_CRL_EXISTS_GET("if CRL has expired", false);
00111
00112 if (X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)) < 0)
00113 {
00114 debug("CRL appears to be expired.\n");
00115 return true;
00116 }
00117
00118 debug("CRL appears not to be expired.\n");
00119 return false;
00120 }
00121
00122
00123 bool WvCRL::has_critical_extensions() const
00124 {
00125 CHECK_CRL_EXISTS_GET("if CRL has critical extensions", false);
00126
00127 int critical = X509_CRL_get_ext_by_critical(crl, 1, 0);
00128 return (critical > 0);
00129 }
00130
00131
00132 WvString WvCRL::get_aki() const
00133 {
00134 CHECK_CRL_EXISTS_GET("CRL's AKI", WvString::null);
00135
00136 AUTHORITY_KEYID *aki = NULL;
00137 int i;
00138
00139 aki = static_cast<AUTHORITY_KEYID *>(
00140 X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier,
00141 &i, NULL));
00142 if (aki)
00143 {
00144 char *tmp = hex_to_string(aki->keyid->data, aki->keyid->length);
00145 WvString str(tmp);
00146
00147 OPENSSL_free(tmp);
00148 AUTHORITY_KEYID_free(aki);
00149
00150 return str;
00151 }
00152
00153 return WvString::null;
00154 }
00155
00156
00157 WvString WvCRL::get_issuer() const
00158 {
00159 CHECK_CRL_EXISTS_GET("CRL's issuer", WvString::null);
00160
00161 char *name = X509_NAME_oneline(X509_CRL_get_issuer(crl), 0, 0);
00162 WvString retval(name);
00163 OPENSSL_free(name);
00164
00165 return retval;
00166 }
00167
00168
00169 WvString WvCRL::encode(const DumpMode mode) const
00170 {
00171 WvDynBuf retval;
00172 encode(mode, retval);
00173
00174 return retval.getstr();
00175 }
00176
00177
00178 void WvCRL::encode(const DumpMode mode, WvBuf &buf) const
00179 {
00180 if (mode == CRLFileDER || mode == CRLFilePEM)
00181 return;
00182
00183 if (!crl)
00184 {
00185 debug(WvLog::Warning, "Tried to encode CRL, but CRL is blank!\n");
00186 return;
00187 }
00188
00189 BIO *bufbio = BIO_new(BIO_s_mem());
00190 BUF_MEM *bm;
00191 switch (mode)
00192 {
00193 case CRLPEM:
00194 debug("Dumping CRL in PEM format.\n");
00195 PEM_write_bio_X509_CRL(bufbio, crl);
00196 break;
00197 case CRLDER:
00198 debug("Dumping CRL in DER format.\n");
00199 i2d_X509_CRL_bio(bufbio, crl);
00200 break;
00201 default:
00202 debug("Tried to dump CRL in unknown format!\n");
00203 break;
00204 }
00205
00206 BIO_get_mem_ptr(bufbio, &bm);
00207 buf.put(bm->data, bm->length);
00208 BIO_free(bufbio);
00209 }
00210
00211
00212 void WvCRL::decode(const DumpMode mode, WvStringParm str)
00213 {
00214 if (crl)
00215 {
00216 debug("Replacing already existant CRL.\n");
00217 X509_CRL_free(crl);
00218 crl = NULL;
00219 }
00220
00221 if (mode == CRLFileDER)
00222 {
00223 BIO *bio = BIO_new(BIO_s_file());
00224
00225 if (BIO_read_filename(bio, str.cstr()) <= 0)
00226 {
00227 debug(WvLog::Warning, "Import CRL from '%s': %s\n",
00228 str, wvssl_errstr());
00229 BIO_free(bio);
00230 return;
00231 }
00232
00233 if (!(crl = d2i_X509_CRL_bio(bio, NULL)))
00234 debug(WvLog::Warning, "Read CRL from '%s': %s\n",
00235 str, wvssl_errstr());
00236
00237 BIO_free(bio);
00238 return;
00239 }
00240 else if (mode == CRLFilePEM)
00241 {
00242 FILE * fp = fopen(str, "rb");
00243 if (!fp)
00244 {
00245 int errnum = errno;
00246 debug(WvLog::Warning,
00247 "Import CRL from '%s': %s\n",
00248 str, strerror(errnum));
00249 return;
00250 }
00251
00252 if (!(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)))
00253 debug(WvLog::Warning, "Can't read CRL from file");
00254
00255 fclose(fp);
00256 return;
00257 }
00258
00259
00260 WvDynBuf buf;
00261 buf.putstr(str);
00262 decode(mode, buf);
00263 }
00264
00265
00266 void WvCRL::decode(const DumpMode mode, WvBuf &buf)
00267 {
00268 if (crl)
00269 {
00270 debug("Replacing already existant CRL.\n");
00271 X509_CRL_free(crl);
00272 crl = NULL;
00273 }
00274
00275 if (mode == CRLFileDER || mode == CRLFilePEM)
00276 {
00277 decode(mode, buf.getstr());
00278 return;
00279 }
00280
00281 BIO *bufbio = BIO_new(BIO_s_mem());
00282 BIO_write(bufbio, buf.get(buf.used()), buf.used());
00283
00284 if (mode == CRLPEM)
00285 {
00286 debug("Decoding CRL from PEM format.\n");
00287 crl = PEM_read_bio_X509_CRL(bufbio, NULL, NULL, NULL);
00288 }
00289 else if (mode == CRLDER)
00290 {
00291 debug("Decoding CRL from DER format.\n");
00292 crl = d2i_X509_CRL_bio(bufbio, NULL);
00293 }
00294 else
00295 debug(WvLog::Warning, "Attempted to decode unknown format.\n");
00296
00297 if (!crl)
00298 debug(WvLog::Warning, "Couldn't decode CRL.\n");
00299
00300 BIO_free(bufbio);
00301 }
00302
00303
00304 bool WvCRL::isrevoked(const WvX509 &cert) const
00305 {
00306 if (cert.cert)
00307 {
00308 debug("Checking to see if certificate with name '%s' and serial "
00309 "number '%s' is revoked.\n", cert.get_subject(),
00310 cert.get_serial());
00311 return isrevoked(cert.get_serial());
00312 }
00313 else
00314 {
00315 debug(WvLog::Error, "Given certificate to check revocation status, "
00316 "but certificate is blank. Declining.\n");
00317 return true;
00318 }
00319 }
00320
00321
00322 bool WvCRL::isrevoked(WvStringParm serial_number) const
00323 {
00324 CHECK_CRL_EXISTS_GET("if certificate is revoked in CRL", false);
00325
00326 if (!!serial_number)
00327 {
00328 ASN1_INTEGER *serial = serial_to_int(serial_number);
00329 if (serial)
00330 {
00331 X509_REVOKED mayberevoked;
00332 mayberevoked.serialNumber = serial;
00333 if (crl->crl->revoked)
00334 {
00335 int idx = sk_X509_REVOKED_find(crl->crl->revoked,
00336 &mayberevoked);
00337 ASN1_INTEGER_free(serial);
00338 if (idx >= 0)
00339 {
00340 debug("Certificate is revoked.\n");
00341 return true;
00342 }
00343 else
00344 {
00345 debug("Certificate is not revoked.\n");
00346 return false;
00347 }
00348 }
00349 else
00350 {
00351 ASN1_INTEGER_free(serial);
00352 debug("CRL does not have revoked list.\n");
00353 return false;
00354 }
00355
00356 }
00357 else
00358 debug(WvLog::Warning, "Can't convert serial number to ASN1 format. "
00359 "Saying it's not revoked.\n");
00360 }
00361 else
00362 debug(WvLog::Warning, "Serial number for certificate is blank.\n");
00363
00364 debug("Certificate is not revoked (or could not determine whether it "
00365 "was).\n");
00366 return false;
00367 }
00368
00369
00370 WvCRL::Valid WvCRL::validate(const WvX509 &cacert) const
00371 {
00372 if (!issuedbyca(cacert))
00373 return NOT_THIS_CA;
00374
00375 if (!signedbyca(cacert))
00376 return NO_VALID_SIGNATURE;
00377
00378 if (expired())
00379 return EXPIRED;
00380
00381
00382 if (has_critical_extensions())
00383 {
00384 debug("CRL has unhandled critical extensions.\n");
00385 return UNHANDLED_CRITICAL_EXTENSIONS;
00386 }
00387
00388 return VALID;
00389 }
00390
00391
00392 int WvCRL::numcerts() const
00393 {
00394 CHECK_CRL_EXISTS_GET("number of certificates in CRL", 0);
00395
00396 STACK_OF(X509_REVOKED) *rev;
00397 rev = X509_CRL_get_REVOKED(crl);
00398 int certcount = sk_X509_REVOKED_num(rev);
00399
00400 if (certcount < 0)
00401 certcount = 0;
00402
00403 return certcount;
00404 }
00405
00406
00407 void WvCRL::addcert(const WvX509 &cert)
00408 {
00409 if (!crl)
00410 {
00411 debug(WvLog::Warning, "Tried to add certificate to CRL, but CRL is "
00412 "blank!\n");
00413 return;
00414 }
00415
00416 if (cert.isok())
00417 {
00418 ASN1_INTEGER *serial = serial_to_int(cert.get_serial());
00419 X509_REVOKED *revoked = X509_REVOKED_new();
00420 ASN1_GENERALIZEDTIME *now = ASN1_GENERALIZEDTIME_new();
00421 X509_REVOKED_set_serialNumber(revoked, serial);
00422 X509_gmtime_adj(now, 0);
00423 X509_REVOKED_set_revocationDate(revoked, now);
00424
00425 X509_CRL_add0_revoked(crl, revoked);
00426 ASN1_GENERALIZEDTIME_free(now);
00427 ASN1_INTEGER_free(serial);
00428 }
00429 else
00430 {
00431 debug(WvLog::Warning, "Tried to add a certificate to the CRL, but "
00432 "certificate is either bad or broken.\n");
00433 }
00434 }
00435