<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// OpenSSL binding for SSLClientSocket. The class layout and general principle
// of operation is derived from SSLClientSocketNSS.

#include "net/socket/ssl_client_socket_openssl.h"

#include <openssl/ssl.h>
#include <openssl/err.h>

#include "net/base/cert_verifier.h"
#include "net/base/net_errors.h"
#include "net/base/ssl_connection_status_flags.h"
#include "net/base/ssl_info.h"
#include "net/base/test_certificate_data.h"  // TODO(joth): Remove!!

namespace net {

namespace {

// Enable this to see logging for state machine state transitions.
#if 0
#define GotoState(s) do { DVLOG(2) << (void *)this << " " << __FUNCTION__ << \
                           " jump to state " << s; \
                           next_handshake_state_ = s; } while (0)
#else
#define GotoState(s) next_handshake_state_ = s
#endif

const size_t kMaxRecvBufferSize = 4096;
static SSL_CTX* g_ctx = NULL;
static int g_app_data_index = -1;

void MaybeLogSSLError() {
  int error_num;
  while ((error_num = ERR_get_error()) != 0) {
    char buf[128];  // this buffer must be at least 120 chars long.
    ERR_error_string_n(error_num, buf, arraysize(buf));
    DVLOG(1) << "SSL error " << error_num << ": " << buf;
  }
}

int MapOpenSSLError(int err) {
  switch (err) {
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
      return ERR_IO_PENDING;
    case SSL_ERROR_SYSCALL:
      DVLOG(1) << "OpenSSL SYSVCALL error, errno " << errno;
      MaybeLogSSLError();
      return ERR_SSL_PROTOCOL_ERROR;
    default:
      // TODO(joth): Implement full mapping.
      LOG(WARNING) << "Unknown OpenSSL error " << err;
      MaybeLogSSLError();
      return ERR_SSL_PROTOCOL_ERROR;
  }
}

// Registered with |g_ctx| as global verify callback handler; we unpack the
// SSLClientSocketOpenSSL instance associated with this callback and delegate
// the handling to it.
int VerifyCallback(int preverify_ok, X509_STORE_CTX* x509_ctx) {
  DCHECK_GE(g_app_data_index, 0);
  // Retrieve the pointer to the SSL of the connection currently treated
  // and from there, the application specific data stored in the SSL object.
  SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(
      x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
  DCHECK(ssl);
  SSLClientSocketOpenSSL* self = static_cast<SSLClientSocketOpenSSL*>(
      SSL_get_ex_data(ssl, g_app_data_index));
  DCHECK(self);
  return self->SSLVerifyCallback(preverify_ok, ssl, x509_ctx);
}

}  // namespace

SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
    ClientSocketHandle* transport_socket,
    const std::string& hostname,
    const SSLConfig& ssl_config)
    : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_(
          this, &SSLClientSocketOpenSSL::BufferSendComplete)),
      ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_(
          this, &SSLClientSocketOpenSSL::BufferRecvComplete)),
      transport_send_busy_(false),
      transport_recv_busy_(false),
      user_connect_callback_(NULL),
      user_read_callback_(NULL),
      user_write_callback_(NULL),
      client_auth_cert_needed_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(handshake_io_callback_(
          this, &SSLClientSocketOpenSSL::OnHandshakeIOComplete)),
      ssl_(NULL),
      transport_bio_(NULL),
      transport_(transport_socket),
      hostname_(hostname),
      ssl_config_(ssl_config),
      completed_handshake_(false),
      net_log_(transport_socket->socket()->NetLog()) {
}

SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
  Disconnect();
}

// TODO(joth): This method needs to be thread-safe.
bool SSLClientSocketOpenSSL::InitOpenSSL() {
  if (g_ctx)
    return true;

  SSL_load_error_strings();
  MaybeLogSSLError();
  SSL_library_init();
  MaybeLogSSLError();

  g_ctx = SSL_CTX_new(TLSv1_client_method());

  if (!g_ctx) {
    MaybeLogSSLError();
    return false;
  }
  g_app_data_index = SSL_get_ex_new_index(__LINE__, g_ctx, NULL, NULL, NULL);
  DCHECK_GE(g_app_data_index, 0);

  SSL_CTX_set_verify(g_ctx, SSL_VERIFY_PEER, VerifyCallback);

  // For now, just let OpenSSL load CA certs directly from the filesystem.
  if (!SSL_CTX_set_default_verify_paths(g_ctx)) {
    MaybeLogSSLError();
    return false;
  }

  return true;
}

bool SSLClientSocketOpenSSL::Init() {
  DCHECK(g_ctx);
  ssl_ = SSL_new(g_ctx);
  if (!ssl_) {
    MaybeLogSSLError();
    return false;
  }

  if (!SSL_set_ex_data(ssl_, g_app_data_index, this)) {
    MaybeLogSSLError();
    return false;
  }

  if (!SSL_set_tlsext_host_name(ssl_, hostname_.c_str())) {
    MaybeLogSSLError();
    return false;
  }

  BIO* ssl_bio = NULL;
  // TODO(joth): Provide explicit write buffer sizes, rather than use defaults?
  if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) {
    MaybeLogSSLError();
    return false;
  }
  DCHECK(ssl_bio);
  DCHECK(transport_bio_);

  SSL_set_bio(ssl_, ssl_bio, ssl_bio);

  return true;
}

int SSLClientSocketOpenSSL::SSLVerifyCallback(int preverify_ok,
                                              SSL* ssl,
                                              X509_STORE_CTX* x509_ctx) {
  DCHECK_EQ(ssl_, ssl);
  int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
  DVLOG(preverify_ok ? 3 : 1) << "SSLVerifyCallback " << preverify_ok
                              << " depth " << depth;
  MaybeLogSSLError();
  return preverify_ok;
}

// SSLClientSocket methods

void SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
  ssl_info->Reset();
  if (!server_cert_)
    return;

  // Chrome DCHECKs that https pages provide a valid cert. For now (whilst in
  // early dev) we just create a spoof cert.
  // TODO(joth): implement X509Certificate for OpenSSL and remove this hack.
  LOG(WARNING) << "Filling in certificate with bogus content";
  ssl_info->cert = server_cert_;
  ssl_info->cert_status = server_cert_verify_result_.cert_status;
  ssl_info->security_bits = 56;
  ssl_info->connection_status =
      ((TLS1_CK_RSA_EXPORT1024_WITH_DES_CBC_SHA) &
          SSL_CONNECTION_CIPHERSUITE_MASK) << SSL_CONNECTION_CIPHERSUITE_SHIFT;
}

void SSLClientSocketOpenSSL::GetSSLCertRequestInfo(
    SSLCertRequestInfo* cert_request_info) {
  NOTREACHED();
}
<commit_msg>OpenSSL socket: Add SSLv3 fallback support<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// OpenSSL binding for SSLClientSocket. The class layout and general principle
// of operation is derived from SSLClientSocketNSS.

#include "net/socket/ssl_client_socket_openssl.h"

#include <openssl/ssl.h>
#include <openssl/err.h>

#include "net/base/cert_verifier.h"
#include "base/histogram.h"
#include "net/base/net_errors.h"
#include "net/base/ssl_connection_status_flags.h"
#include "net/base/ssl_info.h"
#include "net/base/test_certificate_data.h"  // TODO(joth): Remove!!

namespace net {

namespace {

// Enable this to see logging for state machine state transitions.
#if 0
#define GotoState(s) do { DVLOG(2) << (void *)this << " " << __FUNCTION__ << \
                           " jump to state " << s; \
                           next_handshake_state_ = s; } while (0)
#else
#define GotoState(s) next_handshake_state_ = s
#endif

const size_t kMaxRecvBufferSize = 4096;
static SSL_CTX* g_ctx = NULL;
static int g_app_data_index = -1;

void MaybeLogSSLError() {
  int error_num;
  while ((error_num = ERR_get_error()) != 0) {
    char buf[128];  // this buffer must be at least 120 chars long.
    ERR_error_string_n(error_num, buf, arraysize(buf));
    DVLOG(1) << "SSL error " << error_num << ": " << buf;
  }
}

int MapOpenSSLError(int err) {
  switch (err) {
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
      return ERR_IO_PENDING;
    case SSL_ERROR_SYSCALL:
      DVLOG(1) << "OpenSSL SYSVCALL error, errno " << errno;
      MaybeLogSSLError();
      return ERR_SSL_PROTOCOL_ERROR;
    default:
      // TODO(joth): Implement full mapping.
      LOG(WARNING) << "Unknown OpenSSL error " << err;
      MaybeLogSSLError();
      return ERR_SSL_PROTOCOL_ERROR;
  }
}

// Registered with |g_ctx| as global verify callback handler; we unpack the
// SSLClientSocketOpenSSL instance associated with this callback and delegate
// the handling to it.
int VerifyCallback(int preverify_ok, X509_STORE_CTX* x509_ctx) {
  DCHECK_GE(g_app_data_index, 0);
  // Retrieve the pointer to the SSL of the connection currently treated
  // and from there, the application specific data stored in the SSL object.
  SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(
      x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
  DCHECK(ssl);
  SSLClientSocketOpenSSL* self = static_cast<SSLClientSocketOpenSSL*>(
      SSL_get_ex_data(ssl, g_app_data_index));
  DCHECK(self);
  return self->SSLVerifyCallback(preverify_ok, ssl, x509_ctx);
}

}  // namespace

SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
    ClientSocketHandle* transport_socket,
    const std::string& hostname,
    const SSLConfig& ssl_config)
    : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_(
          this, &SSLClientSocketOpenSSL::BufferSendComplete)),
      ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_(
          this, &SSLClientSocketOpenSSL::BufferRecvComplete)),
      transport_send_busy_(false),
      transport_recv_busy_(false),
      user_connect_callback_(NULL),
      user_read_callback_(NULL),
      user_write_callback_(NULL),
      client_auth_cert_needed_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(handshake_io_callback_(
          this, &SSLClientSocketOpenSSL::OnHandshakeIOComplete)),
      ssl_(NULL),
      transport_bio_(NULL),
      transport_(transport_socket),
      hostname_(hostname),
      ssl_config_(ssl_config),
      completed_handshake_(false),
      net_log_(transport_socket->socket()->NetLog()) {
}

SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
  Disconnect();
}

// TODO(joth): This method needs to be thread-safe.
bool SSLClientSocketOpenSSL::InitOpenSSL() {
  if (g_ctx)
    return true;

  SSL_load_error_strings();
  MaybeLogSSLError();
  SSL_library_init();
  MaybeLogSSLError();

  // Allow all versions here; we disable the unneeded ones according to the
  // SSL config options in Init().
  g_ctx = SSL_CTX_new(SSLv23_client_method());

  if (!g_ctx) {
    MaybeLogSSLError();
    return false;
  }
  g_app_data_index = SSL_get_ex_new_index(__LINE__, g_ctx, NULL, NULL, NULL);
  DCHECK_GE(g_app_data_index, 0);

  SSL_CTX_set_verify(g_ctx, SSL_VERIFY_PEER, VerifyCallback);

  // For now, just let OpenSSL load CA certs directly from the filesystem.
  if (!SSL_CTX_set_default_verify_paths(g_ctx)) {
    MaybeLogSSLError();
    return false;
  }

  return true;
}

bool SSLClientSocketOpenSSL::Init() {
  DCHECK(g_ctx);
  DCHECK(!ssl_);
  DCHECK(!transport_bio_);

  ssl_ = SSL_new(g_ctx);
  if (!ssl_) {
    MaybeLogSSLError();
    return false;
  }

  if (!SSL_set_ex_data(ssl_, g_app_data_index, this)) {
    MaybeLogSSLError();
    return false;
  }

  if (!SSL_set_tlsext_host_name(ssl_, hostname_.c_str())) {
    MaybeLogSSLError();
    return false;
  }

  BIO* ssl_bio = NULL;
  // TODO(joth): Provide explicit write buffer sizes, rather than use defaults?
  if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) {
    MaybeLogSSLError();
    return false;
  }
  DCHECK(ssl_bio);
  DCHECK(transport_bio_);

  SSL_set_bio(ssl_, ssl_bio, ssl_bio);

#define SET_SSL_CONFIG_OPTION(option, value)   \
      (((value) ? set_mask : clear_mask) |= (option))

  // OpenSSL defaults some options to on, others to off. To avoid ambiguity,
  // set everything we care about to an absolute value.
  long set_mask = 0;
  long clear_mask = 0;
  SET_SSL_CONFIG_OPTION(SSL_OP_NO_SSLv2, !ssl_config_.ssl2_enabled);
  SET_SSL_CONFIG_OPTION(SSL_OP_NO_SSLv3, !ssl_config_.ssl3_enabled);
  SET_SSL_CONFIG_OPTION(SSL_OP_NO_TLSv1, !ssl_config_.tls1_enabled);

  // TODO(joth): Set this conditionally, see http://crbug.com/55410
  SET_SSL_CONFIG_OPTION(SSL_OP_LEGACY_SERVER_CONNECT, true);

  // Make sure we haven't got any intersection in the set & clear options.
  DCHECK_EQ(0, set_mask & clear_mask);

  SSL_set_options(ssl_, set_mask);
  SSL_clear_options(ssl_, clear_mask);
#undef SET_SSL_CONFIG_OPTION

  return true;
}

int SSLClientSocketOpenSSL::SSLVerifyCallback(int preverify_ok,
                                              SSL* ssl,
                                              X509_STORE_CTX* x509_ctx) {
  DCHECK_EQ(ssl_, ssl);
  int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
  DVLOG(preverify_ok ? 3 : 1) << "SSLVerifyCallback " << preverify_ok
                              << " depth " << depth;
  MaybeLogSSLError();
  return preverify_ok;
}

// SSLClientSocket methods

void SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
  ssl_info->Reset();
  if (!server_cert_)
    return;

  // Chrome DCHECKs that https pages provide a valid cert. For now (whilst in
  // early dev) we just create a spoof cert.
  // TODO(joth): implement X509Certificate for OpenSSL and remove this hack.
  LOG(WARNING) << "Filling in certificate with bogus content";
  ssl_info->cert = server_cert_;
  ssl_info->cert_status = server_cert_verify_result_.cert_status;
  ssl_info->security_bits = 56;
  ssl_info->connection_status =
      ((TLS1_CK_RSA_EXPORT1024_WITH_DES_CBC_SHA) &
          SSL_CONNECTION_CIPHERSUITE_MASK) << SSL_CONNECTION_CIPHERSUITE_SHIFT;

  bool peer_supports_renego_ext = !!SSL_get_secure_renegotiation_support(ssl_);
  if (!peer_supports_renego_ext)
    ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION;
    UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported",
                              (int)peer_supports_renego_ext, 2);

  if (ssl_config_.ssl3_fallback)
    ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK;
}

void SSLClientSocketOpenSSL::GetSSLCertRequestInfo(
    SSLCertRequestInfo* cert_request_info) {
  NOTREACHED();
}

{'fraction_non_alphanumeric': 0.10243141702700786, 'fraction_numerical': 0.010987453037499114, 'mean_word_length': 3.9071304347826086, 'pattern_counts': {'":': 2, '<': 87, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3616336', 'n_tokens_mistral': 4925, 'n_tokens_neox': 4755, 'n_words': 1037}