Certificate.cpp

Go to the documentation of this file.
00001 /*
00002  * Distributed under the OpenDDS License.
00003  * See: http://www.OpenDDS.org/license.html
00004  */
00005 
00006 #include "Certificate.h"
00007 #include "dds/DCPS/security/CommonUtilities.h"
00008 #include "dds/DCPS/SequenceIterator.h"
00009 #include "Err.h"
00010 #include <algorithm>
00011 #include <cstring>
00012 #include <fstream>
00013 #include <iterator>
00014 #include <cerrno>
00015 #include <openssl/pem.h>
00016 #include <openssl/x509v3.h>
00017 #include "../OpenSSL_legacy.h"  // Must come after all other OpenSSL includes
00018 #include <sstream>
00019 
00020 namespace OpenDDS {
00021 namespace Security {
00022 namespace SSL {
00023   Certificate::Certificate(const std::string& uri,
00024                            const std::string& password)
00025     : x_(NULL), original_bytes_(), dsign_algo_("")
00026   {
00027     DDS::Security::SecurityException ex;
00028     if (! load(ex, uri, password)) {
00029       ACE_ERROR((LM_WARNING, "(%P|%t) %C\n", ex.message.in()));
00030     }
00031   }
00032 
00033   Certificate::Certificate(const DDS::OctetSeq& src)
00034     : x_(NULL), original_bytes_(), dsign_algo_("")
00035   {
00036     deserialize(src);
00037   }
00038 
00039   Certificate::Certificate()
00040     : x_(NULL), original_bytes_(), dsign_algo_("")
00041   {
00042 
00043   }
00044 
00045   Certificate::Certificate(const Certificate& other)
00046     : x_(NULL), original_bytes_(), dsign_algo_("")
00047   {
00048     if (0 < other.original_bytes_.length()) {
00049       deserialize(other.original_bytes_);
00050     }
00051   }
00052 
00053   Certificate::~Certificate()
00054   {
00055     if (x_) X509_free(x_);
00056   }
00057 
00058   Certificate& Certificate::operator=(const Certificate& rhs)
00059   {
00060     if (this != &rhs) {
00061       if (rhs.x_ && (0 < rhs.original_bytes_.length())) {
00062         deserialize(rhs.original_bytes_);
00063       }
00064 
00065     }
00066     return *this;
00067   }
00068 
00069   bool Certificate::load(DDS::Security::SecurityException& ex,
00070                          const std::string& uri,
00071                          const std::string& password)
00072   {
00073     using namespace CommonUtilities;
00074 
00075     if (x_) {
00076       set_security_error(ex, -1, 0, "SSL::Certificate::load: WARNING: document already loaded");
00077       return false;
00078     }
00079 
00080     URI uri_info(uri);
00081 
00082     switch (uri_info.scheme) {
00083       case URI::URI_FILE:
00084         load_cert_bytes(uri_info.everything_else);
00085         x_ = x509_from_pem(original_bytes_, password);
00086         break;
00087 
00088       case URI::URI_DATA:
00089         load_cert_data_bytes(uri_info.everything_else);
00090         x_ = x509_from_pem(original_bytes_, password);
00091         break;
00092 
00093       case URI::URI_PKCS11:
00094       case URI::URI_UNKNOWN:
00095       default:
00096         ACE_ERROR((LM_WARNING,
00097                   "(%P|%t) SSL::Certificate::load: WARNING: Unsupported URI scheme\n"));
00098 
00099         break;
00100     }
00101 
00102     if (! loaded()) {
00103       std::stringstream msg;
00104       msg << "SSL::Certificate::load: WARNING: Failed to load document supplied "
00105              "with URI '"  << uri << "'";
00106       set_security_error(ex, -1, 0, msg.str().c_str());
00107       return false;
00108     }
00109 
00110     int err = cache_dsign_algo();
00111     if (err) {
00112       set_security_error(ex, -1, 0, "SSL::Certificate::load: WARNING: Failed to cache signature algorithm");
00113       return false;
00114     }
00115     return true;
00116   }
00117 
00118   int Certificate::validate(const Certificate& ca, unsigned long int flags) const
00119   {
00120     int result = X509_V_ERR_UNSPECIFIED;
00121 
00122     if (x_) {
00123       if (ca.x_) {
00124         X509_STORE* certs = X509_STORE_new();
00125         if (certs) {
00126           X509_STORE_add_cert(certs, ca.x_);
00127 
00128           X509_STORE_CTX* validation_ctx = X509_STORE_CTX_new();
00129           if (validation_ctx) {
00130             X509_STORE_CTX_init(validation_ctx, certs, x_, NULL);
00131             X509_STORE_CTX_set_flags(validation_ctx, flags);
00132 
00133             if (X509_verify_cert(validation_ctx) == 1) {
00134               result = X509_V_OK;
00135 
00136             } else {
00137               int err = X509_STORE_CTX_get_error(validation_ctx),
00138                   depth = X509_STORE_CTX_get_error_depth(validation_ctx);
00139 
00140               ACE_ERROR((LM_WARNING,
00141                          ACE_TEXT("(%P|%t) SSL::Certificate::verify: WARNING '%C' occurred using cert at "
00142                                   "depth '%i', validation failed.\n"),
00143                          X509_verify_cert_error_string(err),
00144                          depth));
00145 
00146               result = err;
00147             }
00148 
00149             X509_STORE_CTX_free(validation_ctx);
00150           }
00151 
00152           X509_STORE_free(certs);
00153 
00154         } else {
00155           OPENDDS_SSL_LOG_ERR("failed to create X509_STORE");
00156         }
00157 
00158       } else {
00159         ACE_ERROR((LM_WARNING,
00160                    ACE_TEXT("(%P|%t) SSL::Certificate::verify: WARNING, passed-in CA has not loaded a "
00161                             "certificate\n")));
00162       }
00163 
00164     } else {
00165       ACE_ERROR((LM_WARNING,
00166                  ACE_TEXT("(%P|%t) SSL::Certificate::verify: WARNING, a certificate must be loaded before "
00167                           "it can be verified\n")));
00168     }
00169 
00170     return result;
00171   }
00172 
00173   class verify_implementation
00174   {
00175    public:
00176     explicit verify_implementation(EVP_PKEY* pkey)
00177       : public_key(pkey), md_ctx(NULL), pkey_ctx(NULL)
00178     {
00179     }
00180     ~verify_implementation() { EVP_MD_CTX_free(md_ctx); }
00181 
00182     int operator()(const DDS::OctetSeq& src,
00183                    const std::vector<const DDS::OctetSeq*>& expected_contents)
00184     {
00185       if (!public_key) return 1;
00186 
00187       int pk_id = 0;
00188       std::vector<const DDS::OctetSeq*>::const_iterator i, n;
00189 
00190       md_ctx = EVP_MD_CTX_new();
00191       if (!md_ctx) {
00192         OPENDDS_SSL_LOG_ERR("EVP_MD_CTX_new failed");
00193         return 1;
00194       }
00195 
00196       EVP_MD_CTX_init(md_ctx);
00197 
00198       if (1 != EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha256(), NULL,
00199                                     public_key)) {
00200         OPENDDS_SSL_LOG_ERR("EVP_DigestVerifyInit failed");
00201         return 1;
00202       }
00203 
00204       // Determine which signature type is being verified
00205       pk_id = EVP_PKEY_id(public_key);
00206 
00207       if (pk_id == EVP_PKEY_RSA) {
00208         if (1 !=
00209             EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
00210           OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_padding failed");
00211           return 1;
00212         }
00213 
00214         if (1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256())) {
00215           OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_mgf1_md failed");
00216           return 1;
00217         }
00218       }
00219 
00220       n = expected_contents.end();
00221       for (i = expected_contents.begin(); i != n; ++i) {
00222         if ((*i)->length() > 0) {
00223           if (1 != EVP_DigestVerifyUpdate(md_ctx, (*i)->get_buffer(),
00224                                           (*i)->length())) {
00225             OPENDDS_SSL_LOG_ERR("EVP_DigestVerifyUpdate failed");
00226             return 1;
00227           }
00228         }
00229       }
00230 
00231       int err = EVP_DigestVerifyFinal(md_ctx, src.get_buffer(), src.length());
00232       if (0 == err) {
00233         return 1;  // Verification failed, but no error occurred
00234 
00235       } else if (1 != err) {
00236         OPENDDS_SSL_LOG_ERR("EVP_DigestVerifyFinal failed");
00237         return 1;
00238       }
00239 
00240       return 0;
00241     }
00242 
00243    private:
00244     EVP_PKEY* public_key;
00245     EVP_MD_CTX* md_ctx;
00246     EVP_PKEY_CTX* pkey_ctx;
00247   };
00248 
00249   int Certificate::verify_signature(
00250     const DDS::OctetSeq& src,
00251     const std::vector<const DDS::OctetSeq*>& expected_contents) const
00252   {
00253 #ifdef OPENSSL_V_1_0
00254     struct EVP_PKEY_Handle {
00255       EVP_PKEY* pkey_;
00256       explicit EVP_PKEY_Handle(EVP_PKEY* pkey) : pkey_(pkey) {}
00257       operator EVP_PKEY*() { return pkey_; }
00258       ~EVP_PKEY_Handle() { EVP_PKEY_free(pkey_); }
00259     } pkey(X509_get_pubkey(x_));
00260 #else
00261     EVP_PKEY* pkey = X509_get0_pubkey(x_);
00262 #endif
00263     verify_implementation verify(pkey);
00264     return verify(src, expected_contents);
00265   }
00266 
00267   int Certificate::subject_name_to_str(std::string& dst,
00268                                        unsigned long flags) const
00269   {
00270     int result = 1;
00271 
00272     dst.clear();
00273 
00274     if (x_) {
00275       /* Do not free name! */
00276       X509_NAME* name = X509_get_subject_name(x_);
00277       if (name) {
00278         BIO* buffer = BIO_new(BIO_s_mem());
00279         if (buffer) {
00280           int len = X509_NAME_print_ex(buffer, name, 0, flags);
00281           if (len > 0) {
00282             std::vector<char> tmp(len +
00283                                   1);  // BIO_gets will add null hence +1
00284             len = BIO_gets(buffer, &tmp[0], len + 1);
00285             if (len > 0) {
00286               std::copy(
00287                 tmp.begin(),
00288                 tmp.end() -
00289                   1,  // But... string inserts a null so it's not needed
00290                 std::back_inserter(dst));
00291               result = 0;
00292 
00293             } else {
00294               OPENDDS_SSL_LOG_ERR("failed to write BIO to string");
00295             }
00296 
00297           } else {
00298             OPENDDS_SSL_LOG_ERR("failed to read X509_NAME into BIO buffer");
00299           }
00300 
00301           BIO_free(buffer);
00302         }
00303       }
00304     }
00305 
00306     return result;
00307   }
00308 
00309   int Certificate::subject_name_digest(std::vector<CORBA::Octet>& dst) const
00310   {
00311     dst.clear();
00312 
00313     if (!x_) return 1;
00314 
00315     /* Do not free name! */
00316     X509_NAME* name = X509_get_subject_name(x_);
00317     if (NULL == name) {
00318       OPENDDS_SSL_LOG_ERR("X509_get_subject_name failed");
00319       return 1;
00320     }
00321 
00322     std::vector<CORBA::Octet> tmp(EVP_MAX_MD_SIZE);
00323 
00324     unsigned int len = 0;
00325     if (1 != X509_NAME_digest(name, EVP_sha256(), &tmp[0], &len)) {
00326       OPENDDS_SSL_LOG_ERR("X509_NAME_digest failed");
00327       return 1;
00328     }
00329 
00330     dst.insert(dst.begin(), tmp.begin(), tmp.begin() + len);
00331 
00332     return 0;
00333   }
00334 
00335   const char* Certificate::keypair_algo() const
00336   {
00337     // This should probably be pulling the information directly from
00338     // the certificate.
00339     if (std::string("RSASSA-PSS-SHA256") == dsign_algo_) {
00340       return "RSA-2048";
00341 
00342     } else if (std::string("ECDSA-SHA256") == dsign_algo_) {
00343       return "EC-prime256v1";
00344 
00345     } else {
00346       return "UNKNOWN";
00347     }
00348   }
00349 
00350   struct cache_dsign_algo_impl
00351   {
00352     cache_dsign_algo_impl() : pkey_(NULL), rsa_(NULL), ec_(NULL) {}
00353     ~cache_dsign_algo_impl()
00354     {
00355       EVP_PKEY_free(pkey_);
00356       RSA_free(rsa_);
00357       EC_KEY_free(ec_);
00358     }
00359 
00360     int operator() (X509* cert, std::string& dst)
00361     {
00362       if (!cert) {
00363         ACE_ERROR((LM_WARNING,
00364                    "(%P|%t) SSL::Certificate::cache_dsign_algo: WARNING, failed to "
00365                    "get pubkey from X509 cert\n"));
00366         return 1;
00367       }
00368 
00369       pkey_ = X509_get_pubkey(cert);
00370       if (!pkey_) {
00371         OPENDDS_SSL_LOG_ERR("cache_dsign_algo_impl::operator(): x509_get_pubkey failed");
00372         return 1;
00373       }
00374 
00375       rsa_ = EVP_PKEY_get1_RSA(pkey_);
00376       if (rsa_) {
00377         dst = "RSASSA-PSS-SHA256";
00378         return 0;
00379       }
00380 
00381       ec_ = EVP_PKEY_get1_EC_KEY(pkey_);
00382       if (ec_) {
00383         dst = "ECDSA-SHA256";
00384         return 0;
00385       }
00386 
00387       ACE_ERROR((LM_WARNING,
00388                  "(%P|%t) SSL::Certificate::cache_dsign_algo: WARNING, only RSASSA-PSS-SHA256 or "
00389                  "ECDSA-SHA256 are currently supported signature/verification algorithms\n"));
00390 
00391       return 1;
00392     }
00393 
00394   private:
00395     EVP_PKEY* pkey_;
00396     RSA* rsa_;
00397     EC_KEY* ec_;
00398   };
00399 
00400   int Certificate::cache_dsign_algo()
00401   {
00402     return cache_dsign_algo_impl()(x_, dsign_algo_);
00403   }
00404 
00405   void Certificate::load_cert_bytes(const std::string& path)
00406   {
00407     std::ifstream in(path.c_str(), std::ios::binary);
00408 
00409     if (in.fail()) {
00410       ACE_ERROR((LM_ERROR,
00411                 "(%P|%t) Certificate::load_cert_bytes:"
00412                 "WARNING: Failed to load file '%C'; errno: '%C'\n",
00413                 path.c_str(), strerror(errno)));
00414       return;
00415     }
00416 
00417     DCPS::SequenceBackInsertIterator<DDS::OctetSeq> back_inserter(original_bytes_);
00418 
00419     std::copy((std::istreambuf_iterator<char>(in)),
00420               std::istreambuf_iterator<char>(),
00421               back_inserter);
00422 
00423     // To appease the other DDS security implementations which
00424     // append a null byte at the end of the cert.
00425     *back_inserter = 0u;
00426   }
00427 
00428   void Certificate::load_cert_data_bytes(const std::string& data)
00429   {
00430     // The minus 1 is because path contains a comma in element 0 and that
00431     // comma is not included in the cert string
00432     original_bytes_.length(data.size() - 1);
00433     std::memcpy(original_bytes_.get_buffer(), &data[1],
00434                 original_bytes_.length());
00435 
00436     // To appease the other DDS security implementations which
00437     // append a null byte at the end of the cert.
00438     original_bytes_.length(original_bytes_.length() + 1);
00439     original_bytes_[original_bytes_.length() - 1] = 0;
00440   }
00441 
00442   X509* Certificate::x509_from_pem(const std::string& path,
00443                                    const std::string& password)
00444   {
00445     X509* result = NULL;
00446 
00447     BIO* filebuf = BIO_new_file(path.c_str(), "r");
00448     if (filebuf) {
00449       if (password != "") {
00450         result =
00451           PEM_read_bio_X509_AUX(filebuf, NULL, NULL, (void*)password.c_str());
00452         if (!result) {
00453           OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
00454         }
00455 
00456       } else {
00457         result = PEM_read_bio_X509_AUX(filebuf, NULL, NULL, NULL);
00458         if (!result) {
00459           OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
00460         }
00461       }
00462 
00463       BIO_free(filebuf);
00464 
00465     } else {
00466       std::stringstream errmsg;
00467       errmsg << "failed to read file '" << path << "' using BIO_new_file";
00468       OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
00469     }
00470 
00471     return result;
00472   }
00473 
00474   X509* Certificate::x509_from_pem(const DDS::OctetSeq& bytes,
00475                                    const std::string& password)
00476   {
00477     X509* result = NULL;
00478 
00479     BIO* filebuf = BIO_new(BIO_s_mem());
00480     do {
00481       if (filebuf) {
00482         if (0 >= BIO_write(filebuf, bytes.get_buffer(), bytes.length())) {
00483           OPENDDS_SSL_LOG_ERR("BIO_write failed");
00484           break;
00485         }
00486         if (password != "") {
00487           result = PEM_read_bio_X509_AUX(filebuf, NULL, NULL,
00488                                          (void*)password.c_str());
00489           if (!result) {
00490             OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
00491             break;
00492           }
00493 
00494         } else {
00495           result = PEM_read_bio_X509_AUX(filebuf, NULL, NULL, NULL);
00496           if (!result) {
00497             OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
00498             break;
00499           }
00500         }
00501 
00502       } else {
00503         std::stringstream errmsg;
00504         errmsg << "BIO_new failed";
00505         OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
00506         break;
00507       }
00508 
00509     } while (0);
00510 
00511     BIO_free(filebuf);
00512 
00513     return result;
00514   }
00515 
00516   int Certificate::serialize(DDS::OctetSeq& dst) const
00517   {
00518     std::copy(DCPS::const_sequence_begin(original_bytes_),
00519               DCPS::const_sequence_end(original_bytes_),
00520               DCPS::back_inserter(dst));
00521 
00522     if (dst.length() == original_bytes_.length()) {
00523       return 0;
00524     }
00525 
00526     return 1;
00527   }
00528 
00529   struct deserialize_impl
00530   {
00531     explicit deserialize_impl(const DDS::OctetSeq& src)
00532       : src_(src), buffer_(BIO_new(BIO_s_mem()))
00533     {}
00534 
00535     ~deserialize_impl()
00536     {
00537       BIO_free(buffer_);
00538     }
00539 
00540     int operator() (X509*& dst)
00541     {
00542       if (dst) {
00543         ACE_ERROR((LM_WARNING,
00544                    "(%P|%t) SSL::Certificate::deserialize: WARNING, an X509 certificate "
00545                    "has already been loaded\n"));
00546         return 1;
00547       }
00548 
00549       if (0 == src_.length()) {
00550         ACE_ERROR((LM_WARNING,
00551                    "(%P|%t) SSL::Certificate::deserialize: WARNING, source OctetSeq contains no data"));
00552         return 1;
00553       }
00554 
00555       if (! buffer_) {
00556         OPENDDS_SSL_LOG_ERR("failed to allocate buffer with BIO_new");
00557         return 1;
00558       }
00559 
00560       int len = BIO_write(buffer_, src_.get_buffer(), src_.length());
00561       if (len <= 0) {
00562         OPENDDS_SSL_LOG_ERR("failed to write OctetSeq to BIO");
00563         return 1;
00564       }
00565 
00566       dst = PEM_read_bio_X509_AUX(buffer_, NULL, NULL, NULL);
00567       if (! dst) {
00568         OPENDDS_SSL_LOG_ERR("failed to read X509 from BIO");
00569         return 1;
00570       }
00571 
00572       return 0;
00573     }
00574 
00575   private:
00576     const DDS::OctetSeq& src_;
00577     BIO* buffer_;
00578   };
00579 
00580   int Certificate::deserialize(const DDS::OctetSeq& src)
00581   {
00582     int err = deserialize_impl(src)(x_) || cache_dsign_algo();
00583     if (! err) {
00584       original_bytes_ = src;
00585     }
00586 
00587     return err;
00588   }
00589 
00590   std::ostream& operator<<(std::ostream& lhs, const Certificate& rhs)
00591   {
00592     if (rhs.x_) {
00593       lhs << "Certificate: { is_ca? '"
00594           << (X509_check_ca(rhs.x_) ? "yes" : "no") << "'; }";
00595 
00596     } else {
00597       lhs << "NULL";
00598     }
00599     return lhs;
00600   }
00601 
00602   bool operator==(const Certificate& lhs, const Certificate& rhs)
00603   {
00604     if (lhs.x_ && rhs.x_) {
00605       return (0 == X509_cmp(lhs.x_, rhs.x_)) &&
00606              (lhs.original_bytes_ == rhs.original_bytes_);
00607     }
00608     return (lhs.x_ == rhs.x_) &&
00609            (lhs.original_bytes_ == rhs.original_bytes_);
00610   }
00611 }  // namespace SSL
00612 }  // namespace Security
00613 }  // namespace OpenDDS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated on 10 Aug 2018 for OpenDDS by  doxygen 1.6.1