]> err.no Git - dak/commitdiff
autosigning foo
authorJoerg Jaspert <joerg@debian.org>
Fri, 25 Mar 2011 14:47:50 +0000 (15:47 +0100)
committerJoerg Jaspert <joerg@debian.org>
Fri, 25 Mar 2011 14:47:50 +0000 (15:47 +0100)
add 3 scripts to handle the keystuff

Signed-off-by: Joerg Jaspert <joerg@debian.org>
scripts/debian/buildd-add-keys [new file with mode: 0755]
scripts/debian/buildd-prepare-dir [new file with mode: 0755]
scripts/debian/buildd-remove-keys [new file with mode: 0755]

diff --git a/scripts/debian/buildd-add-keys b/scripts/debian/buildd-add-keys
new file mode 100755 (executable)
index 0000000..1393256
--- /dev/null
@@ -0,0 +1,241 @@
+#!/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
diff --git a/scripts/debian/buildd-prepare-dir b/scripts/debian/buildd-prepare-dir
new file mode 100755 (executable)
index 0000000..8366d3c
--- /dev/null
@@ -0,0 +1,61 @@
+#!/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
diff --git a/scripts/debian/buildd-remove-keys b/scripts/debian/buildd-remove-keys
new file mode 100755 (executable)
index 0000000..6252b45
--- /dev/null
@@ -0,0 +1,196 @@
+#!/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