Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Enable compact index when OpenSSL FIPS mode is enabled but not active #5440

Merged
merged 3 commits into from
Feb 18, 2017

Conversation

wjordan
Copy link
Contributor

@wjordan wjordan commented Feb 16, 2017

Fixes #5433. Since there is no easy accessor in Ruby to detect whether or not FIPS mode is currently active, the best approach I could come up with is to fork a separate process and attempt to generate a build MD5 object as a test of whether MD5 module is currently available.

Because fork approach won't work on some platforms (JRuby, Windows etc), md5_supported? returns false on any platforms where FIPS mode is enabled and Process.respond_to?(:fork) is false.

I've added a spec that simulates behavior when OpenSSL FIPS mode is active - an error message is output to STDERR and the process is killed with the ABRT signal.

@colby-swandale
Copy link
Member

colby-swandale commented Feb 16, 2017

How does this behave on Windows environments? Specifically when you're writing to /dev/null

@wjordan
Copy link
Contributor Author

wjordan commented Feb 16, 2017

I believe that md5_supported? will continue to return false on windows environments, assuming that Process.respond_to?(:fork) returns false in that environment. However, because Bundler's tests don't cover the Windows platform (at least doesn't appear to at first glance) that isn't something I can easily confirm.

@@ -122,14 +122,24 @@ def call(path, headers)
end

def md5_available?
return true unless fips_enabled? && Process.respond_to?(:fork)
pid = fork do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think forking on every Bundler process is going to be acceptable performance-wise (in addition to all the platforms that aren't fork-safe) -- is there no way to determine in-process if the MD5 digest is available?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Platforms that aren't fork-safe will not reach this line of code because Process.respond_to?(:fork) will be false.
  • Performance-wise, I expect a process fork to perform better than disabling the compact-index entirely, so for the case where FIPS mode is available but not active this should be a performance improvement. The tradeoff is that this causes the case where FIPS mode is active to perform the fork.
  • As far as I can tell, there is no way via Ruby's stdlib to determine in-process if the MD5 digest is available. The process is aborted as soon as OpenSSH's MD5 module is invoked, so in order to detect this failure without aborting the parent process, it's necessary to perform the test on a child process.

@wjordan
Copy link
Contributor Author

wjordan commented Feb 16, 2017

I don't think this PR is a great solution, but it demonstrates the best I can do to extend #5222's detection-based approach to the underlying FIPS compatibility issue (#4989). I would actually prefer to see #5222 reverted and pursue an alternate solution to FIPS mode support in Bundler.

@colby-swandale
Copy link
Member

The solution to #5222 is the best we have currently, some users were unable to use Bundler when FIPS was enabled so loosing Compact Index is a suitable compromise. I'll ask the OpenSSL gem maintainers if we can work to improve the API to better detect FIPS.

@wjordan wjordan mentioned this pull request Feb 16, 2017
@colby-swandale
Copy link
Member

Looking at the OpenSSL gem src there is a method called .fips_mode that looks like it can be used to check if FIPS is actually enabled.

@simi
Copy link
Member

simi commented Feb 16, 2017

@colby-swandale it was introduced in Ruby 2.0 and it is just setter (fips_mode = true).

You can check if fips mode is enabled by this code:

require 'openssl'
require 'fiddle'
puts Fiddle::Function.new(Fiddle.dlopen(nil)["FIPS_mode"], [], Fiddle::TYPE_INT).call()

I can try to open PR to Ruby OpenSSL to add this as OpenSSL.fips_mode getter.

@rhenium
Copy link
Contributor

rhenium commented Feb 17, 2017

Alternatively OpenSSL::Digest which uses the EVP interface can be used.

def md5_available?
  return true unless defined?(OpenSSL)

  OpenSSL::Digest::MD5.digest("")
  true
rescue OpenSSL::Digest::DigestError
  false
end

@colby-swandale
Copy link
Member

colby-swandale commented Feb 17, 2017

@rhenium Does that catch the C error that gets printed? See #4989

md5_dgst.c(80): OpenSSL internal error, assertion failed: Digest MD5 forbidden in FIPS mode!

@rhenium
Copy link
Contributor

rhenium commented Feb 17, 2017

@rhenium Does that catch the C error that gets printed? See #5222

Yes. That code will not reach MD5_Init() that aborts the process.

@wjordan
Copy link
Contributor Author

wjordan commented Feb 17, 2017

Thanks @rhenium and @simi! I didn't know about those two Ruby alternatives. I've updated the PR to use OpenSSL::Digest::MD5 to test MD5 availability- this gives a more general MD5 check that doesn't require Bundler to fork a separate process or even reference the FIPS-mode API directly.

# OpenSSL writes to STDERR and kills the current process with SIGABRT
# when FIPS mode prevents MD5 from being used.
$stderr.write "Digest MD5 forbidden in FIPS mode!"
Process.kill("ABRT", Process.pid)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is not necessary anymore for the spec to run correctly, I can remove.

@segiddins
Copy link
Member

Looks good to me!
@indirect r?

@segiddins segiddins added this to the 1.14.5 milestone Feb 17, 2017
@indirect
Copy link
Member

This is awesome teamwork, everyone. Thanks so much!

@bundlerbot r+

@bundlerbot
Copy link
Collaborator

📌 Commit 21b3358 has been approved by indirect

@bundlerbot
Copy link
Collaborator

⌛ Testing commit 21b3358 with merge 13f4cc1...

bundlerbot added a commit that referenced this pull request Feb 18, 2017
Enable compact index when OpenSSL FIPS mode is enabled but not active

Fixes #5433. Since there is no easy accessor in Ruby to detect whether or not FIPS mode is currently active, the best approach I could come up with is to `fork` a separate process and attempt to generate a build MD5 object as a test of whether MD5 module is currently available.

Because `fork` approach won't work on some platforms (JRuby, Windows etc), `md5_supported?` returns `false` on any platforms where FIPS mode is enabled and `Process.respond_to?(:fork)` is `false`.

I've added a spec that simulates behavior when OpenSSL FIPS mode is active - an error message is output to STDERR and the process is killed with the `ABRT` signal.
@bundlerbot
Copy link
Collaborator

☀️ Test successful - status-travis
Approved by: indirect
Pushing 13f4cc1 to master...

@bundlerbot bundlerbot merged commit 21b3358 into rubygems:master Feb 18, 2017
segiddins pushed a commit that referenced this pull request Feb 22, 2017
Enable compact index when OpenSSL FIPS mode is enabled but not active

Fixes #5433. Since there is no easy accessor in Ruby to detect whether or not FIPS mode is currently active, the best approach I could come up with is to `fork` a separate process and attempt to generate a build MD5 object as a test of whether MD5 module is currently available.

Because `fork` approach won't work on some platforms (JRuby, Windows etc), `md5_supported?` returns `false` on any platforms where FIPS mode is enabled and `Process.respond_to?(:fork)` is `false`.

I've added a spec that simulates behavior when OpenSSL FIPS mode is active - an error message is output to STDERR and the process is killed with the `ABRT` signal.

(cherry picked from commit 13f4cc1)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants