LCOV - code coverage report
Current view: top level - DCPS/security/SSL - SignedDocument.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 109 145 75.2 %
Date: 2023-04-30 01:32:43 Functions: 23 25 92.0 %

          Line data    Source code
       1             : /*
       2             :  * Distributed under the OpenDDS License.
       3             :  * See: http://www.OpenDDS.org/license.html
       4             :  */
       5             : 
       6             : #include "SignedDocument.h"
       7             : 
       8             : #include "Err.h"
       9             : 
      10             : #include <dds/DCPS/security/CommonUtilities.h>
      11             : 
      12             : #include <dds/DCPS/Definitions.h>
      13             : 
      14             : #include <openssl/pem.h>
      15             : #include <openssl/x509.h>
      16             : 
      17             : #include <cstring>
      18             : #include <sstream>
      19             : #include <iterator>
      20             : #include <algorithm>
      21             : #include <fstream>
      22             : 
      23             : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
      24             : 
      25             : namespace OpenDDS {
      26             : namespace Security {
      27             : namespace SSL {
      28             : 
      29             : namespace {
      30             :   const char* const default_filename = "<no filename: not loaded>";
      31             :   const char* const data_filename = "<no filename: data uri>";
      32             : }
      33             : 
      34          52 : SignedDocument::SignedDocument()
      35          52 :   : original_()
      36          52 :   , content_()
      37          52 :   , verified_(false)
      38         104 :   , filename_(default_filename)
      39             : {
      40          52 : }
      41             : 
      42          11 : SignedDocument::SignedDocument(const DDS::OctetSeq& src)
      43          11 :   : original_(src)
      44          11 :   , content_()
      45          11 :   , verified_(false)
      46          22 :   , filename_(data_filename)
      47             : {
      48          11 : }
      49             : 
      50          65 : SignedDocument::~SignedDocument()
      51             : {
      52          65 : }
      53             : 
      54          51 : bool SignedDocument::load(const std::string& uri, DDS::Security::SecurityException& ex)
      55             : {
      56             :   using namespace CommonUtilities;
      57             : 
      58          51 :   original_.length(0);
      59          51 :   content_.clear();
      60          51 :   verified_ = false;
      61          51 :   filename_ = default_filename;
      62             : 
      63          51 :   URI uri_info(uri);
      64             : 
      65          51 :   switch (uri_info.scheme) {
      66          51 :   case URI::URI_FILE: {
      67          51 :     load_file(uri_info.everything_else);
      68          51 :     break;
      69             :   }
      70           0 :   case URI::URI_DATA:
      71           0 :     original_.length(static_cast<unsigned int>(uri_info.everything_else.length() + 1));
      72           0 :     std::memcpy(original_.get_buffer(), uri_info.everything_else.c_str(), uri_info.everything_else.length() + 1);
      73           0 :     filename_ = data_filename;
      74           0 :     break;
      75             : 
      76           0 :   case URI::URI_PKCS11:
      77             :   case URI::URI_UNKNOWN:
      78             :   default:
      79           0 :     ACE_ERROR((LM_WARNING,
      80             :                "(%P|%t) SSL::SignedDocument::load: WARNING: Unsupported URI scheme\n"));
      81           0 :     break;
      82             :   }
      83             : 
      84          51 :   if (original_.length() == 0) {
      85           0 :     std::stringstream msg;
      86             :     msg << "SSL::SignedDocument::load: WARNING: Failed to load document supplied "
      87           0 :       "with URI '"  << uri << "'";
      88           0 :     set_security_error(ex, -1, 0, msg.str().c_str());
      89           0 :     return false;
      90           0 :   }
      91             : 
      92          51 :   return true;
      93          51 : }
      94             : 
      95             : class StackOfX509 {
      96             : public:
      97          52 :   StackOfX509()
      98          52 :     : certs_(sk_X509_new_null())
      99          52 :   {}
     100             : 
     101          52 :   ~StackOfX509()
     102             :   {
     103          52 :     if (certs_) {
     104          52 :       sk_X509_free(certs_);
     105             :     }
     106          52 :   }
     107             : 
     108          52 :   STACK_OF(X509)* certs() const { return certs_; }
     109          52 :   operator bool() const {return certs_;}
     110             : 
     111          52 :   bool push(const Certificate& certificate)
     112             :   {
     113          52 :     if (sk_X509_push(certs_, certificate.x509()) != 1) {
     114           0 :       OPENDDS_SSL_LOG_ERR("sk_X509_push failed");
     115           0 :       return false;
     116             :     }
     117             : 
     118          52 :     return true;
     119             :   }
     120             : 
     121             : private:
     122             :   // No copy.
     123             :   StackOfX509(const StackOfX509&);
     124             :   STACK_OF(X509)* certs_;
     125             : };
     126             : 
     127             : class X509Store {
     128             : public:
     129             :   X509Store()
     130             :     : store_(X509_STORE_new())
     131             :   {
     132             :     if (!store_) {
     133             :       OPENDDS_SSL_LOG_ERR("X509_STORE_new failed");
     134             :     }
     135             :   }
     136             : 
     137             :   ~X509Store()
     138             :   {
     139             :     if (store_) {
     140             :       X509_STORE_free(store_);
     141             :     }
     142             :   }
     143             : 
     144           0 :   X509_STORE* store() const { return store_; }
     145             :   operator bool() const {return store_;}
     146             : 
     147             :   bool add_cert(const Certificate& certificate)
     148             :   {
     149             :     if (X509_STORE_add_cert(store_, certificate.x509()) != 1) {
     150             :       OPENDDS_SSL_LOG_ERR("X509_STORE_add_cert failed");
     151             :       return false;
     152             :     }
     153             : 
     154             :     return true;
     155             :   }
     156             : 
     157             : private:
     158             :   // No copy.
     159             :   X509Store(const X509Store&);
     160             :   X509_STORE* store_;
     161             : };
     162             : 
     163             : class Bio {
     164             : public:
     165         156 :   Bio()
     166         156 :     : bio_(0)
     167         156 :   {}
     168             : 
     169         104 :   bool new_mem()
     170             :   {
     171         104 :     OPENDDS_ASSERT(!bio_);
     172         104 :     bio_ = BIO_new(BIO_s_mem());
     173         104 :     if (!bio_) {
     174           0 :       OPENDDS_SSL_LOG_ERR("BIO_new failed");
     175           0 :       return false;
     176             :     }
     177             : 
     178         104 :     return true;
     179             :   }
     180             : 
     181         156 :   ~Bio()
     182             :   {
     183         156 :     if (bio_) {
     184         156 :       BIO_free(bio_);
     185             :     }
     186         156 :   }
     187             : 
     188         104 :   BIO* bio() const { return bio_; }
     189         104 :   BIO*& bio() { return bio_; }
     190             :   operator bool() const {return bio_;}
     191             : 
     192          52 :   bool write(const void* data, int dlen)
     193             :   {
     194          52 :     if (BIO_write(bio_, data, dlen) != dlen) {
     195           0 :       OPENDDS_SSL_LOG_ERR("BIO_write failed");
     196           0 :       return false;
     197             :     }
     198             : 
     199          52 :     return true;
     200             :   }
     201             : 
     202          52 :   long get_mem_data(char **pp)
     203             :   {
     204          52 :     const long size = BIO_get_mem_data(bio_, pp);
     205          52 :     if (size < 0) {
     206           0 :       OPENDDS_SSL_LOG_ERR("BIO_get_mem_data failed");
     207             :     }
     208             : 
     209          52 :     return size;
     210             :   }
     211             : 
     212             : private:
     213             :   // No copy.
     214             :   Bio(const Bio&);
     215             :   BIO* bio_;
     216             : };
     217             : 
     218             : class PKCS7Doc {
     219             : public:
     220          52 :   PKCS7Doc(PKCS7* doc)
     221          52 :     : doc_(doc)
     222          52 :   {}
     223             : 
     224          52 :   ~PKCS7Doc()
     225             :   {
     226          52 :     if (doc_) {
     227          52 :       PKCS7_free(doc_);
     228             :     }
     229          52 :   }
     230             : 
     231          52 :   operator bool() const { return doc_; }
     232             : 
     233          52 :   bool verify(const StackOfX509* certs,
     234             :               const X509Store* store,
     235             :               const Bio& indata,
     236             :               const Bio& outdata,
     237             :               int flags)
     238             :   {
     239          52 :     if (PKCS7_verify(doc_, certs ? certs->certs() : 0, store ? store->store() : 0,
     240          52 :                      indata.bio(), outdata.bio(), flags) != 1) {
     241           0 :       OPENDDS_SSL_LOG_ERR("SMIME_read_PKCS7 failed");
     242           0 :       return false;
     243             :     }
     244             : 
     245          52 :     return true;
     246             :   }
     247             : 
     248             : private:
     249             :   // No copy.
     250             :   PKCS7Doc(const PKCS7Doc&);
     251             :   PKCS7* doc_;
     252             : };
     253             : 
     254          52 : bool SignedDocument::verify(const Certificate& ca)
     255             : {
     256          52 :   content_.clear();
     257          52 :   verified_ = false;
     258             : 
     259          52 :   StackOfX509 certs;
     260          52 :   if (!certs) {
     261           0 :     return false;
     262             :   }
     263             : 
     264          52 :   if (!certs.push(ca)) {
     265           0 :     return false;
     266             :   }
     267             : 
     268          52 :   Bio filebuf;
     269          52 :   if (!filebuf.new_mem()) {
     270           0 :     return false;
     271             :   }
     272             : 
     273          52 :   if (!filebuf.write(original_.get_buffer(), original_.length())) {
     274           0 :     return false;
     275             :   }
     276             : 
     277          52 :   Bio bcont;
     278          52 :   PKCS7Doc doc(SMIME_read_PKCS7(filebuf.bio(), &bcont.bio()));
     279          52 :   if (!doc) {
     280           0 :     OPENDDS_SSL_LOG_ERR("SMIME_read_PKCS7 failed");
     281           0 :     return false;
     282             :   }
     283             : 
     284          52 :   Bio content;
     285          52 :   if (!content.new_mem()) {
     286           0 :     return false;
     287             :   }
     288             : 
     289          52 :   if (!doc.verify(&certs, 0, bcont, content, PKCS7_TEXT | PKCS7_NOVERIFY | PKCS7_NOINTERN)) {
     290           0 :     return false;
     291             :   }
     292             : 
     293          52 :   char* p = 0;
     294          52 :   long size = content.get_mem_data(&p);
     295          52 :   if (size < 0) {
     296           0 :     return false;
     297             :   }
     298             : 
     299          52 :   content_ = std::string(p, size);
     300          52 :   verified_ = true;
     301             : 
     302          52 :   return verified_;
     303          52 : }
     304             : 
     305          51 : void SignedDocument::load_file(const std::string& path)
     306             : {
     307          51 :   filename_ = path;
     308             : 
     309             : #ifdef ACE_ANDROID
     310             :   CORBA::Octet *buffer;
     311             : 
     312             :   char b[1024];
     313             :   FILE* fp = ACE_OS::fopen(path.c_str(), "rb");
     314             : 
     315             :   int n;
     316             :   int i = 0;
     317             :   while (!feof(fp)) {
     318             :     n = ACE_OS::fread(&b, 1, 1024, fp);
     319             :     i += n;
     320             : 
     321             :     original_.length(i + 1); // +1 for null byte at end of cert
     322             :     buffer = original_.get_buffer();
     323             :     ACE_OS::memcpy(buffer + i - n, b, n);
     324             :   }
     325             : 
     326             :   ACE_OS::fclose(fp);
     327             : 
     328             :   // To appease the other DDS security implementations which
     329             :   // append a null byte at the end of the cert.
     330             :   buffer[i + 1] = 0u;
     331             : 
     332             : #else
     333          51 :   std::ifstream in(path.c_str(), std::ios::binary);
     334             : 
     335          51 :   if (!in) {
     336           0 :     ACE_ERROR((LM_WARNING,
     337             :                "(%P|%t) SignedDocument::PKCS7_from_SMIME_file:"
     338             :                "WARNING: Failed to load file '%C'; '%m'\n",
     339             :                path.c_str()));
     340           0 :     return;
     341             :   }
     342             : 
     343          51 :   const std::ifstream::pos_type begin = in.tellg();
     344          51 :   in.seekg(0, std::ios::end);
     345          51 :   const std::ifstream::pos_type end = in.tellg();
     346          51 :   in.seekg(0, std::ios::beg);
     347             : 
     348          51 :   original_.length(static_cast<CORBA::ULong>(end - begin + 1));
     349          51 :   in.read(reinterpret_cast<char*>(original_.get_buffer()), end - begin);
     350             : 
     351          51 :   if (!in) {
     352           0 :     ACE_ERROR((LM_WARNING,
     353             :                "(%P|%t) SignedDocument::PKCS7_from_SMIME_file:"
     354             :                "WARNING: Failed to load file '%C'; '%m'\n",
     355             :                path.c_str()));
     356           0 :     return;
     357             :   }
     358             : 
     359             :   // To appease the other DDS security implementations
     360          51 :   original_[original_.length() - 1] = 0u;
     361             : #endif
     362          51 : }
     363             : 
     364           6 : bool SignedDocument::operator==(const SignedDocument& other) const
     365             : {
     366           6 :   return original_ == other.original_ && content_ == other.content_ && verified_ == other.verified_;
     367             : }
     368             : 
     369             : }  // namespace SSL
     370             : }  // namespace Security
     371             : }  // namespace OpenDDS
     372             : 
     373             : OPENDDS_END_VERSIONED_NAMESPACE_DECL

Generated by: LCOV version 1.16