text: /*
   * libjingle
   * Copyright 2004--2008, Google Inc.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   *  1. Redistributions of source code must retain the above copyright notice,
   *     this list of conditions and the following disclaimer.
   *  2. Redistributions in binary form must reproduce the above copyright notice,
   *     this list of conditions and the following disclaimer in the documentation
   *     and/or other materials provided with the distribution.
   *  3. The name of the author may not be used to endorse or promote products
   *     derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
   * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #if HAVE_CONFIG_H
  #include "config.h"
  #endif  // HAVE_CONFIG_H
  
  #if HAVE_OPENSSL_SSL_H
  
  #include "talk/base/opensslstreamadapter.h"
  
  #include <openssl/bio.h>
  #include <openssl/crypto.h>
  #include <openssl/err.h>
  #include <openssl/rand.h>
  #include <openssl/ssl.h>
  #include <openssl/x509v3.h>
  
  #include <vector>
  
  #include "talk/base/common.h"
  #include "talk/base/logging.h"
  #include "talk/base/stream.h"
  #include "talk/base/openssladapter.h"
  #include "talk/base/opensslidentity.h"
  #include "talk/base/stringutils.h"
  #include "talk/base/thread.h"
  
  namespace talk_base {
  
  #if (OPENSSL_VERSION_NUMBER >= 0x10001000L)
  #define HAVE_DTLS_SRTP
  #endif
  
  #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
  #define HAVE_DTLS
  #endif
  
  #ifdef HAVE_DTLS_SRTP
  // SRTP cipher suite table
  struct SrtpCipherMapEntry {
    const char* external_name;
    const char* internal_name;
  };
  
  // This isn't elegant, but it's better than an external reference
  static SrtpCipherMapEntry SrtpCipherMap[] = {
    {"AES_CM_128_HMAC_SHA1_80", "SRTP_AES128_CM_SHA1_80"},
    {"AES_CM_128_HMAC_SHA1_32", "SRTP_AES128_CM_SHA1_32"},
    {NULL, NULL}
  };
  #endif
  
  //////////////////////////////////////////////////////////////////////
  // StreamBIO
  //////////////////////////////////////////////////////////////////////
  
  static int stream_write(BIO* h, const char* buf, int num);
  static int stream_read(BIO* h, char* buf, int size);
  static int stream_puts(BIO* h, const char* str);
  static long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2);
  static int stream_new(BIO* h);
  static int stream_free(BIO* data);
  
  static BIO_METHOD methods_stream = {
    BIO_TYPE_BIO,
    "stream",
    stream_write,
    stream_read,
    stream_puts,
    0,
    stream_ctrl,
    stream_new,
    stream_free,
    NULL,
  };
  
  static BIO_METHOD* BIO_s_stream() { return(&methods_stream); }
  
  static BIO* BIO_new_stream(StreamInterface* stream) {
    BIO* ret = BIO_new(BIO_s_stream());
    if (ret == NULL)
      return NULL;
    ret->ptr = stream;
    return ret;
  }
  
  // bio methods return 1 (or at least non-zero) on success and 0 on failure.
  
  static int stream_new(BIO* b) {
    b->shutdown = 0;
    b->init = 1;
    b->num = 0;  // 1 means end-of-stream
    b->ptr = 0;
    return 1;
  }
  
  static int stream_free(BIO* b) {
    if (b == NULL)
      return 0;
    return 1;
  }
  
  static int stream_read(BIO* b, char* out, int outl) {
    if (!out)
      return -1;
    StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
    BIO_clear_retry_flags(b);
    size_t read;
    int error;
    StreamResult result = stream->Read(out, outl, &read, &error);
    if (result == SR_SUCCESS) {
      return read;
    } else if (result == SR_EOS) {
      b->num = 1;
    } else if (result == SR_BLOCK) {
      BIO_set_retry_read(b);
    }
    return -1;
  }
  
  static int stream_write(BIO* b, const char* in, int inl) {
    if (!in)
      return -1;
    StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
    BIO_clear_retry_flags(b);
    size_t written;
    int error;
    StreamResult result = stream->Write(in, inl, &written, &error);
    if (result == SR_SUCCESS) {
      return written;
    } else if (result == SR_BLOCK) {
      BIO_set_retry_write(b);
    }
    return -1;
  }
  
  static int stream_puts(BIO* b, const char* str) {
    return stream_write(b, str, strlen(str));
  }
  
  static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) {
    UNUSED(num);
    UNUSED(ptr);
  
    switch (cmd) {
      case BIO_CTRL_RESET:
        return 0;
      case BIO_CTRL_EOF:
        return b->num;
      case BIO_CTRL_WPENDING:
      case BIO_CTRL_PENDING:
        return 0;
      case BIO_CTRL_FLUSH:
        return 1;
      default:
        return 0;
    }
  }
  
  /////////////////////////////////////////////////////////////////////////////
  // OpenSSLStreamAdapter
  /////////////////////////////////////////////////////////////////////////////
  
  OpenSSLStreamAdapter::OpenSSLStreamAdapter(StreamInterface* stream)
      : SSLStreamAdapter(stream),
        state_(SSL_NONE),
        role_(SSL_CLIENT),
        ssl_read_needs_write_(false), ssl_write_needs_read_(false),
        ssl_(NULL), ssl_ctx_(NULL),
        custom_verification_succeeded_(false),
        ssl_mode_(SSL_MODE_TLS) {
  }
  
  OpenSSLStreamAdapter::~OpenSSLStreamAdapter() {
    Cleanup();
  }
  
  void OpenSSLStreamAdapter::SetIdentity(SSLIdentity* identity) {
    ASSERT(identity_.get() == NULL);
    identity_.reset(static_cast<OpenSSLIdentity*>(identity));
  }
  
  void OpenSSLStreamAdapter::SetServerRole(SSLRole role) {
    role_ = role;
  }
  
  void OpenSSLStreamAdapter::SetPeerCertificate(SSLCertificate* cert) {
    ASSERT(peer_certificate_.get() == NULL);
    ASSERT(peer_certificate_digest_algorithm_.empty());
    ASSERT(ssl_server_name_.empty());
    peer_certificate_.reset(static_cast<OpenSSLCertificate*>(cert));
  }
  
  bool OpenSSLStreamAdapter::SetPeerCertificateDigest(const std::string
                                                      &digest_alg,
                                                      const unsigned char*
                                                      digest_val,
                                                      size_t digest_len) {
    ASSERT(peer_certificate_.get() == NULL);
    ASSERT(peer_certificate_digest_algorithm_.size() == 0);
    ASSERT(ssl_server_name_.empty());
    size_t expected_len;
  
    if (!OpenSSLCertificate::GetDigestLength(digest_alg, &expected_len)) {
      LOG(LS_WARNING) << "Unknown digest algorithm: " << digest_alg;
      return false;
    }
    if (expected_len != digest_len)
      return false;
  
    peer_certificate_digest_value_.SetData(digest_val, digest_len);
    peer_certificate_digest_algorithm_ = digest_alg;
  
    return true;
  }
  
  // Key Extractor interface
  bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label,
                                                  const uint8* context,
                                                  size_t context_len,
                                                  bool use_context,
                                                  uint8* result,
                                                  size_t result_len) {
  #ifdef HAVE_DTLS_SRTP
    int i;
  
    i = SSL_export_keying_material(ssl_, result, result_len,
                                   label.c_str(), label.length(),
                                   const_cast<uint8 *>(context),
                                   context_len, use_context);
  
    if (i != 1)
      return false;
  
    return true;
  #else
    return false;
  #endif
  }
  
  bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers(
      const std::vector<std::string>& ciphers) {
    std::string internal_ciphers;
  
    if (state_ != SSL_NONE)
      return false;
  
  #ifdef HAVE_DTLS_SRTP
    for (std::vector<std::string>::const_iterator cipher = ciphers.begin();
         cipher != ciphers.end(); ++cipher) {
      bool found = false;
      for (SrtpCipherMapEntry *entry = SrtpCipherMap; entry->internal_name;
           ++entry) {
        if (*cipher == entry->external_name) {
          found = true;
          if (!internal_ciphers.empty())
            internal_ciphers += ":";
          internal_ciphers += entry->internal_name;
          break;
        }
      }
  
      if (!found) {
        LOG(LS_ERROR) << "Could not find cipher: " << *cipher;
        return false;
      }
    }
  
    if (internal_ciphers.empty())
      return false;
  
    srtp_ciphers_ = internal_ciphers;
    return true;
  #else
    return false;
  #endif
  }
  
  bool OpenSSLStreamAdapter::GetDtlsSrtpCipher(std::string* cipher) {
  #ifdef HAVE_DTLS_SRTP
    ASSERT(state_ == SSL_CONNECTED);
    if (state_ != SSL_CONNECTED)
      return false;
  
    SRTP_PROTECTION_PROFILE *srtp_profile =
        SSL_get_selected_srtp_profile(ssl_);
  
    if (!srtp_profile)
      return NULL;
  
    for (SrtpCipherMapEntry *entry = SrtpCipherMap;
         entry->internal_name; ++entry) {
      if (!strcmp(entry->internal_name, srtp_profile->name)) {
        *cipher = entry->external_name;
        return true;
      }
    }
  
    ASSERT(false);  // This should never happen
  
    return false;
  #else
    return false;
  #endif
  }
  
  int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) {
    ASSERT(server_name != NULL && server_name[0] != '\0');
    ssl_server_name_ = server_name;
    return StartSSL();
  }
  
  int OpenSSLStreamAdapter::StartSSLWithPeer() {
    ASSERT(ssl_server_name_.empty());
    // It is permitted to specify peer_certificate_ only later.
    return StartSSL();
  }
  
  void OpenSSLStreamAdapter::SetMode(SSLMode mode) {
    ASSERT(state_ == SSL_NONE);
    ssl_mode_ = mode;
  }
  
  //
  // StreamInterface Implementation
  //
  
  StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len,
                                           size_t* written, int* error) {
    LOG(LS_INFO) << "OpenSSLStreamAdapter::Write(" << data_len << ")";
  
    switch (state_) {
    case SSL_NONE:
      // pass-through in clear text
      return StreamAdapterInterface::Write(data, data_len, written, error);
  
    case SSL_WAIT:
    case SSL_CONNECTING:
      return SR_BLOCK;
  
    case SSL_CONNECTED:
      break;
  
    case SSL_ERROR:
    case SSL_CLOSED:
    default:
      if (error)
        *error = ssl_error_code_;
      return SR_ERROR;
    }
  
    // OpenSSL will return an error if we try to write zero bytes
    if (data_len == 0) {
      if (written)
        *written = 0;
      return SR_SUCCESS;
    }
  
    ssl_write_needs_read_ = false;
  
    int code = SSL_write(ssl_, data, data_len);
    int ssl_error = SSL_get_error(ssl_, code);
    switch (ssl_error) {
    case SSL_ERROR_NONE:
      LOG(LS_INFO) << " -- success";
      ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
      if (written)
        *written = code;
      return SR_SUCCESS;
    case SSL_ERROR_WANT_READ:
      LOG(LS_INFO) << " -- error want read";
      ssl_write_needs_read_ = true;
      return SR_BLOCK;
    case SSL_ERROR_WANT_WRITE:
      LOG(LS_INFO) << " -- error want write";
      return SR_BLOCK;
  
    case SSL_ERROR_ZERO_RETURN:
    default:
      Error("SSL_write", (ssl_error ? ssl_error : -1), false);
      if (error)
        *error = ssl_error_code_;
      return SR_ERROR;
    }
    // not reached
  }
  
  StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len,
                                          size_t* read, int* error) {
    LOG(LS_INFO) << "OpenSSLStreamAdapter::Read(" << data_len << ")";
    switch (state_) {
      case SSL_NONE:
        // pass-through in clear text
        return StreamAdapterInterface::Read(data, data_len, read, error);
  
      case SSL_WAIT:
      case SSL_CONNECTING:
        return SR_BLOCK;
  
      case SSL_CONNECTED:
        break;
  
      case SSL_CLOSED:
        return SR_EOS;
  
      case SSL_ERROR:
      default:
        if (error)
          *error = ssl_error_code_;
        return SR_ERROR;
    }
  
    // Don't trust OpenSSL with zero byte reads
    if (data_len == 0) {
      if (read)
        *read = 0;
      return SR_SUCCESS;
    }
  
    ssl_read_needs_write_ = false;
  
    int code = SSL_read(ssl_, data, data_len);
    int ssl_error = SSL_get_error(ssl_, code);
    switch (ssl_error) {
      case SSL_ERROR_NONE:
        LOG(LS_INFO) << " -- success";
        ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
        if (read)
          *read = code;
  
        if (ssl_mode_ == SSL_MODE_DTLS) {
          // Enforce atomic reads -- this is a short read
          unsigned int pending = SSL_pending(ssl_);
  
          if (pending) {
            LOG(LS_INFO) << " -- short DTLS read. flushing";
            FlushInput(pending);
            if (error)
              *error = SSE_MSG_TRUNC;
            return SR_ERROR;
          }
        }
        return SR_SUCCESS;
      case SSL_ERROR_WANT_READ:
        LOG(LS_INFO) << " -- error want read";
        return SR_BLOCK;
      case SSL_ERROR_WANT_WRITE:
        LOG(LS_INFO) << " -- error want write";
        ssl_read_needs_write_ = true;
        return SR_BLOCK;
      case SSL_ERROR_ZERO_RETURN:
        LOG(LS_INFO) << " -- remote side closed";
        return SR_EOS;
        break;
      default:
        LOG(LS_INFO) << " -- error " << code;
        Error("SSL_read", (ssl_error ? ssl_error : -1), false);
        if (error)
          *error = ssl_error_code_;
        return SR_ERROR;
    }
    // not reached
  }
  
  void OpenSSLStreamAdapter::FlushInput(unsigned int left) {
    unsigned char buf[2048];
  
    while (left) {
      // This should always succeed
      int toread = (sizeof(buf) < left) ? sizeof(buf) : left;
      int code = SSL_read(ssl_, buf, toread);
  
      int ssl_error = SSL_get_error(ssl_, code);
      ASSERT(ssl_error == SSL_ERROR_NONE);
  
      if (ssl_error != SSL_ERROR_NONE) {
        LOG(LS_INFO) << " -- error " << code;
        Error("SSL_read", (ssl_error ? ssl_error : -1), false);
        return;
      }
      LOG(LS_INFO) << " -- flushed " << code << " bytes";
      left -= code;
    }
  }
  
  void OpenSSLStreamAdapter::Close() {
    Cleanup();
    ASSERT(state_ == SSL_CLOSED || state_ == SSL_ERROR);
    StreamAdapterInterface::Close();
  }
  
  StreamState OpenSSLStreamAdapter::GetState() const {
    switch (state_) {
      case SSL_WAIT:
      case SSL_CONNECTING:
        return SS_OPENING;
      case SSL_CONNECTED:
        return SS_OPEN;
      default:
        return SS_CLOSED;
    };
    // not reached
  }
  
  void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
                                     int err) {
    int events_to_signal = 0;
    int signal_error = 0;
    ASSERT(stream == this->stream());
    if ((events & SE_OPEN)) {
      LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent SE_OPEN";
      if (state_ != SSL_WAIT) {
        ASSERT(state_ == SSL_NONE);
        events_to_signal |= SE_OPEN;
      } else {
        state_ = SSL_CONNECTING;
        if (int err = BeginSSL()) {
          Error("BeginSSL", err, true);
          return;
        }
      }
    }
    if ((events & (SE_READ|SE_WRITE))) {
      LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent"
                   << ((events & SE_READ) ? " SE_READ" : "")
                   << ((events & SE_WRITE) ? " SE_WRITE" : "");
      if (state_ == SSL_NONE) {
        events_to_signal |= events & (SE_READ|SE_WRITE);
      } else if (state_ == SSL_CONNECTING) {
        if (int err = ContinueSSL()) {
          Error("ContinueSSL", err, true);
          return;
        }
      } else if (state_ == SSL_CONNECTED) {
        if (((events & SE_READ) && ssl_write_needs_read_) ||
            (events & SE_WRITE)) {
          LOG(LS_INFO) << " -- onStreamWriteable";
          events_to_signal |= SE_WRITE;
        }
        if (((events & SE_WRITE) && ssl_read_needs_write_) ||
            (events & SE_READ)) {
          LOG(LS_INFO) << " -- onStreamReadable";
          events_to_signal |= SE_READ;
        }
      }
    }
    if ((events & SE_CLOSE)) {
      LOG(LS_INFO) << "OpenSSLStreamAdapter::OnEvent(SE_CLOSE, " << err << ")";
      Cleanup();
      events_to_signal |= SE_CLOSE;
      // SE_CLOSE is the only event that uses the final parameter to OnEvent().
      ASSERT(signal_error == 0);
      signal_error = err;
    }
    if (events_to_signal)
      StreamAdapterInterface::OnEvent(stream, events_to_signal, signal_error);
  }
  
  int OpenSSLStreamAdapter::StartSSL() {
    ASSERT(state_ == SSL_NONE);
  
    if (StreamAdapterInterface::GetState() != SS_OPEN) {
      state_ = SSL_WAIT;
      return 0;
    }
  
    state_ = SSL_CONNECTING;
    if (int err = BeginSSL()) {
      Error("BeginSSL", err, false);
      return err;
    }
  
    return 0;
  }
  
  int OpenSSLStreamAdapter::BeginSSL() {
    ASSERT(state_ == SSL_CONNECTING);
    // The underlying stream has open. If we are in peer-to-peer mode
    // then a peer certificate must have been specified by now.
    ASSERT(!ssl_server_name_.empty() ||
           peer_certificate_.get() != NULL ||
           !peer_certificate_digest_algorithm_.empty());
    LOG(LS_INFO) << "BeginSSL: "
                 << (!ssl_server_name_.empty() ? ssl_server_name_ :
                                                 "with peer");
  
    BIO* bio = NULL;
  
    // First set up the context
    ASSERT(ssl_ctx_ == NULL);
    ssl_ctx_ = SetupSSLContext();
    if (!ssl_ctx_)
      return -1;
  
    bio = BIO_new_stream(static_cast<StreamInterface*>(stream()));
    if (!bio)
      return -1;
  
    ssl_ = SSL_new(ssl_ctx_);
    if (!ssl_) {
      BIO_free(bio);
      return -1;
    }
  
    SSL_set_app_data(ssl_, this);
  
    SSL_set_bio(ssl_, bio, bio);  // the SSL object owns the bio now.
  
    SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
                 SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
  
    // Do the connect
    return ContinueSSL();
  }
  
  int OpenSSLStreamAdapter::ContinueSSL() {
    LOG(LS_INFO) << "ContinueSSL";
    ASSERT(state_ == SSL_CONNECTING);
  
    // Clear the DTLS timer
    Thread::Current()->Clear(this, MSG_TIMEOUT);
  
    int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_);
    int ssl_error;
    switch (ssl_error = SSL_get_error(ssl_, code)) {
      case SSL_ERROR_NONE:
        LOG(LS_INFO) << " -- success";
  
        if (!SSLPostConnectionCheck(ssl_, ssl_server_name_.c_str(),
                                    peer_certificate_.get() != NULL
                                    ? peer_certificate_->x509() : NULL,
                                    peer_certificate_digest_algorithm_)) {
          LOG(LS_ERROR) << "TLS post connection check failed";
          return -1;
        }
  
        state_ = SSL_CONNECTED;
        StreamAdapterInterface::OnEvent(stream(), SE_OPEN|SE_READ|SE_WRITE, 0);
        break;
  
      case SSL_ERROR_WANT_READ: {
          LOG(LS_INFO) << " -- error want read";
  #ifdef HAVE_DTLS
          struct timeval timeout;
          if (DTLSv1_get_timeout(ssl_, &timeout)) {
            int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000;
  
            Thread::Current()->PostDelayed(delay, this, MSG_TIMEOUT, 0);
          }
  #endif
        }
        break;
  
      case SSL_ERROR_WANT_WRITE:
        LOG(LS_INFO) << " -- error want write";
        break;
  
      case SSL_ERROR_ZERO_RETURN:
      default:
        LOG(LS_INFO) << " -- error " << code;
        return (ssl_error != 0) ? ssl_error : -1;
    }
  
    return 0;
  }
  
  void OpenSSLStreamAdapter::Error(const char* context, int err, bool signal) {
    LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error("
                    << context << ", " << err << ")";
    state_ = SSL_ERROR;
    ssl_error_code_ = err;
    Cleanup();
    if (signal)
      StreamAdapterInterface::OnEvent(stream(), SE_CLOSE, err);
  }
  
  void OpenSSLStreamAdapter::Cleanup() {
    LOG(LS_INFO) << "Cleanup";
  
    if (state_ != SSL_ERROR) {
      state_ = SSL_CLOSED;
      ssl_error_code_ = 0;
    }
  
    if (ssl_) {
      SSL_free(ssl_);
      ssl_ = NULL;
    }
    if (ssl_ctx_) {
      SSL_CTX_free(ssl_ctx_);
      ssl_ctx_ = NULL;
    }
    identity_.reset();
    peer_certificate_.reset();
  
    // Clear the DTLS timer
    Thread::Current()->Clear(this, MSG_TIMEOUT);
  }
  
  
  void OpenSSLStreamAdapter::OnMessage(Message* msg) {
    // Process our own messages and then pass others to the superclass
    if (MSG_TIMEOUT == msg->message_id) {
      LOG(LS_INFO) << "DTLS timeout expired";
  #ifdef HAVE_DTLS
      DTLSv1_handle_timeout(ssl_);
  #endif
      ContinueSSL();
    } else {
      StreamInterface::OnMessage(msg);
    }
  }
  
  SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
    SSL_CTX *ctx = NULL;
  
    if (role_ == SSL_CLIENT) {
  #ifdef HAVE_DTLS
      ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ?
          DTLSv1_client_method() : TLSv1_client_method());
  #else
      ctx = SSL_CTX_new(TLSv1_client_method());
  #endif
    } else {
  #ifdef HAVE_DTLS
      ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ?
          DTLSv1_server_method() : TLSv1_server_method());
  #else
      ctx = SSL_CTX_new(TLSv1_server_method());
  #endif
    }
    if (ctx == NULL)
      return NULL;
  
    if (identity_.get() && !identity_->ConfigureIdentity(ctx)) {
      SSL_CTX_free(ctx);
      return NULL;
    }
  
    if (peer_certificate_.get() == NULL) {  // traditional mode
      // Add the root cert to the SSL context
      if (!OpenSSLAdapter::ConfigureTrustedRootCertificates(ctx)) {
        SSL_CTX_free(ctx);
        return NULL;
      }
    }
  
    if (peer_certificate_.get() != NULL && role_ == SSL_SERVER)
      // we must specify which client cert to ask for
      SSL_CTX_add_client_CA(ctx, peer_certificate_->x509());
  
  #ifdef _DEBUG
    SSL_CTX_set_info_callback(ctx, OpenSSLAdapter::SSLInfoCallback);
  #endif
  
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                       SSLVerifyCallback);
    SSL_CTX_set_verify_depth(ctx, 4);
    SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
  
  #ifdef HAVE_DTLS_SRTP
    if (!srtp_ciphers_.empty()) {
      if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) {
        SSL_CTX_free(ctx);
        return NULL;
      }
    }
  #endif
  
    return ctx;
  }
  
  int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
  #if _DEBUG
    if (!ok) {
      char data[256];
      X509* cert = X509_STORE_CTX_get_current_cert(store);
      int depth = X509_STORE_CTX_get_error_depth(store);
      int err = X509_STORE_CTX_get_error(store);
  
      LOG(LS_INFO) << "Error with certificate at depth: " << depth;
      X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
      LOG(LS_INFO) << "  issuer  = " << data;
      X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
      LOG(LS_INFO) << "  subject = " << data;
      LOG(LS_INFO) << "  err     = " << err
        << ":" << X509_verify_cert_error_string(err);
    }
  #endif
  
    // Get our SSL structure from the store
    SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
                                          store,
                                          SSL_get_ex_data_X509_STORE_CTX_idx()));
  
    OpenSSLStreamAdapter* stream =
      reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
  
    // In peer-to-peer mode, no root cert / certificate authority was
    // specified, so the libraries knows of no certificate to accept,
    // and therefore it will necessarily call here on the first cert it
    // tries to verify.
    if (!ok && stream->peer_certificate_.get() != NULL) {
      X509* cert = X509_STORE_CTX_get_current_cert(store);
      int err = X509_STORE_CTX_get_error(store);
      // peer-to-peer mode: allow the certificate to be self-signed,
      // assuming it matches the cert that was specified.
      if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT &&
          X509_cmp(cert, stream->peer_certificate_->x509()) == 0) {
        LOG(LS_INFO) << "Accepted self-signed peer certificate authority";
        ok = 1;
      }
    } else if (!ok && !stream->peer_certificate_digest_algorithm_.empty()) {
      X509* cert = X509_STORE_CTX_get_current_cert(store);
      int err = X509_STORE_CTX_get_error(store);
  
      // peer-to-peer mode: allow the certificate to be self-signed,
      // assuming it matches the digest that was specified.
      if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
        unsigned char digest[EVP_MAX_MD_SIZE];
        std::size_t digest_length;
  
        if (OpenSSLCertificate::
           ComputeDigest(cert,
                         stream->peer_certificate_digest_algorithm_,
                         digest, sizeof(digest),
                         &digest_length)) {
          Buffer computed_digest(digest, digest_length);
          if (computed_digest == stream->peer_certificate_digest_value_) {
            LOG(LS_INFO) <<
                "Accepted self-signed peer certificate authority";
            ok = 1;
          }
        }
      }
    } else if (!ok && OpenSSLAdapter::custom_verify_callback_) {
      // this applies only in traditional mode
      void* cert =
          reinterpret_cast<void*>(X509_STORE_CTX_get_current_cert(store));
      if (OpenSSLAdapter::custom_verify_callback_(cert)) {
        stream->custom_verification_succeeded_ = true;
        LOG(LS_INFO) << "validated certificate using custom callback";
        ok = 1;
      }
    }
  
    if (!ok && stream->ignore_bad_cert()) {
      LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
      ok = 1;
    }
  
    return ok;
  }
  
  // This code is taken from the "Network Security with OpenSSL"
  // sample in chapter 5
  bool OpenSSLStreamAdapter::SSLPostConnectionCheck(SSL* ssl,
                                                    const char* server_name,
                                                    const X509* peer_cert,
                                                    const std::string
                                                    &peer_digest) {
    ASSERT(server_name != NULL);
    bool ok;
    if (server_name[0] != '\0') {  // traditional mode
      ok = OpenSSLAdapter::VerifyServerName(ssl, server_name, ignore_bad_cert());
  
      if (ok) {
        ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
              custom_verification_succeeded_);
      }
    } else {  // peer-to-peer mode
      ASSERT((peer_cert != NULL) || (!peer_digest.empty()));
      // no server name validation
      ok = true;
    }
  
    if (!ok && ignore_bad_cert()) {
      LOG(LS_ERROR) << "SSL_get_verify_result(ssl) = "
                    << SSL_get_verify_result(ssl);
      LOG(LS_INFO) << "Other TLS post connection checks failed.";
      ok = true;
    }
  
    return ok;
  }
  
  bool OpenSSLStreamAdapter::HaveDtls() {
  #ifdef HAVE_DTLS
    return true;
  #else
    return false;
  #endif
  }
  
  bool OpenSSLStreamAdapter::HaveDtlsSrtp() {
  #ifdef HAVE_DTLS_SRTP
    return true;
  #else
    return false;
  #endif
  }
  
  bool OpenSSLStreamAdapter::HaveExporter() {
  #ifdef HAVE_DTLS_SRTP
    return true;
  #else
    return false;
  #endif
  }
  
  }  // namespace talk_base
  
  #endif  // HAVE_OPENSSL_SSL_H
  