From 3c0d330b6999a33d30cf632b77bdab91c0a15b33 Mon Sep 17 00:00:00 2001 From: Matt Wrock Date: Fri, 29 Apr 2016 19:50:04 -0700 Subject: adds a system check for fips enablement and runs in fips mode if enabled --- chef-config/lib/chef-config/config.rb | 6 +- chef-config/lib/chef-config/fips.rb | 51 ++++++++++++++ chef-config/spec/unit/config_spec.rb | 40 +++++++++++ chef-config/spec/unit/fips_spec.rb | 122 ++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 chef-config/lib/chef-config/fips.rb create mode 100644 chef-config/spec/unit/fips_spec.rb diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index e6192c22cb..e237f10412 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -22,6 +22,7 @@ require "mixlib/config" require "pathname" +require "chef-config/fips" require "chef-config/logger" require "chef-config/windows" require "chef-config/path_helper" @@ -513,7 +514,9 @@ module ChefConfig default :recipe_url, nil # Set to true if Chef is to set OpenSSL to run in FIPS mode - default(:fips) { ENV["CHEF_FIPS"] == "1" } + default(:fips) do + !ENV["CHEF_FIPS"].nil? || ChefConfig.fips? + end # Initialize openssl def self.init_openssl @@ -966,6 +969,7 @@ module ChefConfig Digest.const_set("SHA1", OpenSSL::Digest::SHA1) OpenSSL::Digest.send(:remove_const, "MD5") if OpenSSL::Digest.const_defined?("MD5") OpenSSL::Digest.const_set("MD5", Digest::MD5) + ChefConfig.logger.debug "FIPS mode is enabled." end end end diff --git a/chef-config/lib/chef-config/fips.rb b/chef-config/lib/chef-config/fips.rb new file mode 100644 index 0000000000..623ce87686 --- /dev/null +++ b/chef-config/lib/chef-config/fips.rb @@ -0,0 +1,51 @@ +# +# Author:: Matt Wrock () +# Copyright:: Copyright (c) 2016 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module ChefConfig + + def self.fips? + if ChefConfig.windows? + begin + require "win32/registry" + rescue LoadError + return false + end + + # from http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129(v=vs.85).aspx + reg_type = + case ::RbConfig::CONFIG["target_cpu"] + when "i386" + Win32::Registry::KEY_READ | 0x100 + when "x86_64" + Win32::Registry::KEY_READ | 0x200 + else + Win32::Registry::KEY_READ + end + begin + Win32::Registry::HKEY_LOCAL_MACHINE.open('System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy', reg_type) do |policy| + policy["Enabled"] != 0 + end + rescue Win32::Registry::Error + false + end + else + fips_path = "/proc/sys/crypto/fips_enabled" + File.exist?(fips_path) && File.read(fips_path).chomp != "0" + end + end +end diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb index 72c0981eca..f09dbb517a 100644 --- a/chef-config/spec/unit/config_spec.rb +++ b/chef-config/spec/unit/config_spec.rb @@ -165,6 +165,46 @@ RSpec.describe ChefConfig::Config do allow(ChefConfig::Config).to receive(:path_accessible?).and_return(false) end + describe "ChefConfig::Config[:fips]" do + let(:fips_enabled) { false } + + before(:all) do + @original_env = ENV.to_hash + end + + after(:all) do + ENV.clear + ENV.update(@original_env) + end + + before(:each) do + ENV["CHEF_FIPS"] = nil + allow(ChefConfig).to receive(:fips?).and_return(fips_enabled) + end + + it "returns false when no environment is set and not enabled on system" do + expect(ChefConfig::Config[:fips]).to eq(false) + end + + context "when ENV['CHEF_FIPS'] is set" do + before do + ENV["CHEF_FIPS"] = "1" + end + + it "returns true" do + expect(ChefConfig::Config[:fips]).to eq(true) + end + end + + context "when fips is enabled on system" do + let(:fips_enabled) { true } + + it "returns true" do + expect(ChefConfig::Config[:fips]).to eq(true) + end + end + end + describe "ChefConfig::Config[:chef_server_root]" do context "when chef_server_url isn't set manually" do it "returns the default of 'https://localhost:443'" do diff --git a/chef-config/spec/unit/fips_spec.rb b/chef-config/spec/unit/fips_spec.rb new file mode 100644 index 0000000000..cf5af22ef1 --- /dev/null +++ b/chef-config/spec/unit/fips_spec.rb @@ -0,0 +1,122 @@ +# +# Author:: Matt Wrock () +# Copyright:: Copyright (c) 2016 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef-config/fips" +require "spec_helper" + +RSpec.describe "ChefConfig.fips?" do + let(:enabled) { "0" } + + context "on *nix" do + let(:fips_path) { "/proc/sys/crypto/fips_enabled" } + + before(:each) do + allow(ChefConfig).to receive(:windows?).and_return(false) + allow(::File).to receive(:exist?).with(fips_path).and_return(true) + allow(::File).to receive(:read).with(fips_path).and_return(enabled) + end + + context "fips file is present and contains 1" do + let(:enabled) { "1" } + + it "returns true" do + expect(ChefConfig.fips?).to be(true) + end + end + + context "fips file does not contain 1" do + let(:enabled) { "0" } + + it "returns false" do + expect(ChefConfig.fips?).to be(false) + end + end + + context "fips file is not present" do + before do + allow(::File).to receive(:exist?).with(fips_path).and_return(false) + end + + it "returns false" do + expect(ChefConfig.fips?).to be(false) + end + end + end + + context "on windows", :windows_only do + let(:fips_key) { 'System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy' } + let(:win_reg_entry) { { "Enabled" => enabled } } + + before(:each) do + allow(ChefConfig).to receive(:windows?).and_return(true) + allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with(fips_key, arch).and_yield(win_reg_entry) + end + + shared_examples "fips_detection" do + context "fips enabled key is set to 1" do + let(:enabled) { 1 } + + it "returns true" do + expect(ChefConfig.fips?).to be(true) + end + end + + context "fips enabled key is set to 0" do + let(:enabled) { 0 } + + it "returns false" do + expect(ChefConfig.fips?).to be(false) + end + end + + context "fips key does not exist" do + before do + allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).and_raise(Win32::Registry::Error, 50) + end + + it "returns false" do + expect(ChefConfig.fips?).to be(false) + end + end + end + + context "on 32 bit ruby" do + let(:arch) { Win32::Registry::KEY_READ | 0x100 } + + before { stub_const("::RbConfig::CONFIG", { "target_cpu" => "i386" } ) } + + it_behaves_like "fips_detection" + end + + context "on 64 bit ruby" do + let(:arch) { Win32::Registry::KEY_READ | 0x200 } + + before { stub_const("::RbConfig::CONFIG", { "target_cpu" => "x86_64" } ) } + + it_behaves_like "fips_detection" + end + + context "on unknown ruby" do + let(:arch) { Win32::Registry::KEY_READ } + + before { stub_const("::RbConfig::CONFIG", { "target_cpu" => nil } ) } + + it_behaves_like "fips_detection" + end + end +end -- cgit v1.2.1