The macro is now included in the standard fedora macros.f38
parent
16c2f5115a
commit
ae605b0cac
@ -1,316 +0,0 @@
|
|||||||
# The gpg_verify macro is defined further down in this document.
|
|
||||||
|
|
||||||
# gpg_verify takes one option and a list of 2- or 3-tuples.
|
|
||||||
#
|
|
||||||
# With no arguments, attempts to figure everything out. Finds one keyring and
|
|
||||||
# tries to pair each signature file with a source. If there is no source found
|
|
||||||
# which matches a signature, the build is aborted.
|
|
||||||
#
|
|
||||||
# -k gives a common keyring to verify all signatures against, except when an
|
|
||||||
# argument specifies its own keyring.
|
|
||||||
#
|
|
||||||
# Each argument must be of the form "F,S,K" or "F,S", where each of F, S and K
|
|
||||||
# is either the number or the filename of one of the source files in the
|
|
||||||
# package. A pathname including directories is not allowed.
|
|
||||||
# F is a source file to check.
|
|
||||||
# S is a signature.
|
|
||||||
# K is a keyring.
|
|
||||||
#
|
|
||||||
# When an argument specifies a keyring, that signature will be verified against
|
|
||||||
# the keys in that keyring. For arguments that don't specify a keyring, the one
|
|
||||||
# specified with -k will be used, if any. If no keyring is specified either
|
|
||||||
# way, the macro will default to the first one it finds in the source list.
|
|
||||||
#
|
|
||||||
# It is assumed that all the keys in all keyrings, whether automatically found
|
|
||||||
# or explicitly specified, are trusted to authenticate the source files. There
|
|
||||||
# must not be any untrusted keys included.
|
|
||||||
|
|
||||||
# Some utility functions to the global namespace
|
|
||||||
# Most of these should come from the utility macros in the other repo.
|
|
||||||
%define gpg_macros_init %{lua:
|
|
||||||
function db(str)
|
|
||||||
io.stderr:write(tostring(str) .. '\\n')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Simple basename clone
|
|
||||||
function basename(str)
|
|
||||||
local name = string.gsub(str, "(.*/)(.*)", "%2")
|
|
||||||
return name
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Get the numbered or source file.
|
|
||||||
-- The spec writer can use any numbering scheme. The sources table
|
|
||||||
-- always counts from 1 and has no gaps, so we have to go back to the
|
|
||||||
-- SOURCEN macros.
|
|
||||||
function get_numbered_source(num)
|
|
||||||
local macro = "%SOURCE" .. num
|
|
||||||
local val = rpm.expand(macro)
|
|
||||||
if val == macro then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
-- Get the named source file. This returns the full path to a source file,
|
|
||||||
-- or nil if no such source exists.
|
|
||||||
function get_named_source(name)
|
|
||||||
local path
|
|
||||||
for _,path in ipairs(sources) do
|
|
||||||
if name == basename(path) then
|
|
||||||
return path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Determine whether the supplied filename contains a signature
|
|
||||||
-- Assumes the file will be closed when the handle goes out of scope
|
|
||||||
function is_signature(fname)
|
|
||||||
-- I don't really like this, but you can have completely binary sigs
|
|
||||||
if string.find(fname, '%.sig$') then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
local file = io.open(fname, 'r')
|
|
||||||
if file == nil then return false end
|
|
||||||
\
|
|
||||||
local c = 1
|
|
||||||
while true do
|
|
||||||
local line = file:read('*line')
|
|
||||||
if (line == nil or c > 10) then break end
|
|
||||||
if string.find(line, "BEGIN PGP SIGNATURE") then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
c = c+1
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Determine whether the supplied filename looks like a keyring
|
|
||||||
-- Ends in .gpg (might be binary data)? Contains "BEGIN PGP PUBLIC KEY BLOCK"
|
|
||||||
function is_keyring(fname)
|
|
||||||
-- XXX Have to hack this now to make it not find macros.gpg while we're testing.
|
|
||||||
if string.find(fname, '%.gpg$') and not string.find(fname, 'macros.gpg$') then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
\
|
|
||||||
local file = io.open(fname, 'r')
|
|
||||||
if file == nil then return false end
|
|
||||||
io.input(file)
|
|
||||||
local c = 1
|
|
||||||
while true do
|
|
||||||
local line = io.read('*line')
|
|
||||||
if (line == nil or c > 10) then break end
|
|
||||||
if string.find(line, "BEGIN PGP PUBLIC KEY BLOCK") then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
c = c+1
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Output code to have the current scriptlet echo something
|
|
||||||
function echo(str)
|
|
||||||
print("echo " .. str .. "\\n")
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Output an exit statement with nonzero return to the current scriptlet
|
|
||||||
function exit()
|
|
||||||
print("exit 1\\n")
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Call the RPM %error macro
|
|
||||||
function rpmerror(str)
|
|
||||||
echo("gpg_verify: " .. str)
|
|
||||||
rpm.expand("%{error:gpg_verify: " .. str .. "}")
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- XXX How to we get just a flag and no option?
|
|
||||||
function getflag(flag)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Extract the value of a passed option
|
|
||||||
function getoption(opt)
|
|
||||||
out = rpm.expand("%{-" .. opt .. "*}")
|
|
||||||
-- if string.len(out) == 0 then
|
|
||||||
if #out == 0 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
\
|
|
||||||
function unknownarg(a)
|
|
||||||
rpmerror("Unknown argument to %%gpg_verify: " .. a)
|
|
||||||
end
|
|
||||||
\
|
|
||||||
function rprint(s, l, i) -- recursive Print (structure, limit, indent)
|
|
||||||
l = (l) or 100; i = i or ""; -- default item limit, indent string
|
|
||||||
if (l<1) then db("ERROR: Item limit reached."); return l-1 end;
|
|
||||||
local ts = type(s);
|
|
||||||
if (ts ~= "table") then db(i,ts,s); return l-1 end
|
|
||||||
db(i,ts); -- print "table"
|
|
||||||
for k,v in pairs(s) do -- db("[KEY] VALUE")
|
|
||||||
l = rprint(v, l, i.."\t["..tostring(k).."]");
|
|
||||||
if (l < 0) then break end
|
|
||||||
end
|
|
||||||
return l
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Given a list of source file numbers or file names, validate them and
|
|
||||||
-- convert them to a list of full filenames.
|
|
||||||
function check_sources_list(arr)
|
|
||||||
local files = {}
|
|
||||||
local src,fpath
|
|
||||||
for _, src in ipairs(arr) do
|
|
||||||
if tonumber(src) then
|
|
||||||
-- We have a number; turn it to a full path to the corresponding source file
|
|
||||||
fpath = get_numbered_source(src)
|
|
||||||
else
|
|
||||||
fpath = get_named_source(src)
|
|
||||||
end
|
|
||||||
if not src then
|
|
||||||
err = 'Not a valid source: ' .. src
|
|
||||||
if src == '1' then
|
|
||||||
err = err .. '. Note that "Source:" is the 0th source file, not the 1st.'
|
|
||||||
end
|
|
||||||
rpmerror(err)
|
|
||||||
end
|
|
||||||
table.insert(files, fpath)
|
|
||||||
end
|
|
||||||
return files
|
|
||||||
end
|
|
||||||
rpm.define("gpg_macros_init %{nil}")
|
|
||||||
}#
|
|
||||||
|
|
||||||
# The actual macro
|
|
||||||
%define gpg_verify(k:) %gpg_macros_init%{lua:
|
|
||||||
-- RPM will ignore the first thing we output unless we give it a newline.
|
|
||||||
print('\\n')
|
|
||||||
\
|
|
||||||
local defkeyspec = getoption("k")
|
|
||||||
local args = rpm.expand("%*")
|
|
||||||
local sourcefiles = {}
|
|
||||||
local signature_table = {}
|
|
||||||
local signatures = {}
|
|
||||||
local keyrings = {}
|
|
||||||
local defkey, match, captures, s
|
|
||||||
\
|
|
||||||
local function storematch(m, c)
|
|
||||||
match = m; captures = c
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Scan all of the sources and try to categorize them.
|
|
||||||
-- Move to a function
|
|
||||||
for i,s in pairs(sources) do
|
|
||||||
sourcefiles[s] = true
|
|
||||||
-- db('File: ' .. i .. ", " .. s)
|
|
||||||
if is_signature(s) then
|
|
||||||
table.insert(signatures, s)
|
|
||||||
signature_table[s] = true
|
|
||||||
db('Found signature: ' .. s)
|
|
||||||
elseif is_keyring(s) then
|
|
||||||
table.insert(keyrings, s)
|
|
||||||
db('Found keyring: ' .. s)
|
|
||||||
else
|
|
||||||
-- Must be a source
|
|
||||||
db('Found source: ' .. s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
if defkeyspec then
|
|
||||||
defkey = check_sources_list({defkeyspec})[1]
|
|
||||||
if not defkey then
|
|
||||||
rpmerror('The provided keyring ' .. defkeyspec .. ' is not a valid source number or filename.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
if defkey then
|
|
||||||
db('Defkey: ' .. defkey)
|
|
||||||
else
|
|
||||||
db('No common key yet')
|
|
||||||
if keyrings[1] then
|
|
||||||
defkey = keyrings[1]
|
|
||||||
db('Using first found keyring file: '..defkey)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Check over any given args to make sure they're valid, and to see if a
|
|
||||||
-- common key is required.
|
|
||||||
local needdefkey = false
|
|
||||||
local double = rex.newPOSIX('^([^,]+),([^,]+)$')
|
|
||||||
local triple = rex.newPOSIX('^([^,]+),([^,]+),([^,]+)$')
|
|
||||||
local arglist = {}
|
|
||||||
\
|
|
||||||
-- RPM gives us the arguments in a single string.
|
|
||||||
-- Split on spaces and iterate
|
|
||||||
for arg in args:gmatch('%S+') do
|
|
||||||
db('Checking ' .. arg)
|
|
||||||
if triple:gmatch(arg, storematch) > 0 then
|
|
||||||
db('Looks OK')
|
|
||||||
local parsed = {srcnum=captures[1], signum=captures[2], keynum=captures[3]}
|
|
||||||
s = check_sources_list({captures[1], captures[2], captures[3]})
|
|
||||||
parsed.srcfile = s[1]
|
|
||||||
parsed.sigfile = s[2]
|
|
||||||
parsed.keyfile = s[3]
|
|
||||||
table.insert(arglist, parsed)
|
|
||||||
elseif double:gmatch(arg, storematch) > 0 then
|
|
||||||
db('Looks OK; needs common key')
|
|
||||||
needdefkey = true
|
|
||||||
local parsed = {srcnum=captures[1], signum=captures[2], keynum=defkeyspec, keyfile=defkey}
|
|
||||||
s = check_sources_list({captures[1], captures[2]})
|
|
||||||
parsed.srcfile = s[1]
|
|
||||||
parsed.sigfile = s[2]
|
|
||||||
table.insert(arglist, parsed)
|
|
||||||
else
|
|
||||||
rpmerror('Provided argument '..arg..' is not valid.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- So we now know if one of those args needs a common key
|
|
||||||
if needdefkey and not defkey then
|
|
||||||
rpmerror('No common key was specified or found, yet the arguments require one.')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- And if we have no arguments at all and no common key was found,
|
|
||||||
-- then we can't do an automatic check
|
|
||||||
if not defkey and args == '' then
|
|
||||||
rpmerror('No keyring specified and none found; cannot auto-check.')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Nothing to check means automatic mode
|
|
||||||
if #arglist == 0 then
|
|
||||||
local noext
|
|
||||||
for i,_ in pairs(signature_table) do
|
|
||||||
-- Find the name without the extension
|
|
||||||
noext = string.gsub(i, '%.[^.]+$', '')
|
|
||||||
if sourcefiles[noext] then
|
|
||||||
table.insert(arglist, {srcfile=noext, sigfile=i, keyfile=defkey})
|
|
||||||
else
|
|
||||||
rpmerror('Found signature ' .. i .. ' with no matching source file.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Now actually check things
|
|
||||||
for _,arg in ipairs(arglist) do
|
|
||||||
local gpgfile = '$GPGHOME/' .. basename(arg.keyfile) .. '.gpg'
|
|
||||||
echo('Checking signature: file ' .. arg.srcfile .. ' sig ' .. arg.sigfile .. ' key ' .. arg.keyfile)
|
|
||||||
\
|
|
||||||
-- We need a secure temp directorry
|
|
||||||
print('GPGHOME=$(mktemp -qd)\\n')
|
|
||||||
\
|
|
||||||
-- Call gpg2 to generate the dearmored key
|
|
||||||
print('gpg2 --homedir $GPGHOME --no-default-keyring --quiet --yes ')
|
|
||||||
print('--output '.. gpgfile .. ' --dearmor ' .. arg.keyfile .. "\\n")
|
|
||||||
\
|
|
||||||
-- Call gpgv2 to verify the signature against the source file with the dearmored key
|
|
||||||
print('gpgv2 --homedir $GPGHOME --keyring ' .. gpgfile .. ' ' .. arg.sigfile .. ' ' .. arg.srcfile .. '\\n')
|
|
||||||
\
|
|
||||||
print('rm -rf $GPGHOME\\n')
|
|
||||||
echo('')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
db('------------')
|
|
||||||
}#
|
|
||||||
|
|
||||||
# vim: set filetype=spec:
|
|
Loading…
Reference in new issue