--- /dev/null
+#!/bin/bash
+# No way I try to deal with a crippled sh just for POSIX foo.
+
+# Copyright (C) 2011 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
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+# exit on errors
+set -e
+# make sure to only use defined variables
+set -u
+# ERR traps should be inherited from functions too.
+set -E
+
+# import the general variable set.
+export SCRIPTVARS=/srv/ftp-master.debian.org/dak/config/debian/vars
+. $SCRIPTVARS
+
+umask 027
+
+# And use one locale, no matter what the caller has set
+export LANG=C
+export LC_ALL=C
+PROGRAM="buildd-add-keys"
+
+# common functions are "outsourced"
+. "${configdir}/common"
+
+function cleanup() {
+ ERRVAL=$?
+ trap - ERR EXIT TERM HUP INT QUIT
+
+ for TEMPFILE in GPGSTATUS GPGLOGS GPGOUTF TEMPKEYDATA; do
+ TFILE=${TEMPFILE:=$TEMPFILE}
+ DELF=${!TFILE:-""}
+ 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}/admins"
+
+# Default options for our gpg calls
+DEFGPGOPT="--no-default-keyring --batch --no-tty --no-options --exit-on-status-write-error --no-greeting"
+
+if ! [ -d "${INCOMING}" ]; then
+ log "Missing incoming dir, nothing to do"
+ exit 1
+fi
+
+# 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
+ 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
+ ARCH=${BASH_REMATCH[1]}
+ BUILDD=${BASH_REMATCH[2]}
+ # Right now timestamp is unused
+ TIMESTAMP=${BASH_REMATCH[3]}
+ else
+ log "Unknown file ${file}, not processing"
+ mv "${INCOMING}/${file}" "${ERRORS}/unknown.${file}.$(date -Is)"
+ continue
+ fi
+
+ # Do we know the architecture?
+ found=0
+ for carch in ${archs}; do
+ if [ "${ARCH}" == "${carch}" ]; then
+ log "Known arch ${ARCH}, buildd ${BUILDD}"
+ found=1
+ break
+ fi
+ done
+
+ if [ ${found} -eq 0 ]; then
+ log "Unknown architecture ${ARCH}"
+ mv "${INCOMING}/${file}" "${ERRORS}/unknownarch.${file}.$(date -Is)"
+ continue
+ fi
+
+ # If we did have a file with this name already somethings wrong
+ if [ -f "${base}/${ARCH}/${file}" ]; then
+ log "Already processed this file"
+ mv "${INCOMING}/${file}" "${ERRORS}/duplicate.${file}.$(date -Is)"
+ continue
+ fi
+
+ # Where we want the status-fd from gpgv turn up
+ GPGSTATUS=$(mktemp -p "${TMPDIR}" GPGSTATUS.XXXXXX)
+ # Same for the loggger-fd
+ GPGLOGS=$(mktemp -p "${TMPDIR}" GPGLOGS.XXXXXX)
+ # And "decrypt" gives us output, the key without the pgp sig around it
+ GPGOUTF=$(mktemp -p "${TMPDIR}" GPGOUTF.XXXXXX)
+
+ # Open the filehandles, assigning them to the two files, so we can let gpg use them
+ exec 4> "${GPGSTATUS}"
+ exec 5> "${GPGLOGS}"
+
+ # So lets run gpg, status/logger into the two files, to "decrypt" the keyfile
+ if ! gpg ${DEFGPGOPT} --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 "${INCOMING}/${file}" "${ERRORS}/gpgerror.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/gpgerror.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/gpgerror.${file}.gpglogs.${DATE}"
+ rm -f "${GPGOUTF}"
+ continue
+ fi
+
+ # Read in the status output
+ GPGSTAT=$(cat "${GPGSTATUS}")
+ # And check if we like the sig. It has to be both, GOODISG and VALIDSIG or we don't accept it
+ if [[ ${GPGSTAT} =~ "GOODSIG" ]] && [[ ${GPGSTAT} =~ "VALIDSIG" ]]; then
+ log "Signature for ${file} accepted"
+ else
+ log "We are missing one of GOODSIG or VALIDSIG"
+ 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}"
+ continue
+ fi
+
+ # So at this point we know we accepted the signature of the file as valid,
+ # that is it is from a key allowed for this architecture. Which only
+ # leaves us with the task of checking if the key fulfills the requirements
+ # before we add it to the architectures keyring.
+
+ # Those currently are:
+ # - 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
+ # - maximum 2 keys per architecture and buildd
+
+ TEMPKEYDATA=$(mktemp -p "${TMPDIR}" BDKEYS.XXXXXX)
+
+ gpg ${DEFGPGOPT} --with-colons "${GPGOUTF}" > "${TEMPKEYDATA}"
+
+ # Read in the TEMPKEYDATAFILE, but avoid using a subshell like a
+ # while read line otherwise would do
+ exec 4<> "${TEMPKEYDATA}"
+ error=""
+ 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
+ done
+ if [ -n "${error}" ]; then
+ log ${error}
+ 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}"
+ rm -f "${GPGOUTF}"
+ continue
+ fi
+
+ # And now lets check how many keys this buildd already has. 2 is the maximum, so key
+ # rollover works. 3 won't, they have to rm one first
+ # 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:')
+ if [ ${KEYNO} -gt 2 ]; then
+ DATE=$(date -Is)
+ mv "${INCOMING}/${file}" "${ERRORS}/toomany.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/toomany.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/toomany.${file}.gpglogs.${DATE}"
+ rm -f "${GPGOUTF}"
+ continue
+ fi
+
+ # 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}"
+ gpg ${DEFGPGOPT} --status-fd 4 --logger-fd 5 --keyring "${ARCHKEYRING}" --import "${GPGOUTF}" 2>/dev/null
+
+ mv "${INCOMING}/${file}" "${base}/${ARCH}"
+done
--- /dev/null
+#!/bin/bash
+# No way I try to deal with a crippled sh just for POSIX foo.
+
+# Copyright (C) 2011 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
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+# exit on errors
+set -e
+# make sure to only use defined variables
+set -u
+# ERR traps should be inherited from functions too.
+set -E
+
+# import the general variable set.
+export SCRIPTVARS=/srv/ftp-master.debian.org/dak/config/debian/vars
+. $SCRIPTVARS
+
+umask 027
+
+# And use one locale, no matter what the caller has set
+export LANG=C
+export LC_ALL=C
+PROGRAM="buildd-prepare-dir"
+
+# common functions are "outsourced"
+. "${configdir}/common"
+
+# should be relative to the general base dir later
+TARGET="${base}/scripts/builddkeyrings"
+COPYTARGET="${base}/keyrings"
+REMOVED="${base}/removed-buildd-keys.gpg"
+
+mkdir -p "${TARGET}/keyrings"
+
+for arch in $archs; do
+ if [ -f ${base}/${arch}/keyring.gpg ]; then
+ cp -al ${base}/${arch}/keyring.gpg ${TARGET}/keyrings/buildd-${arch}-keyring.gpg
+ chmod 0644 ${TARGET}/keyrings/buildd-${arch}-keyring.gpg
+ fi
+done
+
+cd ${TARGET}
+sha512sum keyrings/* > sha512sums
+
+rm -f ${TARGET}/sha512sums.txt
+SIGNINGKEY=$(dak admin c signingkeyids)
+gpg --no-options --batch --no-tty --armour --default-key ${SIGNINKEY} --clearsign -o "${TARGET}/sha512sums.txt" "${TARGET}/sha512sums"
+rm -f ${TARGET}/sha512sums
--- /dev/null
+#!/bin/bash
+# No way I try to deal with a crippled sh just for POSIX foo.
+
+# Copyright (C) 2011 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
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+# exit on errors
+set -e
+# make sure to only use defined variables
+set -u
+# ERR traps should be inherited from functions too.
+set -E
+
+# import the general variable set.
+export SCRIPTVARS=/srv/ftp-master.debian.org/dak/config/debian/vars
+. $SCRIPTVARS
+
+umask 027
+
+# And use one locale, no matter what the caller has set
+export LANG=C
+export LC_ALL=C
+PROGRAM="buildd-remove-keys"
+
+# common functions are "outsourced"
+. "${configdir}/common"
+
+function cleanup() {
+ ERRVAL=$?
+ trap - ERR EXIT TERM HUP INT QUIT
+
+ for TEMPFILE in GPGSTATUS GPGLOGS GPGOUTF TEMPKEYDATA; do
+ TFILE=${TEMPFILE:=$TEMPFILE}
+ DELF=${!TFILE:-""}
+ 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}/admins"
+REMOVED="${base}/removed-buildd-keys.gpg"
+
+# Default options for our gpg calls
+DEFGPGOPT="--no-default-keyring --batch --no-tty --no-options --exit-on-status-write-error --no-greeting"
+
+if ! [ -d "${INCOMING}" ]; then
+ log "Missing incoming dir, nothing to do"
+ exit 1
+fi
+
+# 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}/*.del ); 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.del
+ if [[ $file =~ (.*)_(.*).([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}).del ]]; then
+ ARCH=${BASH_REMATCH[1]}
+ BUILDD=${BASH_REMATCH[2]}
+ # Right now timestamp is unused
+ TIMESTAMP=${BASH_REMATCH[3]}
+ else
+ log "Unknown file ${file}, not processing"
+ mv "${INCOMING}/${file}" "${ERRORS}/unknown.${file}.$(date -Is)"
+ continue
+ fi
+
+ # Do we know the architecture?
+ found=0
+ for carch in ${archs}; do
+ if [ "${ARCH}" == "${carch}" ]; then
+ log "Known arch ${ARCH}, buildd ${BUILDD}"
+ found=1
+ break
+ fi
+ done
+
+ if [ ${found} -eq 0 ]; then
+ log "Unknown architecture ${ARCH}"
+ mv "${INCOMING}/${file}" "${ERRORS}/unknownarch.${file}.$(date -Is)"
+ continue
+ fi
+
+ # If we did have a file with this name already somethings wrong
+ if [ -f "${base}/${ARCH}/${file}" ]; then
+ log "Already processed this file"
+ mv "${INCOMING}/${file}" "${ERRORS}/duplicate.${file}.$(date -Is)"
+ continue
+ fi
+
+ # Where we want the status-fd from gpgv turn up
+ GPGSTATUS=$(mktemp -p "${TMPDIR}" GPGSTATUS.XXXXXX)
+ # Same for the loggger-fd
+ GPGLOGS=$(mktemp -p "${TMPDIR}" GPGLOGS.XXXXXX)
+ # And "decrypt" gives us output, the key without the pgp sig around it
+ GPGOUTF=$(mktemp -p "${TMPDIR}" GPGOUTF.XXXXXX)
+
+ # Open the filehandles, assigning them to the two files, so we can let gpg use them
+ exec 4> "${GPGSTATUS}"
+ exec 5> "${GPGLOGS}"
+
+ # So lets run gpg, status/logger into the two files, to "decrypt" the keyfile
+ if ! gpg ${DEFGPGOPT} --status-fd 4 --logger-fd 5 --decrypt "${INCOMING}/${file}" > "${GPGOUTF}"; then
+ ret=$?
+ log "gpg returned with ${ret}, not removing key using ${file}"
+ DATE=$(date -Is)
+ mv "${INCOMING}/${file}" "${ERRORS}/gpgerror.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/gpgerror.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/gpgerror.${file}.gpglogs.${DATE}"
+ rm -f "${GPGOUTF}"
+ continue
+ fi
+
+ # Read in the status output
+ GPGSTAT=$(cat "${GPGSTATUS}")
+ # And check if we like the sig. It has to be both, GOODISG and VALIDSIG or we don't accept it
+ if [[ ${GPGSTAT} =~ "GOODSIG" ]] && [[ ${GPGSTAT} =~ "VALIDSIG" ]]; then
+ log "Signature for ${file} accepted"
+ else
+ log "We are missing one of GOODSIG or VALIDSIG"
+ 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}"
+ continue
+ fi
+
+ # So at this point we know we accepted the signature of the file as valid,
+ # that is it is from a key allowed for this architecture. Which only
+ # leaves us with the task of checking if there is a key to remove, and then remove
+ # it. We won't even check they have a key left, so if they want to they can
+ # empty out the set for an architecture
+
+ # Read in the GPGOUTF, but avoid using a subshell like a
+ # while read line otherwise would do
+ exec 4<> "${GPGOUTF}"
+ error=""
+ while read line <&4; do
+ if [[ $line =~ key:.([0-9A-F]{16}) ]]; then
+ KEYID=${BASH_REMATCH[1]}
+ elif [[ $line =~ comment:.(.*) ]]; then
+ COMMENT=${BASH_REMATCH[1]}
+ else
+ echo "Nay"
+ fi
+ done
+
+ # Right, we have the keyid, know the arch, lets see if we can remove it
+ ARCHKEYRING="${base}/${ARCH}/keyring.gpg"
+
+ # Is the key in there?
+ KEYNO=$(gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --with-colons --list-keys ${KEYID} | grep -c '^pub:')
+
+ if [ $KEYNO -eq 1 ]; then
+ # Right, exactly one there, lets get rid of it
+ # So put it into the removed keyring
+ gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --export ${KEYID} | gpg ${DEFGPGOPT} --keyring "${REMOVED}" --import 2>/dev/null
+ if gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --yes --delete-keys ${KEYID}; then
+ log "Removed key ${KEYID}, reason: ${COMMENT}"
+ mv "${INCOMING}/${file}" "${base}/${ARCH}"
+ continue
+ fi
+ else
+ log "Found more (or less) than one key I could delete. Not doing anything"
+ DATE=$(date -Is)
+ mv "${INCOMING}/${file}" "${ERRORS}/toomanykeys.${file}.${DATE}"
+ mv "${GPGSTATUS}" "${ERRORS}/toomanykeys.${file}.gpgstatus.${DATE}"
+ mv "${GPGLOGS}" "${ERRORS}/toomanykeys.${file}.gpglogs.${DATE}"
+ echo "${error}" >> "${ERRORS}/toomanykeys.${file}.error.${DATE}"
+ rm -f "${GPGOUTF}"
+ continue
+ fi
+done