]> git.decadent.org.uk Git - dak.git/blob - scripts/debian/buildd-add-keys
yuck, that was one debug line too much ending up in commit
[dak.git] / scripts / debian / buildd-add-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-add-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}/adminkeys.gpg"
60
61 # Default options for our gpg calls
62 DEFGPGOPT="--no-default-keyring --batch --no-tty --no-options --exit-on-status-write-error --no-greeting"
63
64 if ! [ -d "${INCOMING}" ]; then
65     log "Missing incoming dir, nothing to do"
66     exit 1
67 fi
68
69 # Whenever something goes wrong, its put in there.
70 mkdir -p "${ERRORS}"
71
72 # We process all new files in our incoming directory
73 for file in $(ls -1 ${INCOMING}/*.key); do
74     file=${file##*/}
75     # First we want to see if we recognize the filename. The buildd people have
76     # to follow a certain schema:
77     # architecture_builddname.YEAR-MONTH-DAY_HOURMINUTE.key
78     if [[ $file =~ (.*)_(.*).([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}[0-9]{2}).key ]]; then
79         ARCH=${BASH_REMATCH[1]}
80         BUILDD=${BASH_REMATCH[2]}
81         # Right now timestamp is unused
82         TIMESTAMP=${BASH_REMATCH[3]}
83     else
84         log "Unknown file ${file}, not processing"
85         mv "${INCOMING}/${file}" "${ERRORS}/unknown.${file}.$(date -Is)"
86         continue
87     fi
88
89     # Do we know the architecture?
90     found=0
91     for carch in ${archs}; do
92         if [ "${ARCH}" == "${carch}" ]; then
93             log "Known arch ${ARCH}, buildd ${BUILDD}"
94             found=1
95             break
96         fi
97     done
98
99     if [ ${found} -eq 0 ]; then
100         log "Unknown architecture ${ARCH}"
101         mv "${INCOMING}/${file}" "${ERRORS}/unknownarch.${file}.$(date -Is)"
102         continue
103     fi
104
105     # If we did have a file with this name already somethings wrong
106     if [ -f "${base}/${ARCH}/${file}" ]; then
107         log "Already processed this file"
108         mv "${INCOMING}/${file}" "${ERRORS}/duplicate.${file}.$(date -Is)"
109         continue
110     fi
111
112     # Where we want the status-fd from gpgv turn up
113     GPGSTATUS=$(mktemp -p "${TMPDIR}" GPGSTATUS.XXXXXX)
114     # Same for the loggger-fd
115     GPGLOGS=$(mktemp -p "${TMPDIR}" GPGLOGS.XXXXXX)
116     # And "decrypt" gives us output, the key without the pgp sig around it
117     GPGOUTF=$(mktemp -p "${TMPDIR}" GPGOUTF.XXXXXX)
118
119     # Open the filehandles, assigning them to the two files, so we can let gpg use them
120     exec 4> "${GPGSTATUS}"
121     exec 5> "${GPGLOGS}"
122
123     # So lets run gpg, status/logger into the two files, to "decrypt" the keyfile
124     if ! gpg ${DEFGPGOPT} --keyring "${ADMINS}" --status-fd 4 --logger-fd 5 --decrypt "${INCOMING}/${file}" > "${GPGOUTF}"; then
125         ret=$?
126         log "gpg returned with ${ret}, not adding key from file ${file}"
127         DATE=$(date -Is)
128         mv "${INCOMING}/${file}" "${ERRORS}/gpgerror.${file}.${DATE}"
129         mv "${GPGSTATUS}" "${ERRORS}/gpgerror.${file}.gpgstatus.${DATE}"
130         mv "${GPGLOGS}" "${ERRORS}/gpgerror.${file}.gpglogs.${DATE}"
131         rm -f "${GPGOUTF}"
132         continue
133     fi
134
135     # Read in the status output
136     GPGSTAT=$(cat "${GPGSTATUS}")
137     # And check if we like the sig. It has to be both, GOODISG and VALIDSIG or we don't accept it
138     if [[ ${GPGSTAT} =~ "GOODSIG" ]] && [[ ${GPGSTAT} =~ "VALIDSIG" ]]; then
139         log "Signature for ${file} accepted"
140     else
141         log "We are missing one of GOODSIG or VALIDSIG"
142         DATE=$(date -Is)
143         mv "${INCOMING}/${file}" "${ERRORS}/badsig.${file}.${DATE}"
144         mv "${GPGSTATUS}" "${ERRORS}/badsig.${file}.gpgstatus.${DATE}"
145         mv "${GPGLOGS}" "${ERRORS}/badsig.${file}.gpglogs.${DATE}"
146         rm -f "${GPGOUTF}"
147         continue
148     fi
149
150     # So at this point we know we accepted the signature of the file as valid,
151     # that is it is from a key allowed for this architecture. Which only
152     # leaves us with the task of checking if the key fulfills the requirements
153     # before we add it to the architectures keyring.
154
155     # Those currently are:
156     # - keysize 4096 or larger
157     # - RSA key, no encryption capability
158     # - UID matching "buildd autosigning key BUILDDNAME <buildd_ARCH-BUILDDNAME@buildd.debian.org>
159     # - expire within a 120 days
160     # - maximum 2 keys per architecture and buildd
161
162     TEMPKEYDATA=$(mktemp -p "${TMPDIR}" BDKEYS.XXXXXX)
163
164     gpg ${DEFGPGOPT} --with-colons "${GPGOUTF}" > "${TEMPKEYDATA}"
165
166     # Read in the TEMPKEYDATAFILE, but avoid using a subshell like a
167     # while read line otherwise would do
168     exec 4<> "${TEMPKEYDATA}"
169     error=""
170     while read line <&4; do
171         #pub:-:4096:1:FAB983612A6554FA:2011-03-24:2011-07-22::-:buildd autosigning key poulenc <buildd_powerpc-poulenc@buildd.debian.org>:
172
173         # Besides fiddling out the data we need to check later, this regex also check:
174         # - the keytype (:1:, 1 there means RSA)
175         # - the UID
176         # - that the key does have an expiration date (or it wont match, the second date
177         #   field would be empty
178         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>:$"
179         if [[ $line =~ $regex ]]; then
180             KEYSIZE=${BASH_REMATCH[1]}
181             KEYID=${BASH_REMATCH[2]}
182             KEYCREATE=${BASH_REMATCH[3]}
183             KEYEXPIRE=${BASH_REMATCH[4]}
184
185             # We do want 4096 or anything above
186             if [ ${KEYSIZE} -lt 4096 ]; then
187                 log "Keysize ${KEYSIZE} too small"
188                 error="${error} Keysize ${KEYSIZE} too small"
189                 continue
190             fi
191
192             # We want a maximum lifetime of 120 days, so check that.
193             # Easiest to compare in epoch, so lets see, 120 days midnight from now,
194             # compared with their set expiration date at midnight
195             # maxdate should turn out higher. just in case we make it 121 for this check
196             maxdate=$(date -d '121 day 00:00:00' +%s)
197             theirexpire=$(date -d "${KEYEXPIRE} 00:00:00" +%s)
198             if [ ${theirexpire} -gt ${maxdate} ]; then
199                 log "Key expiry ${KEYEXPIRE} wrong"
200                 error="${error} Key expiry ${KEYEXPIRE} wrong"
201                 continue
202             fi
203         else
204             log "Unknown line $line, sod off"
205             error="${error} Unknown line $line, sod off"
206             continue
207         fi
208     done
209     if [ -n "${error}" ]; then
210         log ${error}
211         DATE=$(date -Is)
212         mv "${INCOMING}/${file}" "${ERRORS}/badkey.${file}.${DATE}"
213         mv "${GPGSTATUS}" "${ERRORS}/badkey.${file}.gpgstatus.${DATE}"
214         mv "${GPGLOGS}" "${ERRORS}/badkey.${file}.gpglogs.${DATE}"
215         echo "${error}" >> "${ERRORS}/badkey.${file}.error.${DATE}"
216         rm -f "${GPGOUTF}"
217         continue
218     fi
219
220     # And now lets check how many keys this buildd already has. 2 is the maximum, so key
221     # rollover works. 3 won't, they have to rm one first
222     # We need to check for the amount of keys
223     ARCHKEYRING="${base}/${ARCH}/keyring.gpg"
224
225     KEYNO=$(gpg ${DEFGPGOPT} --keyring "${ARCHKEYRING}" --with-colons --list-keys "buildd_${ARCH}-${BUILDD}@buildd.debian.org" | grep -c '^pub:' || /bin/true )
226     if [ ${KEYNO} -gt 2 ]; then
227         DATE=$(date -Is)
228         mv "${INCOMING}/${file}" "${ERRORS}/toomany.${file}.${DATE}"
229         mv "${GPGSTATUS}" "${ERRORS}/toomany.${file}.gpgstatus.${DATE}"
230         mv "${GPGLOGS}" "${ERRORS}/toomany.${file}.gpglogs.${DATE}"
231         rm -f "${GPGOUTF}"
232         continue
233     fi
234
235     # Right. At this point everything should be in order, which means we should put the key into
236     # the keyring
237     log "Accepting key ${KEYID} for ${ARCH} buildd ${BUILDD}, expire ${KEYEXPIRE}"
238     gpg ${DEFGPGOPT} --status-fd 4 --logger-fd 5 --keyring "${ARCHKEYRING}" --import "${GPGOUTF}" 2>/dev/null
239
240     mv "${INCOMING}/${file}" "${base}/${ARCH}"
241 done