OpenDDS  Snapshot(2023/04/28-20:55)
SignedDocument.cpp
Go to the documentation of this file.
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 
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 
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 
35  : original_()
36  , content_()
37  , verified_(false)
38  , filename_(default_filename)
39 {
40 }
41 
43  : original_(src)
44  , content_()
45  , verified_(false)
46  , filename_(data_filename)
47 {
48 }
49 
51 {
52 }
53 
54 bool SignedDocument::load(const std::string& uri, DDS::Security::SecurityException& ex)
55 {
56  using namespace CommonUtilities;
57 
58  original_.length(0);
59  content_.clear();
60  verified_ = false;
61  filename_ = default_filename;
62 
63  URI uri_info(uri);
64 
65  switch (uri_info.scheme) {
66  case URI::URI_FILE: {
67  load_file(uri_info.everything_else);
68  break;
69  }
70  case URI::URI_DATA:
71  original_.length(static_cast<unsigned int>(uri_info.everything_else.length() + 1));
72  std::memcpy(original_.get_buffer(), uri_info.everything_else.c_str(), uri_info.everything_else.length() + 1);
73  filename_ = data_filename;
74  break;
75 
76  case URI::URI_PKCS11:
77  case URI::URI_UNKNOWN:
78  default:
80  "(%P|%t) SSL::SignedDocument::load: WARNING: Unsupported URI scheme\n"));
81  break;
82  }
83 
84  if (original_.length() == 0) {
85  std::stringstream msg;
86  msg << "SSL::SignedDocument::load: WARNING: Failed to load document supplied "
87  "with URI '" << uri << "'";
88  set_security_error(ex, -1, 0, msg.str().c_str());
89  return false;
90  }
91 
92  return true;
93 }
94 
95 class StackOfX509 {
96 public:
98  : certs_(sk_X509_new_null())
99  {}
100 
102  {
103  if (certs_) {
104  sk_X509_free(certs_);
105  }
106  }
107 
108  STACK_OF(X509)* certs() const { return certs_; }
109  operator bool() const {return certs_;}
110 
111  bool push(const Certificate& certificate)
112  {
113  if (sk_X509_push(certs_, certificate.x509()) != 1) {
114  OPENDDS_SSL_LOG_ERR("sk_X509_push failed");
115  return false;
116  }
117 
118  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:
130  : store_(X509_STORE_new())
131  {
132  if (!store_) {
133  OPENDDS_SSL_LOG_ERR("X509_STORE_new failed");
134  }
135  }
136 
138  {
139  if (store_) {
140  X509_STORE_free(store_);
141  }
142  }
143 
144  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  Bio()
166  : bio_(0)
167  {}
168 
169  bool new_mem()
170  {
171  OPENDDS_ASSERT(!bio_);
172  bio_ = BIO_new(BIO_s_mem());
173  if (!bio_) {
174  OPENDDS_SSL_LOG_ERR("BIO_new failed");
175  return false;
176  }
177 
178  return true;
179  }
180 
182  {
183  if (bio_) {
184  BIO_free(bio_);
185  }
186  }
187 
188  BIO* bio() const { return bio_; }
189  BIO*& bio() { return bio_; }
190  operator bool() const {return bio_;}
191 
192  bool write(const void* data, int dlen)
193  {
194  if (BIO_write(bio_, data, dlen) != dlen) {
195  OPENDDS_SSL_LOG_ERR("BIO_write failed");
196  return false;
197  }
198 
199  return true;
200  }
201 
202  long get_mem_data(char **pp)
203  {
204  const long size = BIO_get_mem_data(bio_, pp);
205  if (size < 0) {
206  OPENDDS_SSL_LOG_ERR("BIO_get_mem_data failed");
207  }
208 
209  return size;
210  }
211 
212 private:
213  // No copy.
214  Bio(const Bio&);
215  BIO* bio_;
216 };
217 
218 class PKCS7Doc {
219 public:
220  PKCS7Doc(PKCS7* doc)
221  : doc_(doc)
222  {}
223 
225  {
226  if (doc_) {
227  PKCS7_free(doc_);
228  }
229  }
230 
231  operator bool() const { return doc_; }
232 
233  bool verify(const StackOfX509* certs,
234  const X509Store* store,
235  const Bio& indata,
236  const Bio& outdata,
237  int flags)
238  {
239  if (PKCS7_verify(doc_, certs ? certs->certs() : 0, store ? store->store() : 0,
240  indata.bio(), outdata.bio(), flags) != 1) {
241  OPENDDS_SSL_LOG_ERR("SMIME_read_PKCS7 failed");
242  return false;
243  }
244 
245  return true;
246  }
247 
248 private:
249  // No copy.
250  PKCS7Doc(const PKCS7Doc&);
251  PKCS7* doc_;
252 };
253 
255 {
256  content_.clear();
257  verified_ = false;
258 
259  StackOfX509 certs;
260  if (!certs) {
261  return false;
262  }
263 
264  if (!certs.push(ca)) {
265  return false;
266  }
267 
268  Bio filebuf;
269  if (!filebuf.new_mem()) {
270  return false;
271  }
272 
273  if (!filebuf.write(original_.get_buffer(), original_.length())) {
274  return false;
275  }
276 
277  Bio bcont;
278  PKCS7Doc doc(SMIME_read_PKCS7(filebuf.bio(), &bcont.bio()));
279  if (!doc) {
280  OPENDDS_SSL_LOG_ERR("SMIME_read_PKCS7 failed");
281  return false;
282  }
283 
284  Bio content;
285  if (!content.new_mem()) {
286  return false;
287  }
288 
289  if (!doc.verify(&certs, 0, bcont, content, PKCS7_TEXT | PKCS7_NOVERIFY | PKCS7_NOINTERN)) {
290  return false;
291  }
292 
293  char* p = 0;
294  long size = content.get_mem_data(&p);
295  if (size < 0) {
296  return false;
297  }
298 
299  content_ = std::string(p, size);
300  verified_ = true;
301 
302  return verified_;
303 }
304 
305 void SignedDocument::load_file(const std::string& path)
306 {
307  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  std::ifstream in(path.c_str(), std::ios::binary);
334 
335  if (!in) {
337  "(%P|%t) SignedDocument::PKCS7_from_SMIME_file:"
338  "WARNING: Failed to load file '%C'; '%m'\n",
339  path.c_str()));
340  return;
341  }
342 
343  const std::ifstream::pos_type begin = in.tellg();
344  in.seekg(0, std::ios::end);
345  const std::ifstream::pos_type end = in.tellg();
346  in.seekg(0, std::ios::beg);
347 
348  original_.length(static_cast<CORBA::ULong>(end - begin + 1));
349  in.read(reinterpret_cast<char*>(original_.get_buffer()), end - begin);
350 
351  if (!in) {
353  "(%P|%t) SignedDocument::PKCS7_from_SMIME_file:"
354  "WARNING: Failed to load file '%C'; '%m'\n",
355  path.c_str()));
356  return;
357  }
358 
359  // To appease the other DDS security implementations
360  original_[original_.length() - 1] = 0u;
361 #endif
362 }
363 
365 {
366  return original_ == other.original_ && content_ == other.content_ && verified_ == other.verified_;
367 }
368 
369 } // namespace SSL
370 } // namespace Security
371 } // namespace OpenDDS
372 
bool verify(const StackOfX509 *certs, const X509Store *store, const Bio &indata, const Bio &outdata, int flags)
bool load(const std::string &uri, DDS::Security::SecurityException &ex)
int fclose(FILE *fp)
This URI abstraction is currently naive and only separates the URI scheme on the LHS from the "everyt...
#define ACE_ERROR(X)
void * memcpy(void *t, const void *s, size_t len)
#define OPENDDS_ASSERT(C)
Definition: Definitions.h:72
FILE * fopen(const char *filename, const char *mode)
size_t fread(void *ptr, size_t size, size_t nelems, FILE *fp)
bool push(const Certificate &certificate)
bool add_cert(const Certificate &certificate)
const std::string & content() const
LM_WARNING
sequence< octet > OctetSeq
Definition: DdsDcpsCore.idl:64
void load_file(const std::string &path)
bool operator==(const SignedDocument &other) const
bool verify(const Certificate &ca)
bool write(const void *data, int dlen)
ACE_CDR::Octet Octet
#define OPENDDS_END_VERSIONED_NAMESPACE_DECL
bool set_security_error(DDS::Security::SecurityException &ex, int code, int minor_code, const char *message)
The Internal API and Implementation of OpenDDS.
Definition: AddressCache.h:28
struct x509_st X509
#define OPENDDS_SSL_LOG_ERR(MSG)
Definition: Err.h:12