You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gverify 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/ruby
  2. require 'optparse'
  3. require 'yaml'
  4. require 'fileutils'
  5. require 'pathname'
  6. @options = {}
  7. def system!(cmd)
  8. system(cmd) or raise "failed to run #{cmd}"
  9. end
  10. def sanitize(str, where)
  11. raise "unsanitary string in #{where}" if (str =~ /[^\w.-]/)
  12. str
  13. end
  14. def sanitize_path(str, where)
  15. raise "unsanitary string in #{where}" if (str =~ /[^@\w\/.:+-]/)
  16. str
  17. end
  18. def info(str)
  19. puts str if @options[:verbose]
  20. end
  21. ################################
  22. OptionParser.new do |opts|
  23. opts.banner = "Usage: build [options] <build-description>.yml"
  24. opts.on("-v", "--verbose", "be more verbose") do |v|
  25. @options[:verbose] = v
  26. end
  27. opts.on("-r REL", "--release REL", "release name") do |v|
  28. @options[:release] = v
  29. end
  30. opts.on("-d DEST", "--destination DEST", "directory to place signature in") do |v|
  31. @options[:destination] = v
  32. end
  33. opts.on("-c SIGNER", "--compare-to SIGNER", "compare other manifests to SIGNER's, if not given pick first") do |v|
  34. @options[:compareto] = v
  35. end
  36. end.parse!
  37. base_dir = Pathname.new(__FILE__).expand_path.dirname.parent
  38. build_desc_file = ARGV.shift or raise "must supply YAML build description file"
  39. build_desc = YAML.load_file(build_desc_file)
  40. in_sums = []
  41. result_dir = 'result'
  42. package_name = build_desc["name"] or raise "must supply name"
  43. package_name = sanitize(package_name, "package name")
  44. destination = @options[:destination] || File.join(base_dir, "sigs", package_name)
  45. release = @options[:release] || "current"
  46. release = sanitize(release, "release")
  47. verbose = @options[:verbose]
  48. release_path = File.join(destination, release)
  49. File.exists?(release_path) or raise "#{release_path} does not exist"
  50. result_file = "#{package_name}-build.assert"
  51. sig_file = "#{result_file}.sig"
  52. current_manifest = nil
  53. if @options[:compareto]
  54. # Load a specific 'golden' manifest to compare to
  55. compareto = @options[:compareto]
  56. result_path = sanitize_path(File.join(release_path, compareto, result_file), "result path")
  57. result = YAML.load_file(result_path)
  58. current_manifest = result['out_manifest']
  59. end
  60. did_fail = false
  61. Dir.foreach(release_path) do |signer_dir|
  62. next if signer_dir == "." or signer_dir == ".."
  63. signer_path = sanitize_path(File.join(release_path, signer_dir), "signer path")
  64. next if !File.directory?(signer_path)
  65. result_path = sanitize_path(File.join(signer_path, result_file), "result path")
  66. sig_path = sanitize_path(File.join(signer_path, sig_file), "result path")
  67. if !File.exist?(result_path)
  68. puts "missing result at #{result_path}"
  69. next
  70. end
  71. if !File.exist?(sig_path)
  72. puts "missing signature at #{sig_path}"
  73. next
  74. end
  75. result = YAML.load_file(result_path)
  76. system("gpg --keyserver pgp.mit.edu --recv-keys `gpg --quiet --batch --verify \"#{File.join(signer_path, 'signature.pgp')}\" \"#{result_path}\" 2>&1 | head -n1 | grep \"key ID\" | awk '{ print $15 }'` > /dev/null 2>&1")
  77. out = `gpg --quiet --batch --verify \"#{sig_path}\" \"#{result_path}\" 2>&1`
  78. if $? != 0
  79. out.each_line do |line|
  80. if line =~ /^gpg: Signature made/
  81. info(line)
  82. else
  83. puts line
  84. end
  85. end
  86. puts "#{signer_dir}: BAD SIGNATURE"
  87. did_fail = true
  88. elsif current_manifest and (result['out_manifest'] != current_manifest or result['release'] != release or result['name'] != package_name)
  89. out.each_line do |line|
  90. if line =~ /^gpg: Signature made/
  91. info(line)
  92. elsif line =~ /^gpg: Good signature/
  93. info(line)
  94. elsif line =~ /^gpg: aka/
  95. info(line)
  96. else
  97. puts line
  98. end
  99. end
  100. puts "#{signer_dir}: MISMATCH"
  101. if verbose
  102. lines1 = current_manifest.each_line
  103. lines2 = result['out_manifest'].each_line
  104. lines1.zip(lines2) do |line|
  105. if line[0] != line[1]
  106. puts "- "+line[0]
  107. puts "+ "+line[1]
  108. end
  109. end
  110. end
  111. did_fail = true
  112. else
  113. out.each_line do |line|
  114. if line =~ /^gpg: Signature made/
  115. info(line)
  116. elsif line =~ /^gpg: Good signature/
  117. info(line)
  118. elsif line =~ /^gpg: aka/
  119. info(line)
  120. else
  121. puts line
  122. end
  123. end
  124. puts "#{signer_dir}: OK"
  125. end
  126. if !current_manifest
  127. # take first manifest as 'current' to compare against
  128. current_manifest = result['out_manifest']
  129. end
  130. end
  131. exit 1 if did_fail