123456789_123456789_123456789_123456789_123456789_

Class: Puma::MiniSSL::SSLContext

Relationships & Source Files
Inherits: Object
Defined in: ext/puma_http11/mini_ssl.c

Class Method Summary

Constructor Details

.new(mini_ssl_ctx)

[ GitHub ]

  
# File 'ext/puma_http11/mini_ssl.c', line 210

VALUE
sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
  SSL_CTX* ctx;

#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
  int min;
#endif
  int ssl_options;
  VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
    verification_flags, session_id_bytes, cert_pem, key_pem;
#ifndef HAVE_SSL_CTX_SET_DH_AUTO
  DH *dh;
#endif
  BIO *bio;
  X509 *x509;
  EVP_PKEY *pkey;

#if OPENSSL_VERSION_NUMBER < 0x10002000L
  EC_KEY *ecdh;
#endif

  TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);

  key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);

  cert = rb_funcall(mini_ssl_ctx, rb_intern_const("cert"), 0);

  ca = rb_funcall(mini_ssl_ctx, rb_intern_const("ca"), 0);

  cert_pem = rb_funcall(mini_ssl_ctx, rb_intern_const("cert_pem"), 0);

  key_pem = rb_funcall(mini_ssl_ctx, rb_intern_const("key_pem"), 0);

  verify_mode = rb_funcall(mini_ssl_ctx, rb_intern_const("verify_mode"), 0);

  ssl_cipher_filter = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_cipher_filter"), 0);

  no_tlsv1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1"), 0);

  no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);

  if (!NIL_P(cert)) {
    StringValue(cert);

    if (SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert)) != 1) {
      raise_file_error("SSL_CTX_use_certificate_chain_file", RSTRING_PTR(cert));
    }
  }

  if (!NIL_P(key)) {
    StringValue(key);

    if (SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM) != 1) {
      raise_file_error("SSL_CTX_use_PrivateKey_file", RSTRING_PTR(key));
    }
  }

  if (!NIL_P(cert_pem)) {
    bio = BIO_new(BIO_s_mem());
    BIO_puts(bio, RSTRING_PTR(cert_pem));
    x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);

    if (SSL_CTX_use_certificate(ctx, x509) != 1) {
      raise_file_error("SSL_CTX_use_certificate", RSTRING_PTR(cert_pem));
    }
  }

  if (!NIL_P(key_pem)) {
    bio = BIO_new(BIO_s_mem());
    BIO_puts(bio, RSTRING_PTR(key_pem));
    pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

    if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
      raise_file_error("SSL_CTX_use_PrivateKey", RSTRING_PTR(key_pem));
    }
  }

  verification_flags = rb_funcall(mini_ssl_ctx, rb_intern_const("verification_flags"), 0);

  if (!NIL_P(verification_flags)) {
    X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ctx);
    X509_VERIFY_PARAM_set_flags(param, NUM2INT(verification_flags));
    SSL_CTX_set1_param(ctx, param);
  }

  if (!NIL_P(ca)) {
    StringValue(ca);
    if (SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL) != 1) {
      raise_file_error("SSL_CTX_load_verify_locations", RSTRING_PTR(ca));
    }
  }

  ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;

#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
  if (RTEST(no_tlsv1_1)) {
    min = TLS1_2_VERSION;
  }
  else if (RTEST(no_tlsv1)) {
    min = TLS1_1_VERSION;
  }
  else {
    min = TLS1_VERSION;
  }

  SSL_CTX_set_min_proto_version(ctx, min);

  SSL_CTX_set_options(ctx, ssl_options);

#else
  /* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
  ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;

  if (RTEST(no_tlsv1)) {
    ssl_options |= SSL_OP_NO_TLSv1;
  }
  if(RTEST(no_tlsv1_1)) {
    ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
  }
  SSL_CTX_set_options(ctx, ssl_options);
#endif

  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);

  if (!NIL_P(ssl_cipher_filter)) {
    StringValue(ssl_cipher_filter);
    SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter));
  }
  else {
    SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
  }

#if OPENSSL_VERSION_NUMBER < 0x10002000L
  // Remove this case if OpenSSL 1.0.1 (now EOL) support is no
  // longer needed.
  ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
  if (ecdh) {
    SSL_CTX_set_tmp_ecdh(ctx, ecdh);
    EC_KEY_free(ecdh);
  }
#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
  SSL_CTX_set_ecdh_auto(ctx, 1);
#endif

  if (NIL_P(verify_mode)) {
    /* SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); */
  } else {
    SSL_CTX_set_verify(ctx, NUM2INT(verify_mode), engine_verify_callback);
  }

  // Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
  session_id_bytes = rb_funcall(
#ifdef HAVE_RANDOM_BYTES
    rb_cRandom,
#else
    rb_const_get(rb_cRandom, rb_intern_const("DEFAULT")),
#endif
    rb_intern_const("bytes"),
    1, ULL2NUM(SSL_MAX_SSL_SESSION_ID_LENGTH));

  SSL_CTX_set_session_id_context(ctx,
                                 (unsigned char *) RSTRING_PTR(session_id_bytes),
                                 SSL_MAX_SSL_SESSION_ID_LENGTH);

  // printf("\ninitialize end security_level %d\n", SSL_CTX_get_security_level(ctx));

#ifdef HAVE_SSL_CTX_SET_DH_AUTO
  // https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_dh_auto.html
  SSL_CTX_set_dh_auto(ctx, 1);
#else
  dh = get_dh2048();
  SSL_CTX_set_tmp_dh(ctx, dh);
#endif

  rb_obj_freeze(self);
  return self;
}