macros.gpg 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. # The gpg_verify macro is defined further down in this document.
  2. # gpg_verify takes one option and a list of 2- or 3-tuples.
  3. #
  4. # With no arguments, attempts to figure everything out. Finds one keyring and
  5. # tries to pair each signature file with a source. If there is no source found
  6. # which matches a signature, the build is aborted.
  7. #
  8. # -k gives a common keyring to verify all signatures against, except when an
  9. # argument specifies its own keyring.
  10. #
  11. # Each argument must be of the form "F,S,K" or "F,S", where each of F, S and K
  12. # is either the number or the filename of one of the source files in the
  13. # package. A pathname including directories is not allowed.
  14. # F is a source file to check.
  15. # S is a signature.
  16. # K is a keyring.
  17. #
  18. # When an argument specifies a keyring, that signature will be verified against
  19. # the keys in that keyring. For arguments that don't specify a keyring, the one
  20. # specified with -k will be used, if any. If no keyring is specified either
  21. # way, the macro will default to the first one it finds in the source list.
  22. #
  23. # It is assumed that all the keys in all keyrings, whether automatically found
  24. # or explicitly specified, are trusted to authenticate the source files. There
  25. # must not be any untrusted keys included.
  26. # Some utility functions to the global namespace
  27. # Most of these should come from the utility macros in the other repo.
  28. %define gpg_macros_init %{lua:
  29. function db(str)
  30. io.stderr:write(tostring(str) .. '\\n')
  31. end
  32. \
  33. -- Simple basename clone
  34. function basename(str)
  35. local name = string.gsub(str, "(.*/)(.*)", "%2")
  36. return name
  37. end
  38. \
  39. -- Get the numbered or source file.
  40. -- The spec writer can use any numbering scheme. The sources table
  41. -- always counts from 1 and has no gaps, so we have to go back to the
  42. -- SOURCEN macros.
  43. function get_numbered_source(num)
  44. local macro = "%SOURCE" .. num
  45. local val = rpm.expand(macro)
  46. if val == macro then
  47. return nil
  48. end
  49. return val
  50. end
  51. -- Get the named source file. This returns the full path to a source file,
  52. -- or nil if no such source exists.
  53. function get_named_source(name)
  54. local path
  55. for _,path in ipairs(sources) do
  56. if name == basename(path) then
  57. return path
  58. end
  59. end
  60. return nil
  61. end
  62. \
  63. -- Determine whether the supplied filename contains a signature
  64. -- Assumes the file will be closed when the handle goes out of scope
  65. function is_signature(fname)
  66. -- I don't really like this, but you can have completely binary sigs
  67. if string.find(fname, '%.sig$') then
  68. return true
  69. end
  70. local file = io.open(fname, 'r')
  71. if file == nil then return false end
  72. \
  73. local c = 1
  74. while true do
  75. local line = file:read('*line')
  76. if (line == nil or c > 10) then break end
  77. if string.find(line, "BEGIN PGP SIGNATURE") then
  78. return true
  79. end
  80. c = c+1
  81. end
  82. return false
  83. end
  84. \
  85. -- Determine whether the supplied filename looks like a keyring
  86. -- Ends in .gpg (might be binary data)? Contains "BEGIN PGP PUBLIC KEY BLOCK"
  87. function is_keyring(fname)
  88. -- XXX Have to hack this now to make it not find macros.gpg while we're testing.
  89. if string.find(fname, '%.gpg$') and not string.find(fname, 'macros.gpg$') then
  90. return true
  91. end
  92. \
  93. local file = io.open(fname, 'r')
  94. if file == nil then return false end
  95. io.input(file)
  96. local c = 1
  97. while true do
  98. local line = io.read('*line')
  99. if (line == nil or c > 10) then break end
  100. if string.find(line, "BEGIN PGP PUBLIC KEY BLOCK") then
  101. return true
  102. end
  103. c = c+1
  104. end
  105. return false
  106. end
  107. \
  108. -- Output code to have the current scriptlet echo something
  109. function echo(str)
  110. print("echo " .. str .. "\\n")
  111. end
  112. \
  113. -- Output an exit statement with nonzero return to the current scriptlet
  114. function exit()
  115. print("exit 1\\n")
  116. end
  117. \
  118. -- Call the RPM %error macro
  119. function rpmerror(str)
  120. echo("gpg_verify: " .. str)
  121. rpm.expand("%{error:gpg_verify: " .. str .. "}")
  122. exit(1)
  123. end
  124. \
  125. -- XXX How to we get just a flag and no option?
  126. function getflag(flag)
  127. return nil
  128. end
  129. \
  130. -- Extract the value of a passed option
  131. function getoption(opt)
  132. out = rpm.expand("%{-" .. opt .. "*}")
  133. -- if string.len(out) == 0 then
  134. if #out == 0 then
  135. return nil
  136. end
  137. return out
  138. end
  139. \
  140. function unknownarg(a)
  141. rpmerror("Unknown argument to %%gpg_verify: " .. a)
  142. end
  143. \
  144. function rprint(s, l, i) -- recursive Print (structure, limit, indent)
  145. l = (l) or 100; i = i or ""; -- default item limit, indent string
  146. if (l<1) then db("ERROR: Item limit reached."); return l-1 end;
  147. local ts = type(s);
  148. if (ts ~= "table") then db(i,ts,s); return l-1 end
  149. db(i,ts); -- print "table"
  150. for k,v in pairs(s) do -- db("[KEY] VALUE")
  151. l = rprint(v, l, i.."\t["..tostring(k).."]");
  152. if (l < 0) then break end
  153. end
  154. return l
  155. end
  156. \
  157. -- Given a list of source file numbers or file names, validate them and
  158. -- convert them to a list of full filenames.
  159. function check_sources_list(arr)
  160. local files = {}
  161. local src,fpath
  162. for _, src in ipairs(arr) do
  163. if tonumber(src) then
  164. -- We have a number; turn it to a full path to the corresponding source file
  165. fpath = get_numbered_source(src)
  166. else
  167. fpath = get_named_source(src)
  168. end
  169. if not src then
  170. err = 'Not a valid source: ' .. src
  171. if src == '1' then
  172. err = err .. '. Note that "Source:" is the 0th source file, not the 1st.'
  173. end
  174. rpmerror(err)
  175. end
  176. table.insert(files, fpath)
  177. end
  178. return files
  179. end
  180. rpm.define("gpg_macros_init %{nil}")
  181. }#
  182. # The actual macro
  183. %define gpg_verify(k:) %gpg_macros_init%{lua:
  184. -- RPM will ignore the first thing we output unless we give it a newline.
  185. print('\\n')
  186. \
  187. local defkeyspec = getoption("k")
  188. local args = rpm.expand("%*")
  189. local sourcefiles = {}
  190. local signature_table = {}
  191. local signatures = {}
  192. local keyrings = {}
  193. local defkey, match, captures, s
  194. \
  195. local function storematch(m, c)
  196. match = m; captures = c
  197. end
  198. \
  199. -- Scan all of the sources and try to categorize them.
  200. -- Move to a function
  201. for i,s in pairs(sources) do
  202. sourcefiles[s] = true
  203. -- db('File: ' .. i .. ", " .. s)
  204. if is_signature(s) then
  205. table.insert(signatures, s)
  206. signature_table[s] = true
  207. db('Found signature: ' .. s)
  208. elseif is_keyring(s) then
  209. table.insert(keyrings, s)
  210. db('Found keyring: ' .. s)
  211. else
  212. -- Must be a source
  213. db('Found source: ' .. s)
  214. end
  215. end
  216. \
  217. if defkeyspec then
  218. defkey = check_sources_list({defkeyspec})[1]
  219. if not defkey then
  220. rpmerror('The provided keyring ' .. defkeyspec .. ' is not a valid source number or filename.')
  221. end
  222. end
  223. \
  224. if defkey then
  225. db('Defkey: ' .. defkey)
  226. else
  227. db('No common key yet')
  228. if keyrings[1] then
  229. defkey = keyrings[1]
  230. db('Using first found keyring file: '..defkey)
  231. end
  232. end
  233. \
  234. -- Check over any given args to make sure they're valid, and to see if a
  235. -- common key is required.
  236. local needdefkey = false
  237. local double = rex.newPOSIX('^([^,]+),([^,]+)$')
  238. local triple = rex.newPOSIX('^([^,]+),([^,]+),([^,]+)$')
  239. local arglist = {}
  240. \
  241. -- RPM gives us the arguments in a single string.
  242. -- Split on spaces and iterate
  243. for arg in args:gmatch('%S+') do
  244. db('Checking ' .. arg)
  245. if triple:gmatch(arg, storematch) > 0 then
  246. db('Looks OK')
  247. local parsed = {srcnum=captures[1], signum=captures[2], keynum=captures[3]}
  248. s = check_sources_list({captures[1], captures[2], captures[3]})
  249. parsed.srcfile = s[1]
  250. parsed.sigfile = s[2]
  251. parsed.keyfile = s[3]
  252. table.insert(arglist, parsed)
  253. elseif double:gmatch(arg, storematch) > 0 then
  254. db('Looks OK; needs common key')
  255. needdefkey = true
  256. local parsed = {srcnum=captures[1], signum=captures[2], keynum=defkeyspec, keyfile=defkey}
  257. s = check_sources_list({captures[1], captures[2]})
  258. parsed.srcfile = s[1]
  259. parsed.sigfile = s[2]
  260. table.insert(arglist, parsed)
  261. else
  262. rpmerror('Provided argument '..arg..' is not valid.')
  263. end
  264. end
  265. \
  266. -- So we now know if one of those args needs a common key
  267. if needdefkey and not defkey then
  268. rpmerror('No common key was specified or found, yet the arguments require one.')
  269. end
  270. \
  271. -- And if we have no arguments at all and no common key was found,
  272. -- then we can't do an automatic check
  273. if not defkey and args == '' then
  274. rpmerror('No keyring specified and none found; cannot auto-check.')
  275. end
  276. \
  277. -- Nothing to check means automatic mode
  278. if #arglist == 0 then
  279. local noext
  280. for i,_ in pairs(signature_table) do
  281. -- Find the name without the extension
  282. noext = string.gsub(i, '%.[^.]+$', '')
  283. if sourcefiles[noext] then
  284. table.insert(arglist, {srcfile=noext, sigfile=i, keyfile=defkey})
  285. else
  286. rpmerror('Found signature ' .. i .. ' with no matching source file.')
  287. end
  288. end
  289. end
  290. \
  291. -- Now actually check things
  292. for _,arg in ipairs(arglist) do
  293. local gpgfile = '$GPGHOME/' .. basename(arg.keyfile) .. '.gpg'
  294. echo('Checking signature: file ' .. arg.srcfile .. ' sig ' .. arg.sigfile .. ' key ' .. arg.keyfile)
  295. \
  296. -- We need a secure temp directorry
  297. print('GPGHOME=$(mktemp -qd)\\n')
  298. \
  299. -- Call gpg2 to generate the dearmored key
  300. print('gpg2 --homedir $GPGHOME --no-default-keyring --quiet --yes ')
  301. print('--output '.. gpgfile .. ' --dearmor ' .. arg.keyfile .. "\\n")
  302. \
  303. -- Call gpgv2 to verify the signature against the source file with the dearmored key
  304. print('gpgv2 --homedir $GPGHOME --keyring ' .. gpgfile .. ' ' .. arg.sigfile .. ' ' .. arg.srcfile .. '\\n')
  305. \
  306. print('rm -rf $GPGHOME\\n')
  307. echo('')
  308. end
  309. \
  310. db('------------')
  311. }#
  312. # vim: set filetype=spec: