wvx509.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2005 Net Integration Technologies, Inc.
00004  * 
00005  * X.509 certificate management classes.
00006  */ 
00007 #include "wvx509.h"
00008 #include "wvcrl.h"
00009 #include "wvsslhacks.h"
00010 #include "wvcrypto.h"
00011 #include "wvstringlist.h"
00012 #include "wvbase64.h"
00013 #include "wvstrutils.h"
00014 
00015 #include <openssl/pem.h>
00016 #include <openssl/x509v3.h>
00017 #include <openssl/err.h>
00018 #include <openssl/sha.h>
00019 #include <openssl/ssl.h>
00020 
00021 // enable this to add some extra debugging trace messages (this can be VERY
00022 // verbose)
00023 #if 0
00024 # define TRACE(x, y...) debug(x, ## y); 
00025 #else
00026 #ifndef _MSC_VER
00027 # define TRACE(x, y...)
00028 #else
00029 # define TRACE
00030 #endif
00031 #endif
00032 
00033 // helper method to let us return and warn gracefully when getting/setting an 
00034 // element in a null certificate
00035 static const char * warning_str_set = "Tried to set %s, but certificate not ok.\n";
00036 static const char * warning_str_get = "Tried to set %s, but certificate not ok.\n";
00037 #define CHECK_CERT_EXISTS_SET(x)                                        \
00038     if (!cert) {                                                        \
00039         debug(WvLog::Warning, warning_str_set, x);                      \
00040         return;                                                         \
00041     }
00042 #define CHECK_CERT_EXISTS_GET(x, y)                                     \
00043     if (!cert) {                                                        \
00044         debug(WvLog::Warning, warning_str_get, x);                      \
00045         return y;                                                       \
00046     }
00047         
00048 
00049 UUID_MAP_BEGIN(WvX509)
00050   UUID_MAP_ENTRY(IObject)
00051   UUID_MAP_END
00052 
00053 static int ssl_init_count = 0;
00054 
00055 
00056 void wvssl_init()
00057 {
00058     if (!ssl_init_count)
00059     {
00060         SSL_library_init();
00061         SSL_load_error_strings();
00062         ERR_load_BIO_strings();
00063         ERR_load_crypto_strings();
00064         OpenSSL_add_all_algorithms();
00065         OpenSSL_add_all_ciphers();
00066         OpenSSL_add_all_digests();
00067     }
00068     
00069     ssl_init_count++;
00070 }
00071 
00072 
00073 void wvssl_free()
00074 {
00075     if (ssl_init_count >= 1)
00076         ssl_init_count--;
00077 
00078     if (!ssl_init_count)
00079     {
00080         ERR_free_strings();
00081         EVP_cleanup();
00082     }
00083 }
00084 
00085 
00086 WvString wvssl_errstr()
00087 {
00088     char buf[256];
00089     ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
00090     buf[sizeof(buf)-1] = 0;
00091     return buf;
00092 }
00093 
00094 
00095 WvX509::WvX509(X509 *_cert)
00096     : debug("X509", WvLog::Debug5)
00097 {
00098     wvssl_init();
00099     cert = _cert;
00100 }
00101 
00102 
00103 WvX509::WvX509()
00104     : debug("X509", WvLog::Debug5)
00105 {
00106     wvssl_init();
00107     cert = NULL;
00108 }
00109 
00110 
00111 WvX509::~WvX509()
00112 {
00113     TRACE("Deleting.\n");
00114     
00115     if (cert)
00116         X509_free(cert);
00117 
00118     wvssl_free();
00119 }
00120 
00121 
00122 
00123 // The people who designed this garbage should be shot!
00124 // Support old versions of openssl...
00125 #ifndef NID_domainComponent
00126 #define NID_domainComponent 391
00127 #endif
00128 
00129 #ifndef NID_Domain
00130 #define NID_Domain 392
00131 #endif
00132 
00133 
00134 // returns some approximation of the server's fqdn, or an empty string.
00135 static WvString set_name_entry(X509_NAME *name, WvStringParm dn)
00136 {
00137     WvString fqdn(""), force_fqdn("");
00138     X509_NAME_ENTRY *ne = NULL;
00139     int count = 0, nid;
00140     
00141     WvStringList l;
00142     l.split(dn, ",");
00143     
00144     // dn is of the form: c=ca,o=foo organization,dc=foo,dc=com
00145     // (ie. name=value pairs separated by commas)
00146     WvStringList::Iter i(l);
00147     for (i.rewind(); i.next(); )
00148     {
00149         WvString s(*i), sid;
00150         char *cptr, *value;
00151         
00152         cptr = s.edit();
00153         value = strchr(cptr, '=');
00154         if (value)
00155             *value++ = 0;
00156         else
00157             value = "NULL";
00158         
00159         sid = strlwr(trim_string(cptr));
00160         
00161         if (sid == "c")
00162             nid = NID_countryName;
00163         else if (sid == "st")
00164             nid = NID_stateOrProvinceName;
00165         else if (sid == "l")
00166             nid = NID_localityName;
00167         else if (sid == "o")
00168             nid = NID_organizationName;
00169         else if (sid == "ou")
00170             nid = NID_organizationalUnitName;
00171         else if (sid == "cn")
00172         {
00173             nid = NID_commonName;
00174             force_fqdn = value;
00175         }
00176         else if (sid == "dc")
00177         {
00178             nid = NID_domainComponent;
00179             if (!!fqdn)
00180                 fqdn.append(".");
00181             fqdn.append(value);
00182         }
00183         else if (sid == "domain")
00184         {
00185             nid = NID_Domain;
00186             force_fqdn = value;
00187         }
00188         else if (sid == "email")
00189             nid = NID_pkcs9_emailAddress;
00190         else
00191             nid = NID_domainComponent;
00192         
00193         // Sometimes we just want to parse dn into fqdn.
00194         if (name == NULL)
00195             continue;
00196         
00197         if (!ne)
00198             ne = X509_NAME_ENTRY_create_by_NID(NULL, nid,
00199                                V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
00200         else
00201             X509_NAME_ENTRY_create_by_NID(&ne, nid,
00202                                V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
00203         if (!ne)
00204             continue;
00205         
00206         X509_NAME_add_entry(name, ne, count++, 0);
00207     }
00208     
00209     X509_NAME_ENTRY_free(ne);
00210     
00211     if (!!force_fqdn)
00212         return force_fqdn;
00213 
00214     return fqdn;
00215 }
00216 
00217 
00218 WvRSAKey *WvX509::get_rsa_pub() const
00219 {
00220     EVP_PKEY *pkcert = X509_get_pubkey(cert);
00221     RSA *certrsa = EVP_PKEY_get1_RSA(pkcert);
00222     EVP_PKEY_free(pkcert);
00223     return new WvRSAKey(certrsa, false); 
00224 }
00225 
00226 
00227 WvString WvX509::certreq(WvStringParm subject, const WvRSAKey &rsa)
00228 {
00229     WvLog debug("X509::certreq", WvLog::Debug5);
00230 
00231     EVP_PKEY *pk = NULL;
00232     X509_NAME *name = NULL;
00233     X509_REQ *certreq = NULL;
00234 
00235     // double check RSA key
00236     if (rsa.isok())
00237         debug("RSA Key is fine.\n");
00238     else
00239     {
00240         debug(WvLog::Warning, "RSA Key is bad");
00241         return WvString::null;
00242     }
00243 
00244     if ((pk=EVP_PKEY_new()) == NULL)
00245     {
00246         debug(WvLog::Warning, "Error creating key handler for new certificate");
00247         return WvString::null;
00248     }
00249     
00250     if ((certreq=X509_REQ_new()) == NULL)
00251     {
00252         debug(WvLog::Warning, "Error creating new PKCS#10 object");
00253         EVP_PKEY_free(pk);
00254         return WvString::null;
00255     }
00256 
00257     if (!EVP_PKEY_set1_RSA(pk, rsa.rsa))
00258     {
00259         debug(WvLog::Warning, "Error adding RSA keys to certificate");
00260         X509_REQ_free(certreq);
00261         EVP_PKEY_free(pk);
00262         return WvString::null;
00263     }
00264     
00265     X509_REQ_set_version(certreq, 0); /* version 1 */
00266 
00267     X509_REQ_set_pubkey(certreq, pk);
00268 
00269     name = X509_REQ_get_subject_name(certreq);
00270 
00271     debug("Creating Certificate request for %s\n", subject);
00272     set_name_entry(name, subject);
00273     X509_REQ_set_subject_name(certreq, name);
00274     char *sub_name = X509_NAME_oneline(X509_REQ_get_subject_name(certreq), 
00275                                        0, 0);
00276     debug("SubjectDN: %s\n", sub_name);
00277     OPENSSL_free(sub_name);
00278     
00279     if (!X509_REQ_sign(certreq, pk, EVP_sha1()))
00280     {
00281         debug(WvLog::Warning, "Could not self sign the request");
00282         X509_REQ_free(certreq);
00283         EVP_PKEY_free(pk);
00284         return WvString::null;
00285     }
00286 
00287     int verify_result = X509_REQ_verify(certreq, pk);
00288     if (verify_result == 0)
00289     {
00290         debug(WvLog::Warning, "Self signed request failed");
00291         X509_REQ_free(certreq);
00292         EVP_PKEY_free(pk);
00293         return WvString::null;
00294     }
00295     else
00296     {
00297         debug("Self Signed Certificate Request verifies OK!\n");
00298     }
00299 
00300     // Horribly involuted hack to get around the fact that the
00301     // OpenSSL people are too braindead to have a PEM_write function
00302     // that returns a char *
00303     WvDynBuf retval;
00304     BIO *bufbio = BIO_new(BIO_s_mem());
00305     BUF_MEM *bm;
00306     
00307     PEM_write_bio_X509_REQ(bufbio, certreq);
00308     BIO_get_mem_ptr(bufbio, &bm);
00309     retval.put(bm->data, bm->length);
00310     
00311     X509_REQ_free(certreq);
00312     EVP_PKEY_free(pk);
00313     BIO_free(bufbio);
00314 
00315     return retval.getstr();
00316 }
00317 
00318 
00319 bool WvX509::validate(WvX509 *cacert) const
00320 {
00321     if (cert == NULL)
00322     {
00323         debug(WvLog::Warning, "Tried to validate certificate against CA, but "
00324               "certificate is blank!\n");
00325         return false;
00326     }
00327 
00328     bool retval = true;
00329 
00330     // Check and make sure that the certificate is still valid
00331     if (X509_cmp_current_time(X509_get_notAfter(cert)) < 0)
00332     {
00333         debug("Certificate has expired.\n");
00334         retval = false;
00335     }
00336     
00337     if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0)
00338     {
00339         debug("Certificate is not yet valid.\n");
00340         retval = false;
00341     }
00342 
00343     if (cacert)
00344     {
00345         retval &= signedbyca(*cacert);
00346         retval &= issuedbyca(*cacert);
00347     }
00348     
00349     return retval;
00350 }
00351 
00352 
00353 bool WvX509::signedbyca(WvX509 &cacert) const
00354 {
00355     if (!cert || !cacert.cert)
00356     {
00357         debug(WvLog::Warning, "Tried to determine if certificate was signed "
00358               "by CA, but either client or CA certificate (or both) are "
00359               "blank.\n");
00360         return false;
00361     } 
00362 
00363     EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
00364     int result = X509_verify(cert, pkey); 
00365     EVP_PKEY_free(pkey);
00366 
00367     if (result < 0)
00368     {
00369         debug("There was an error determining whether or not we were signed by "
00370               "CA '%s'.\n", cacert.get_subject());
00371         return false;
00372     }
00373     bool issigned = (result > 0);
00374 
00375     debug("Certificate was%s signed by CA %s.\n", issigned ? "" : " NOT", 
00376           cacert.get_subject());
00377 
00378     return issigned;
00379 }
00380 
00381 
00382 bool WvX509::issuedbyca(WvX509 &cacert) const
00383 {
00384     if (!cert || !cacert.cert)
00385     {
00386         debug(WvLog::Warning, "Tried to determine if certificate was issued "
00387               "by CA, but either client or CA certificate (or both) are "
00388               "blank.\n");
00389         return false;
00390     } 
00391 
00392     int ret = X509_check_issued(cacert.cert, cert);
00393     debug("issuedbyca: %s==X509_V_OK(%s)\n", ret, X509_V_OK);
00394     if (ret != X509_V_OK)
00395         return false;
00396 
00397     return true;
00398 }
00399 
00400 
00401 WvString WvX509::encode(const DumpMode mode) const
00402 {
00403     WvDynBuf retval;
00404     encode(mode, retval);
00405     return retval.getstr();
00406 }
00407 
00408 
00409 void WvX509::encode(const DumpMode mode, WvBuf &buf) const
00410 {
00411     if (mode == CertFileDER || mode == CertFilePEM)
00412         return; // file modes are no ops with encode
00413 
00414     if (!cert)
00415     {
00416         debug(WvLog::Warning, "Tried to encode certificate, but certificate "
00417               "is blank!\n");
00418         return;
00419     }
00420 
00421     debug("Encoding X509 certificate.\n");
00422 
00423     if (mode == CertHex)
00424     {
00425         size_t size;
00426         unsigned char *keybuf, *iend;
00427         WvString enccert;
00428         
00429         size = i2d_X509(cert, NULL);
00430         iend = keybuf = new unsigned char[size];
00431         i2d_X509(cert, &iend);
00432         
00433         enccert.setsize(size * 2 +1);
00434         ::hexify(enccert.edit(), keybuf, size);
00435         
00436         deletev keybuf;
00437         buf.putstr(enccert);
00438     }
00439     else
00440     {
00441         BIO *bufbio = BIO_new(BIO_s_mem());
00442         BUF_MEM *bm;
00443         
00444         if (mode == CertPEM)
00445             PEM_write_bio_X509(bufbio, cert);
00446         else if (mode == CertDER)
00447             i2d_X509_bio(bufbio, cert);
00448         else
00449             debug(WvLog::Warning, "Tried to encode certificate with unknown "
00450                   "mode!\n");
00451 
00452         BIO_get_mem_ptr(bufbio, &bm);
00453         buf.put(bm->data, bm->length);
00454         BIO_free(bufbio);
00455     }
00456 }
00457 
00458 
00459 void WvX509::decode(const DumpMode mode, WvStringParm str)
00460 {
00461     if (cert)
00462     {
00463         debug("Replacing an already existant X509 certificate.\n");
00464         X509_free(cert);
00465         cert = NULL;
00466     }
00467 
00468     if (mode == CertFileDER)
00469     {
00470         BIO *bio = BIO_new(BIO_s_file());
00471         
00472         if (BIO_read_filename(bio, str.cstr()) <= 0)
00473         {
00474             debug(WvLog::Warning, "Open '%s': %s\n", str, wvssl_errstr());
00475             BIO_free(bio);
00476             return;
00477         }
00478         
00479         if (!(cert = d2i_X509_bio(bio, NULL)))
00480             debug(WvLog::Warning, "Import DER from '%s': %s\n",
00481                   str, wvssl_errstr());
00482         
00483         BIO_free(bio);
00484         return;
00485     }
00486     else if (mode == CertFilePEM)
00487     {
00488         FILE *fp = fopen(str, "rb");
00489         if (!fp)
00490         {
00491             int errnum = errno;
00492             debug("Open '%s': %s\n", str, strerror(errnum));
00493             return;
00494         }
00495 
00496         if (!(cert = PEM_read_X509(fp, NULL, NULL, NULL)))
00497             debug(WvLog::Warning, "Import PEM from '%s': %s\n",
00498                   str, wvssl_errstr());
00499         
00500         fclose(fp);
00501         return;
00502     }
00503     else if (mode == CertHex)
00504     {
00505         int hexbytes = str.len();
00506         int bufsize = hexbytes/2;
00507         unsigned char *certbuf = new unsigned char[bufsize];
00508         unsigned char *cp = certbuf;
00509         X509 *tmpcert;
00510         
00511         ::unhexify(certbuf, str);
00512         tmpcert = cert = X509_new();
00513         cert = wv_d2i_X509(&tmpcert, &cp, bufsize);    
00514         delete[] certbuf;
00515         return;
00516     }
00517 
00518     // we use the buffer decode functions for everything else
00519     WvDynBuf buf;
00520     buf.putstr(str);
00521     decode(mode, buf);
00522 }
00523 
00524 
00525 void WvX509::decode(const DumpMode mode, WvBuf &encoded)
00526 {
00527     if (cert)
00528     {
00529         debug("Replacing an already existant X509 certificate.\n");
00530         X509_free(cert);
00531         cert = NULL;
00532     }
00533     
00534     if (mode == CertHex || mode == CertFileDER || mode == CertFilePEM)
00535         decode(mode, encoded.getstr());
00536     else
00537     {        
00538         BIO *membuf = BIO_new(BIO_s_mem());
00539         BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
00540 
00541         if (mode == CertPEM)
00542             cert = PEM_read_bio_X509(membuf, NULL, NULL, NULL);
00543         else if (mode == CertDER)
00544             cert = d2i_X509_bio(membuf, NULL);
00545         else
00546             debug(WvLog::Warning, "Tried to encode certificate with unknown "
00547                   "mode!\n");
00548 
00549         BIO_free_all(membuf);
00550     }
00551 }
00552 
00553 
00554 WvString WvX509::get_issuer() const
00555 { 
00556     CHECK_CERT_EXISTS_GET("issuer", WvString::null);
00557 
00558     char *name = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
00559     WvString retval(name);
00560     OPENSSL_free(name);
00561     return retval;
00562 }
00563 
00564 
00565 void WvX509::set_issuer(WvStringParm issuer)
00566 {
00567     CHECK_CERT_EXISTS_SET("issuer");
00568 
00569     X509_NAME *name = X509_get_issuer_name(cert);
00570     set_name_entry(name, issuer);
00571     X509_set_issuer_name(cert, name);
00572 }
00573 
00574 
00575 void WvX509::set_issuer(const WvX509 &cacert)
00576 {
00577     CHECK_CERT_EXISTS_SET("issuer");
00578 
00579     X509_NAME *casubj = X509_get_subject_name(cacert.cert);
00580     X509_set_issuer_name(cert, casubj);
00581 }
00582 
00583 
00584 WvString WvX509::get_subject() const
00585 {
00586     CHECK_CERT_EXISTS_GET("subject", WvString::null);
00587 
00588     char *name = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
00589     WvString retval(name);
00590     OPENSSL_free(name);
00591     return retval;
00592 }
00593 
00594 
00595 void WvX509::set_subject(WvStringParm subject)
00596 {    
00597     CHECK_CERT_EXISTS_SET("subject");
00598 
00599     X509_NAME *name = X509_get_subject_name(cert);
00600     set_name_entry(name, subject);
00601     X509_set_subject_name(cert, name);
00602 }
00603 
00604 
00605 void WvX509::set_subject(X509_NAME *name)
00606 {
00607     CHECK_CERT_EXISTS_SET("subject");
00608 
00609     X509_set_subject_name(cert, name);
00610 }
00611 
00612 
00613 void WvX509::set_pubkey(WvRSAKey &_rsa)
00614 {
00615     CHECK_CERT_EXISTS_SET("pubkey");
00616 
00617     EVP_PKEY *pk = EVP_PKEY_new();
00618     assert(pk);
00619 
00620     // Assign RSA Key from WvRSAKey into stupid package that OpenSSL needs
00621     if (!EVP_PKEY_set1_RSA(pk, _rsa.rsa))
00622     {
00623         debug("Error adding RSA keys to certificate.\n");
00624         return;
00625     }
00626     
00627     X509_set_pubkey(cert, pk);
00628 
00629     EVP_PKEY_free(pk);
00630 }
00631 
00632 
00633 
00634 void WvX509::set_nsserver(WvStringParm servername)
00635 {
00636     CHECK_CERT_EXISTS_SET("nsserver");
00637     
00638     WvString fqdn;
00639     
00640     // FQDN cannot have a = in it, therefore it
00641     // must be a distinguished name :)
00642     if (strchr(servername, '='))
00643         fqdn = set_name_entry(NULL, servername);
00644     else
00645         fqdn = servername;
00646     
00647     if (!fqdn)
00648         fqdn = "null.noname.null";
00649     
00650     debug("Setting Netscape SSL server name extension to '%s'.\n", fqdn);
00651 
00652     // Add in the netscape-specific server extension
00653     set_extension(NID_netscape_cert_type, "server");
00654     set_extension(NID_netscape_ssl_server_name, fqdn);
00655 }
00656 
00657 
00658 WvString WvX509::get_nsserver() const
00659 {
00660     return get_extension(NID_netscape_ssl_server_name);
00661 }
00662 
00663 
00664 WvString WvX509::get_serial() const
00665 {
00666     CHECK_CERT_EXISTS_GET("serial", WvString::null);
00667 
00668     BIGNUM *bn = BN_new();
00669     bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), bn);
00670     char * c = BN_bn2dec(bn);
00671     WvString ret("%s", c);
00672     OPENSSL_free(c);
00673     BN_free(bn);
00674     return ret;
00675 }
00676 
00677 
00678 void WvX509::set_version()
00679 {
00680     CHECK_CERT_EXISTS_SET("version");
00681 
00682     X509_set_version(cert, 0x2);
00683 }
00684 
00685 
00686 void WvX509::set_serial(long serial)
00687 {
00688     CHECK_CERT_EXISTS_SET("serial");
00689 
00690     ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
00691 }
00692 
00693 
00694 WvString WvX509::get_crl_dp() const
00695 {
00696     return get_extension(NID_crl_distribution_points);
00697 }
00698 
00699 
00700 void WvX509::set_lifetime(long seconds)
00701 {
00702     CHECK_CERT_EXISTS_SET("lifetime");
00703 
00704     // Set the NotBefore time to now.
00705     X509_gmtime_adj(X509_get_notBefore(cert), 0);
00706     
00707     // Now + 10 years... should be shorter, but since we don't currently
00708     // have a set of routines to refresh the certificates, make it
00709     // REALLY long.
00710     X509_gmtime_adj(X509_get_notAfter(cert), seconds);
00711 }
00712 
00713 
00714 void WvX509::set_key_usage(WvStringParm values)
00715 {
00716     set_extension(NID_key_usage, values);
00717 }
00718 
00719 
00720 WvString WvX509::get_key_usage() const
00721 {
00722     return get_extension(NID_key_usage);
00723 }
00724 
00725 
00726 void WvX509::set_ext_key_usage(WvStringParm values)
00727 {
00728     set_extension(NID_ext_key_usage, values);
00729 }
00730 
00731 
00732 WvString WvX509::get_ext_key_usage() const
00733 {
00734     return get_extension(NID_ext_key_usage);
00735 }
00736 
00737 
00738 WvString WvX509::get_altsubject() const
00739 {
00740     return get_extension(NID_subject_alt_name);
00741 }
00742 
00743 
00744 bool WvX509::get_basic_constraints(bool &ca, int &pathlen) const
00745 {
00746     CHECK_CERT_EXISTS_GET("basic constraints", false);
00747     
00748     BASIC_CONSTRAINTS *constraints = NULL;
00749     int i;
00750 
00751     constraints = static_cast<BASIC_CONSTRAINTS *>(X509_get_ext_d2i(
00752                                                        cert, NID_basic_constraints,
00753                                                        &i, NULL));
00754     if (constraints)
00755     {
00756         ca = constraints->ca;
00757         if (constraints->pathlen)
00758         {
00759             if ((constraints->pathlen->type == V_ASN1_NEG_INTEGER) || !ca)
00760             {
00761                 debug("Path length type not valid when getting basic "
00762                       "constraints.\n");
00763                 BASIC_CONSTRAINTS_free(constraints);
00764                 pathlen = 0;
00765                 return false;
00766             }
00767             
00768             pathlen = ASN1_INTEGER_get(constraints->pathlen);
00769         }
00770         else
00771             pathlen = (-1);
00772 
00773         BASIC_CONSTRAINTS_free(constraints);
00774         return true;
00775     }
00776     
00777     debug("Basic constraints extension not present.\n");
00778     return false;
00779 }
00780 
00781 
00782 void WvX509::set_basic_constraints(bool ca, int pathlen)
00783 {
00784     CHECK_CERT_EXISTS_SET("basic constraints");
00785 
00786     BASIC_CONSTRAINTS *constraints = BASIC_CONSTRAINTS_new();
00787     
00788     constraints->ca = static_cast<int>(ca);
00789     if (pathlen != (-1))
00790     {
00791         ASN1_INTEGER *i = ASN1_INTEGER_new();
00792         ASN1_INTEGER_set(i, pathlen);
00793         constraints->pathlen = i;
00794     }
00795 
00796     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_basic_constraints, 0, 
00797                                         constraints);
00798     while (int idx = X509_get_ext_by_NID(cert, NID_basic_constraints, 0) >= 0)
00799     {
00800         debug("Found extension at idx %s\n", idx);
00801         X509_EXTENSION *tmpex = X509_delete_ext(cert, idx);
00802         X509_EXTENSION_free(tmpex);
00803     }
00804 
00805     X509_add_ext(cert, ex, NID_basic_constraints);
00806     X509_EXTENSION_free(ex);
00807     BASIC_CONSTRAINTS_free(constraints);
00808 }
00809 
00810 
00811 bool WvX509::get_policy_constraints(int &require_explicit_policy, 
00812                                     int &inhibit_policy_mapping) const
00813 {
00814     CHECK_CERT_EXISTS_GET("policy constraints", false);
00815 
00816     POLICY_CONSTRAINTS *constraints = NULL;
00817     int i;
00818     
00819     constraints = static_cast<POLICY_CONSTRAINTS *>(X509_get_ext_d2i(
00820                                                 cert, NID_policy_constraints, 
00821                                                 &i, NULL));
00822     if (constraints)
00823     {
00824         if (constraints->requireExplicitPolicy)
00825             require_explicit_policy = ASN1_INTEGER_get(
00826                 constraints->requireExplicitPolicy);
00827         else
00828             require_explicit_policy = (-1);
00829 
00830         if (constraints->inhibitPolicyMapping)
00831             inhibit_policy_mapping = ASN1_INTEGER_get(
00832                 constraints->inhibitPolicyMapping);
00833         else
00834             inhibit_policy_mapping = (-1);
00835         POLICY_CONSTRAINTS_free(constraints);
00836         return true;
00837     }
00838 
00839     return false;
00840 }
00841 
00842 
00843 void WvX509::set_policy_constraints(int require_explicit_policy, 
00844                                        int inhibit_policy_mapping)
00845 {
00846     CHECK_CERT_EXISTS_SET("policy constraints");
00847 
00848     POLICY_CONSTRAINTS *constraints = POLICY_CONSTRAINTS_new();
00849     
00850     ASN1_INTEGER *i = ASN1_INTEGER_new();
00851     ASN1_INTEGER_set(i, require_explicit_policy);
00852     constraints->requireExplicitPolicy = i;
00853     i = ASN1_INTEGER_new();
00854     ASN1_INTEGER_set(i, inhibit_policy_mapping);
00855     constraints->inhibitPolicyMapping = i;
00856 
00857     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_constraints, 0, 
00858                                         constraints);
00859     X509_add_ext(cert, ex, -1);
00860     X509_EXTENSION_free(ex);
00861     POLICY_CONSTRAINTS_free(constraints);
00862 }
00863 
00864 
00865 bool WvX509::get_policy_mapping(PolicyMapList &list) const
00866 {
00867     CHECK_CERT_EXISTS_GET("policy mapping", false);
00868 
00869     POLICY_MAPPINGS *mappings = NULL;
00870     POLICY_MAPPING *map = NULL;
00871     int i;
00872 
00873     mappings = static_cast<POLICY_MAPPINGS *>(X509_get_ext_d2i(
00874                                                 cert, NID_policy_mappings, 
00875                                                 &i, NULL));
00876     if (!mappings)
00877         return false;
00878 
00879     const int POLICYID_MAXLEN = 80;
00880     char tmp1[80];
00881     char tmp2[80];
00882     for(int j = 0; j < sk_POLICY_MAPPING_num(mappings); j++) 
00883     {
00884         map = sk_POLICY_MAPPING_value(mappings, j);
00885         OBJ_obj2txt(tmp1, POLICYID_MAXLEN, map->issuerDomainPolicy, true);
00886         OBJ_obj2txt(tmp2, POLICYID_MAXLEN, map->subjectDomainPolicy, true);
00887         list.append(new PolicyMap(tmp1, tmp2), true);
00888     }
00889 
00890     sk_POLICY_MAPPING_pop_free(mappings, POLICY_MAPPING_free);
00891     
00892     return true;
00893 }
00894 
00895 
00896 void WvX509::set_policy_mapping(PolicyMapList &list)
00897 {
00898     CHECK_CERT_EXISTS_SET("policy mapping");
00899 
00900     POLICY_MAPPINGS *maps = sk_POLICY_MAPPING_new_null();
00901     
00902     PolicyMapList::Iter i(list);
00903     for (i.rewind(); i.next();)
00904     {
00905         POLICY_MAPPING *map = POLICY_MAPPING_new();
00906         map->issuerDomainPolicy = OBJ_txt2obj(i().issuer_domain.cstr(), 0);
00907         map->subjectDomainPolicy = OBJ_txt2obj(i().subject_domain.cstr(), 0);
00908         sk_POLICY_MAPPING_push(maps, map);
00909         printf("Push!\n");
00910     }
00911 
00912     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_mappings, 0, maps);
00913     X509_add_ext(cert, ex, -1);
00914     X509_EXTENSION_free(ex);
00915     sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free);
00916 }
00917 
00918 
00919 static void add_aia(WvStringParm type, WvString identifier, AUTHORITY_INFO_ACCESS *ainfo)
00920 {
00921     ACCESS_DESCRIPTION *acc = ACCESS_DESCRIPTION_new();
00922     sk_ACCESS_DESCRIPTION_push(ainfo, acc);
00923     acc->method = OBJ_txt2obj(type.cstr(), 0);
00924     acc->location->type = GEN_URI;
00925     acc->location->d.ia5 = M_ASN1_IA5STRING_new();
00926     unsigned char *cident = reinterpret_cast<unsigned char *>(identifier.edit());
00927     ASN1_STRING_set(acc->location->d.ia5, cident, identifier.len());
00928 }
00929 
00930 
00931 void WvX509::set_aia(WvStringList &ca_urls,
00932                      WvStringList &responders)
00933 {
00934     CHECK_CERT_EXISTS_SET("aia");
00935 
00936     AUTHORITY_INFO_ACCESS *ainfo = sk_ACCESS_DESCRIPTION_new_null();
00937 
00938     WvStringList::Iter i(ca_urls);
00939     for (i.rewind(); i.next();)
00940         add_aia("caIssuers", i(), ainfo);
00941 
00942     WvStringList::Iter j(responders);
00943     for (j.rewind(); j.next();)
00944         add_aia("OCSP", j(), ainfo);
00945 
00946     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_info_access, 0, ainfo);
00947     X509_add_ext(cert, ex, -1);
00948     X509_EXTENSION_free(ex);
00949     sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free);
00950 }
00951 
00952 
00953 WvString WvX509::get_aia() const
00954 {
00955     return get_extension(NID_info_access);
00956 }
00957 
00958 
00959 static void parse_stack(WvStringParm ext, WvStringList &list, WvStringParm prefix)
00960 {
00961     WvStringList stack;
00962     stack.split(ext, ";\n");
00963     WvStringList::Iter i(stack);
00964     for (i.rewind();i.next();)
00965     {
00966         WvString stack_entry(*i);
00967         if (strstr(stack_entry, prefix))
00968         {
00969             WvString uri(stack_entry.edit() + prefix.len());
00970             list.append(uri);  
00971         }
00972     }
00973 }
00974 
00975 
00976 void WvX509::get_ocsp(WvStringList &responders) const
00977 {
00978     parse_stack(get_aia(), responders, "OCSP - URI:");
00979 }
00980 
00981 
00982 void WvX509::get_ca_urls(WvStringList &urls) const
00983 {
00984     parse_stack(get_aia(), urls, "CA Issuers - URI:");
00985 }
00986 
00987 
00988 void WvX509::get_crl_urls(WvStringList &urls) const
00989 {
00990     parse_stack(get_crl_dp(), urls, "URI:");
00991 }
00992 
00993 
00994 void WvX509::set_crl_urls(WvStringList &urls)
00995 {
00996     CHECK_CERT_EXISTS_SET("CRL urls");
00997 
00998     STACK_OF(DIST_POINT) *crldp = sk_DIST_POINT_new_null();
00999     WvStringList::Iter i(urls);
01000     for (i.rewind(); i.next();)
01001     {
01002         DIST_POINT *point = DIST_POINT_new();
01003         sk_DIST_POINT_push(crldp, point);
01004 
01005         GENERAL_NAMES *uris = GENERAL_NAMES_new();
01006         GENERAL_NAME *uri = GENERAL_NAME_new();
01007         uri->type = GEN_URI;
01008         uri->d.ia5 = M_ASN1_IA5STRING_new();
01009         unsigned char *cident = reinterpret_cast<unsigned char *>(i().edit());    
01010         ASN1_STRING_set(uri->d.ia5, cident, i().len());
01011         sk_GENERAL_NAME_push(uris, uri);
01012 
01013         point->distpoint = DIST_POINT_NAME_new();
01014         point->distpoint->name.fullname = uris;
01015         point->distpoint->type = 0;
01016     }
01017 
01018     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_crl_distribution_points, 0, crldp);
01019     X509_add_ext(cert, ex, -1);
01020     X509_EXTENSION_free(ex);
01021     sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
01022 }
01023 
01024 
01025 bool WvX509::get_policies(WvStringList &policy_oids) const
01026 {
01027     CHECK_CERT_EXISTS_GET("policies", false);
01028 
01029     int critical;
01030     CERTIFICATEPOLICIES * policies = static_cast<CERTIFICATEPOLICIES *>(
01031         X509_get_ext_d2i(cert, NID_certificate_policies, &critical, NULL));
01032     if (policies)
01033     {
01034         for (int i = 0; i < sk_POLICYINFO_num(policies); i++)
01035         {
01036             POLICYINFO * policy = sk_POLICYINFO_value(policies, i);
01037             const int POLICYID_MAXLEN = 80;
01038 
01039             char policyid[POLICYID_MAXLEN];
01040             OBJ_obj2txt(policyid, POLICYID_MAXLEN, policy->policyid, 
01041                         true); // don't substitute human-readable names
01042             policy_oids.append(policyid);
01043         }
01044 
01045         sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
01046         return true;
01047     }
01048 
01049     return false;
01050 }
01051 
01052 
01053 void WvX509::set_policies(WvStringList &policy_oids)
01054 {
01055     CHECK_CERT_EXISTS_SET("policies");
01056 
01057     STACK_OF(POLICYINFO) *sk_pinfo = sk_POLICYINFO_new_null();
01058 
01059     WvStringList::Iter i(policy_oids);
01060     for (i.rewind(); i.next();)
01061     {
01062         ASN1_OBJECT *pobj = OBJ_txt2obj(i(), 0);
01063         POLICYINFO *pol = POLICYINFO_new();
01064         pol->policyid = pobj;
01065         sk_POLICYINFO_push(sk_pinfo, pol);
01066     }
01067 
01068 #if 0
01069     // this code would let you set URL information to a policy
01070     // qualifier
01071     POLICYQUALINFO *qual = NULL;
01072     WvString url(_url);
01073     if (!!url)
01074     {
01075         pol->qualifiers = sk_POLICYQUALINFO_new_null();
01076         qual = POLICYQUALINFO_new();
01077         qual->pqualid = OBJ_nid2obj(NID_id_qt_cps);
01078         qual->d.cpsouri = M_ASN1_IA5STRING_new();
01079         ASN1_STRING_set(qual->d.cpsuri, url.edit(), url.len());
01080         sk_POLICYQUALINFO_push(pol->qualifiers, qual);
01081     }
01082 #endif
01083 
01084     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_certificate_policies, 0, 
01085                                         sk_pinfo);
01086     X509_add_ext(cert, ex, -1);
01087     X509_EXTENSION_free(ex);
01088     sk_POLICYINFO_pop_free(sk_pinfo, POLICYINFO_free);
01089 }
01090 
01091 
01092 WvString WvX509::get_extension(int nid) const
01093 {
01094     CHECK_CERT_EXISTS_GET("extension", WvString::null);
01095 
01096     WvString retval = WvString::null;
01097     
01098     int index = X509_get_ext_by_NID(cert, nid, -1);
01099     if (index >= 0)
01100     {
01101         X509_EXTENSION *ext = X509_get_ext(cert, index);
01102         
01103         if (ext)
01104         {
01105             X509V3_EXT_METHOD *method = X509V3_EXT_get(ext);
01106             if (!method)
01107             {
01108                 WvDynBuf buf;
01109                 buf.put(ext->value->data, ext->value->length);
01110                 retval = buf.getstr();
01111             }
01112             else
01113             {
01114                 void *ext_data = NULL;
01115                 // we NEED to use a temporary pointer for ext_value_data,
01116                 // as openssl's ASN1_item_d2i will muck around with it, 
01117                 // even though it's const (at least as of version 0.9.8e). 
01118                 // gah.
01119 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
01120                 const unsigned char * ext_value_data = ext->value->data;
01121 #else
01122                 unsigned char *ext_value_data = ext->value->data;
01123 #endif
01124                 if (method->it)
01125                 {
01126                     ext_data = ASN1_item_d2i(NULL, &ext_value_data,
01127                                              ext->value->length, 
01128                                              ASN1_ITEM_ptr(method->it));
01129                     TRACE("Applied generic conversion!\n");
01130                 }
01131                 else
01132                 {
01133                     ext_data = method->d2i(NULL, &ext_value_data,
01134                                            ext->value->length);
01135                     TRACE("Applied method specific conversion!\n");
01136                 }
01137                 
01138                 if (method->i2s)
01139                 {
01140                     TRACE("String Extension!\n");
01141                     char *s = method->i2s(method, ext_data); 
01142                     retval = s;
01143                     OPENSSL_free(s);
01144                 }
01145                 else if (method->i2v)
01146                 {
01147                     TRACE("Stack Extension!\n");
01148                     CONF_VALUE *val = NULL;
01149                     STACK_OF(CONF_VALUE) *svals = NULL;
01150                     svals = method->i2v(method, ext_data, NULL);
01151                     if (!sk_CONF_VALUE_num(svals))
01152                         retval = "EMPTY";
01153                     else
01154                     {
01155                         WvStringList list;
01156                         for(int i = 0; i < sk_CONF_VALUE_num(svals); i++)
01157                         {
01158                             val = sk_CONF_VALUE_value(svals, i);
01159                             if (!val->name)
01160                                 list.append(WvString(val->value));
01161                             else if (!val->value)
01162                                 list.append(WvString(val->name));
01163                             else 
01164                             {
01165                                 WvString pair("%s:%s", val->name, val->value);
01166                                 list.append(pair);
01167                             }
01168                         }
01169                         retval = list.join(";\n");
01170                         }
01171                     sk_CONF_VALUE_pop_free(svals, X509V3_conf_free);
01172                 }
01173                 else if (method->i2r)
01174                 {
01175                     TRACE("Raw Extension!\n");
01176                     WvDynBuf retvalbuf;
01177                     BIO *bufbio = BIO_new(BIO_s_mem());
01178                     BUF_MEM *bm;
01179                     method->i2r(method, ext_data, bufbio, 0);
01180                     BIO_get_mem_ptr(bufbio, &bm);
01181                     retvalbuf.put(bm->data, bm->length);
01182                     BIO_free(bufbio);
01183                     retval = retvalbuf.getstr();
01184                 }
01185                     
01186                 if (method->it)
01187                     ASN1_item_free((ASN1_VALUE *)ext_data, 
01188                                    ASN1_ITEM_ptr(method->it));
01189                 else
01190                     method->ext_free(ext_data);
01191 
01192             }
01193         }
01194     }
01195     else
01196     {
01197         TRACE("Extension not present!\n");
01198     }
01199 
01200     if (!!retval)
01201         TRACE("Returning: %s\n", retval);
01202 
01203     return retval;
01204 }
01205 
01206 
01207 void WvX509::set_extension(int nid, WvStringParm _values)
01208 {
01209     CHECK_CERT_EXISTS_SET("extension");
01210 
01211     WvString values(_values);
01212     X509_EXTENSION *ex = NULL;
01213     ex = X509V3_EXT_conf_nid(NULL, NULL, nid, values.edit());
01214     X509_add_ext(cert, ex, -1);
01215     X509_EXTENSION_free(ex);
01216 }
01217 
01218 
01219 bool WvX509::isok() const
01220 {
01221     return cert;
01222 }
01223 
01224 
01225 WvString WvX509::errstr() const
01226 {
01227     if (!cert)
01228         return "No certificate.";
01229 
01230     return WvString::empty;
01231 }
01232 
01233 
01234 bool WvX509::verify(WvStringParm original, WvStringParm signature) const
01235 {
01236     WvDynBuf buf;
01237     buf.putstr(original);
01238     return verify(buf, signature);
01239 }
01240 
01241 
01242 bool WvX509::verify(WvBuf &original, WvStringParm signature) const
01243 {    
01244     unsigned char sig_buf[4096];
01245     size_t sig_size = sizeof(sig_buf);
01246     WvBase64Decoder().flushstrmem(signature, sig_buf, &sig_size, true);
01247     
01248     EVP_PKEY *pk = X509_get_pubkey(cert);
01249     if (!pk) 
01250         return false;
01251     
01252     /* Verify the signature */
01253     EVP_MD_CTX sig_ctx;
01254     EVP_VerifyInit(&sig_ctx, EVP_sha1());
01255     EVP_VerifyUpdate(&sig_ctx, original.peek(0, original.used()), original.used());
01256     int sig_err = EVP_VerifyFinal(&sig_ctx, sig_buf, sig_size, pk);
01257     EVP_PKEY_free(pk);
01258     EVP_MD_CTX_cleanup(&sig_ctx); // Again, not my fault... 
01259     if (sig_err != 1) 
01260     {
01261         debug("Verify failed!\n");
01262         return false;
01263     }
01264     else
01265         return true;
01266 }
01267 
01268 
01269 time_t ASN1_TIME_to_time_t(ASN1_TIME *t)
01270 {
01271     struct tm newtime;
01272     char *p = NULL;
01273     char d[18];
01274     memset(&d,'\0',sizeof(d));    
01275     memset(&newtime,'\0',sizeof newtime);
01276     
01277     if (t->type == V_ASN1_GENERALIZEDTIME) 
01278     {
01279          // For time values >= 2050, OpenSSL uses
01280          // ASN1_GENERALIZEDTIME - which we'll worry about
01281          // later.
01282         return 0;
01283     }
01284 
01285     p = (char *)t->data;
01286     sscanf(p,"%2s%2s%2s%2s%2s%2sZ", d, &d[3], &d[6], &d[9], &d[12], &d[15]);
01287     
01288     int year = strtol(d, (char **)NULL, 10);
01289     if (year < 49)
01290         year += 100;
01291     else
01292         year += 50;
01293     
01294     newtime.tm_year = year;
01295     newtime.tm_mon = strtol(&d[3], (char **)NULL, 10) - 1;
01296     newtime.tm_mday = strtol(&d[6], (char **)NULL, 10);
01297     newtime.tm_hour = strtol(&d[9], (char **)NULL, 10);
01298     newtime.tm_min = strtol(&d[12], (char **)NULL, 10);
01299     newtime.tm_sec = strtol(&d[15], (char **)NULL, 10);
01300 
01301     return mktime(&newtime);
01302 }
01303 
01304 
01305 time_t WvX509::get_notvalid_before() const
01306 {
01307     CHECK_CERT_EXISTS_GET("not valid before", 0);
01308 
01309     return ASN1_TIME_to_time_t(X509_get_notBefore(cert));
01310 }
01311 
01312 
01313 time_t WvX509::get_notvalid_after() const
01314 {
01315     CHECK_CERT_EXISTS_GET("not valid after", 0);
01316 
01317     return ASN1_TIME_to_time_t(X509_get_notAfter(cert));
01318 }
01319 
01320 
01321 WvString WvX509::get_ski() const
01322 {
01323     CHECK_CERT_EXISTS_GET("ski", WvString::null);
01324 
01325     return get_extension(NID_subject_key_identifier);
01326 }
01327 
01328 
01329 WvString WvX509::get_aki() const
01330 {
01331     CHECK_CERT_EXISTS_GET("aki", WvString::null);
01332 
01333     WvStringList aki_list;
01334     parse_stack(get_extension(NID_authority_key_identifier), aki_list, "keyid:");
01335     if (aki_list.count())
01336         return aki_list.popstr();
01337 
01338     return WvString::null;
01339 }
01340 
01341 
01342 void WvX509::set_ski()
01343 {
01344     CHECK_CERT_EXISTS_SET("ski");
01345 
01346     ASN1_OCTET_STRING *oct = M_ASN1_OCTET_STRING_new();
01347     ASN1_BIT_STRING *pk = cert->cert_info->key->public_key;
01348     unsigned char pkey_dig[EVP_MAX_MD_SIZE];
01349     unsigned int diglen;
01350 
01351     EVP_Digest(pk->data, pk->length, pkey_dig, &diglen, EVP_sha1(), NULL);
01352 
01353     M_ASN1_OCTET_STRING_set(oct, pkey_dig, diglen);
01354     X509_EXTENSION *ext = X509V3_EXT_i2d(NID_subject_key_identifier, 0, 
01355                                         oct);
01356     X509_add_ext(cert, ext, -1);
01357     X509_EXTENSION_free(ext);
01358     M_ASN1_OCTET_STRING_free(oct);
01359 }
01360 
01361 
01362 void WvX509::set_aki(const WvX509 &cacert)
01363 {
01364     CHECK_CERT_EXISTS_SET("aki");
01365 
01366     // can't set a meaningful AKI for subordinate certification without the 
01367     // parent having an SKI
01368     ASN1_OCTET_STRING *ikeyid = NULL;
01369     X509_EXTENSION *ext;
01370     int i = X509_get_ext_by_NID(cacert.cert, NID_subject_key_identifier, -1);
01371     if ((i >= 0) && (ext = X509_get_ext(cacert.cert, i)))
01372         ikeyid = static_cast<ASN1_OCTET_STRING *>(X509V3_EXT_d2i(ext));
01373 
01374     if (!ikeyid)
01375         return;
01376 
01377     AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
01378     akeyid->issuer = NULL;
01379     akeyid->serial = NULL;
01380     akeyid->keyid = ikeyid;
01381     ext = X509V3_EXT_i2d(NID_authority_key_identifier, 0, akeyid);
01382     X509_add_ext(cert, ext, -1);
01383     X509_EXTENSION_free(ext); 
01384     AUTHORITY_KEYID_free(akeyid);
01385 }
01386 

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