wvcrl.cc

00001 /* -*- Mode: C++ -*-
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2005 Net Integration Technologies, Inc.
00004  *
00005  * X.509v3 CRL management classes.
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; // file modes are no ops with encode
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     // we use the buffer decode functions for everything else
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     // neither we or openssl handles any critical extensions yet
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         // FIXME: We don't deal with the reason here...
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 

Generated on Thu Jan 24 16:50:56 2008 for WvStreams by  doxygen 1.5.4