From 415b006ebdce4c21635273062ff2567c7e34b248 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Wed, 24 May 2023 15:31:47 -0700 Subject: [PATCH 1/4] Add helper for loading OpenSSL providers On OpenSSL 3, engines have been deprecated being replaced by providers. The Ruby openssl library supported loading specific engines, but there is no replacement currently using providers. The winrm communicator specifically relies on a MD4 which OpenSSL has marked as legacy and no longer loads by default. The extension included loads the legacy provider as well as the default provider. The legacy provider includes MD4, thus allowing winrm to function again. --- .gitignore | 4 ++++ Rakefile | 5 +++++ ext/vagrant_ssl/extconf.rb | 11 +++++++++++ ext/vagrant_ssl/vagrant_ssl.c | 32 ++++++++++++++++++++++++++++++++ ext/vagrant_ssl/vagrant_ssl.h | 14 ++++++++++++++ lib/vagrant.rb | 16 ++++++++++++++++ vagrant.gemspec | 2 ++ 7 files changed, 84 insertions(+) create mode 100644 ext/vagrant_ssl/extconf.rb create mode 100644 ext/vagrant_ssl/vagrant_ssl.c create mode 100644 ext/vagrant_ssl/vagrant_ssl.h diff --git a/.gitignore b/.gitignore index ec3ec3c50..557a08f97 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,7 @@ __debug_bin # Ignore generated binaries bin/vagrant-go* + +# extension +tmp* +lib/vagrant/vagrant_ssl.so diff --git a/Rakefile b/Rakefile index 2af398a1e..34ce6386e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,11 +1,16 @@ require 'rubygems' require 'bundler/setup' +require "rake/extensiontask" # Immediately sync all stdout so that tools like buildbot can # immediately load in the output. $stdout.sync = true $stderr.sync = true +Rake::ExtensionTask.new "vagrant_ssl" do |ext| + ext.lib_dir = "lib/vagrant" +end + # Load all the rake tasks from the "tasks" folder. This folder # allows us to nicely separate rake tasks into individual files # based on their role, which makes development and debugging easier diff --git a/ext/vagrant_ssl/extconf.rb b/ext/vagrant_ssl/extconf.rb new file mode 100644 index 000000000..44b307d1b --- /dev/null +++ b/ext/vagrant_ssl/extconf.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +require "mkmf" + +if have_header("openssl/opensslv.h") + if ENV["LDFLAGS"] + append_ldflags(ENV["LDFLAGS"].split(" ")) + end + append_ldflags(["-lssl"]) + create_makefile("vagrant_ssl") +end diff --git a/ext/vagrant_ssl/vagrant_ssl.c b/ext/vagrant_ssl/vagrant_ssl.c new file mode 100644 index 000000000..4c65c26e1 --- /dev/null +++ b/ext/vagrant_ssl/vagrant_ssl.c @@ -0,0 +1,32 @@ +#include "vagrant_ssl.h" + +#if defined(_VAGRANT_SSL_PROVIDER_) + +static VALUE vagrant_ssl_load(VALUE self) { + OSSL_PROVIDER *legacy; + OSSL_PROVIDER *deflt; + + legacy = OSSL_PROVIDER_load(NULL, "legacy"); + if(legacy == NULL) { + rb_raise(rb_eStandardError, "Failed to load OpenSSL legacy provider"); + return self; + } + + deflt = OSSL_PROVIDER_load(NULL, "default"); + if(deflt == NULL) { + rb_raise(rb_eStandardError, "Failed to load OpenSSL default provider"); + return self; + } +} + +void Init_vagrant_ssl(void) { + VALUE vagrant; + vagrant = rb_define_module("Vagrant"); + rb_define_singleton_method(vagrant, "vagrant_ssl_load", vagrant_ssl_load, 0); +} + +#else + +void Init_vagrant_ssl(void) {} + +#endif diff --git a/ext/vagrant_ssl/vagrant_ssl.h b/ext/vagrant_ssl/vagrant_ssl.h new file mode 100644 index 000000000..4f9eccf14 --- /dev/null +++ b/ext/vagrant_ssl/vagrant_ssl.h @@ -0,0 +1,14 @@ +#if !defined(_VAGRANT_SSL_H_) +#define _VAGRANT_SSL_H_ + +#include +#if OPENSSL_VERSION_NUMBER >= (3 << 28) +#define _VAGRANT_SSL_PROVIDER_ + +#include +#include +#endif + +void Init_vagrant_ssl(void); + +#endif diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 0aa58a582..78adf49b2 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -121,6 +121,22 @@ ENV.each do |k, v| global_logger.info("#{k}=#{v.inspect}") if k.start_with?("VAGRANT_") end +# If the vagrant_ssl library exists, a recent version +# of openssl is in use and its needed to load all the +# providers needed +if File.exist?(File.expand_path("vagrant/vagrant_ssl.so", __dir__)) + global_logger.debug("vagrant ssl helper found for loading ssl providers") + begin + require "vagrant/vagrant_ssl" + Vagrant.vagrant_ssl_load + global_logger.debug("ssl providers successfully loaded") + rescue LoadError => err + global_logger.warn("failed to load ssl providers, attempting to continue (#{err})") + rescue => err + global_logger.warn("unexpected failure loading ssl providers, attempting to continue (#{err})") + end +end + # We need these components always so instead of an autoload we # just require them explicitly here. require "vagrant/plugin" diff --git a/vagrant.gemspec b/vagrant.gemspec index eb595a41e..2a740089a 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -49,6 +49,7 @@ Gem::Specification.new do |s| # Constraint rake to properly handle deprecated method usage # from within rspec s.add_development_dependency "rake", "~> 13.0" + s.add_development_dependency "rake-compiler" s.add_development_dependency "rspec", "~> 3.11" s.add_development_dependency "rspec-its", "~> 1.3.0" s.add_development_dependency "fake_ftp", "~> 0.3.0" @@ -104,5 +105,6 @@ Gem::Specification.new do |s| s.files = unignored_files s.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact + s.extensions = ["ext/vagrant_ssl/extconf.rb"] s.require_path = 'lib' end From e3c8ad33ffae87595e495404e55d5c52d7852844 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Thu, 25 May 2023 16:36:44 -0700 Subject: [PATCH 2/4] Properly handle when header file is not found When header file is not found, generate a dummy Makefile and target result so extension build will be successful but essentially a no-op --- ext/vagrant_ssl/extconf.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/vagrant_ssl/extconf.rb b/ext/vagrant_ssl/extconf.rb index 44b307d1b..b551b18ae 100644 --- a/ext/vagrant_ssl/extconf.rb +++ b/ext/vagrant_ssl/extconf.rb @@ -8,4 +8,11 @@ if have_header("openssl/opensslv.h") end append_ldflags(["-lssl"]) create_makefile("vagrant_ssl") +else + # If the header file isn't found, just create a dummy + # Makefile and stub the library to make it a noop + File.open("Makefile", "wb") do |f| + f.write(dummy_makefile(__dir__).join("\n")) + end + FileUtils.touch("vagrant_ssl.so") end From 75faafaa3a5fdddbc823525768e19a1d1cdaca59 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Fri, 26 May 2023 09:14:12 -0700 Subject: [PATCH 3/4] Update flags prior to checks and makefile generation --- ext/vagrant_ssl/extconf.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ext/vagrant_ssl/extconf.rb b/ext/vagrant_ssl/extconf.rb index b551b18ae..43bc44a49 100644 --- a/ext/vagrant_ssl/extconf.rb +++ b/ext/vagrant_ssl/extconf.rb @@ -1,11 +1,14 @@ #!/usr/bin/env ruby require "mkmf" +require "shellwords" + +# If extra flags are included via the environment, append them +append_cflags(Shellwords.shellwords(ENV["CFLAGS"])) if ENV["CFLAGS"] +append_cppflags(Shellwords.shellwords(ENV["CPPFLAGS"])) if ENV["CPPFLAGS"] +append_ldflags(Shellwords.shellwords(ENV["LDFLAGS"])) if ENV["LDFLAGS"] if have_header("openssl/opensslv.h") - if ENV["LDFLAGS"] - append_ldflags(ENV["LDFLAGS"].split(" ")) - end append_ldflags(["-lssl"]) create_makefile("vagrant_ssl") else From f14883dc892839200e4f877600f052544502bc93 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Fri, 26 May 2023 09:38:52 -0700 Subject: [PATCH 4/4] Include libcrypto when linking --- ext/vagrant_ssl/extconf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/vagrant_ssl/extconf.rb b/ext/vagrant_ssl/extconf.rb index 43bc44a49..d85c3b210 100644 --- a/ext/vagrant_ssl/extconf.rb +++ b/ext/vagrant_ssl/extconf.rb @@ -9,7 +9,7 @@ append_cppflags(Shellwords.shellwords(ENV["CPPFLAGS"])) if ENV["CPPFLAGS"] append_ldflags(Shellwords.shellwords(ENV["LDFLAGS"])) if ENV["LDFLAGS"] if have_header("openssl/opensslv.h") - append_ldflags(["-lssl"]) + append_ldflags(["-lssl", "-lcrypto"]) create_makefile("vagrant_ssl") else # If the header file isn't found, just create a dummy