class Puma::MiniSSL::SSLContext
Public Class Methods
new(p1)
click to toggle source
VALUE sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) { SSL_CTX* ctx; 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, 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); 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 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; }