Class | Gem::Security::Policy |
In: |
lib/rubygems/security.rb
|
Parent: | Object |
A Gem::Security::Policy object encapsulates the settings for verifying signed gem files. This is the base class. You can either declare an instance of this or use one of the preset security policies below.
only_signed | [RW] | |
only_trusted | [RW] | |
verify_chain | [RW] | |
verify_data | [RW] | |
verify_root | [RW] | |
verify_signer | [RW] |
Create a new Gem::Security::Policy object with the given mode and options.
# File lib/rubygems/security.rb, line 381 381: def initialize(policy = {}, opt = {}) 382: # set options 383: @opt = Gem::Security::OPT.merge(opt) 384: 385: # build policy 386: policy.each_pair do |key, val| 387: case key 388: when :verify_data then @verify_data = val 389: when :verify_signer then @verify_signer = val 390: when :verify_chain then @verify_chain = val 391: when :verify_root then @verify_root = val 392: when :only_trusted then @only_trusted = val 393: when :only_signed then @only_signed = val 394: end 395: end 396: end
Get the path to the file for this cert.
# File lib/rubygems/security.rb, line 401 401: def self.trusted_cert_path(cert, opt = {}) 402: opt = Gem::Security::OPT.merge(opt) 403: 404: # get digest algorithm, calculate checksum of root.subject 405: algo = opt[:dgst_algo] 406: dgst = algo.hexdigest(cert.subject.to_s) 407: 408: # build path to trusted cert file 409: name = "cert-#{dgst}.pem" 410: 411: # join and return path components 412: File::join(opt[:trust_dir], name) 413: end
Verify that the gem data with the given signature and signing chain matched this security policy at the specified time.
# File lib/rubygems/security.rb, line 419 419: def verify_gem(signature, data, chain, time = Time.now) 420: Gem.ensure_ssl_available 421: cert_class = OpenSSL::X509::Certificate 422: exc = Gem::Security::Exception 423: chain ||= [] 424: 425: chain = chain.map{ |str| cert_class.new(str) } 426: signer, ch_len = chain[-1], chain.size 427: 428: # make sure signature is valid 429: if @verify_data 430: # get digest algorithm (TODO: this should be configurable) 431: dgst = @opt[:dgst_algo] 432: 433: # verify the data signature (this is the most important part, so don't 434: # screw it up :D) 435: v = signer.public_key.verify(dgst.new, signature, data) 436: raise exc, "Invalid Gem Signature" unless v 437: 438: # make sure the signer is valid 439: if @verify_signer 440: # make sure the signing cert is valid right now 441: v = signer.check_validity(nil, time) 442: raise exc, "Invalid Signature: #{v[:desc]}" unless v[:is_valid] 443: end 444: end 445: 446: # make sure the certificate chain is valid 447: if @verify_chain 448: # iterate down over the chain and verify each certificate against it's 449: # issuer 450: (ch_len - 1).downto(1) do |i| 451: issuer, cert = chain[i - 1, 2] 452: v = cert.check_validity(issuer, time) 453: raise exc, "%s: cert = '%s', error = '%s'" % [ 454: 'Invalid Signing Chain', cert.subject, v[:desc] 455: ] unless v[:is_valid] 456: end 457: 458: # verify root of chain 459: if @verify_root 460: # make sure root is self-signed 461: root = chain[0] 462: raise exc, "%s: %s (subject = '%s', issuer = '%s')" % [ 463: 'Invalid Signing Chain Root', 464: 'Subject does not match Issuer for Gem Signing Chain', 465: root.subject.to_s, 466: root.issuer.to_s, 467: ] unless root.issuer.to_s == root.subject.to_s 468: 469: # make sure root is valid 470: v = root.check_validity(root, time) 471: raise exc, "%s: cert = '%s', error = '%s'" % [ 472: 'Invalid Signing Chain Root', root.subject, v[:desc] 473: ] unless v[:is_valid] 474: 475: # verify that the chain root is trusted 476: if @only_trusted 477: # get digest algorithm, calculate checksum of root.subject 478: algo = @opt[:dgst_algo] 479: path = Gem::Security::Policy.trusted_cert_path(root, @opt) 480: 481: # check to make sure trusted path exists 482: raise exc, "%s: cert = '%s', error = '%s'" % [ 483: 'Untrusted Signing Chain Root', 484: root.subject.to_s, 485: "path \"#{path}\" does not exist", 486: ] unless File.exist?(path) 487: 488: # load calculate digest from saved cert file 489: save_cert = OpenSSL::X509::Certificate.new(File.read(path)) 490: save_dgst = algo.digest(save_cert.public_key.to_s) 491: 492: # create digest of public key 493: pkey_str = root.public_key.to_s 494: cert_dgst = algo.digest(pkey_str) 495: 496: # now compare the two digests, raise exception 497: # if they don't match 498: raise exc, "%s: %s (saved = '%s', root = '%s')" % [ 499: 'Invalid Signing Chain Root', 500: "Saved checksum doesn't match root checksum", 501: save_dgst, cert_dgst, 502: ] unless save_dgst == cert_dgst 503: end 504: end 505: 506: # return the signing chain 507: chain.map { |cert| cert.subject } 508: end 509: end