#!/bin/bash
# No way I try to deal with a crippled sh just for POSIX foo.
-# Copyright (C) 2011 Joerg Jaspert <joerg@debian.org>
+# Copyright (C) 2011,2012 Joerg Jaspert <joerg@debian.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
trap - ERR EXIT TERM HUP INT QUIT
for TEMPFILE in GPGSTATUS GPGLOGS GPGOUTF TEMPKEYDATA; do
- TFILE=${TEMPFILE:=$TEMPFILE}
- DELF=${!TFILE:-""}
+ DELF=${!TEMPFILE:-""}
if [ -n "${DELF}" ] && [ -f "${DELF}" ]; then
rm -f "${DELF}"
fi
done
exit $ERRVAL
}
-trap cleanup ERR EXIT TERM HUP INT QUIT
base="${base}/scripts/builddkeyrings"
INCOMING="${base}/incoming"
ERRORS="${base}/errors"
ADMINS="${base}/adminkeys.gpg"
+ARCHADMINS="${base}/archadminkeys"
+STAMPFILE="${base}/updatedkeyring"
# Default options for our gpg calls
DEFGPGOPT="--no-default-keyring --batch --no-tty --no-options --exit-on-status-write-error --no-greeting"
exit 1
fi
+cd "${INCOMING}"
+KEYS=$(find . -maxdepth 1 -mindepth 1 -type f -name \*.key | sed -e "s,./,," | xargs)
+if [ -z "${KEYS}" ]; then
+ exit 0
+fi
+
+trap cleanup ERR EXIT TERM HUP INT QUIT
+
+# Tell prepare-dir that there is an update and it can run
+touch "${STAMPFILE}"
+
# Whenever something goes wrong, its put in there.
mkdir -p "${ERRORS}"
# We process all new files in our incoming directory
-for file in $(ls -1 ${INCOMING}/*.key); do
+for file in ${KEYS}; do
file=${file##*/}
# First we want to see if we recognize the filename. The buildd people have
# to follow a certain schema:
- # architecture_builddname.YEAR-MONTH-DAY_HOUR:MINUTE.key
- if [[ $file =~ (.*)_(.*).([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}).key ]]; then
+ # architecture_builddname.YEAR-MONTH-DAY_HOURMINUTE.key
+ if [[ $file =~ (.*)_(.*).([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}[0-9]{2}).key ]]; then
ARCH=${BASH_REMATCH[1]}
BUILDD=${BASH_REMATCH[2]}
# Right now timestamp is unused
exec 4> "${GPGSTATUS}"
exec 5> "${GPGLOGS}"
+ KEYRINGS="--keyring ${ADMINS}"
+ if [ -f "${ARCHADMINS}/${ARCH}.gpg" ]; then
+ KEYRINGS="${KEYRINGS} --keyring ${ARCHADMINS}/${ARCH}.gpg"
+ fi
# So lets run gpg, status/logger into the two files, to "decrypt" the keyfile
- if ! gpg ${DEFGPGOPT} --keyring "${ADMINS}" --status-fd 4 --logger-fd 5 --decrypt "${INCOMING}/${file}" > "${GPGOUTF}"; then
+ if ! gpg ${DEFGPGOPT} ${KEYRINGS} --status-fd 4 --logger-fd 5 --decrypt "${INCOMING}/${file}" > "${GPGOUTF}"; then
ret=$?
log "gpg returned with ${ret}, not adding key from file ${file}"
DATE=$(date -Is)
mv "${GPGLOGS}" "${ERRORS}/gpgerror.${file}.gpglogs.${DATE}"
rm -f "${GPGOUTF}"
continue
- fi
+ fi # gpg broke
# Read in the status output
GPGSTAT=$(cat "${GPGSTATUS}")
# - keysize 4096 or larger
# - RSA key, no encryption capability
# - UID matching "buildd autosigning key BUILDDNAME <buildd_ARCH-BUILDDNAME@buildd.debian.org>
- # - expire within a 120 days
+ # - expire within a 360 days
# - maximum 2 keys per architecture and buildd
TEMPKEYDATA=$(mktemp -p "${TMPDIR}" BDKEYS.XXXXXX)
- gpg ${DEFGPGOPT} --with-colons "${GPGOUTF}" > "${TEMPKEYDATA}"
+ # We also need to ensure this works, otherwise manually mangled files can break us here
+ if ! gpg ${DEFGPGOPT} --with-colons "${GPGOUTF}" > "${TEMPKEYDATA}"; then
+ log "For some reason we could validate the sig but failed on getting key details"
+ DATE=$(date -Is)
+ mv "${INCOMING}/${file}" "${ERRORS}/badsig.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/badsig.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/badsig.${file}.gpglogs.${DATE}"
+ rm -f "${GPGOUTF}"
+ rm -f "${TMPKEYDATA}"
+ continue
+ fi
# Read in the TEMPKEYDATAFILE, but avoid using a subshell like a
# while read line otherwise would do
exec 4<> "${TEMPKEYDATA}"
- error=""
+ KEYUID=""
+ #pub:-:4096:1:FAB983612A6554FA:2011-03-24:2011-07-22::-:buildd autosigning key poulenc <buildd_powerpc-poulenc@buildd.debian.org>:
+
+ # Of course this sucky gpg crapshit of an "interface" does give you different things depending on how people
+ # created their keys. And of course the buildd people created the test keys differently to what they now do
+ # which just means extra work for nothing. So as they now do other steps, the thing we get back suddenly looks like
+
+ #pub:-:4096:1:99595DC7865BEAD2:2011-03-26:2011-07-24::-:
+ #uid:::::::::buildd autosigning key corelli <buildd_mips-corelli@buildd.debian.org>:
+
+ # Besides fiddling out the data we need to check later, this regex also check:
+ # - the keytype (:1:, 1 there means RSA)
+ # - the UID
+ # - that the key does have an expiration date (or it wont match, the second date
+ # field would be empty
+ regex="^pub:-:([0-9]{4}):1:([0-9A-F]{16}):([0-9]{4}-[0-9]{2}-[0-9]{2}):([0-9]{4}-[0-9]{2}-[0-9]{2})::-:(buildd autosigning key ${BUILDD} <buildd_${ARCH}-${BUILDD}@buildd.debian.org>):$"
+ regex2="^pub:-:([0-9]{4}):1:([0-9A-F]{16}):([0-9]{4}-[0-9]{2}-[0-9]{2}):([0-9]{4}-[0-9]{2}-[0-9]{2})::-:$"
+ regex3="^uid:::::::::(buildd autosigning key ${BUILDD} <buildd_${ARCH}-${BUILDD}@buildd.debian.org>):$"
while read line <&4; do
- #pub:-:4096:1:FAB983612A6554FA:2011-03-24:2011-07-22::-:buildd autosigning key poulenc <buildd_powerpc-poulenc@buildd.debian.org>:
-
- # Besides fiddling out the data we need to check later, this regex also check:
- # - the keytype (:1:, 1 there means RSA)
- # - the UID
- # - that the key does have an expiration date (or it wont match, the second date
- # field would be empty
- regex="^pub:-:([0-9]{4}):1:([0-9A-F]{16}):([0-9]{4}-[0-9]{2}-[0-9]{2}):([0-9]{4}-[0-9]{2}-[0-9]{2})::-:buildd autosigning key ${BUILDD} <buildd_${ARCH}-${BUILDD}@buildd.debian.org>:$"
if [[ $line =~ $regex ]]; then
KEYSIZE=${BASH_REMATCH[1]}
KEYID=${BASH_REMATCH[2]}
KEYCREATE=${BASH_REMATCH[3]}
KEYEXPIRE=${BASH_REMATCH[4]}
-
- # We do want 4096 or anything above
- if [ ${KEYSIZE} -lt 4096 ]; then
- log "Keysize ${KEYSIZE} too small"
- error="${error} Keysize ${KEYSIZE} too small"
- continue
- fi
-
- # We want a maximum lifetime of 120 days, so check that.
- # Easiest to compare in epoch, so lets see, 120 days midnight from now,
- # compared with their set expiration date at midnight
- # maxdate should turn out higher. just in case we make it 121 for this check
- maxdate=$(date -d '121 day 00:00:00' +%s)
- theirexpire=$(date -d "${KEYEXPIRE} 00:00:00" +%s)
- if [ ${theirexpire} -gt ${maxdate} ]; then
- log "Key expiry ${KEYEXPIRE} wrong"
- error="${error} Key expiry ${KEYEXPIRE} wrong"
- continue
- fi
- else
- log "Unknown line $line, sod off"
- error="${error} Unknown line $line, sod off"
- continue
- fi
+ KEYUID=${BASH_REMATCH[5]}
+ elif [[ $line =~ $regex2 ]]; then
+ KEYSIZE=${BASH_REMATCH[1]}
+ KEYID=${BASH_REMATCH[2]}
+ KEYCREATE=${BASH_REMATCH[3]}
+ KEYEXPIRE=${BASH_REMATCH[4]}
+ elif [[ $line =~ $regex3 ]]; then
+ KEYUID=${BASH_REMATCH[1]}
+ else
+ log "Didn't recognize the key. Go kiss gpg"
+ DATE=$(date -Is)
+ mv "${INCOMING}/${file}" "${ERRORS}/badkey.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/badkey.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/badkey.${file}.gpglogs.${DATE}"
+ rm -f "${GPGOUTF}"
+ continue
+ fi
done
- if [ -n "${error}" ]; then
- log ${error}
+ if [ -z "${KEYUID}" ]; then
+ log "Did not recognize the UID format"
DATE=$(date -Is)
- mv "${INCOMING}/${file}" "${ERRORS}/badkey.${file}.${DATE}"
- mv "${GPGSTATUS}" "${ERRORS}/badkey.${file}.gpgstatus.${DATE}"
- mv "${GPGLOGS}" "${ERRORS}/badkey.${file}.gpglogs.${DATE}"
- echo "${error}" >> "${ERRORS}/badkey.${file}.error.${DATE}"
+ mv "${INCOMING}/${file}" "${ERRORS}/keyuid.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/keyuid.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/keyuid.${file}.gpglogs.${DATE}"
rm -f "${GPGOUTF}"
- continue
+ continue
+ fi
+ # We do want 4096 or anything above
+ if [ ${KEYSIZE} -lt 4096 ]; then
+ log "Keysize ${KEYSIZE} too small"
+ DATE=$(date -Is)
+ mv "${INCOMING}/${file}" "${ERRORS}/keysize.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/keysize.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/keysize.${file}.gpglogs.${DATE}"
+ rm -f "${GPGOUTF}"
+ continue
+ fi
+
+ # We want a maximum lifetime of 365 days, so check that.
+ # Easiest to compare in epoch, so lets see, 365 days midnight from now,
+ # compared with their set expiration date at midnight
+ # maxdate should turn out higher. just in case we make it 366 for this check
+ maxdate=$(date -d '366 day 00:00:00' +%s)
+ theirexpire=$(date -d "${KEYEXPIRE} 00:00:00" +%s)
+ if [ ${theirexpire} -gt ${maxdate} ]; then
+ log "Key expiry ${KEYEXPIRE} wrong"
+ DATE=$(date -Is)
+ mv "${INCOMING}/${file}" "${ERRORS}/keyexpire.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/keyexpire.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/keyexpire.${file}.gpglogs.${DATE}"
+ rm -f "${GPGOUTF}"
+ continue
fi
# And now lets check how many keys this buildd already has. 2 is the maximum, so key
# We need to check for the amount of keys
ARCHKEYRING="${base}/${ARCH}/keyring.gpg"
- KEYNO=$(gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --with-colons --list-keys "buildd_${ARCH}-${BUILDD}@buildd.debian.org" | grep -c '^pub:')
+ KEYNO=$(gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --with-colons --list-keys "buildd_${ARCH}-${BUILDD}@buildd.debian.org" 2>/dev/null | grep -c '^pub:' || /bin/true )
if [ ${KEYNO} -gt 2 ]; then
+ log "Too many keys for ${ARCH} buildd ${BUILDD}"
DATE=$(date -Is)
mv "${INCOMING}/${file}" "${ERRORS}/toomany.${file}.${DATE}"
mv "${GPGSTATUS}" "${ERRORS}/toomany.${file}.gpgstatus.${DATE}"
# Right. At this point everything should be in order, which means we should put the key into
# the keyring
- log "Accepting key ${KEYID} for ${ARCH} buildd ${BUILDD}, expire ${KEYEXPIRE}"
+ KEYSUBMITTER=$(cat "${GPGSTATUS}"|grep GOODSIG)
+ KEYSUBMITTER=${KEYSUBMITTER##*GOODSIG}
+ log "${KEYSUBMITTER} added key ${KEYID} for ${ARCH} buildd ${BUILDD}, expire ${KEYEXPIRE}"
gpg ${DEFGPGOPT} --status-fd 4 --logger-fd 5 --keyring "${ARCHKEYRING}" --import "${GPGOUTF}" 2>/dev/null
mv "${INCOMING}/${file}" "${base}/${ARCH}"