Line data Source code
1 : /*
2 : * Distributed under the OpenDDS License.
3 : * See: http://www.OpenDDS.org/license.html
4 : */
5 :
6 : #include "Certificate.h"
7 : #include "dds/DCPS/security/CommonUtilities.h"
8 : #include "Err.h"
9 : #include <algorithm>
10 : #include <cstring>
11 : #include <fstream>
12 : #include <iterator>
13 : #include <cerrno>
14 : #include <openssl/pem.h>
15 : #include <openssl/x509v3.h>
16 : #include "../OpenSSL_legacy.h" // Must come after all other OpenSSL includes
17 : #include <sstream>
18 :
19 : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
20 :
21 : namespace OpenDDS {
22 : namespace Security {
23 : namespace SSL {
24 :
25 220 : Certificate::Certificate(const std::string& uri,
26 220 : const std::string& password)
27 220 : : x_(0), original_bytes_(), dsign_algo_("")
28 : {
29 220 : DDS::Security::SecurityException ex;
30 220 : if (! load(ex, uri, password)) {
31 1 : ACE_ERROR((LM_WARNING, "(%P|%t) %C\n", ex.message.in()));
32 : }
33 220 : }
34 :
35 6 : Certificate::Certificate(const DDS::OctetSeq& src)
36 6 : : x_(0), original_bytes_(), dsign_algo_("")
37 : {
38 6 : deserialize(src);
39 6 : }
40 :
41 20 : Certificate::Certificate()
42 20 : : x_(0), original_bytes_(), dsign_algo_("")
43 : {
44 20 : }
45 :
46 2 : Certificate::Certificate(const Certificate& other)
47 2 : : x_(0), original_bytes_(), dsign_algo_("")
48 : {
49 2 : if (0 < other.original_bytes_.length()) {
50 2 : deserialize(other.original_bytes_);
51 : }
52 2 : }
53 :
54 332 : Certificate::~Certificate()
55 : {
56 248 : if (x_) {
57 247 : X509_free(x_);
58 : }
59 332 : }
60 :
61 2 : Certificate& Certificate::operator=(const Certificate& rhs)
62 : {
63 2 : if (this != &rhs) {
64 2 : if (rhs.x_ && (0 < rhs.original_bytes_.length())) {
65 2 : deserialize(rhs.original_bytes_);
66 : }
67 :
68 : }
69 2 : return *this;
70 : }
71 :
72 220 : bool Certificate::load(DDS::Security::SecurityException& ex,
73 : const std::string& uri,
74 : const std::string& password)
75 : {
76 : using namespace CommonUtilities;
77 :
78 220 : if (x_) {
79 0 : set_security_error(ex, -1, 0, "SSL::Certificate::load: WARNING: document already loaded");
80 0 : return false;
81 : }
82 :
83 220 : URI uri_info(uri);
84 :
85 220 : switch (uri_info.scheme) {
86 173 : case URI::URI_FILE:
87 173 : load_cert_bytes(uri_info.everything_else);
88 173 : x_ = x509_from_pem(original_bytes_, password);
89 173 : break;
90 :
91 47 : case URI::URI_DATA:
92 47 : load_cert_data_bytes(uri_info.everything_else);
93 47 : x_ = x509_from_pem(original_bytes_, password);
94 47 : break;
95 :
96 0 : case URI::URI_PKCS11:
97 : case URI::URI_UNKNOWN:
98 : default:
99 0 : ACE_ERROR((LM_WARNING,
100 : "(%P|%t) SSL::Certificate::load: WARNING: Unsupported URI scheme\n"));
101 :
102 0 : break;
103 : }
104 :
105 220 : if (!loaded()) {
106 1 : std::stringstream msg;
107 : msg << "SSL::Certificate::load: WARNING: Failed to load document supplied "
108 1 : "with URI '" << uri << "'";
109 1 : set_security_error(ex, -1, 0, msg.str().c_str());
110 1 : return false;
111 1 : }
112 :
113 219 : const int err = cache_dsign_algo();
114 219 : if (err) {
115 0 : set_security_error(ex, -1, 0, "SSL::Certificate::load: WARNING: Failed to cache signature algorithm");
116 0 : return false;
117 : }
118 219 : return true;
119 220 : }
120 :
121 39 : int Certificate::validate(const Certificate& ca, unsigned long int flags) const
122 : {
123 39 : if (!x_) {
124 1 : ACE_ERROR_RETURN((LM_WARNING,
125 : ACE_TEXT("(%P|%t) SSL::Certificate::verify: WARNING, a ")
126 : ACE_TEXT("certificate must be loaded before it can be verified\n")), 1);
127 : }
128 :
129 38 : if (!ca.x_) {
130 0 : ACE_ERROR_RETURN((LM_WARNING,
131 : ACE_TEXT("(%P|%t) SSL::Certificate::verify: WARNING, passed-in ")
132 : ACE_TEXT("CA has not loaded a certificate\n")), 1);
133 : }
134 :
135 38 : X509_STORE* const certs = X509_STORE_new();
136 38 : if (!certs) {
137 0 : OPENDDS_SSL_LOG_ERR("failed to create X509_STORE");
138 0 : return 1;
139 : }
140 :
141 38 : X509_STORE_add_cert(certs, ca.x_);
142 :
143 38 : X509_STORE_CTX* validation_ctx = X509_STORE_CTX_new();
144 38 : if (!validation_ctx) {
145 0 : X509_STORE_free(certs);
146 0 : return 1;
147 : }
148 :
149 38 : X509_STORE_CTX_init(validation_ctx, certs, x_, 0);
150 38 : X509_STORE_CTX_set_flags(validation_ctx, flags);
151 :
152 : int result =
153 38 : X509_verify_cert(validation_ctx) == 1
154 38 : ? X509_V_OK : 1; // X509_V_ERR_UNSPECIFIED is not provided by all OpenSSL versions
155 :
156 38 : if (result == 1) {
157 1 : const int err = X509_STORE_CTX_get_error(validation_ctx),
158 1 : depth = X509_STORE_CTX_get_error_depth(validation_ctx);
159 1 : ACE_ERROR((LM_WARNING,
160 : ACE_TEXT("(%P|%t) SSL::Certificate::verify: WARNING '%C' occurred using cert at ")
161 : ACE_TEXT("depth '%i', validation failed.\n"),
162 : X509_verify_cert_error_string(err), depth));
163 1 : result = err;
164 : }
165 :
166 38 : X509_STORE_CTX_free(validation_ctx);
167 38 : X509_STORE_free(certs);
168 38 : return result;
169 : }
170 :
171 : class verify_implementation
172 : {
173 : public:
174 15 : explicit verify_implementation(EVP_PKEY* pkey)
175 15 : : public_key(pkey), md_ctx(0), pkey_ctx(0)
176 : {
177 15 : }
178 :
179 15 : ~verify_implementation() { EVP_MD_CTX_free(md_ctx); }
180 :
181 15 : int operator()(const DDS::OctetSeq& src,
182 : const std::vector<const DDS::OctetSeq*>& expected_contents)
183 : {
184 15 : if (!public_key) return 1;
185 :
186 15 : int pk_id = 0;
187 15 : std::vector<const DDS::OctetSeq*>::const_iterator i, n;
188 :
189 15 : md_ctx = EVP_MD_CTX_new();
190 15 : if (!md_ctx) {
191 0 : OPENDDS_SSL_LOG_ERR("EVP_MD_CTX_new failed");
192 0 : return 1;
193 : }
194 :
195 15 : EVP_MD_CTX_init(md_ctx);
196 :
197 15 : if (1 != EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha256(), 0,
198 : public_key)) {
199 0 : OPENDDS_SSL_LOG_ERR("EVP_DigestVerifyInit failed");
200 0 : return 1;
201 : }
202 :
203 : // Determine which signature type is being verified
204 15 : pk_id = EVP_PKEY_id(public_key);
205 :
206 15 : if (pk_id == EVP_PKEY_RSA) {
207 14 : if (1 !=
208 14 : EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
209 0 : OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_padding failed");
210 0 : return 1;
211 : }
212 :
213 14 : if (1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256())) {
214 0 : OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_mgf1_md failed");
215 0 : return 1;
216 : }
217 : }
218 :
219 15 : n = expected_contents.end();
220 33 : for (i = expected_contents.begin(); i != n; ++i) {
221 18 : if ((*i)->length() > 0) {
222 18 : if (1 != EVP_DigestVerifyUpdate(md_ctx, (*i)->get_buffer(),
223 18 : (*i)->length())) {
224 0 : OPENDDS_SSL_LOG_ERR("EVP_DigestVerifyUpdate failed");
225 0 : return 1;
226 : }
227 : }
228 : }
229 :
230 : #ifdef OPENSSL_V_1_0
231 : // some versions of OpenSSL take a pointer to non-const
232 : unsigned char* buffer = const_cast<unsigned char*>(src.get_buffer());
233 : #else
234 15 : const unsigned char* buffer = src.get_buffer();
235 : #endif
236 15 : const int err = EVP_DigestVerifyFinal(md_ctx, buffer, src.length());
237 15 : if (0 == err) {
238 1 : return 1; // Verification failed, but no error occurred
239 :
240 14 : } else if (1 != err) {
241 0 : OPENDDS_SSL_LOG_ERR("EVP_DigestVerifyFinal failed");
242 0 : return 1;
243 : }
244 :
245 14 : return 0;
246 : }
247 :
248 : private:
249 : EVP_PKEY* public_key;
250 : EVP_MD_CTX* md_ctx;
251 : EVP_PKEY_CTX* pkey_ctx;
252 : };
253 :
254 15 : int Certificate::verify_signature(
255 : const DDS::OctetSeq& src,
256 : const std::vector<const DDS::OctetSeq*>& expected_contents) const
257 : {
258 : #ifdef OPENSSL_V_1_0
259 : struct EVP_PKEY_Handle {
260 : EVP_PKEY* pkey_;
261 : explicit EVP_PKEY_Handle(EVP_PKEY* pkey) : pkey_(pkey) {}
262 : operator EVP_PKEY*() { return pkey_; }
263 : ~EVP_PKEY_Handle() { EVP_PKEY_free(pkey_); }
264 : } pkey(X509_get_pubkey(x_));
265 : #else
266 15 : EVP_PKEY* pkey = X509_get0_pubkey(x_);
267 : #endif
268 15 : verify_implementation verify(pkey);
269 30 : return verify(src, expected_contents);
270 15 : }
271 :
272 55 : int Certificate::subject_name_to_str(std::string& dst,
273 : unsigned long flags) const
274 : {
275 55 : int result = 1;
276 :
277 55 : dst.clear();
278 :
279 55 : if (x_) {
280 : /* Do not free name! */
281 55 : X509_NAME* name = X509_get_subject_name(x_);
282 55 : if (name) {
283 55 : BIO* buffer = BIO_new(BIO_s_mem());
284 55 : if (buffer) {
285 55 : int len = X509_NAME_print_ex(buffer, name, 0, flags);
286 55 : if (len > 0) {
287 55 : std::vector<char> tmp(len +
288 55 : 1); // BIO_gets will add null hence +1
289 55 : len = BIO_gets(buffer, &tmp[0], len + 1);
290 55 : if (len > 0) {
291 110 : std::copy(
292 : tmp.begin(),
293 55 : tmp.end() -
294 : 1, // But... string inserts a null so it's not needed
295 : std::back_inserter(dst));
296 55 : result = 0;
297 :
298 : } else {
299 0 : OPENDDS_SSL_LOG_ERR("failed to write BIO to string");
300 : }
301 :
302 55 : } else {
303 0 : OPENDDS_SSL_LOG_ERR("failed to read X509_NAME into BIO buffer");
304 : }
305 :
306 55 : BIO_free(buffer);
307 : }
308 : }
309 : }
310 :
311 55 : return result;
312 : }
313 :
314 60 : int Certificate::subject_name_digest(std::vector<CORBA::Octet>& dst) const
315 : {
316 60 : dst.clear();
317 :
318 60 : if (!x_) return 1;
319 :
320 : /* Do not free name! */
321 60 : X509_NAME* name = X509_get_subject_name(x_);
322 60 : if (0 == name) {
323 0 : OPENDDS_SSL_LOG_ERR("X509_get_subject_name failed");
324 0 : return 1;
325 : }
326 :
327 60 : std::vector<CORBA::Octet> tmp(EVP_MAX_MD_SIZE);
328 :
329 60 : unsigned int len = 0;
330 60 : if (1 != X509_NAME_digest(name, EVP_sha256(), &tmp[0], &len)) {
331 0 : OPENDDS_SSL_LOG_ERR("X509_NAME_digest failed");
332 0 : return 1;
333 : }
334 :
335 60 : dst.insert(dst.begin(), tmp.begin(), tmp.begin() + len);
336 :
337 60 : return 0;
338 60 : }
339 :
340 42 : const char* Certificate::keypair_algo() const
341 : {
342 : // This should probably be pulling the information directly from
343 : // the certificate.
344 42 : if (std::string("RSASSA-PSS-SHA256") == dsign_algo_) {
345 42 : return "RSA-2048";
346 :
347 0 : } else if (std::string("ECDSA-SHA256") == dsign_algo_) {
348 0 : return "EC-prime256v1";
349 :
350 : } else {
351 0 : return "UNKNOWN";
352 : }
353 : }
354 :
355 : struct cache_dsign_algo_impl
356 : {
357 : #ifndef OPENSSL_V_3_0
358 : cache_dsign_algo_impl() : pkey_(0), rsa_(0), ec_(0) {}
359 : ~cache_dsign_algo_impl()
360 : {
361 : EVP_PKEY_free(pkey_);
362 : RSA_free(rsa_);
363 : EC_KEY_free(ec_);
364 : }
365 : #else
366 247 : cache_dsign_algo_impl() : pkey_(0) {}
367 247 : ~cache_dsign_algo_impl()
368 : {
369 247 : EVP_PKEY_free(pkey_);
370 247 : }
371 : #endif
372 :
373 247 : int operator() (X509* cert, std::string& dst)
374 : {
375 247 : if (!cert) {
376 0 : ACE_ERROR((LM_WARNING,
377 : "(%P|%t) SSL::Certificate::cache_dsign_algo: WARNING, failed to "
378 : "get pubkey from X509 cert\n"));
379 0 : return 1;
380 : }
381 :
382 247 : pkey_ = X509_get_pubkey(cert);
383 247 : if (!pkey_) {
384 0 : OPENDDS_SSL_LOG_ERR("cache_dsign_algo_impl::operator(): x509_get_pubkey failed");
385 0 : return 1;
386 : }
387 :
388 : #ifndef OPENSSL_V_3_0
389 : rsa_ = EVP_PKEY_get1_RSA(pkey_);
390 : if (rsa_) {
391 : dst = "RSASSA-PSS-SHA256";
392 : return 0;
393 : }
394 :
395 : ec_ = EVP_PKEY_get1_EC_KEY(pkey_);
396 : if (ec_) {
397 : dst = "ECDSA-SHA256";
398 : return 0;
399 : }
400 : #else
401 247 : const int ptype = EVP_PKEY_id (pkey_);
402 247 : if (ptype == EVP_PKEY_RSA || ptype == EVP_PKEY_RSA_PSS) {
403 224 : dst = "RSASSA-PSS-SHA256";
404 224 : return 0;
405 23 : } else if (ptype == EVP_PKEY_EC) {
406 23 : dst = "ECDSA-SHA256";
407 23 : return 0;
408 : }
409 : #endif
410 :
411 0 : ACE_ERROR((LM_WARNING,
412 : "(%P|%t) SSL::Certificate::cache_dsign_algo: WARNING, only RSASSA-PSS-SHA256 or "
413 : "ECDSA-SHA256 are currently supported signature/verification algorithms\n"));
414 :
415 0 : return 1;
416 : }
417 :
418 : private:
419 : EVP_PKEY* pkey_;
420 : #ifndef OPENSSL_V_3_0
421 : RSA* rsa_;
422 : EC_KEY* ec_;
423 : #endif
424 : };
425 :
426 247 : int Certificate::cache_dsign_algo()
427 : {
428 247 : return cache_dsign_algo_impl()(x_, dsign_algo_);
429 : }
430 :
431 173 : void Certificate::load_cert_bytes(const std::string& path)
432 : {
433 : #ifdef ACE_ANDROID
434 : CORBA::Octet *buffer;
435 :
436 : char b[1024];
437 : FILE* fp = ACE_OS::fopen(path.c_str(), "rb");
438 :
439 : int n;
440 : int i = 0;
441 : while (!feof(fp)) {
442 : n = ACE_OS::fread(&b, 1, 1024, fp);
443 : i += n;
444 :
445 : original_bytes_.length(i + 1); // +1 for null byte at end of cert
446 : buffer = original_bytes_.get_buffer();
447 : ACE_OS::memcpy(buffer + i - n, b, n);
448 : }
449 :
450 : ACE_OS::fclose(fp);
451 :
452 : // To appease the other DDS security implementations which
453 : // append a null byte at the end of the cert.
454 : buffer[i + 1] = 0u;
455 :
456 : #else
457 173 : std::ifstream in(path.c_str(), std::ios::binary);
458 :
459 173 : if (!in) {
460 0 : ACE_ERROR((LM_WARNING,
461 : "(%P|%t) Certificate::load_cert_bytes:"
462 : "WARNING: Failed to load file '%C'; '%m'\n",
463 : path.c_str()));
464 0 : return;
465 : }
466 :
467 173 : const std::ifstream::pos_type begin = in.tellg();
468 173 : in.seekg(0, std::ios::end);
469 173 : const std::ifstream::pos_type end = in.tellg();
470 173 : in.seekg(0, std::ios::beg);
471 :
472 173 : original_bytes_.length(static_cast<CORBA::ULong>(end - begin + 1));
473 173 : in.read(reinterpret_cast<char*>(original_bytes_.get_buffer()), end - begin);
474 :
475 173 : if (!in) {
476 0 : ACE_ERROR((LM_WARNING,
477 : "(%P|%t) Certificate::load_cert_bytes:"
478 : "WARNING: Failed to load file '%C'; '%m'\n",
479 : path.c_str()));
480 0 : return;
481 : }
482 :
483 : // To appease the other DDS security implementations which
484 : // append a null byte at the end of the cert.
485 173 : original_bytes_[original_bytes_.length() - 1] = 0u;
486 : #endif
487 173 : }
488 :
489 47 : void Certificate::load_cert_data_bytes(const std::string& data)
490 : {
491 : // Start at position 1 because path contains a comma in element 0
492 : // and that comma is not included in the cert string
493 : // copy the full length to get the terminating null
494 47 : original_bytes_.length(static_cast<unsigned int>(data.size()));
495 47 : std::memcpy(original_bytes_.get_buffer(), data.c_str() + 1, data.size());
496 47 : }
497 :
498 0 : X509* Certificate::x509_from_pem(const std::string& path,
499 : const std::string& password)
500 : {
501 0 : X509* result = NULL;
502 :
503 0 : BIO* filebuf = BIO_new_file(path.c_str(), "r");
504 0 : if (filebuf) {
505 0 : if (!password.empty()) {
506 : result =
507 0 : PEM_read_bio_X509_AUX(filebuf, 0, 0, (void*)password.c_str());
508 0 : if (!result) {
509 0 : OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
510 : }
511 :
512 : } else {
513 0 : result = PEM_read_bio_X509_AUX(filebuf, 0, 0, 0);
514 0 : if (!result) {
515 0 : OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
516 : }
517 : }
518 :
519 0 : BIO_free(filebuf);
520 :
521 : } else {
522 0 : std::stringstream errmsg;
523 0 : errmsg << "failed to read file '" << path << "' using BIO_new_file";
524 0 : OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
525 0 : }
526 :
527 0 : return result;
528 : }
529 :
530 220 : X509* Certificate::x509_from_pem(const DDS::OctetSeq& bytes,
531 : const std::string& password)
532 : {
533 220 : X509* result = 0;
534 :
535 220 : BIO* filebuf = BIO_new(BIO_s_mem());
536 : do {
537 220 : if (filebuf) {
538 220 : if (0 >= BIO_write(filebuf, bytes.get_buffer(), bytes.length())) {
539 0 : OPENDDS_SSL_LOG_ERR("BIO_write failed");
540 0 : break;
541 : }
542 220 : if (!password.empty()) {
543 0 : result = PEM_read_bio_X509_AUX(filebuf, 0, 0,
544 0 : (void*)password.c_str());
545 0 : if (!result) {
546 0 : OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
547 0 : break;
548 : }
549 :
550 : } else {
551 220 : result = PEM_read_bio_X509_AUX(filebuf, 0, 0, 0);
552 220 : if (!result) {
553 2 : OPENDDS_SSL_LOG_ERR("PEM_read_bio_X509_AUX failed");
554 1 : break;
555 : }
556 : }
557 :
558 : } else {
559 0 : std::stringstream errmsg;
560 0 : errmsg << "BIO_new failed";
561 0 : OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
562 0 : break;
563 0 : }
564 :
565 : } while (0);
566 :
567 220 : BIO_free(filebuf);
568 :
569 220 : return result;
570 : }
571 :
572 2 : int Certificate::serialize(DDS::OctetSeq& dst) const
573 : {
574 2 : dst = original_bytes_;
575 :
576 2 : if (dst.length() == original_bytes_.length()) {
577 2 : return 0;
578 : }
579 :
580 0 : return 1;
581 : }
582 :
583 : struct deserialize_impl
584 : {
585 28 : explicit deserialize_impl(const DDS::OctetSeq& src)
586 28 : : src_(src), buffer_(BIO_new(BIO_s_mem()))
587 28 : {}
588 :
589 28 : ~deserialize_impl()
590 : {
591 28 : BIO_free(buffer_);
592 28 : }
593 :
594 28 : int operator() (X509*& dst)
595 : {
596 28 : if (dst) {
597 0 : ACE_ERROR((LM_WARNING,
598 : "(%P|%t) SSL::Certificate::deserialize: WARNING, an X509 certificate "
599 : "has already been loaded\n"));
600 0 : return 1;
601 : }
602 :
603 28 : if (0 == src_.length()) {
604 0 : ACE_ERROR((LM_WARNING,
605 : "(%P|%t) SSL::Certificate::deserialize: WARNING, source OctetSeq contains no data"));
606 0 : return 1;
607 : }
608 :
609 28 : if (!buffer_) {
610 0 : OPENDDS_SSL_LOG_ERR("failed to allocate buffer with BIO_new");
611 0 : return 1;
612 : }
613 :
614 28 : const int len = BIO_write(buffer_, src_.get_buffer(), src_.length());
615 28 : if (len <= 0) {
616 0 : OPENDDS_SSL_LOG_ERR("failed to write OctetSeq to BIO");
617 0 : return 1;
618 : }
619 :
620 28 : dst = PEM_read_bio_X509_AUX(buffer_, 0, 0, 0);
621 28 : if (! dst) {
622 0 : OPENDDS_SSL_LOG_ERR("failed to read X509 from BIO");
623 0 : return 1;
624 : }
625 :
626 28 : return 0;
627 : }
628 :
629 : private:
630 : const DDS::OctetSeq& src_;
631 : BIO* buffer_;
632 : };
633 :
634 28 : int Certificate::deserialize(const DDS::OctetSeq& src)
635 : {
636 28 : int err = deserialize_impl(src)(x_) || cache_dsign_algo();
637 28 : if (!err) {
638 28 : original_bytes_ = src;
639 : }
640 :
641 28 : return err;
642 : }
643 :
644 0 : std::ostream& operator<<(std::ostream& lhs, const Certificate& rhs)
645 : {
646 0 : if (rhs.x_) {
647 : lhs << "Certificate: { is_ca? '"
648 0 : << (X509_check_ca(rhs.x_) ? "yes" : "no") << "'; }";
649 :
650 : } else {
651 0 : lhs << "NULL";
652 : }
653 0 : return lhs;
654 : }
655 :
656 8 : bool operator==(const Certificate& lhs, const Certificate& rhs)
657 : {
658 8 : if (lhs.x_ && rhs.x_) {
659 16 : return (0 == X509_cmp(lhs.x_, rhs.x_)) &&
660 16 : (lhs.original_bytes_ == rhs.original_bytes_);
661 : }
662 0 : return (lhs.x_ == rhs.x_) &&
663 0 : (lhs.original_bytes_ == rhs.original_bytes_);
664 : }
665 :
666 : } // namespace SSL
667 : } // namespace Security
668 : } // namespace OpenDDS
669 :
670 : OPENDDS_END_VERSIONED_NAMESPACE_DECL
|