diff options
Diffstat (limited to 'lib/rubygems/security')
-rw-r--r-- | lib/rubygems/security/policies.rb | 97 | ||||
-rw-r--r-- | lib/rubygems/security/policy.rb | 59 | ||||
-rw-r--r-- | lib/rubygems/security/signer.rb | 28 | ||||
-rw-r--r-- | lib/rubygems/security/trust_dir.rb | 25 |
4 files changed, 106 insertions, 103 deletions
diff --git a/lib/rubygems/security/policies.rb b/lib/rubygems/security/policies.rb index 8f6ad99316..41f66043ad 100644 --- a/lib/rubygems/security/policies.rb +++ b/lib/rubygems/security/policies.rb @@ -1,17 +1,17 @@ # frozen_string_literal: true -module Gem::Security +module Gem::Security ## # No security policy: all package signature checks are disabled. NoSecurity = Policy.new( - 'No Security', - :verify_data => false, - :verify_signer => false, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + "No Security", + verify_data: false, + verify_signer: false, + verify_chain: false, + verify_root: false, + only_trusted: false, + only_signed: false ) ## @@ -23,13 +23,13 @@ module Gem::Security # easily spoofed, and is not recommended. AlmostNoSecurity = Policy.new( - 'Almost No Security', - :verify_data => true, - :verify_signer => false, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + "Almost No Security", + verify_data: true, + verify_signer: false, + verify_chain: false, + verify_root: false, + only_trusted: false, + only_signed: false ) ## @@ -40,13 +40,13 @@ module Gem::Security # is not recommended. LowSecurity = Policy.new( - 'Low Security', - :verify_data => true, - :verify_signer => true, - :verify_chain => false, - :verify_root => false, - :only_trusted => false, - :only_signed => false + "Low Security", + verify_data: true, + verify_signer: true, + verify_chain: false, + verify_root: false, + only_trusted: false, + only_signed: false ) ## @@ -59,13 +59,13 @@ module Gem::Security # gem off as unsigned. MediumSecurity = Policy.new( - 'Medium Security', - :verify_data => true, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => true, - :only_signed => false + "Medium Security", + verify_data: true, + verify_signer: true, + verify_chain: true, + verify_root: true, + only_trusted: true, + only_signed: false ) ## @@ -78,38 +78,37 @@ module Gem::Security # a reasonable guarantee that the contents of the gem have not been altered. HighSecurity = Policy.new( - 'High Security', - :verify_data => true, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => true, - :only_signed => true + "High Security", + verify_data: true, + verify_signer: true, + verify_chain: true, + verify_root: true, + only_trusted: true, + only_signed: true ) ## # Policy used to verify a certificate and key when signing a gem SigningPolicy = Policy.new( - 'Signing Policy', - :verify_data => false, - :verify_signer => true, - :verify_chain => true, - :verify_root => true, - :only_trusted => false, - :only_signed => false + "Signing Policy", + verify_data: false, + verify_signer: true, + verify_chain: true, + verify_root: true, + only_trusted: false, + only_signed: false ) ## # Hash of configured security policies Policies = { - 'NoSecurity' => NoSecurity, - 'AlmostNoSecurity' => AlmostNoSecurity, - 'LowSecurity' => LowSecurity, - 'MediumSecurity' => MediumSecurity, - 'HighSecurity' => HighSecurity, + "NoSecurity" => NoSecurity, + "AlmostNoSecurity" => AlmostNoSecurity, + "LowSecurity" => LowSecurity, + "MediumSecurity" => MediumSecurity, + "HighSecurity" => HighSecurity, # SigningPolicy is not intended for use by `gem -P` so do not list it }.freeze - end diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb index 9683e55b32..7b86ac5763 100644 --- a/lib/rubygems/security/policy.rb +++ b/lib/rubygems/security/policy.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -require_relative '../user_interaction' + +require_relative "../user_interaction" ## # A Gem::Security::Policy object encapsulates the settings for verifying @@ -53,8 +54,8 @@ class Gem::Security::Policy # and is valid for the given +time+. def check_chain(chain, time) - raise Gem::Security::Exception, 'missing signing chain' unless chain - raise Gem::Security::Exception, 'empty signing chain' if chain.empty? + raise Gem::Security::Exception, "missing signing chain" unless chain + raise Gem::Security::Exception, "empty signing chain" if chain.empty? begin chain.each_cons 2 do |issuer, cert| @@ -83,21 +84,21 @@ class Gem::Security::Policy # If the +issuer+ is +nil+ no verification is performed. def check_cert(signer, issuer, time) - raise Gem::Security::Exception, 'missing signing certificate' unless + raise Gem::Security::Exception, "missing signing certificate" unless signer message = "certificate #{signer.subject}" - if not_before = signer.not_before and not_before > time + if (not_before = signer.not_before) && not_before > time raise Gem::Security::Exception, "#{message} not valid before #{not_before}" end - if not_after = signer.not_after and not_after < time + if (not_after = signer.not_after) && not_after < time raise Gem::Security::Exception, "#{message} not valid after #{not_after}" end - if issuer and not signer.verify issuer.public_key + if issuer && !signer.verify(issuer.public_key) raise Gem::Security::Exception, "#{message} was not issued by #{issuer.subject}" end @@ -109,15 +110,15 @@ class Gem::Security::Policy # Ensures the public key of +key+ matches the public key in +signer+ def check_key(signer, key) - unless signer and key + unless signer && key return true unless @only_signed - raise Gem::Security::Exception, 'missing key or signature' + raise Gem::Security::Exception, "missing key or signature" end raise Gem::Security::Exception, "certificate #{signer.subject} does not match the signing key" unless - signer.public_key.to_pem == key.public_key.to_pem + signer.check_private_key(key) true end @@ -127,14 +128,14 @@ class Gem::Security::Policy # +time+. def check_root(chain, time) - raise Gem::Security::Exception, 'missing signing chain' unless chain + raise Gem::Security::Exception, "missing signing chain" unless chain root = chain.first - raise Gem::Security::Exception, 'missing root certificate' unless root + raise Gem::Security::Exception, "missing root certificate" unless root raise Gem::Security::Exception, - "root certificate #{root.subject} is not self-signed " + + "root certificate #{root.subject} is not self-signed " \ "(issuer #{root.issuer})" if root.issuer != root.subject @@ -146,11 +147,11 @@ class Gem::Security::Policy # the digests of the two certificates match according to +digester+ def check_trust(chain, digester, trust_dir) - raise Gem::Security::Exception, 'missing signing chain' unless chain + raise Gem::Security::Exception, "missing signing chain" unless chain root = chain.first - raise Gem::Security::Exception, 'missing root certificate' unless root + raise Gem::Security::Exception, "missing root certificate" unless root path = Gem::Security.trust_dir.cert_path root @@ -164,13 +165,13 @@ class Gem::Security::Policy end save_cert = OpenSSL::X509::Certificate.new File.read path - save_dgst = digester.digest save_cert.public_key.to_s + save_dgst = digester.digest save_cert.public_key.to_pem - pkey_str = root.public_key.to_s + pkey_str = root.public_key.to_pem cert_dgst = digester.digest pkey_str raise Gem::Security::Exception, - "trusted root certificate #{root.subject} checksum " + + "trusted root certificate #{root.subject} checksum " \ "does not match signing root certificate checksum" unless save_dgst == cert_dgst @@ -182,7 +183,7 @@ class Gem::Security::Policy def subject(certificate) # :nodoc: certificate.extensions.each do |extension| - next unless extension.oid == 'subjectAltName' + next unless extension.oid == "subjectAltName" return extension.value end @@ -191,11 +192,8 @@ class Gem::Security::Policy end def inspect # :nodoc: - ("[Policy: %s - data: %p signer: %p chain: %p root: %p " + - "signed-only: %p trusted-only: %p]") % [ - @name, @verify_chain, @verify_data, @verify_root, @verify_signer, - @only_signed, @only_trusted - ] + format("[Policy: %s - data: %p signer: %p chain: %p root: %p " \ + "signed-only: %p trusted-only: %p]", @name, @verify_chain, @verify_data, @verify_root, @verify_signer, @only_signed, @only_trusted) end ## @@ -205,8 +203,7 @@ class Gem::Security::Policy # # If +key+ is given it is used to validate the signing certificate. - def verify(chain, key = nil, digests = {}, signatures = {}, - full_name = '(unknown)') + def verify(chain, key = nil, digests = {}, signatures = {}, full_name = "(unknown)") if signatures.empty? if @only_signed raise Gem::Security::Exception, @@ -225,13 +222,13 @@ class Gem::Security::Policy trust_dir = opt[:trust_dir] time = Time.now - _, signer_digests = digests.find do |algorithm, file_digests| + _, signer_digests = digests.find do |_algorithm, file_digests| file_digests.values.first.name == Gem::Security::DIGEST_NAME end if @verify_data - raise Gem::Security::Exception, 'no digests provided (probable bug)' if - signer_digests.nil? or signer_digests.empty? + raise Gem::Security::Exception, "no digests provided (probable bug)" if + signer_digests.nil? || signer_digests.empty? else signer_digests = {} end @@ -248,7 +245,7 @@ class Gem::Security::Policy if @only_trusted check_trust chain, digester, trust_dir - elsif signatures.empty? and digests.empty? + elsif signatures.empty? && digests.empty? # trust is irrelevant if there's no signatures to verify else alert_warning "#{subject signer} is not trusted for #{full_name}" @@ -287,5 +284,5 @@ class Gem::Security::Policy true end - alias to_s name # :nodoc: + alias_method :to_s, :name # :nodoc: end diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb index c5c2c4f220..5732fb57fd 100644 --- a/lib/rubygems/security/signer.rb +++ b/lib/rubygems/security/signer.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + ## # Basic OpenSSL-based package signing class. @@ -42,7 +43,7 @@ class Gem::Security::Signer def self.re_sign_cert(expired_cert, expired_cert_path, private_key) return unless expired_cert.not_after < Time.now - expiry = expired_cert.not_after.strftime('%Y%m%d%H%M%S') + expiry = expired_cert.not_after.strftime("%Y%m%d%H%M%S") expired_cert_file = "#{File.basename(expired_cert_path)}.expired.#{expiry}" new_expired_cert_path = File.join(Gem.user_home, ".gem", expired_cert_file) @@ -83,8 +84,8 @@ class Gem::Security::Signer @digest_name = Gem::Security::DIGEST_NAME @digest_algorithm = Gem::Security.create_digest(@digest_name) - if @key && !@key.is_a?(OpenSSL::PKey::RSA) - @key = OpenSSL::PKey::RSA.new(File.read(@key), @passphrase) + if @key && !@key.is_a?(OpenSSL::PKey::PKey) + @key = OpenSSL::PKey.read(File.read(@key), @passphrase) end if @cert_chain @@ -105,7 +106,7 @@ class Gem::Security::Signer # this value is preferred, otherwise the subject is used. def extract_name(cert) # :nodoc: - subject_alt_name = cert.extensions.find {|e| 'subjectAltName' == e.oid } + subject_alt_name = cert.extensions.find {|e| e.oid == "subjectAltName" } if subject_alt_name /\Aemail:/ =~ subject_alt_name.value # rubocop:disable Performance/StartWith @@ -139,9 +140,9 @@ class Gem::Security::Signer def sign(data) return unless @key - raise Gem::Security::Exception, 'no certs provided' if @cert_chain.empty? + raise Gem::Security::Exception, "no certs provided" if @cert_chain.empty? - if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now + if @cert_chain.length == 1 && @cert_chain.last.not_after < Time.now alert("Your certificate has expired, trying to re-sign it...") re_sign_key( @@ -174,16 +175,23 @@ class Gem::Security::Signer old_cert = @cert_chain.last disk_cert_path = File.join(Gem.default_cert_path) - disk_cert = File.read(disk_cert_path) rescue nil + disk_cert = begin + File.read(disk_cert_path) + rescue StandardError + nil + end disk_key_path = File.join(Gem.default_key_path) - disk_key = - OpenSSL::PKey::RSA.new(File.read(disk_key_path), @passphrase) rescue nil + disk_key = begin + OpenSSL::PKey.read(File.read(disk_key_path), @passphrase) + rescue StandardError + nil + end return unless disk_key if disk_key.to_pem == @key.to_pem && disk_cert == old_cert.to_pem - expiry = old_cert.not_after.strftime('%Y%m%d%H%M%S') + expiry = old_cert.not_after.strftime("%Y%m%d%H%M%S") old_cert_file = "gem-public_cert.pem.expired.#{expiry}" old_cert_path = File.join(Gem.user_home, ".gem", old_cert_file) diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb index 456947274c..d23d161cfe 100644 --- a/lib/rubygems/security/trust_dir.rb +++ b/lib/rubygems/security/trust_dir.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + ## # The TrustDir manages the trusted certificates for gem signature # verification. @@ -8,8 +9,8 @@ class Gem::Security::TrustDir # Default permissions for the trust directory and its contents DEFAULT_PERMISSIONS = { - :trust_dir => 0700, - :trusted_cert => 0600, + trust_dir: 0o700, + trusted_cert: 0o600, }.freeze ## @@ -41,16 +42,14 @@ class Gem::Security::TrustDir def each_certificate return enum_for __method__ unless block_given? - glob = File.join @dir, '*.pem' + glob = File.join @dir, "*.pem" Dir[glob].each do |certificate_file| - begin - certificate = load_certificate certificate_file + certificate = load_certificate certificate_file - yield certificate, certificate_file - rescue OpenSSL::X509::CertificateError - next # HACK warn - end + yield certificate, certificate_file + rescue OpenSSL::X509::CertificateError + next # HACK: warn end end @@ -92,7 +91,7 @@ class Gem::Security::TrustDir destination = cert_path certificate - File.open destination, 'wb', 0600 do |io| + File.open destination, "wb", 0o600 do |io| io.write certificate.to_pem io.chmod(@permissions[:trusted_cert]) end @@ -104,15 +103,15 @@ class Gem::Security::TrustDir # permissions. def verify - require 'fileutils' + require "fileutils" if File.exist? @dir raise Gem::Security::Exception, "trust directory #{@dir} is not a directory" unless File.directory? @dir - FileUtils.chmod 0700, @dir + FileUtils.chmod 0o700, @dir else - FileUtils.mkdir_p @dir, :mode => @permissions[:trust_dir] + FileUtils.mkdir_p @dir, mode: @permissions[:trust_dir] end end end |