1 /*
2 * libjingle
3 * Copyright 2004--2008, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #if HAVE_CONFIG_H
29 #include "config.h"
30 #endif // HAVE_CONFIG_H
31
32 #if HAVE_OPENSSL_SSL_H
33
34 #include "talk/base/opensslstreamadapter.h"
35
36 #include <openssl/bio.h>
37 #include <openssl/crypto.h>
38 #include <openssl/err.h>
39 #include <openssl/rand.h>
40 #include <openssl/ssl.h>
41 #include <openssl/x509v3.h>
42
43 #include "talk/base/common.h"
44 #include "talk/base/logging.h"
45 #include "talk/base/stream.h"
46 #include "talk/base/openssladapter.h"
47 #include "talk/base/opensslidentity.h"
48 #include "talk/base/stringutils.h"
49
50 namespace talk_base {
51
52 //////////////////////////////////////////////////////////////////////
53 // StreamBIO
54 //////////////////////////////////////////////////////////////////////
55
56 static int stream_write(BIO* h, const char* buf, int num);
57 static int stream_read(BIO* h, char* buf, int size);
58 static int stream_puts(BIO* h, const char* str);
59 static long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2);
60 static int stream_new(BIO* h);
61 static int stream_free(BIO* data);
62
63 static BIO_METHOD methods_stream = {
64 BIO_TYPE_BIO,
65 "stream",
66 stream_write,
67 stream_read,
68 stream_puts,
69 0,
70 stream_ctrl,
71 stream_new,
72 stream_free,
73 NULL,
74 };
75
BIO_s_stream()76 static BIO_METHOD* BIO_s_stream() { return(&methods_stream); }
77
BIO_new_stream(StreamInterface * stream)78 static BIO* BIO_new_stream(StreamInterface* stream) {
79 BIO* ret = BIO_new(BIO_s_stream());
80 if (ret == NULL)
81 return NULL;
82 ret->ptr = stream;
83 return ret;
84 }
85
86 // bio methods return 1 (or at least non-zero) on success and 0 on failure.
87
stream_new(BIO * b)88 static int stream_new(BIO* b) {
89 b->shutdown = 0;
90 b->init = 1;
91 b->num = 0; // 1 means end-of-stream
92 b->ptr = 0;
93 return 1;
94 }
95
stream_free(BIO * b)96 static int stream_free(BIO* b) {
97 if (b == NULL)
98 return 0;
99 return 1;
100 }
101
stream_read(BIO * b,char * out,int outl)102 static int stream_read(BIO* b, char* out, int outl) {
103 if (!out)
104 return -1;
105 StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
106 BIO_clear_retry_flags(b);
107 size_t read;
108 int error;
109 StreamResult result = stream->Read(out, outl, &read, &error);
110 if (result == SR_SUCCESS) {
111 return read;
112 } else if (result == SR_EOS) {
113 b->num = 1;
114 } else if (result == SR_BLOCK) {
115 BIO_set_retry_read(b);
116 }
117 return -1;
118 }
119
stream_write(BIO * b,const char * in,int inl)120 static int stream_write(BIO* b, const char* in, int inl) {
121 if (!in)
122 return -1;
123 StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
124 BIO_clear_retry_flags(b);
125 size_t written;
126 int error;
127 StreamResult result = stream->Write(in, inl, &written, &error);
128 if (result == SR_SUCCESS) {
129 return written;
130 } else if (result == SR_BLOCK) {
131 BIO_set_retry_write(b);
132 }
133 return -1;
134 }
135
stream_puts(BIO * b,const char * str)136 static int stream_puts(BIO* b, const char* str) {
137 return stream_write(b, str, strlen(str));
138 }
139
stream_ctrl(BIO * b,int cmd,long num,void * ptr)140 static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) {
141 UNUSED(num);
142 UNUSED(ptr);
143
144 switch (cmd) {
145 case BIO_CTRL_RESET:
146 return 0;
147 case BIO_CTRL_EOF:
148 return b->num;
149 case BIO_CTRL_WPENDING:
150 case BIO_CTRL_PENDING:
151 return 0;
152 case BIO_CTRL_FLUSH:
153 return 1;
154 default:
155 return 0;
156 }
157 }
158
159 /////////////////////////////////////////////////////////////////////////////
160 // OpenSSLStreamAdapter
161 /////////////////////////////////////////////////////////////////////////////
162
OpenSSLStreamAdapter(StreamInterface * stream)163 OpenSSLStreamAdapter::OpenSSLStreamAdapter(StreamInterface* stream)
164 : SSLStreamAdapter(stream),
165 state_(SSL_NONE),
166 role_(SSL_CLIENT),
167 ssl_read_needs_write_(false), ssl_write_needs_read_(false),
168 ssl_(NULL), ssl_ctx_(NULL),
169 custom_verification_succeeded_(false) {
170 }
171
~OpenSSLStreamAdapter()172 OpenSSLStreamAdapter::~OpenSSLStreamAdapter() {
173 Cleanup();
174 }
175
SetIdentity(SSLIdentity * identity)176 void OpenSSLStreamAdapter::SetIdentity(SSLIdentity* identity) {
177 ASSERT(identity_.get() == NULL);
178 identity_.reset(static_cast<OpenSSLIdentity*>(identity));
179 }
180
SetServerRole()181 void OpenSSLStreamAdapter::SetServerRole() {
182 role_ = SSL_SERVER;
183 }
184
SetPeerCertificate(SSLCertificate * cert)185 void OpenSSLStreamAdapter::SetPeerCertificate(SSLCertificate* cert) {
186 ASSERT(peer_certificate_.get() == NULL);
187 ASSERT(ssl_server_name_.empty());
188 peer_certificate_.reset(static_cast<OpenSSLCertificate*>(cert));
189 }
190
StartSSLWithServer(const char * server_name)191 int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) {
192 ASSERT(server_name != NULL && server_name[0] != '\0');
193 ssl_server_name_ = server_name;
194 return StartSSL();
195 }
196
StartSSLWithPeer()197 int OpenSSLStreamAdapter::StartSSLWithPeer() {
198 ASSERT(ssl_server_name_.empty());
199 // It is permitted to specify peer_certificate_ only later.
200 return StartSSL();
201 }
202
203 //
204 // StreamInterface Implementation
205 //
206
Write(const void * data,size_t data_len,size_t * written,int * error)207 StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len,
208 size_t* written, int* error) {
209 LOG(LS_INFO) << "OpenSSLStreamAdapter::Write(" << data_len << ")";
210
211 switch (state_) {
212 case SSL_NONE:
213 // pass-through in clear text
214 return StreamAdapterInterface::Write(data, data_len, written, error);
215
216 case SSL_WAIT:
217 case SSL_CONNECTING:
218 return SR_BLOCK;
219
220 case SSL_CONNECTED:
221 break;
222
223 case SSL_ERROR:
224 case SSL_CLOSED:
225 default:
226 if (error)
227 *error = ssl_error_code_;
228 return SR_ERROR;
229 }
230
231 // OpenSSL will return an error if we try to write zero bytes
232 if (data_len == 0) {
233 if (written)
234 *written = 0;
235 return SR_SUCCESS;
236 }
237
238 ssl_write_needs_read_ = false;
239
240 int code = SSL_write(ssl_, data, data_len);
241 switch (SSL_get_error(ssl_, code)) {
242 case SSL_ERROR_NONE:
243 LOG(LS_INFO) << " -- success";
244 ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
245 if (written)
246 *written = code;
247 return SR_SUCCESS;
248 case SSL_ERROR_WANT_READ:
249 LOG(LS_INFO) << " -- error want read";
250 ssl_write_needs_read_ = true;
251 return SR_BLOCK;
252 case SSL_ERROR_WANT_WRITE:
253 LOG(LS_INFO) << " -- error want write";
254 return SR_BLOCK;
255
256 case SSL_ERROR_ZERO_RETURN:
257 default:
258 Error("SSL_write", (code ? code : -1), false);
259 if (error)
260 *error = ssl_error_code_;
261 return SR_ERROR;
262 }
263 // not reached
264 }
265
Read(void * data,size_t data_len,size_t * read,int * error)266 StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len,
267 size_t* read, int* error) {
268 LOG(LS_INFO) << "OpenSSLStreamAdapter::Read(" << data_len << ")";
269 switch (state_) {
270 case SSL_NONE:
271 // pass-through in clear text
272 return StreamAdapterInterface::Read(data, data_len, read, error);
273
274 case SSL_WAIT:
275 case SSL_CONNECTING:
276 return SR_BLOCK;
277
278 case SSL_CONNECTED:
279 break;
280
281 case SSL_CLOSED:
282 return SR_EOS;
283
284 case SSL_ERROR:
285 default:
286 if (error)
287 *error = ssl_error_code_;
288 return SR_ERROR;
289 }
290
291 // Don't trust OpenSSL with zero byte reads
292 if (data_len == 0) {
293 if (read)
294 *read = 0;
295 return SR_SUCCESS;
296 }
297
298 ssl_read_needs_write_ = false;
299
300 int code = SSL_read(ssl_, data, data_len);
301 switch (SSL_get_error(ssl_, code)) {
302 case SSL_ERROR_NONE:
303 LOG(LS_INFO) << " -- success";
304 ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
305 if (read)
306 *read = code;
307 return SR_SUCCESS;
308 case SSL_ERROR_WANT_READ:
309 LOG(LS_INFO) << " -- error want read";
310 return SR_BLOCK;
311 case SSL_ERROR_WANT_WRITE:
312 LOG(LS_INFO) << " -- error want write";
313 ssl_read_needs_write_ = true;
314 return SR_BLOCK;
315 case SSL_ERROR_ZERO_RETURN:
316 LOG(LS_INFO) << " -- remote side closed";
317 return SR_EOS;
318 break;
319 default:
320 LOG(LS_INFO) << " -- error " << code;
321 Error("SSL_read", (code ? code : -1), false);
322 if (error)
323 *error = ssl_error_code_;
324 return SR_ERROR;
325 }
326 // not reached
327 }
328
Close()329 void OpenSSLStreamAdapter::Close() {
330 Cleanup();
331 ASSERT(state_ == SSL_CLOSED || state_ == SSL_ERROR);
332 StreamAdapterInterface::Close();
333 }
334
GetState() const335 StreamState OpenSSLStreamAdapter::GetState() const {
336 switch(state_) {
337 case SSL_WAIT:
338 case SSL_CONNECTING:
339 return SS_OPENING;
340 case SSL_CONNECTED:
341 return SS_OPEN;
342 default:
343 return SS_CLOSED;
344 };
345 // not reached
346 }
347
OnEvent(StreamInterface * stream,int events,int err)348 void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
349 int err) {
350 int events_to_signal = 0;
351 int signal_error = 0;
352 ASSERT(stream == this->stream());
353 if ((events & SE_OPEN)) {
354 LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent SE_OPEN";
355 if (state_ != SSL_WAIT) {
356 ASSERT(state_ == SSL_NONE);
357 events_to_signal |= SE_OPEN;
358 } else {
359 state_ = SSL_CONNECTING;
360 if (int err = BeginSSL()) {
361 Error("BeginSSL", err, true);
362 return;
363 }
364 }
365 }
366 if ((events & (SE_READ|SE_WRITE))) {
367 LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent"
368 << ((events & SE_READ) ? " SE_READ" : "")
369 << ((events & SE_WRITE) ? " SE_WRITE" : "");
370 if (state_ == SSL_NONE) {
371 events_to_signal |= events & (SE_READ|SE_WRITE);
372 } else if (state_ == SSL_CONNECTING) {
373 if (int err = ContinueSSL()) {
374 Error("ContinueSSL", err, true);
375 return;
376 }
377 } else if (state_ == SSL_CONNECTED) {
378 if (((events & SE_READ) && ssl_write_needs_read_) ||
379 (events & SE_WRITE)) {
380 LOG(LS_INFO) << " -- onStreamWriteable";
381 events_to_signal |= SE_WRITE;
382 }
383 if (((events & SE_WRITE) && ssl_read_needs_write_) ||
384 (events & SE_READ)) {
385 LOG(LS_INFO) << " -- onStreamReadable";
386 events_to_signal |= SE_READ;
387 }
388 }
389 }
390 if ((events & SE_CLOSE)) {
391 LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent(SE_CLOSE, " << err << ")";
392 Cleanup();
393 events_to_signal |= SE_CLOSE;
394 // SE_CLOSE is the only event that uses the final parameter to OnEvent().
395 ASSERT(signal_error == 0);
396 signal_error = err;
397 }
398 if(events_to_signal)
399 StreamAdapterInterface::OnEvent(stream, events_to_signal, signal_error);
400 }
401
StartSSL()402 int OpenSSLStreamAdapter::StartSSL() {
403 ASSERT(state_ == SSL_NONE);
404
405 if (StreamAdapterInterface::GetState() != SS_OPEN) {
406 state_ = SSL_WAIT;
407 return 0;
408 }
409
410 state_ = SSL_CONNECTING;
411 if (int err = BeginSSL()) {
412 Error("BeginSSL", err, false);
413 return err;
414 }
415
416 return 0;
417 }
418
BeginSSL()419 int OpenSSLStreamAdapter::BeginSSL() {
420 ASSERT(state_ == SSL_CONNECTING);
421 // The underlying stream has open. If we are in peer-to-peer mode
422 // then a peer certificate must have been specified by now.
423 ASSERT(!ssl_server_name_.empty() || peer_certificate_.get() != NULL);
424 LOG(LS_INFO) << "BeginSSL: "
425 << (!ssl_server_name_.empty() ? ssl_server_name_ :
426 "with peer");
427
428 BIO* bio = NULL;
429
430 // First set up the context
431 ASSERT(ssl_ctx_ == NULL);
432 ssl_ctx_ = SetupSSLContext();
433 if (!ssl_ctx_)
434 return -1;
435
436 bio = BIO_new_stream(static_cast<StreamInterface*>(stream()));
437 if (!bio)
438 return -1;
439
440 ssl_ = SSL_new(ssl_ctx_);
441 if (!ssl_) {
442 BIO_free(bio);
443 return -1;
444 }
445
446 SSL_set_app_data(ssl_, this);
447
448 SSL_set_bio(ssl_, bio, bio); // the SSL object owns the bio now.
449
450 SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
451 SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
452
453 // Do the connect
454 return ContinueSSL();
455 }
456
ContinueSSL()457 int OpenSSLStreamAdapter::ContinueSSL() {
458 LOG(LS_INFO) << "ContinueSSL";
459 ASSERT(state_ == SSL_CONNECTING);
460
461 int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_);
462 switch (SSL_get_error(ssl_, code)) {
463 case SSL_ERROR_NONE:
464 LOG(LS_INFO) << " -- success";
465
466 if (!SSLPostConnectionCheck(ssl_, ssl_server_name_.c_str(),
467 peer_certificate_.get() != NULL
468 ? peer_certificate_->x509() : NULL)) {
469 LOG(LS_ERROR) << "TLS post connection check failed";
470 return -1;
471 }
472
473 state_ = SSL_CONNECTED;
474 StreamAdapterInterface::OnEvent(stream(), SE_OPEN|SE_READ|SE_WRITE, 0);
475 break;
476
477 case SSL_ERROR_WANT_READ:
478 LOG(LS_INFO) << " -- error want read";
479 break;
480
481 case SSL_ERROR_WANT_WRITE:
482 LOG(LS_INFO) << " -- error want write";
483 break;
484
485 case SSL_ERROR_ZERO_RETURN:
486 default:
487 LOG(LS_INFO) << " -- error " << code;
488 return (code != 0) ? code : -1;
489 }
490
491 return 0;
492 }
493
Error(const char * context,int err,bool signal)494 void OpenSSLStreamAdapter::Error(const char* context, int err, bool signal) {
495 LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error("
496 << context << ", " << err << ")";
497 state_ = SSL_ERROR;
498 ssl_error_code_ = err;
499 Cleanup();
500 if (signal)
501 StreamAdapterInterface::OnEvent(stream(), SE_CLOSE, err);
502 }
503
Cleanup()504 void OpenSSLStreamAdapter::Cleanup() {
505 LOG(LS_INFO) << "Cleanup";
506
507 if (state_ != SSL_ERROR) {
508 state_ = SSL_CLOSED;
509 ssl_error_code_ = 0;
510 }
511
512 if (ssl_) {
513 SSL_free(ssl_);
514 ssl_ = NULL;
515 }
516 if (ssl_ctx_) {
517 SSL_CTX_free(ssl_ctx_);
518 ssl_ctx_ = NULL;
519 }
520 identity_.reset();
521 peer_certificate_.reset();
522 }
523
SetupSSLContext()524 SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
525 SSL_CTX* ctx = SSL_CTX_new(role_ == SSL_CLIENT ? TLSv1_client_method()
526 : TLSv1_server_method());
527 if (ctx == NULL)
528 return NULL;
529
530 if (identity_.get() && !identity_->ConfigureIdentity(ctx)) {
531 SSL_CTX_free(ctx);
532 return NULL;
533 }
534
535 if (peer_certificate_.get() == NULL) { // traditional mode
536 // Add the root cert to the SSL context
537 if(!OpenSSLAdapter::ConfigureTrustedRootCertificates(ctx)) {
538 SSL_CTX_free(ctx);
539 return NULL;
540 }
541 }
542
543 if (peer_certificate_.get() != NULL && role_ == SSL_SERVER)
544 // we must specify which client cert to ask for
545 SSL_CTX_add_client_CA(ctx, peer_certificate_->x509());
546
547 #ifdef _DEBUG
548 SSL_CTX_set_info_callback(ctx, OpenSSLAdapter::SSLInfoCallback);
549 #endif
550
551 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
552 SSLVerifyCallback);
553 SSL_CTX_set_verify_depth(ctx, 4);
554 SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
555
556 return ctx;
557 }
558
SSLVerifyCallback(int ok,X509_STORE_CTX * store)559 int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
560 #if _DEBUG
561 if (!ok) {
562 char data[256];
563 X509* cert = X509_STORE_CTX_get_current_cert(store);
564 int depth = X509_STORE_CTX_get_error_depth(store);
565 int err = X509_STORE_CTX_get_error(store);
566
567 LOG(LS_INFO) << "Error with certificate at depth: " << depth;
568 X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
569 LOG(LS_INFO) << " issuer = " << data;
570 X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
571 LOG(LS_INFO) << " subject = " << data;
572 LOG(LS_INFO) << " err = " << err
573 << ":" << X509_verify_cert_error_string(err);
574 }
575 #endif
576
577 // Get our SSL structure from the store
578 SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
579 store,
580 SSL_get_ex_data_X509_STORE_CTX_idx()));
581
582 OpenSSLStreamAdapter* stream =
583 reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
584
585 // In peer-to-peer mode, no root cert / certificate authority was
586 // specified, so the libraries knows of no certificate to accept,
587 // and therefore it will necessarily call here on the first cert it
588 // tries to verify.
589 if (!ok && stream->peer_certificate_.get() != NULL) {
590 X509* cert = X509_STORE_CTX_get_current_cert(store);
591 int err = X509_STORE_CTX_get_error(store);
592 // peer-to-peer mode: allow the certificate to be self-signed,
593 // assuming it matches the cert that was specified.
594 if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT &&
595 X509_cmp(cert, stream->peer_certificate_->x509()) == 0) {
596 LOG(LS_INFO) << "Accepted self-signed peer certificate authority";
597 ok = 1;
598 }
599 } else if (!ok && OpenSSLAdapter::custom_verify_callback_) {
600 // this applies only in traditional mode
601 void* cert =
602 reinterpret_cast<void*>(X509_STORE_CTX_get_current_cert(store));
603 if (OpenSSLAdapter::custom_verify_callback_(cert)) {
604 stream->custom_verification_succeeded_ = true;
605 LOG(LS_INFO) << "validated certificate using custom callback";
606 ok = 1;
607 }
608 }
609
610 if (!ok && stream->ignore_bad_cert()) {
611 LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
612 ok = 1;
613 }
614
615 return ok;
616 }
617
618 // This code is taken from the "Network Security with OpenSSL"
619 // sample in chapter 5
SSLPostConnectionCheck(SSL * ssl,const char * server_name,const X509 * peer_cert)620 bool OpenSSLStreamAdapter::SSLPostConnectionCheck(SSL* ssl,
621 const char* server_name,
622 const X509* peer_cert) {
623 ASSERT(server_name != NULL);
624 bool ok;
625 if(server_name[0] != '\0') { // traditional mode
626 ok = OpenSSLAdapter::VerifyServerName(ssl, server_name, ignore_bad_cert());
627
628 if (ok) {
629 ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
630 custom_verification_succeeded_);
631 }
632 } else { // peer-to-peer mode
633 ASSERT(peer_cert != NULL);
634 // no server name validation
635 ok = true;
636 }
637
638 if (!ok && ignore_bad_cert()) {
639 LOG(LS_ERROR) << "SSL_get_verify_result(ssl) = "
640 << SSL_get_verify_result(ssl);
641 LOG(LS_INFO) << "Other TLS post connection checks failed.";
642 ok = true;
643 }
644
645 return ok;
646 }
647
648 } // namespace talk_base
649
650 #endif // HAVE_OPENSSL_SSL_H
651