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.8KB

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