# File 'ext/puma_http11/mini_ssl.c', line 228
VALUE
sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
SSL_CTX* ctx;
int ssl_options;
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, ssl_ciphersuites, no_tlsv1, no_tlsv1_1,
verification_flags, session_id_bytes, cert_pem, key_pem, key_password_command, key_password;
BIO *bio;
X509 *x509 = NULL;
EVP_PKEY *pkey;
pem_password_cb *password_cb = NULL;
const char *password = NULL;
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
int min;
#endif
#ifndef HAVE_SSL_CTX_SET_DH_AUTO
DH *dh;
#endif
#if OPENSSL_VERSION_NUMBER < 0x10002000L
EC_KEY *ecdh;
#endif
#ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
VALUE reuse, reuse_cache_size, reuse_timeout;
reuse = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse"), 0);
reuse_cache_size = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse_cache_size"), 0);
reuse_timeout = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse_timeout"), 0);
#endif
key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);
key_password_command = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password_command"), 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);
ssl_ciphersuites = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_ciphersuites"), 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);
TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);
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_password_command)) {
key_password = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password"), 0);
if (!NIL_P(key_password)) {
StringValue(key_password);
password_cb = password_callback;
password = RSTRING_PTR(key_password);
SSL_CTX_set_default_passwd_cb(ctx, password_cb);
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) password);
}
}
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)) {
X509 *ca = NULL;
unsigned long err;
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, RSTRING_PTR(cert_pem));
/**
* Much of this pulled as a simplified version of the `use_certificate_chain_file` method
* from openssl's `ssl_rsa.c` file.
*/
/* first read the cert as the first item in the pem file */
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (NULL == x509) {
BIO_free_all(bio);
raise_param_error("PEM_read_bio_X509", "cert_pem");
}
/* Add the cert to the context */
/* 1 is success - otherwise check the error codes */
if (1 != SSL_CTX_use_certificate(ctx, x509)) {
BIO_free_all(bio);
raise_param_error("SSL_CTX_use_certificate", "cert_pem");
}
X509_free(x509); /* no longer need our reference */
/* Now lets load up the rest of the certificate chain */
/* 1 is success 0 is error */
if (0 == SSL_CTX_clear_chain_certs(ctx)) {
BIO_free_all(bio);
raise_param_error("SSL_CTX_clear_chain_certs","cert_pem");
}
while (1) {
ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (NULL == ca) {
break;
}
if (0 == SSL_CTX_add0_chain_cert(ctx, ca)) {
BIO_free_all(bio);
raise_param_error("SSL_CTX_add0_chain_cert","cert_pem");
}
/* don't free ca - its now owned by the context */
}
/* ca is NULL - so its either the end of the file or an error */
err = ERR_peek_last_error();
/* If its the end of the file - then we are done, in any case free the bio */
BIO_free_all(bio);
if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
ERR_clear_error();
} else {
raise_param_error("PEM_read_bio_X509","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, password_cb, (void *) password);
if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
BIO_free(bio);
raise_file_error("SSL_CTX_use_PrivateKey", RSTRING_PTR(key_pem));
}
EVP_PKEY_free(pkey);
BIO_free(bio);
}
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);
#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;
}
#endif
#ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
if (!NIL_P(reuse)) {
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
if (!NIL_P(reuse_cache_size)) {
SSL_CTX_sess_set_cache_size(ctx, NUM2INT(reuse_cache_size));
}
if (!NIL_P(reuse_timeout)) {
SSL_CTX_set_timeout(ctx, NUM2INT(reuse_timeout));
}
} else {
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
}
#endif
SSL_CTX_set_options(ctx, ssl_options);
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 HAVE_SSL_CTX_SET_CIPHERSUITES
// Only override OpenSSL default ciphersuites if config option is supplied.
if (!NIL_P(ssl_ciphersuites)) {
StringValue(ssl_ciphersuites);
SSL_CTX_set_ciphersuites(ctx, RSTRING_PTR(ssl_ciphersuites));
}
#endif
#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;
}