diff options
Diffstat (limited to 'cpp/src/IceSSL/OpenSSLCertificateI.cpp')
-rw-r--r-- | cpp/src/IceSSL/OpenSSLCertificateI.cpp | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/cpp/src/IceSSL/OpenSSLCertificateI.cpp b/cpp/src/IceSSL/OpenSSLCertificateI.cpp new file mode 100644 index 00000000000..bbee2fdd0ca --- /dev/null +++ b/cpp/src/IceSSL/OpenSSLCertificateI.cpp @@ -0,0 +1,572 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceSSL/Plugin.h> +#include <IceSSL/OpenSSL.h> +#include <IceSSL/CertificateI.h> +#include <IceSSL/OpenSSLUtil.h> +#include <IceSSL/RFC2253.h> + +#include <IceUtil/Mutex.h> +#include <IceUtil/MutexPtrLock.h> + +#include <openssl/x509v3.h> +#include <openssl/pem.h> + +using namespace IceSSL; +using namespace std; + +// +// Avoid old style cast warnings from OpenSSL macros +// +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + +#ifdef __SUNPRO_CC + +// +// The call to sk_GENERAL_NAME_pop_free fails to compile if we don't +// remove the extern "C" vs non extern "C" check with the macro below: +// + +extern "C" typedef void (*FreeFunc)(void*); + +#undef CHECKED_SK_FREE_FUNC +#define CHECKED_SK_FREE_FUNC(type, p) \ + (FreeFunc) (p) + +#endif + +namespace +{ + +static string +convertX509NameToString(X509_name_st* name) +{ + BIO* out = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(out, name, 0, XN_FLAG_RFC2253); + BUF_MEM* p; + BIO_get_mem_ptr(out, &p); + string result = string(p->data, p->length); + BIO_free(out); + return result; +} + +static vector<pair<int, string> > +convertGeneralNames(GENERAL_NAMES* gens) +{ + vector<pair<int, string> > alt; + if (gens == 0) + { + return alt; + } + for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) + { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); + pair<int, string> p; + p.first = gen->type; + switch (gen->type) + { + case GEN_EMAIL: + { + ASN1_IA5STRING* str = gen->d.rfc822Name; + if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) + { + p.second = string(reinterpret_cast<const char*>(str->data), str->length); + } + break; + } + case GEN_DNS: + { + ASN1_IA5STRING* str = gen->d.dNSName; + if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) + { + p.second = string(reinterpret_cast<const char*>(str->data), str->length); + } + break; + } + case GEN_DIRNAME: + { + p.second = convertX509NameToString(gen->d.directoryName); + break; + } + case GEN_URI: + { + ASN1_IA5STRING* str = gen->d.uniformResourceIdentifier; + if (str && str->type == V_ASN1_IA5STRING && str->data && str->length > 0) + { + p.second = string(reinterpret_cast<const char*>(str->data), str->length); + } + break; + } + case GEN_IPADD: + { + ASN1_OCTET_STRING* addr = gen->d.iPAddress; + // TODO: Support IPv6 someday. + if (addr && addr->type == V_ASN1_OCTET_STRING && addr->data && addr->length == 4) + { + ostringstream ostr; + for (int j = 0; j < 4; ++j) + { + if (j > 0) + { + ostr << '.'; + } + ostr << static_cast<int>(addr->data[j]); + } + p.second = ostr.str(); + } + break; + } + case GEN_OTHERNAME: + case GEN_EDIPARTY: + case GEN_X400: + case GEN_RID: + { + // + // TODO: These types are not supported. If the user wants + // them, they have to get at the certificate data. Another + // alternative is to DER encode the data (as the Java + // certificate does). + // + break; + } + } + alt.push_back(p); + } + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return alt; +} + +class DistinguishedNameI : public IceSSL::DistinguishedName +{ +public: + + DistinguishedNameI(X509_name_st* name) : + IceSSL::DistinguishedName(IceSSL::RFC2253::parseStrict(convertX509NameToString(name))) + { + unescape(); + } + +}; + +IceUtil::Mutex* mut = 0; + +class Init +{ +public: + + Init() + { + mut = new IceUtil::Mutex; + } + + ~Init() + { + delete mut; + mut = 0; + } +}; + +Init init; + +#ifdef ICE_CPP11_MAPPING +chrono::system_clock::time_point +#else +static IceUtil::Time +#endif +ASMUtcTimeToTime(const ASN1_UTCTIME* s) +{ + struct tm tm; + int offset; + + memset(&tm, '\0', sizeof tm); + +# define g2(p) (((p)[0]-'0')*10+(p)[1]-'0') + tm.tm_year = g2(s->data); + if(tm.tm_year < 50) + { + tm.tm_year += 100; + } + tm.tm_mon = g2(s->data + 2) - 1; + tm.tm_mday = g2(s->data + 4); + tm.tm_hour = g2(s->data + 6); + tm.tm_min = g2(s->data + 8); + tm.tm_sec = g2(s->data + 10); + if(s->data[12] == 'Z') + { + offset = 0; + } + else + { + offset = g2(s->data + 13) * 60 + g2(s->data + 15); + if(s->data[12] == '-') + { + offset = -offset; + } + } +# undef g2 + + // + // If timegm was on all systems this code could be + // return IceUtil::Time::seconds(timegm(&tm) - offset*60); + // + // Windows doesn't support the re-entrant _r versions. + // +#if defined(_MSC_VER) +# pragma warning(disable:4996) // localtime is depercated +#endif + time_t tzone; + { + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(mut); + time_t now = time(0); + tzone = mktime(localtime(&now)) - mktime(gmtime(&now)); + } +#if defined(_MSC_VER) +# pragma warning(default:4996) // localtime is depercated +#endif + + IceUtil::Time time = IceUtil::Time::seconds(mktime(&tm) - offset * 60 + tzone); + +#ifdef ICE_CPP11_MAPPING + return chrono::system_clock::time_point(chrono::microseconds(time.toMicroSeconds())); +#else + return time; +#endif +} + +class OpenSSLX509ExtensionI : public IceSSL::X509Extension +{ + +public: + + OpenSSLX509ExtensionI(struct X509_extension_st*, const string&, const IceSSL::OpenSSL::CertificatePtr&); + virtual bool isCritical() const; + virtual string getOID() const; + virtual vector<Ice::Byte> getData() const; + +private: + + struct X509_extension_st* _extension; + string _oid; + IceSSL::OpenSSL::CertificatePtr _cert; +}; + +class OpenSSLCertificateI : public IceSSL::OpenSSL::Certificate, + public CertificateI, + public IceUtil::Mutex +{ +public: + + OpenSSLCertificateI(x509_st*); + ~OpenSSLCertificateI(); + + virtual bool operator==(const IceSSL::Certificate&) const; + + virtual vector<Ice::Byte> getAuthorityKeyIdentifier() const; + virtual vector<Ice::Byte> getSubjectKeyIdentifier() const; + virtual bool verify(const IceSSL::CertificatePtr&) const; + virtual string encode() const; + +# ifdef ICE_CPP11_MAPPING + virtual chrono::system_clock::time_point getNotAfter() const; + virtual chrono::system_clock::time_point getNotBefore() const; +# else + virtual IceUtil::Time getNotAfter() const; + virtual IceUtil::Time getNotBefore() const; +# endif + virtual string getSerialNumber() const; + virtual IceSSL::DistinguishedName getIssuerDN() const; + virtual vector<pair<int, string> > getIssuerAlternativeNames() const; + virtual IceSSL::DistinguishedName getSubjectDN() const; + virtual vector<pair<int, string> > getSubjectAlternativeNames() const; + virtual int getVersion() const; + virtual x509_st* getCert() const; + +protected: + + virtual void loadX509Extensions() const; + +private: + + x509_st* _cert; +}; + +} // end anonymous namespace + +OpenSSLX509ExtensionI::OpenSSLX509ExtensionI(struct X509_extension_st* extension, const string& oid, + const IceSSL::OpenSSL::CertificatePtr& cert) : + _extension(extension), + _oid(oid), + _cert(cert) +{ +} + +bool +OpenSSLX509ExtensionI::isCritical() const +{ + return X509_EXTENSION_get_critical(_extension) == 1; +} + +string +OpenSSLX509ExtensionI::getOID() const +{ + return _oid; +} + +vector<Ice::Byte> +OpenSSLX509ExtensionI::getData() const +{ + vector<Ice::Byte> data; + ASN1_OCTET_STRING* buffer = X509_EXTENSION_get_data(_extension); + assert(buffer); + data.resize(buffer->length); + memcpy(&data[0], buffer->data, buffer->length); + return data; +} + +// +// The caller is responsible for incrementing the reference count. +// +OpenSSLCertificateI::OpenSSLCertificateI(x509_st* cert) : _cert(cert) +{ + if(!_cert) + { + throw IceUtil::IllegalArgumentException(__FILE__, __LINE__, "Invalid certificate reference"); + } +} + +OpenSSLCertificateI::~OpenSSLCertificateI() +{ + if(_cert) + { + X509_free(_cert); + } +} + +bool +OpenSSLCertificateI::operator==(const IceSSL::Certificate& r) const +{ + const OpenSSLCertificateI* p = dynamic_cast<const OpenSSLCertificateI*>(&r); + if(!p) + { + return false; + } + + return X509_cmp(_cert, p->_cert) == 0; +} + +vector<Ice::Byte> +OpenSSLCertificateI::getAuthorityKeyIdentifier() const +{ + vector<Ice::Byte> keyid; + int index = X509_get_ext_by_NID(_cert, NID_authority_key_identifier, -1); + if(index >= 0) + { + X509_EXTENSION* ext = X509_get_ext(_cert, index); + if(ext) + { + AUTHORITY_KEYID* decoded = (AUTHORITY_KEYID*)X509V3_EXT_d2i(ext); + if(!decoded) + { + throw IceSSL::CertificateEncodingException(__FILE__, __LINE__, "the extension could not be decoded"); + } + keyid.resize(decoded->keyid->length); + memcpy(&keyid[0], decoded->keyid->data, decoded->keyid->length); + AUTHORITY_KEYID_free(decoded); + } + } + return keyid; +} + +vector<Ice::Byte> +OpenSSLCertificateI::getSubjectKeyIdentifier() const +{ + vector<Ice::Byte> keyid; + int index = X509_get_ext_by_NID(_cert, NID_subject_key_identifier, -1); + if(index >= 0) + { + X509_EXTENSION* ext = X509_get_ext(_cert, index); + if(ext) + { + ASN1_OCTET_STRING* decoded = static_cast<ASN1_OCTET_STRING*>(X509V3_EXT_d2i(ext)); + if(!decoded) + { + throw IceSSL::CertificateEncodingException(__FILE__, __LINE__, "the extension could not be decoded"); + } + keyid.resize(decoded->length); + memcpy(&keyid[0], decoded->data, decoded->length); + ASN1_OCTET_STRING_free(decoded); + } + } + return keyid; +} + +bool +OpenSSLCertificateI::verify(const IceSSL::CertificatePtr& cert) const +{ + OpenSSLCertificateI* c = dynamic_cast<OpenSSLCertificateI*>(cert.get()); + if(c) + { + return X509_verify(_cert, X509_get_pubkey(c->_cert)) > 0; + } + return false; +} + +string +OpenSSLCertificateI::encode() const +{ + BIO* out = BIO_new(BIO_s_mem()); + int i = PEM_write_bio_X509_AUX(out, _cert); + if(i <= 0) + { + BIO_free(out); + throw IceSSL::CertificateEncodingException(__FILE__, __LINE__, IceSSL::OpenSSL::getSslErrors(false)); + } + BUF_MEM* p; + BIO_get_mem_ptr(out, &p); + string result = string(p->data, p->length); + BIO_free(out); + return result; +} + +# ifdef ICE_CPP11_MAPPING +chrono::system_clock::time_point +# else +IceUtil::Time +# endif +OpenSSLCertificateI::getNotAfter() const +{ + return ASMUtcTimeToTime(X509_get_notAfter(_cert)); +} + +# ifdef ICE_CPP11_MAPPING +chrono::system_clock::time_point +# else +IceUtil::Time +# endif +OpenSSLCertificateI::getNotBefore() const +{ + return ASMUtcTimeToTime(X509_get_notBefore(_cert)); +} + +string +OpenSSLCertificateI::getSerialNumber() const +{ + BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(_cert), 0); + char* dec = BN_bn2dec(bn); + string result = dec; + OPENSSL_free(dec); + BN_free(bn); + return result; +} + +IceSSL::DistinguishedName +OpenSSLCertificateI::getIssuerDN() const +{ + return IceSSL::DistinguishedName(IceSSL::RFC2253::parseStrict(convertX509NameToString(X509_get_issuer_name(_cert)))); +} + +vector<pair<int, string> > +OpenSSLCertificateI::getIssuerAlternativeNames() const +{ + return convertGeneralNames(reinterpret_cast<GENERAL_NAMES*>(X509_get_ext_d2i(_cert, NID_issuer_alt_name, 0, 0))); +} + +IceSSL::DistinguishedName +OpenSSLCertificateI::getSubjectDN() const +{ + return IceSSL::DistinguishedName(IceSSL::RFC2253::parseStrict(convertX509NameToString(X509_get_subject_name(_cert)))); +} + +vector<pair<int, string> > +OpenSSLCertificateI::getSubjectAlternativeNames() const +{ + return convertGeneralNames(reinterpret_cast<GENERAL_NAMES*>(X509_get_ext_d2i(_cert, NID_subject_alt_name, 0, 0))); +} + +int +OpenSSLCertificateI::getVersion() const +{ + return static_cast<int>(X509_get_version(_cert)); +} + +x509_st* +OpenSSLCertificateI::getCert() const +{ + return _cert; +} + +void +OpenSSLCertificateI::loadX509Extensions() const +{ + IceUtil::Mutex::Lock sync(*this); + if(_extensions.empty()) + { + int sz = X509_get_ext_count(_cert); + for(int i = 0; i < sz; i++) + { + X509_EXTENSION* ext = X509_get_ext(_cert, i); + ASN1_OBJECT* obj = X509_EXTENSION_get_object(ext); + string oid; + // + // According to OBJ_obj2txt doc a buffer of length 80 should be more than enough to + // handle any OID encountered in practice. + // + int len = 80; + oid.resize(len); + len = OBJ_obj2txt(&oid[0], len, obj, 1); + oid.resize(len); + _extensions.push_back(ICE_DYNAMIC_CAST(IceSSL::X509Extension, + ICE_MAKE_SHARED(OpenSSLX509ExtensionI, ext, oid, + ICE_DYNAMIC_CAST(IceSSL::OpenSSL::Certificate, ICE_SHARED_FROM_CONST_THIS(OpenSSLCertificateI))))); + } + } +} + +IceSSL::OpenSSL::CertificatePtr +IceSSL::OpenSSL::Certificate::create(x509_st* cert) +{ + return ICE_MAKE_SHARED(OpenSSLCertificateI, cert); +} + +IceSSL::OpenSSL::CertificatePtr +IceSSL::OpenSSL::Certificate::load(const std::string& file) +{ + BIO* cert = BIO_new(BIO_s_file()); + if(BIO_read_filename(cert, file.c_str()) <= 0) + { + BIO_free(cert); + throw CertificateReadException(__FILE__, __LINE__, "error opening file"); + } + + x509_st* x = PEM_read_bio_X509_AUX(cert, ICE_NULLPTR, ICE_NULLPTR, ICE_NULLPTR); + if(x == ICE_NULLPTR) + { + BIO_free(cert); + throw CertificateReadException(__FILE__, __LINE__, "error reading file:\n" + getSslErrors(false)); + } + BIO_free(cert); + return ICE_MAKE_SHARED(OpenSSLCertificateI, x); +} + +IceSSL::OpenSSL::CertificatePtr +IceSSL::OpenSSL::Certificate::decode(const std::string& encoding) +{ + BIO *cert = BIO_new_mem_buf(static_cast<void*>(const_cast<char*>(&encoding[0])), static_cast<int>(encoding.size())); + x509_st* x = PEM_read_bio_X509_AUX(cert, ICE_NULLPTR, ICE_NULLPTR, ICE_NULLPTR); + if(x == ICE_NULLPTR) + { + BIO_free(cert); + throw CertificateEncodingException(__FILE__, __LINE__, getSslErrors(false)); + } + BIO_free(cert); + return ICE_MAKE_SHARED(OpenSSLCertificateI, x); +} |