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.

Methods

Attributes

only_signed  [RW] 
only_trusted  [RW] 
verify_chain  [RW] 
verify_data  [RW] 
verify_root  [RW] 
verify_signer  [RW] 

Public Class methods

Create a new Gem::Security::Policy object with the given mode and options.

[Source]

     # 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.

[Source]

     # 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

Public Instance methods

Verify that the gem data with the given signature and signing chain matched this security policy at the specified time.

[Source]

     # 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

[Validate]