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