]> git.decadent.org.uk Git - dak.git/blob - scripts/debian/buildd-remove-keys
Somehow it helps to actually use the admin keyrings
[dak.git] / scripts / debian / buildd-remove-keys
1 #!/bin/bash
2 # No way I try to deal with a crippled sh just for POSIX foo.
3
4 # Copyright (C) 2011 Joerg Jaspert <joerg@debian.org>
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License as
8 # published by the Free Software Foundation; version 2.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19
20 # exit on errors
21 set -e
22 # make sure to only use defined variables
23 set -u
24 # ERR traps should be inherited from functions too.
25 set -E
26
27 # import the general variable set.
28 export SCRIPTVARS=/srv/ftp-master.debian.org/dak/config/debian/vars
29 . $SCRIPTVARS
30
31 umask 027
32
33 # And use one locale, no matter what the caller has set
34 export LANG=C
35 export LC_ALL=C
36 PROGRAM="buildd-remove-keys"
37
38 # common functions are "outsourced"
39 . "${configdir}/common"
40
41 function cleanup() {
42     ERRVAL=$?
43     trap - ERR EXIT TERM HUP INT QUIT
44
45     for TEMPFILE in GPGSTATUS GPGLOGS GPGOUTF TEMPKEYDATA; do
46         TFILE=${TEMPFILE:=$TEMPFILE}
47         DELF=${!TFILE:-""}
48         if [ -n "${DELF}" ] && [ -f "${DELF}" ]; then
49             rm -f "${DELF}"
50         fi
51     done
52     exit $ERRVAL
53 }
54 trap cleanup ERR EXIT TERM HUP INT QUIT
55
56 base="${base}/scripts/builddkeyrings"
57 INCOMING="${base}/incoming"
58 ERRORS="${base}/errors"
59 ADMINS="${base}/admins"
60 REMOVED="${base}/removed-buildd-keys.gpg"
61
62 # Default options for our gpg calls
63 DEFGPGOPT="--no-default-keyring --batch --no-tty --no-options --exit-on-status-write-error --no-greeting"
64
65 if ! [ -d "${INCOMING}" ]; then
66     log "Missing incoming dir, nothing to do"
67     exit 1
68 fi
69
70 # Whenever something goes wrong, its put in there.
71 mkdir -p "${ERRORS}"
72
73 # We process all new files in our incoming directory
74 for file in $(ls -1 ${INCOMING}/*.del ); do
75     file=${file##*/}
76     # First we want to see if we recognize the filename. The buildd people have
77     # to follow a certain schema:
78     # architecture_builddname.YEAR-MONTH-DAY_HOUR:MINUTE.del
79     if [[ $file =~ (.*)_(.*).([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}).del ]]; then
80         ARCH=${BASH_REMATCH[1]}
81         BUILDD=${BASH_REMATCH[2]}
82         # Right now timestamp is unused
83         TIMESTAMP=${BASH_REMATCH[3]}
84     else
85         log "Unknown file ${file}, not processing"
86         mv "${INCOMING}/${file}" "${ERRORS}/unknown.${file}.$(date -Is)"
87         continue
88     fi
89
90     # Do we know the architecture?
91     found=0
92     for carch in ${archs}; do
93         if [ "${ARCH}" == "${carch}" ]; then
94             log "Known arch ${ARCH}, buildd ${BUILDD}"
95             found=1
96             break
97         fi
98     done
99
100     if [ ${found} -eq 0 ]; then
101         log "Unknown architecture ${ARCH}"
102         mv "${INCOMING}/${file}" "${ERRORS}/unknownarch.${file}.$(date -Is)"
103         continue
104     fi
105
106     # If we did have a file with this name already somethings wrong
107     if [ -f "${base}/${ARCH}/${file}" ]; then
108         log "Already processed this file"
109         mv "${INCOMING}/${file}" "${ERRORS}/duplicate.${file}.$(date -Is)"
110         continue
111     fi
112
113     # Where we want the status-fd from gpgv turn up
114     GPGSTATUS=$(mktemp -p "${TMPDIR}" GPGSTATUS.XXXXXX)
115     # Same for the loggger-fd
116     GPGLOGS=$(mktemp -p "${TMPDIR}" GPGLOGS.XXXXXX)
117     # And "decrypt" gives us output, the key without the pgp sig around it
118     GPGOUTF=$(mktemp -p "${TMPDIR}" GPGOUTF.XXXXXX)
119
120     # Open the filehandles, assigning them to the two files, so we can let gpg use them
121     exec 4> "${GPGSTATUS}"
122     exec 5> "${GPGLOGS}"
123
124     # So lets run gpg, status/logger into the two files, to "decrypt" the keyfile
125     if ! gpg ${DEFGPGOPT} --keyring "${ADMINS}/${ARCH}.gpg" --status-fd 4 --logger-fd 5 --decrypt "${INCOMING}/${file}" > "${GPGOUTF}"; then
126         ret=$?
127         log "gpg returned with ${ret}, not removing key using ${file}"
128         DATE=$(date -Is)
129         mv "${INCOMING}/${file}" "${ERRORS}/gpgerror.${file}.${DATE}"
130         mv "${GPGSTATUS}" "${ERRORS}/gpgerror.${file}.gpgstatus.${DATE}"
131         mv "${GPGLOGS}" "${ERRORS}/gpgerror.${file}.gpglogs.${DATE}"
132         rm -f "${GPGOUTF}"
133         continue
134     fi
135
136     # Read in the status output
137     GPGSTAT=$(cat "${GPGSTATUS}")
138     # And check if we like the sig. It has to be both, GOODISG and VALIDSIG or we don't accept it
139     if [[ ${GPGSTAT} =~ "GOODSIG" ]] && [[ ${GPGSTAT} =~ "VALIDSIG" ]]; then
140         log "Signature for ${file} accepted"
141     else
142         log "We are missing one of GOODSIG or VALIDSIG"
143         DATE=$(date -Is)
144         mv "${INCOMING}/${file}" "${ERRORS}/badsig.${file}.${DATE}"
145         mv "${GPGSTATUS}" "${ERRORS}/badsig.${file}.gpgstatus.${DATE}"
146         mv "${GPGLOGS}" "${ERRORS}/badsig.${file}.gpglogs.${DATE}"
147         rm -f "${GPGOUTF}"
148         continue
149     fi
150
151     # So at this point we know we accepted the signature of the file as valid,
152     # that is it is from a key allowed for this architecture. Which only
153     # leaves us with the task of checking if there is a key to remove, and then remove
154     # it. We won't even check they have a key left, so if they want to they can
155     # empty out the set for an architecture
156
157     # Read in the GPGOUTF, but avoid using a subshell like a
158     # while read line otherwise would do
159     exec 4<> "${GPGOUTF}"
160     error=""
161     while read line <&4; do
162         if [[ $line =~ key:.([0-9A-F]{16}) ]]; then
163             KEYID=${BASH_REMATCH[1]}
164         elif [[ $line =~ comment:.(.*) ]]; then
165             COMMENT=${BASH_REMATCH[1]}
166         else
167             echo "Nay"
168         fi
169     done
170
171     # Right, we have the keyid, know the arch, lets see if we can remove it
172     ARCHKEYRING="${base}/${ARCH}/keyring.gpg"
173
174     # Is the key in there?
175     KEYNO=$(gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --with-colons --list-keys ${KEYID} | grep -c '^pub:')
176
177     if [ $KEYNO -eq 1 ]; then
178         # Right, exactly one there, lets get rid of it
179         # So put it into the removed keyring
180         gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --export ${KEYID} | gpg ${DEFGPGOPT} --keyring "${REMOVED}" --import 2>/dev/null
181         if gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --yes --delete-keys ${KEYID}; then
182             log "Removed key ${KEYID}, reason: ${COMMENT}"
183             mv "${INCOMING}/${file}" "${base}/${ARCH}"
184             continue
185         fi
186     else
187         log "Found more (or less) than one key I could delete. Not doing anything"
188         DATE=$(date -Is)
189         mv "${INCOMING}/${file}" "${ERRORS}/toomanykeys.${file}.${DATE}"
190         mv "${GPGSTATUS}" "${ERRORS}/toomanykeys.${file}.gpgstatus.${DATE}"
191         mv "${GPGLOGS}" "${ERRORS}/toomanykeys.${file}.gpglogs.${DATE}"
192         echo "${error}" >> "${ERRORS}/toomanykeys.${file}.error.${DATE}"
193         rm -f "${GPGOUTF}"
194         continue
195     fi
196 done