Make WvStreams compile with gcc 4.4.
[wvstreams.git] / crypto / wvx509.cc
blob93dae06f9ef6ff38a616c1a907d22efc45b870ab
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2005 Net Integration Technologies, Inc.
4 *
5 * X.509 certificate management classes.
6 */
7 #include "wvx509.h"
8 #include "wvcrl.h"
9 #include "wvsslhacks.h"
10 #include "wvcrypto.h"
11 #include "wvstringlist.h"
12 #include "wvbase64.h"
13 #include "wvstrutils.h"
14 #include "wvautoconf.h"
16 #include <openssl/pem.h>
17 #include <openssl/x509v3.h>
18 #include <openssl/err.h>
19 #include <openssl/sha.h>
20 #include <openssl/ssl.h>
22 // enable this to add some extra debugging trace messages (this can be VERY
23 // verbose)
24 #if 0
25 # define TRACE(x, y...) debug(x, ## y);
26 #else
27 #ifndef _MSC_VER
28 # define TRACE(x, y...)
29 #else
30 # define TRACE
31 #endif
32 #endif
34 // helper method to let us return and warn gracefully when getting/setting an
35 // element in a null certificate
36 static const char * warning_str_set
37 = "Tried to set %s, but certificate not ok.\n";
38 static const char * warning_str_get
39 = "Tried to get %s, but certificate not ok.\n";
40 #define CHECK_CERT_EXISTS_SET(x) \
41 if (!cert) { \
42 debug(WvLog::Warning, warning_str_set, x); \
43 return; \
45 #define CHECK_CERT_EXISTS_GET(x, y) \
46 if (!cert) { \
47 debug(WvLog::Warning, warning_str_get, x); \
48 return y; \
52 UUID_MAP_BEGIN(WvX509)
53 UUID_MAP_ENTRY(IObject)
54 UUID_MAP_END
56 static int ssl_init_count = 0;
58 #if !HAVE_OPENSSL_POLICY_MAPPING
60 // HACK: old versions of OpenSSL can't handle ERR_free_strings() being called
61 // more than once in the same process; the next wvssl_init won't work. So
62 // let's make sure to make a global variable that holds the refcount at 1
63 // even when all the objects go away, then clean it up at exit.
64 class WvSSL_Stupid_Refcount
66 public:
67 WvSSL_Stupid_Refcount()
69 wvssl_init();
72 ~WvSSL_Stupid_Refcount()
74 wvssl_free();
78 WvSSL_Stupid_Refcount wvssl_stupid_refcount;
80 #endif // !HAVE_OPENSSL_POLICY_MAPPING
83 void wvssl_init()
85 if (!ssl_init_count)
87 SSL_library_init();
88 SSL_load_error_strings();
89 ERR_load_BIO_strings();
90 ERR_load_crypto_strings();
91 OpenSSL_add_all_algorithms();
92 OpenSSL_add_all_ciphers();
93 OpenSSL_add_all_digests();
96 ssl_init_count++;
100 void wvssl_free()
102 assert(ssl_init_count >= 1);
103 if (ssl_init_count >= 1)
104 ssl_init_count--;
106 if (!ssl_init_count)
108 ERR_free_strings();
109 EVP_cleanup();
114 WvString wvssl_errstr()
116 char buf[256];
117 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
118 buf[sizeof(buf)-1] = 0;
119 return buf;
123 WvX509::WvX509(X509 *_cert)
124 : debug("X509", WvLog::Debug5)
126 wvssl_init();
127 cert = _cert;
131 WvX509::WvX509()
132 : debug("X509", WvLog::Debug5)
134 wvssl_init();
135 cert = NULL;
139 WvX509::WvX509(const WvX509 &x509)
140 : debug("X509", WvLog::Debug5)
142 wvssl_init();
143 if (x509.cert)
144 cert = X509_dup(x509.cert);
145 else
146 cert = NULL;
150 WvX509::~WvX509()
152 TRACE("Deleting.\n");
154 if (cert)
155 X509_free(cert);
157 wvssl_free();
162 // The people who designed this garbage should be shot!
163 // Support old versions of openssl...
164 #ifndef NID_domainComponent
165 #define NID_domainComponent 391
166 #endif
168 #ifndef NID_Domain
169 #define NID_Domain 392
170 #endif
173 // returns some approximation of the server's fqdn, or an empty string.
174 static WvString set_name_entry(X509_NAME *name, WvStringParm dn)
176 WvString fqdn(""), force_fqdn("");
177 X509_NAME_ENTRY *ne = NULL;
178 int count = 0, nid;
180 WvStringList l;
181 l.split(dn, ",");
183 // dn is of the form: c=ca,o=foo organization,dc=foo,dc=com
184 // (ie. name=value pairs separated by commas)
185 WvStringList::Iter i(l);
186 for (i.rewind(); i.next(); )
188 WvString s(*i), sid;
189 char *cptr, *value;
191 cptr = s.edit();
192 value = strchr(cptr, '=');
193 if (value)
194 *value++ = 0;
195 else
196 value = (char*)"NULL";
198 sid = strlwr(trim_string(cptr));
200 if (sid == "c")
201 nid = NID_countryName;
202 else if (sid == "st")
203 nid = NID_stateOrProvinceName;
204 else if (sid == "l")
205 nid = NID_localityName;
206 else if (sid == "o")
207 nid = NID_organizationName;
208 else if (sid == "ou")
209 nid = NID_organizationalUnitName;
210 else if (sid == "cn")
212 nid = NID_commonName;
213 force_fqdn = value;
215 else if (sid == "dc")
217 nid = NID_domainComponent;
218 if (!!fqdn)
219 fqdn.append(".");
220 fqdn.append(value);
222 else if (sid == "domain")
224 nid = NID_Domain;
225 force_fqdn = value;
227 else if (sid == "email")
228 nid = NID_pkcs9_emailAddress;
229 else
230 nid = NID_domainComponent;
232 // Sometimes we just want to parse dn into fqdn.
233 if (name == NULL)
234 continue;
236 if (!ne)
237 ne = X509_NAME_ENTRY_create_by_NID(NULL, nid,
238 V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
239 else
240 X509_NAME_ENTRY_create_by_NID(&ne, nid,
241 V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
242 if (!ne)
243 continue;
245 X509_NAME_add_entry(name, ne, count++, 0);
248 X509_NAME_ENTRY_free(ne);
250 if (!!force_fqdn)
251 return force_fqdn;
253 return fqdn;
257 WvRSAKey *WvX509::get_rsa_pub() const
259 EVP_PKEY *pkcert = X509_get_pubkey(cert);
260 RSA *certrsa = EVP_PKEY_get1_RSA(pkcert);
261 EVP_PKEY_free(pkcert);
262 return new WvRSAKey(certrsa, false);
266 WvString WvX509::certreq(WvStringParm subject, const WvRSAKey &rsa)
268 WvLog debug("X509::certreq", WvLog::Debug5);
270 EVP_PKEY *pk = NULL;
271 X509_NAME *name = NULL;
272 X509_REQ *certreq = NULL;
274 // double check RSA key
275 if (rsa.isok())
276 debug("RSA Key is fine.\n");
277 else
279 debug(WvLog::Warning, "RSA Key is bad");
280 return WvString::null;
283 if ((pk=EVP_PKEY_new()) == NULL)
285 debug(WvLog::Warning,
286 "Error creating key handler for new certificate");
287 return WvString::null;
290 if ((certreq=X509_REQ_new()) == NULL)
292 debug(WvLog::Warning, "Error creating new PKCS#10 object");
293 EVP_PKEY_free(pk);
294 return WvString::null;
297 if (!EVP_PKEY_set1_RSA(pk, rsa.rsa))
299 debug(WvLog::Warning, "Error adding RSA keys to certificate");
300 X509_REQ_free(certreq);
301 EVP_PKEY_free(pk);
302 return WvString::null;
305 X509_REQ_set_version(certreq, 0); /* version 1 */
307 X509_REQ_set_pubkey(certreq, pk);
309 name = X509_REQ_get_subject_name(certreq);
311 debug("Creating Certificate request for %s\n", subject);
312 set_name_entry(name, subject);
313 X509_REQ_set_subject_name(certreq, name);
314 char *sub_name = X509_NAME_oneline(X509_REQ_get_subject_name(certreq),
315 0, 0);
316 debug("SubjectDN: %s\n", sub_name);
317 OPENSSL_free(sub_name);
319 if (!X509_REQ_sign(certreq, pk, EVP_sha1()))
321 debug(WvLog::Warning, "Could not self sign the request");
322 X509_REQ_free(certreq);
323 EVP_PKEY_free(pk);
324 return WvString::null;
327 int verify_result = X509_REQ_verify(certreq, pk);
328 if (verify_result == 0)
330 debug(WvLog::Warning, "Self signed request failed");
331 X509_REQ_free(certreq);
332 EVP_PKEY_free(pk);
333 return WvString::null;
335 else
337 debug("Self Signed Certificate Request verifies OK!\n");
340 // Horribly involuted hack to get around the fact that the
341 // OpenSSL people are too braindead to have a PEM_write function
342 // that returns a char *
343 WvDynBuf retval;
344 BIO *bufbio = BIO_new(BIO_s_mem());
345 BUF_MEM *bm;
347 PEM_write_bio_X509_REQ(bufbio, certreq);
348 BIO_get_mem_ptr(bufbio, &bm);
349 retval.put(bm->data, bm->length);
351 X509_REQ_free(certreq);
352 EVP_PKEY_free(pk);
353 BIO_free(bufbio);
355 return retval.getstr();
359 bool WvX509::validate(WvX509 *cacert) const
361 if (cert == NULL)
363 debug(WvLog::Warning, "Tried to validate certificate against CA, but "
364 "certificate is blank!\n");
365 return false;
368 bool retval = true;
370 // Check and make sure that the certificate is still valid
371 if (X509_cmp_current_time(X509_get_notAfter(cert)) < 0)
373 debug("Certificate has expired.\n");
374 retval = false;
377 if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0)
379 debug("Certificate is not yet valid.\n");
380 retval = false;
383 if (cacert)
385 retval &= signedbyca(*cacert);
386 retval &= issuedbyca(*cacert);
389 return retval;
393 bool WvX509::signedbyca(WvX509 &cacert) const
395 if (!cert || !cacert.cert)
397 debug(WvLog::Warning, "Tried to determine if certificate was signed "
398 "by CA, but either client or CA certificate (or both) are "
399 "blank.\n");
400 return false;
403 EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
404 int result = X509_verify(cert, pkey);
405 EVP_PKEY_free(pkey);
407 if (result < 0)
409 debug("Can't determine if we were signed by CA %s: %s\n",
410 cacert.get_subject(), wvssl_errstr());
411 return false;
413 bool issigned = (result > 0);
415 debug("Certificate was%s signed by CA %s.\n", issigned ? "" : " NOT",
416 cacert.get_subject());
418 return issigned;
422 bool WvX509::issuedbyca(WvX509 &cacert) const
424 if (!cert || !cacert.cert)
426 debug(WvLog::Warning, "Tried to determine if certificate was issued "
427 "by CA, but either client or CA certificate (or both) are "
428 "blank.\n");
429 return false;
432 int ret = X509_check_issued(cacert.cert, cert);
433 debug("issuedbyca: %s==X509_V_OK(%s)\n", ret, X509_V_OK);
434 if (ret != X509_V_OK)
435 return false;
437 return true;
441 WvString WvX509::encode(const DumpMode mode) const
443 WvDynBuf retval;
444 encode(mode, retval);
445 return retval.getstr();
449 void WvX509::encode(const DumpMode mode, WvBuf &buf) const
451 if (mode == CertFileDER || mode == CertFilePEM)
452 return; // file modes are no ops with encode
454 if (!cert)
456 debug(WvLog::Warning, "Tried to encode certificate, but certificate "
457 "is blank!\n");
458 return;
461 debug("Encoding X509 certificate.\n");
463 if (mode == CertHex)
465 size_t size;
466 unsigned char *keybuf, *iend;
467 WvString enccert;
469 size = i2d_X509(cert, NULL);
470 iend = keybuf = new unsigned char[size];
471 i2d_X509(cert, &iend);
473 enccert.setsize(size * 2 +1);
474 ::hexify(enccert.edit(), keybuf, size);
476 deletev keybuf;
477 buf.putstr(enccert);
479 else
481 BIO *bufbio = BIO_new(BIO_s_mem());
482 BUF_MEM *bm;
484 if (mode == CertPEM)
485 PEM_write_bio_X509(bufbio, cert);
486 else if (mode == CertDER)
487 i2d_X509_bio(bufbio, cert);
488 else
489 debug(WvLog::Warning, "Tried to encode certificate with unknown "
490 "mode!\n");
492 BIO_get_mem_ptr(bufbio, &bm);
493 buf.put(bm->data, bm->length);
494 BIO_free(bufbio);
499 void WvX509::decode(const DumpMode mode, WvStringParm str)
501 if (cert)
503 debug("Replacing an already existant X509 certificate.\n");
504 X509_free(cert);
505 cert = NULL;
508 if (mode == CertFileDER)
510 BIO *bio = BIO_new(BIO_s_file());
512 if (BIO_read_filename(bio, str.cstr()) <= 0)
514 debug(WvLog::Warning, "Open '%s': %s\n", str, wvssl_errstr());
515 BIO_free(bio);
516 return;
519 if (!(cert = d2i_X509_bio(bio, NULL)))
520 debug(WvLog::Warning, "Import DER from '%s': %s\n",
521 str, wvssl_errstr());
523 BIO_free(bio);
524 return;
526 else if (mode == CertFilePEM)
528 FILE *fp = fopen(str, "rb");
529 if (!fp)
531 int errnum = errno;
532 debug("Open '%s': %s\n", str, strerror(errnum));
533 return;
536 if (!(cert = PEM_read_X509(fp, NULL, NULL, NULL)))
537 debug(WvLog::Warning, "Import PEM from '%s': %s\n",
538 str, wvssl_errstr());
540 fclose(fp);
541 return;
543 else if (mode == CertHex)
545 int hexbytes = str.len();
546 int bufsize = hexbytes/2;
547 unsigned char *certbuf = new unsigned char[bufsize];
548 unsigned char *cp = certbuf;
549 X509 *tmpcert;
551 ::unhexify(certbuf, str);
552 tmpcert = cert = X509_new();
553 cert = wv_d2i_X509(&tmpcert, &cp, bufsize);
554 delete[] certbuf;
555 return;
558 // we use the buffer decode functions for everything else
559 WvDynBuf buf;
560 buf.putstr(str);
561 decode(mode, buf);
565 void WvX509::decode(const DumpMode mode, WvBuf &encoded)
567 if (cert)
569 debug("Replacing an already existant X509 certificate.\n");
570 X509_free(cert);
571 cert = NULL;
574 if (mode == CertHex || mode == CertFileDER || mode == CertFilePEM)
575 decode(mode, encoded.getstr());
576 else
578 BIO *membuf = BIO_new(BIO_s_mem());
579 BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
581 if (mode == CertPEM)
582 cert = PEM_read_bio_X509(membuf, NULL, NULL, NULL);
583 else if (mode == CertDER)
584 cert = d2i_X509_bio(membuf, NULL);
585 else
586 debug(WvLog::Warning, "Tried to decode certificate with unknown "
587 "mode!\n");
589 BIO_free_all(membuf);
594 WvString WvX509::get_issuer() const
596 CHECK_CERT_EXISTS_GET("issuer", WvString::null);
598 char *name = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
599 WvString retval(name);
600 OPENSSL_free(name);
601 return retval;
605 void WvX509::set_issuer(WvStringParm issuer)
607 CHECK_CERT_EXISTS_SET("issuer");
609 X509_NAME *name = X509_get_issuer_name(cert);
610 set_name_entry(name, issuer);
611 X509_set_issuer_name(cert, name);
615 void WvX509::set_issuer(const WvX509 &cacert)
617 CHECK_CERT_EXISTS_SET("issuer");
619 X509_NAME *casubj = X509_get_subject_name(cacert.cert);
620 X509_set_issuer_name(cert, casubj);
624 WvString WvX509::get_subject() const
626 CHECK_CERT_EXISTS_GET("subject", WvString::null);
628 char *name = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
629 WvString retval(name);
630 OPENSSL_free(name);
631 return retval;
635 void WvX509::set_subject(WvStringParm subject)
637 CHECK_CERT_EXISTS_SET("subject");
639 X509_NAME *name = X509_get_subject_name(cert);
640 set_name_entry(name, subject);
641 X509_set_subject_name(cert, name);
645 void WvX509::set_subject(X509_NAME *name)
647 CHECK_CERT_EXISTS_SET("subject");
649 X509_set_subject_name(cert, name);
653 void WvX509::set_pubkey(WvRSAKey &_rsa)
655 CHECK_CERT_EXISTS_SET("pubkey");
657 EVP_PKEY *pk = EVP_PKEY_new();
658 assert(pk);
660 // Assign RSA Key from WvRSAKey into stupid package that OpenSSL needs
661 if (!EVP_PKEY_set1_RSA(pk, _rsa.rsa))
663 debug("Error adding RSA keys to certificate.\n");
664 return;
667 X509_set_pubkey(cert, pk);
669 EVP_PKEY_free(pk);
674 void WvX509::set_nsserver(WvStringParm servername)
676 CHECK_CERT_EXISTS_SET("nsserver");
678 WvString fqdn;
680 // FQDN cannot have a = in it, therefore it
681 // must be a distinguished name :)
682 if (strchr(servername, '='))
683 fqdn = set_name_entry(NULL, servername);
684 else
685 fqdn = servername;
687 if (!fqdn)
688 fqdn = "null.noname.null";
690 debug("Setting Netscape SSL server name extension to '%s'.\n", fqdn);
692 // Add in the netscape-specific server extension
693 set_extension(NID_netscape_cert_type, "server");
694 set_extension(NID_netscape_ssl_server_name, fqdn);
698 WvString WvX509::get_nsserver() const
700 return get_extension(NID_netscape_ssl_server_name);
704 WvString WvX509::get_serial(bool hex) const
706 CHECK_CERT_EXISTS_GET("serial", WvString::null);
708 BIGNUM *bn = BN_new();
709 bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), bn);
710 char * c;
711 if (hex)
712 c = BN_bn2hex(bn);
713 else
714 c = BN_bn2dec(bn);
715 WvString ret("%s", c);
716 OPENSSL_free(c);
717 BN_free(bn);
718 return ret;
722 void WvX509::set_version()
724 CHECK_CERT_EXISTS_SET("version");
726 X509_set_version(cert, 0x2);
730 void WvX509::set_serial(long serial)
732 CHECK_CERT_EXISTS_SET("serial");
734 ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
738 WvString WvX509::get_crl_dp() const
740 return get_extension(NID_crl_distribution_points);
744 void WvX509::set_lifetime(long seconds)
746 CHECK_CERT_EXISTS_SET("lifetime");
748 // Set the NotBefore time to now.
749 X509_gmtime_adj(X509_get_notBefore(cert), 0);
751 // Now + 10 years... should be shorter, but since we don't currently
752 // have a set of routines to refresh the certificates, make it
753 // REALLY long.
754 X509_gmtime_adj(X509_get_notAfter(cert), seconds);
758 void WvX509::set_key_usage(WvStringParm values)
760 set_extension(NID_key_usage, values);
764 WvString WvX509::get_key_usage() const
766 return get_extension(NID_key_usage);
770 void WvX509::set_ext_key_usage(WvStringParm values)
772 set_extension(NID_ext_key_usage, values);
776 WvString WvX509::get_ext_key_usage() const
778 return get_extension(NID_ext_key_usage);
782 WvString WvX509::get_altsubject() const
784 return get_extension(NID_subject_alt_name);
788 bool WvX509::get_basic_constraints(bool &ca, int &pathlen) const
790 CHECK_CERT_EXISTS_GET("basic constraints", false);
792 BASIC_CONSTRAINTS *constraints = NULL;
793 int i;
795 constraints = static_cast<BASIC_CONSTRAINTS *>
796 (X509_get_ext_d2i(cert, NID_basic_constraints, &i, NULL));
797 if (constraints)
799 ca = constraints->ca;
800 if (constraints->pathlen)
802 if ((constraints->pathlen->type == V_ASN1_NEG_INTEGER) || !ca)
804 debug("Path length type not valid when getting basic "
805 "constraints.\n");
806 BASIC_CONSTRAINTS_free(constraints);
807 pathlen = 0;
808 return false;
811 pathlen = ASN1_INTEGER_get(constraints->pathlen);
813 else
814 pathlen = (-1);
816 BASIC_CONSTRAINTS_free(constraints);
817 return true;
820 debug("Basic constraints extension not present.\n");
821 return false;
825 void WvX509::set_basic_constraints(bool ca, int pathlen)
827 CHECK_CERT_EXISTS_SET("basic constraints");
829 BASIC_CONSTRAINTS *constraints = BASIC_CONSTRAINTS_new();
831 constraints->ca = static_cast<int>(ca);
832 if (pathlen != (-1))
834 ASN1_INTEGER *i = ASN1_INTEGER_new();
835 ASN1_INTEGER_set(i, pathlen);
836 constraints->pathlen = i;
839 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_basic_constraints, 0,
840 constraints);
841 while (int idx = X509_get_ext_by_NID(cert, NID_basic_constraints, 0) >= 0)
843 debug("Found extension at idx %s\n", idx);
844 X509_EXTENSION *tmpex = X509_delete_ext(cert, idx);
845 X509_EXTENSION_free(tmpex);
848 X509_add_ext(cert, ex, NID_basic_constraints);
849 X509_EXTENSION_free(ex);
850 BASIC_CONSTRAINTS_free(constraints);
855 * These functions are optional to the API. If OpenSSL doesn't support them,
856 * we simply won't include them here, and apps that need them won't compile.
858 #ifdef HAVE_OPENSSL_POLICY_MAPPING
860 bool WvX509::get_policy_constraints(int &require_explicit_policy,
861 int &inhibit_policy_mapping) const
863 CHECK_CERT_EXISTS_GET("policy constraints", false);
865 POLICY_CONSTRAINTS *constraints = NULL;
866 int i;
868 constraints = static_cast<POLICY_CONSTRAINTS *>(X509_get_ext_d2i(
869 cert, NID_policy_constraints,
870 &i, NULL));
871 if (constraints)
873 if (constraints->requireExplicitPolicy)
874 require_explicit_policy = ASN1_INTEGER_get(
875 constraints->requireExplicitPolicy);
876 else
877 require_explicit_policy = (-1);
879 if (constraints->inhibitPolicyMapping)
880 inhibit_policy_mapping = ASN1_INTEGER_get(
881 constraints->inhibitPolicyMapping);
882 else
883 inhibit_policy_mapping = (-1);
884 POLICY_CONSTRAINTS_free(constraints);
885 return true;
888 return false;
892 void WvX509::set_policy_constraints(int require_explicit_policy,
893 int inhibit_policy_mapping)
895 CHECK_CERT_EXISTS_SET("policy constraints");
897 POLICY_CONSTRAINTS *constraints = POLICY_CONSTRAINTS_new();
899 ASN1_INTEGER *i = ASN1_INTEGER_new();
900 ASN1_INTEGER_set(i, require_explicit_policy);
901 constraints->requireExplicitPolicy = i;
902 i = ASN1_INTEGER_new();
903 ASN1_INTEGER_set(i, inhibit_policy_mapping);
904 constraints->inhibitPolicyMapping = i;
906 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_constraints, 0,
907 constraints);
908 X509_add_ext(cert, ex, -1);
909 X509_EXTENSION_free(ex);
910 POLICY_CONSTRAINTS_free(constraints);
914 bool WvX509::get_policy_mapping(PolicyMapList &list) const
916 CHECK_CERT_EXISTS_GET("policy mapping", false);
918 POLICY_MAPPINGS *mappings = NULL;
919 POLICY_MAPPING *map = NULL;
920 int i;
922 mappings = static_cast<POLICY_MAPPINGS *>(X509_get_ext_d2i(
923 cert, NID_policy_mappings,
924 &i, NULL));
925 if (!mappings)
926 return false;
928 const int POLICYID_MAXLEN = 80;
929 char tmp1[80];
930 char tmp2[80];
931 for(int j = 0; j < sk_POLICY_MAPPING_num(mappings); j++)
933 map = sk_POLICY_MAPPING_value(mappings, j);
934 OBJ_obj2txt(tmp1, POLICYID_MAXLEN, map->issuerDomainPolicy, true);
935 OBJ_obj2txt(tmp2, POLICYID_MAXLEN, map->subjectDomainPolicy, true);
936 list.append(new PolicyMap(tmp1, tmp2), true);
939 sk_POLICY_MAPPING_pop_free(mappings, POLICY_MAPPING_free);
941 return true;
945 void WvX509::set_policy_mapping(PolicyMapList &list)
947 CHECK_CERT_EXISTS_SET("policy mapping");
949 POLICY_MAPPINGS *maps = sk_POLICY_MAPPING_new_null();
951 PolicyMapList::Iter i(list);
952 for (i.rewind(); i.next();)
954 POLICY_MAPPING *map = POLICY_MAPPING_new();
955 map->issuerDomainPolicy = OBJ_txt2obj(i().issuer_domain.cstr(), 0);
956 map->subjectDomainPolicy = OBJ_txt2obj(i().subject_domain.cstr(), 0);
957 sk_POLICY_MAPPING_push(maps, map);
958 printf("Push!\n");
961 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_mappings, 0, maps);
962 X509_add_ext(cert, ex, -1);
963 X509_EXTENSION_free(ex);
964 sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free);
967 #endif // HAVE_OPENSSL_POLICY_MAPPING
970 static void add_aia(WvStringParm type, WvString identifier,
971 AUTHORITY_INFO_ACCESS *ainfo)
973 ACCESS_DESCRIPTION *acc = ACCESS_DESCRIPTION_new();
974 sk_ACCESS_DESCRIPTION_push(ainfo, acc);
975 acc->method = OBJ_txt2obj(type.cstr(), 0);
976 acc->location->type = GEN_URI;
977 acc->location->d.ia5 = M_ASN1_IA5STRING_new();
978 unsigned char *cident
979 = reinterpret_cast<unsigned char *>(identifier.edit());
980 ASN1_STRING_set(acc->location->d.ia5, cident, identifier.len());
984 void WvX509::set_aia(WvStringList &ca_urls,
985 WvStringList &responders)
987 CHECK_CERT_EXISTS_SET("aia");
989 AUTHORITY_INFO_ACCESS *ainfo = sk_ACCESS_DESCRIPTION_new_null();
991 WvStringList::Iter i(ca_urls);
992 for (i.rewind(); i.next();)
993 add_aia("caIssuers", i(), ainfo);
995 WvStringList::Iter j(responders);
996 for (j.rewind(); j.next();)
997 add_aia("OCSP", j(), ainfo);
999 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_info_access, 0, ainfo);
1000 X509_add_ext(cert, ex, -1);
1001 X509_EXTENSION_free(ex);
1002 sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free);
1006 WvString WvX509::get_aia() const
1008 return get_extension(NID_info_access);
1012 static void parse_stack(WvStringParm ext, WvStringList &list,
1013 WvStringParm prefix)
1015 WvStringList stack;
1016 stack.split(ext, ";\n");
1017 WvStringList::Iter i(stack);
1018 for (i.rewind();i.next();)
1020 WvString stack_entry(*i);
1021 if (strstr(stack_entry, prefix))
1023 WvString uri(stack_entry.edit() + prefix.len());
1024 list.append(uri);
1030 void WvX509::get_ocsp(WvStringList &responders) const
1032 parse_stack(get_aia(), responders, "OCSP - URI:");
1036 void WvX509::get_ca_urls(WvStringList &urls) const
1038 parse_stack(get_aia(), urls, "CA Issuers - URI:");
1042 void WvX509::get_crl_urls(WvStringList &urls) const
1044 parse_stack(get_crl_dp(), urls, "URI:");
1048 void WvX509::set_crl_urls(WvStringList &urls)
1050 CHECK_CERT_EXISTS_SET("CRL urls");
1052 STACK_OF(DIST_POINT) *crldp = sk_DIST_POINT_new_null();
1053 WvStringList::Iter i(urls);
1054 for (i.rewind(); i.next();)
1056 DIST_POINT *point = DIST_POINT_new();
1057 sk_DIST_POINT_push(crldp, point);
1059 GENERAL_NAMES *uris = GENERAL_NAMES_new();
1060 GENERAL_NAME *uri = GENERAL_NAME_new();
1061 uri->type = GEN_URI;
1062 uri->d.ia5 = M_ASN1_IA5STRING_new();
1063 unsigned char *cident
1064 = reinterpret_cast<unsigned char *>(i().edit());
1065 ASN1_STRING_set(uri->d.ia5, cident, i().len());
1066 sk_GENERAL_NAME_push(uris, uri);
1068 point->distpoint = DIST_POINT_NAME_new();
1069 point->distpoint->name.fullname = uris;
1070 point->distpoint->type = 0;
1073 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_crl_distribution_points, 0, crldp);
1074 X509_add_ext(cert, ex, -1);
1075 X509_EXTENSION_free(ex);
1076 sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
1080 bool WvX509::get_policies(WvStringList &policy_oids) const
1082 CHECK_CERT_EXISTS_GET("policies", false);
1084 int critical;
1085 CERTIFICATEPOLICIES * policies = static_cast<CERTIFICATEPOLICIES *>(
1086 X509_get_ext_d2i(cert, NID_certificate_policies, &critical, NULL));
1087 if (policies)
1089 for (int i = 0; i < sk_POLICYINFO_num(policies); i++)
1091 POLICYINFO * policy = sk_POLICYINFO_value(policies, i);
1092 const int POLICYID_MAXLEN = 80;
1094 char policyid[POLICYID_MAXLEN];
1095 OBJ_obj2txt(policyid, POLICYID_MAXLEN, policy->policyid,
1096 true); // don't substitute human-readable names
1097 policy_oids.append(policyid);
1100 sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
1101 return true;
1104 return false;
1108 void WvX509::set_policies(WvStringList &policy_oids)
1110 CHECK_CERT_EXISTS_SET("policies");
1112 STACK_OF(POLICYINFO) *sk_pinfo = sk_POLICYINFO_new_null();
1114 WvStringList::Iter i(policy_oids);
1115 for (i.rewind(); i.next();)
1117 ASN1_OBJECT *pobj = OBJ_txt2obj(i(), 0);
1118 POLICYINFO *pol = POLICYINFO_new();
1119 pol->policyid = pobj;
1120 sk_POLICYINFO_push(sk_pinfo, pol);
1123 #if 0
1124 // this code would let you set URL information to a policy
1125 // qualifier
1126 POLICYQUALINFO *qual = NULL;
1127 WvString url(_url);
1128 if (!!url)
1130 pol->qualifiers = sk_POLICYQUALINFO_new_null();
1131 qual = POLICYQUALINFO_new();
1132 qual->pqualid = OBJ_nid2obj(NID_id_qt_cps);
1133 qual->d.cpsouri = M_ASN1_IA5STRING_new();
1134 ASN1_STRING_set(qual->d.cpsuri, url.edit(), url.len());
1135 sk_POLICYQUALINFO_push(pol->qualifiers, qual);
1137 #endif
1139 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_certificate_policies, 0,
1140 sk_pinfo);
1141 X509_add_ext(cert, ex, -1);
1142 X509_EXTENSION_free(ex);
1143 sk_POLICYINFO_pop_free(sk_pinfo, POLICYINFO_free);
1147 WvString WvX509::get_extension(int nid) const
1149 CHECK_CERT_EXISTS_GET("extension", WvString::null);
1151 WvString retval = WvString::null;
1153 int index = X509_get_ext_by_NID(cert, nid, -1);
1154 if (index >= 0)
1156 X509_EXTENSION *ext = X509_get_ext(cert, index);
1158 if (ext)
1160 X509V3_EXT_METHOD *method = X509V3_EXT_get(ext);
1161 if (!method)
1163 WvDynBuf buf;
1164 buf.put(ext->value->data, ext->value->length);
1165 retval = buf.getstr();
1167 else
1169 void *ext_data = NULL;
1170 // we NEED to use a temporary pointer for ext_value_data,
1171 // as openssl's ASN1_item_d2i will muck around with it,
1172 // even though it's const (at least as of version 0.9.8e).
1173 // gah.
1174 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
1175 const unsigned char * ext_value_data = ext->value->data;
1176 #else
1177 unsigned char *ext_value_data = ext->value->data;
1178 #endif
1179 if (method->it)
1181 ext_data = ASN1_item_d2i(NULL, &ext_value_data,
1182 ext->value->length,
1183 ASN1_ITEM_ptr(method->it));
1184 TRACE("Applied generic conversion!\n");
1186 else
1188 ext_data = method->d2i(NULL, &ext_value_data,
1189 ext->value->length);
1190 TRACE("Applied method specific conversion!\n");
1193 if (method->i2s)
1195 TRACE("String Extension!\n");
1196 char *s = method->i2s(method, ext_data);
1197 retval = s;
1198 OPENSSL_free(s);
1200 else if (method->i2v)
1202 TRACE("Stack Extension!\n");
1203 CONF_VALUE *val = NULL;
1204 STACK_OF(CONF_VALUE) *svals = NULL;
1205 svals = method->i2v(method, ext_data, NULL);
1206 if (!sk_CONF_VALUE_num(svals))
1207 retval = "EMPTY";
1208 else
1210 WvStringList list;
1211 for(int i = 0; i < sk_CONF_VALUE_num(svals); i++)
1213 val = sk_CONF_VALUE_value(svals, i);
1214 if (!val->name)
1215 list.append(WvString(val->value));
1216 else if (!val->value)
1217 list.append(WvString(val->name));
1218 else
1220 WvString pair("%s:%s", val->name, val->value);
1221 list.append(pair);
1224 retval = list.join(";\n");
1226 sk_CONF_VALUE_pop_free(svals, X509V3_conf_free);
1228 else if (method->i2r)
1230 TRACE("Raw Extension!\n");
1231 WvDynBuf retvalbuf;
1232 BIO *bufbio = BIO_new(BIO_s_mem());
1233 BUF_MEM *bm;
1234 method->i2r(method, ext_data, bufbio, 0);
1235 BIO_get_mem_ptr(bufbio, &bm);
1236 retvalbuf.put(bm->data, bm->length);
1237 BIO_free(bufbio);
1238 retval = retvalbuf.getstr();
1241 if (method->it)
1242 ASN1_item_free((ASN1_VALUE *)ext_data,
1243 ASN1_ITEM_ptr(method->it));
1244 else
1245 method->ext_free(ext_data);
1250 else
1252 TRACE("Extension not present!\n");
1255 if (!!retval)
1256 TRACE("Returning: %s\n", retval);
1258 return retval;
1262 void WvX509::set_extension(int nid, WvStringParm _values)
1264 CHECK_CERT_EXISTS_SET("extension");
1266 // first we check to see if the extension already exists, if so we need to
1267 // kill it
1268 int index = X509_get_ext_by_NID(cert, nid, -1);
1269 if (index >= 0)
1271 X509_EXTENSION *ex = X509_delete_ext(cert, index);
1272 X509_EXTENSION_free(ex);
1275 // now set the extension
1276 WvString values(_values);
1277 X509_EXTENSION *ex = NULL;
1278 ex = X509V3_EXT_conf_nid(NULL, NULL, nid, values.edit());
1279 X509_add_ext(cert, ex, -1);
1280 X509_EXTENSION_free(ex);
1284 bool WvX509::isok() const
1286 return cert;
1290 bool WvX509::operator! () const
1292 return !isok();
1296 WvString WvX509::errstr() const
1298 if (!cert)
1299 return "No certificate.";
1301 return WvString::empty;
1305 bool WvX509::verify(WvStringParm original, WvStringParm signature) const
1307 WvDynBuf buf;
1308 buf.putstr(original);
1309 return verify(buf, signature);
1313 bool WvX509::verify(WvBuf &original, WvStringParm signature) const
1315 unsigned char sig_buf[4096];
1316 size_t sig_size = sizeof(sig_buf);
1317 WvBase64Decoder().flushstrmem(signature, sig_buf, &sig_size, true);
1319 EVP_PKEY *pk = X509_get_pubkey(cert);
1320 if (!pk)
1321 return false;
1323 /* Verify the signature */
1324 EVP_MD_CTX sig_ctx;
1325 EVP_VerifyInit(&sig_ctx, EVP_sha1());
1326 EVP_VerifyUpdate(&sig_ctx, original.peek(0, original.used()),
1327 original.used());
1328 int sig_err = EVP_VerifyFinal(&sig_ctx, sig_buf, sig_size, pk);
1329 EVP_PKEY_free(pk);
1330 EVP_MD_CTX_cleanup(&sig_ctx); // Again, not my fault...
1331 if (sig_err != 1)
1333 debug("Verify failed!\n");
1334 return false;
1336 else
1337 return true;
1341 static time_t ASN1_TIME_to_time_t(ASN1_TIME *t)
1343 struct tm newtime;
1344 char *p = NULL;
1345 char d[18];
1346 memset(&d,'\0',sizeof(d));
1347 memset(&newtime,'\0',sizeof newtime);
1349 if (t->type == V_ASN1_GENERALIZEDTIME)
1351 // For time values >= 2050, OpenSSL uses
1352 // ASN1_GENERALIZEDTIME - which we'll worry about
1353 // later.
1354 return 0;
1357 p = (char *)t->data;
1358 sscanf(p,"%2s%2s%2s%2s%2s%2sZ", d, &d[3], &d[6], &d[9], &d[12], &d[15]);
1360 int year = strtol(d, (char **)NULL, 10);
1361 if (year < 49)
1362 year += 100;
1363 else
1364 year += 50;
1366 newtime.tm_year = year;
1367 newtime.tm_mon = strtol(&d[3], (char **)NULL, 10) - 1;
1368 newtime.tm_mday = strtol(&d[6], (char **)NULL, 10);
1369 newtime.tm_hour = strtol(&d[9], (char **)NULL, 10);
1370 newtime.tm_min = strtol(&d[12], (char **)NULL, 10);
1371 newtime.tm_sec = strtol(&d[15], (char **)NULL, 10);
1373 return mktime(&newtime);
1377 time_t WvX509::get_notvalid_before() const
1379 CHECK_CERT_EXISTS_GET("not valid before", 0);
1381 return ASN1_TIME_to_time_t(X509_get_notBefore(cert));
1385 time_t WvX509::get_notvalid_after() const
1387 CHECK_CERT_EXISTS_GET("not valid after", 0);
1389 return ASN1_TIME_to_time_t(X509_get_notAfter(cert));
1393 WvString WvX509::get_ski() const
1395 CHECK_CERT_EXISTS_GET("ski", WvString::null);
1397 return get_extension(NID_subject_key_identifier);
1401 WvString WvX509::get_aki() const
1403 CHECK_CERT_EXISTS_GET("aki", WvString::null);
1405 WvStringList aki_list;
1406 parse_stack(get_extension(NID_authority_key_identifier), aki_list,
1407 "keyid:");
1408 if (aki_list.count())
1409 return aki_list.popstr();
1411 return WvString::null;
1415 WvString WvX509::get_fingerprint(const FprintMode mode) const
1417 CHECK_CERT_EXISTS_GET("fingerprint", WvString::null);
1419 /* Default to SHA-1 because OpenSSL does too */
1420 const EVP_MD *digest = EVP_sha1();
1421 if (mode == FingerMD5)
1422 digest = EVP_md5();
1424 unsigned char md[EVP_MAX_MD_SIZE];
1425 unsigned int n;
1426 if (!X509_digest(cert, digest, md, &n))
1428 errno = -ENOMEM;
1429 debug("get_fingerprint: Out of memory\n");
1430 return WvString::null;
1433 WvDynBuf store;
1434 char buf[3];
1435 unsigned int i = 0;
1436 do {
1437 sprintf(buf, "%02X", md[i]);
1438 store.putstr(buf);
1439 } while (++i < n && (store.putch(':'), 1));
1441 return store.getstr();
1445 void WvX509::set_ski()
1447 CHECK_CERT_EXISTS_SET("ski");
1449 ASN1_OCTET_STRING *oct = M_ASN1_OCTET_STRING_new();
1450 ASN1_BIT_STRING *pk = cert->cert_info->key->public_key;
1451 unsigned char pkey_dig[EVP_MAX_MD_SIZE];
1452 unsigned int diglen;
1454 EVP_Digest(pk->data, pk->length, pkey_dig, &diglen, EVP_sha1(), NULL);
1456 M_ASN1_OCTET_STRING_set(oct, pkey_dig, diglen);
1457 X509_EXTENSION *ext = X509V3_EXT_i2d(NID_subject_key_identifier, 0,
1458 oct);
1459 X509_add_ext(cert, ext, -1);
1460 X509_EXTENSION_free(ext);
1461 M_ASN1_OCTET_STRING_free(oct);
1465 void WvX509::set_aki(const WvX509 &cacert)
1467 CHECK_CERT_EXISTS_SET("aki");
1469 // can't set a meaningful AKI for subordinate certification without the
1470 // parent having an SKI
1471 ASN1_OCTET_STRING *ikeyid = NULL;
1472 X509_EXTENSION *ext;
1473 int i = X509_get_ext_by_NID(cacert.cert, NID_subject_key_identifier, -1);
1474 if ((i >= 0) && (ext = X509_get_ext(cacert.cert, i)))
1475 ikeyid = static_cast<ASN1_OCTET_STRING *>(X509V3_EXT_d2i(ext));
1477 if (!ikeyid)
1478 return;
1480 AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
1481 akeyid->issuer = NULL;
1482 akeyid->serial = NULL;
1483 akeyid->keyid = ikeyid;
1484 ext = X509V3_EXT_i2d(NID_authority_key_identifier, 0, akeyid);
1485 X509_add_ext(cert, ext, -1);
1486 X509_EXTENSION_free(ext);
1487 AUTHORITY_KEYID_free(akeyid);