]> git.saurik.com Git - apt.git/blame - cmdline/apt-key.in
Bug #807012 also involves package dependencies :/.
[apt.git] / cmdline / apt-key.in
CommitLineData
b3d44315
MV
1#!/bin/sh
2
3set -e
dac074b0 4unset GREP_OPTIONS
fecfbf2e 5export IFS="$(printf "\n\b")"
b3d44315 6
5b2c6ddc 7MASTER_KEYRING='&keyring-master-filename;'
bc8f83a5 8eval "$(apt-config shell MASTER_KEYRING APT::Key::MasterKeyring)"
5b2c6ddc 9ARCHIVE_KEYRING='&keyring-filename;'
bc8f83a5 10eval "$(apt-config shell ARCHIVE_KEYRING APT::Key::ArchiveKeyring)"
5b2c6ddc 11REMOVED_KEYS='&keyring-removed-filename;'
bc8f83a5 12eval "$(apt-config shell REMOVED_KEYS APT::Key::RemovedKeys)"
5b2c6ddc 13ARCHIVE_KEYRING_URI='&keyring-uri;'
bc8f83a5 14eval "$(apt-config shell ARCHIVE_KEYRING_URI APT::Key::ArchiveKeyringURI)"
4a242c5b 15
3d0def05
DK
16aptkey_echo() { echo "$@"; }
17
00c6e1a3
MV
18requires_root() {
19 if [ "$(id -u)" -ne 0 ]; then
8e438ede 20 apt_error "This command can only be used by root."
00c6e1a3
MV
21 exit 1
22 fi
23}
24
e23ee4c2 25command_available() {
ee385a36 26 if [ -x "$1" ]; then return 0; fi
e23ee4c2
DK
27 # command -v "$1" >/dev/null 2>&1 # not required by policy, see #747320
28 # which "$1" >/dev/null 2>&1 # is in debianutils (essential) but not on non-debian systems
29 local OLDIFS="$IFS"
30 IFS=:
31 for p in $PATH; do
32 if [ -x "${p}/${1}" ]; then
33 IFS="$OLDIFS"
34 return 0
35 fi
36 done
37 IFS="$OLDIFS"
38 return 1
39}
40
bc8f83a5
DK
41escape_shell() {
42 echo "$@" | sed -e "s#'#'\"'\"'#g"
43}
44
ba72845c 45get_fingerprints_of_keyring() {
fecfbf2e 46 aptkey_execute "$GPG_SH" --keyring "$1" --with-colons --fingerprint | while read publine; do
ba72845c
DK
47 # search for a public key
48 if [ "${publine%%:*}" != 'pub' ]; then continue; fi
49 # search for the associated fingerprint (should be the very next line)
50 while read fprline; do
51 if [ "${fprline%%:*}" = 'sub' ]; then break; # should never happen
52 elif [ "${fprline%%:*}" != 'fpr' ]; then continue; fi
53 echo "$fprline" | cut -d':' -f 10
54 done
25f27319
DK
55 # order in the keyring shouldn't be important
56 done | sort
ba72845c
DK
57}
58
7fbe42c0 59add_keys_with_verify_against_master_keyring() {
bc8f83a5
DK
60 ADD_KEYRING="$1"
61 MASTER="$2"
f87338d2 62
8c56b1e0 63 if [ ! -f "$ADD_KEYRING" ]; then
8e438ede 64 apt_error "Keyring '$ADD_KEYRING' to be added not found"
8c56b1e0 65 return
84b286f6 66 fi
8c56b1e0 67 if [ ! -f "$MASTER" ]; then
8e438ede 68 apt_error "Master-Keyring '$MASTER' not found"
8c56b1e0
MV
69 return
70 fi
71
72 # when adding new keys, make sure that the archive-master-keyring
73 # is honored. so:
3a341a1d
MV
74 # all keys that are exported must have a valid signature
75 # from a key in the $distro-master-keyring
ba72845c 76 add_keys="$(get_fingerprints_of_keyring "$ADD_KEYRING")"
bc8f83a5
DK
77 all_add_keys="$(aptkey_execute "$GPG_SH" --keyring "$ADD_KEYRING" --with-colons --list-keys | grep ^[ps]ub | cut -d: -f5)"
78 master_keys="$(aptkey_execute "$GPG_SH" --keyring "$MASTER" --with-colons --list-keys | grep ^pub | cut -d: -f5)"
f87338d2
DK
79
80 # ensure there are no colisions LP: #857472
81 for all_add_key in $all_add_keys; do
82 for master_key in $master_keys; do
83 if [ "$all_add_key" = "$master_key" ]; then
84 echo >&2 "Keyid collision for '$all_add_key' detected, operation aborted"
85 return 1
86 fi
87 done
88 done
0dae96a2 89
8c56b1e0 90 for add_key in $add_keys; do
f87338d2 91 # export the add keyring one-by-one
0dae96a2 92 local TMP_KEYRING="${GPGHOMEDIR}/tmp-keyring.gpg"
fecfbf2e
DK
93 aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --output "$TMP_KEYRING" --export "$add_key"
94 if ! aptkey_execute "$GPG_SH" --batch --yes --keyring "$TMP_KEYRING" --import "$MASTER" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
c1642be5 95 cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
0dae96a2
DK
96 false
97 fi
98 # check if signed with the master key and only add in this case
99 ADDED=0
8c56b1e0 100 for master_key in $master_keys; do
fecfbf2e
DK
101 if aptkey_execute "$GPG_SH" --keyring "$TMP_KEYRING" --check-sigs --with-colons "$add_key" \
102 | grep '^sig:!:' | cut -d: -f5 | grep -q "$master_key"; then
103 aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --export "$add_key" \
104 | aptkey_execute "$GPG" --batch --yes --import
c9bb37c7 105 ADDED=1
8c56b1e0 106 fi
7fbe42c0 107 done
c9bb37c7
MV
108 if [ $ADDED = 0 ]; then
109 echo >&2 "Key '$add_key' not added. It is not signed with a master key"
110 fi
0dae96a2 111 rm -f "${TMP_KEYRING}"
8c56b1e0 112 done
7fbe42c0 113}
4a242c5b 114
848ecfa4
MV
115# update the current archive signing keyring from a network URI
116# the archive-keyring keys needs to be signed with the master key
117# (otherwise it does not make sense from a security POV)
118net_update() {
bc8f83a5
DK
119 local APT_DIR='/'
120 eval $(apt-config shell APT_DIR Dir)
121
f87338d2 122 # Disabled for now as code is insecure (LP: #1013639 (and 857472, 1013128))
5acf154d
MV
123 APT_KEY_NET_UPDATE_ENABLED=""
124 eval $(apt-config shell APT_KEY_NET_UPDATE_ENABLED APT::Key::Net-Update-Enabled)
125 if [ -z "$APT_KEY_NET_UPDATE_ENABLED" ]; then
126 exit 1
127 fi
f87338d2 128
848ecfa4 129 if [ -z "$ARCHIVE_KEYRING_URI" ]; then
8e438ede 130 apt_error 'Your distribution is not supported in net-update as no uri for the archive-keyring is set'
46e39c8e
MV
131 exit 1
132 fi
133 # in theory we would need to depend on wget for this, but this feature
134 # isn't useable in debian anyway as we have no keyring uri nor a master key
e23ee4c2 135 if ! command_available 'wget'; then
8e438ede 136 apt_error 'wget is required for a network-based update, but it is not installed'
46e39c8e 137 exit 1
848ecfa4 138 fi
fecfbf2e
DK
139 if [ ! -d "${APT_DIR}/var/lib/apt/keyrings" ]; then
140 mkdir -p "${APT_DIR}/var/lib/apt/keyrings"
848ecfa4 141 fi
fecfbf2e 142 keyring="${APT_DIR}/var/lib/apt/keyrings/$(basename "$ARCHIVE_KEYRING_URI")"
93886541
MV
143 old_mtime=0
144 if [ -e $keyring ]; then
bc8f83a5 145 old_mtime=$(stat -c %Y "$keyring")
93886541 146 fi
fecfbf2e
DK
147 (cd "${APT_DIR}/var/lib/apt/keyrings"; wget --timeout=90 -q -N "$ARCHIVE_KEYRING_URI")
148 if [ ! -e "$keyring" ]; then
93886541
MV
149 return
150 fi
fecfbf2e 151 new_mtime=$(stat -c %Y "$keyring")
93886541 152 if [ $new_mtime -ne $old_mtime ]; then
3d0def05 153 aptkey_echo "Checking for new archive signing keys now"
fecfbf2e 154 add_keys_with_verify_against_master_keyring "$keyring" "$MASTER_KEYRING"
93886541 155 fi
848ecfa4
MV
156}
157
4a242c5b 158update() {
f4dcab05
DK
159 if [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then
160 echo >&2 "Warning: 'apt-key update' is deprecated and should not be used anymore!"
161 if [ -z "$ARCHIVE_KEYRING" ]; then
162 echo >&2 "Note: In your distribution this command is a no-op and can therefore be removed safely."
163 exit 0
164 fi
165 fi
fecfbf2e 166 if [ ! -f "$ARCHIVE_KEYRING" ]; then
8e438ede 167 apt_error "Can't find the archive-keyring (Is the &keyring-package; package installed?)"
4a242c5b
MV
168 exit 1
169 fi
170
3a341a1d
MV
171 # add new keys from the package;
172
173 # we do not use add_keys_with_verify_against_master_keyring here,
1171258a 174 # because "update" is run on regular package updates. A
3a341a1d
MV
175 # attacker might as well replace the master-archive-keyring file
176 # in the package and add his own keys. so this check wouldn't
177 # add any security. we *need* this check on net-update though
f14cde2c 178 import_keyring_into_keyring "$ARCHIVE_KEYRING" '' && cat "${GPGHOMEDIR}/gpgoutput.log"
4a242c5b 179
364af2ef
MV
180 if [ -r "$REMOVED_KEYS" ]; then
181 # remove no-longer supported/used keys
2906182d 182 get_fingerprints_of_keyring "$(dearmor_filename "$REMOVED_KEYS")" | while read key; do
5beb682d 183 foreach_keyring_do 'remove_key_from_keyring' "$key"
364af2ef
MV
184 done
185 else
8e438ede 186 apt_warn "Removed keys keyring '$REMOVED_KEYS' missing or not readable"
364af2ef 187 fi
4a242c5b
MV
188}
189
04937adc 190remove_key_from_keyring() {
4b30c1dc
DK
191 local KEYRINGFILE="$1"
192 shift
5beb682d
DK
193 # non-existent keyrings have by definition no keys
194 if [ ! -e "$KEYRINGFILE" ]; then
195 return
196 fi
197
2906182d
DK
198 local FINGERPRINTS="${GPGHOMEDIR}/keyringfile.keylst"
199 local DEARMOR="$(dearmor_filename "$KEYRINGFILE")"
200 get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS"
f7bd44ba 201
2906182d 202 for KEY in "$@"; do
e289907f
DK
203 # strip leading 0x, if present:
204 KEY="$(echo "${KEY#0x}" | tr -d ' ')"
f7bd44ba 205
25f27319
DK
206 # check if the key is in this keyring
207 if ! grep -iq "^[0-9A-F]*${KEY}$" "$FINGERPRINTS"; then
4b30c1dc
DK
208 continue
209 fi
210 if [ ! -w "$KEYRINGFILE" ]; then
2906182d 211 apt_warn "Key ${KEY} is in keyring ${KEYRINGFILE}, but can't be removed as it is read only."
4b30c1dc
DK
212 continue
213 fi
214 # check if it is the only key in the keyring and if so remove the keyring altogether
25f27319 215 if [ '1' = "$(uniq "$FINGERPRINTS" | wc -l)" ]; then
4b30c1dc
DK
216 mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" # behave like gpg
217 return
218 fi
219 # we can't just modify pointed to files as these might be in /usr or something
220 local REALTARGET
2906182d
DK
221 if [ -L "$DEARMOR" ]; then
222 REALTARGET="$(readlink -f "$DEARMOR")"
223 mv -f "$DEARMOR" "${DEARMOR}.dpkg-tmp"
224 cp -a "$REALTARGET" "$DEARMOR"
4b30c1dc
DK
225 fi
226 # delete the key from the keyring
2906182d 227 aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --batch --delete-keys --yes "$KEY"
4b30c1dc
DK
228 if [ -n "$REALTARGET" ]; then
229 # the real backup is the old link, not the copy we made
2906182d
DK
230 mv -f "${DEARMOR}.dpkg-tmp" "${DEARMOR}~"
231 fi
232 if [ "$DEARMOR" != "$KEYRINGFILE" ]; then
233 mv -f "$KEYRINGFILE" "${KEYRINGFILE}~"
234 create_new_keyring "$KEYRINGFILE"
235 aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --armor --export > "$KEYRINGFILE"
4b30c1dc 236 fi
2906182d 237 get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS"
4b30c1dc 238 done
04937adc
DK
239}
240
105503b4
DK
241accessible_file_exists() {
242 if ! test -s "$1"; then
243 return 1
244 fi
245 if test -r "$1"; then
246 return 0
247 fi
8e438ede 248 apt_warn "The key(s) in the keyring $1 are ignored as the file is not readable by user '$USER' executing apt-key."
105503b4
DK
249 return 1
250}
251
4b30c1dc
DK
252foreach_keyring_do() {
253 local ACTION="$1"
254 shift
b0d40854 255 # if a --keyring was given, just work on this one
4b30c1dc 256 if [ -n "$FORCED_KEYRING" ]; then
2906182d 257 $ACTION "$TRUSTEDFILE" "$@"
4b30c1dc
DK
258 else
259 # otherwise all known keyrings are up for inspection
105503b4 260 if accessible_file_exists "$TRUSTEDFILE"; then
5beb682d
DK
261 $ACTION "$TRUSTEDFILE" "$@"
262 fi
4b30c1dc 263 local TRUSTEDPARTS="/etc/apt/trusted.gpg.d"
bc8f83a5 264 eval "$(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d)"
4b30c1dc 265 if [ -d "$TRUSTEDPARTS" ]; then
0cfec3ab 266 TRUSTEDPARTS="$(readlink -f "$TRUSTEDPARTS")"
2906182d 267 local TRUSTEDPARTSLIST="$(cd /; find "$TRUSTEDPARTS" -mindepth 1 -maxdepth 1 \( -name '*.gpg' -o -name '*.asc' \))"
0cfec3ab 268 for trusted in $(echo "$TRUSTEDPARTSLIST" | sort); do
105503b4 269 if accessible_file_exists "$trusted"; then
5beb682d
DK
270 $ACTION "$trusted" "$@"
271 fi
4b30c1dc 272 done
04937adc 273 fi
4b30c1dc 274 fi
04937adc
DK
275}
276
2906182d 277list_keys_in_keyring() {
5beb682d
DK
278 local KEYRINGFILE="$1"
279 shift
0b94a7bc 280 # fingerprint and co will fail if key isn't in this keyring
2906182d
DK
281 aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$KEYRINGFILE")" "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2> "${GPGHOMEDIR}/gpgoutput.err" || true
282 if [ ! -s "${GPGHOMEDIR}/gpgoutput.log" ]; then
283 return
284 fi
285 # we fake gpg header here to refer to the real asc file rather than a temp file
286 if [ "${KEYRINGFILE##*.}" = 'asc' ]; then
287 if expr match "$(sed -n '2p' "${GPGHOMEDIR}/gpgoutput.log")" '^-\+$' >/dev/null 2>&1; then
288 echo "$KEYRINGFILE"
289 echo "$KEYRINGFILE" | sed 's#[^-]#-#g'
290 sed '1,2d' "${GPGHOMEDIR}/gpgoutput.log" || true
291 else
292 cat "${GPGHOMEDIR}/gpgoutput.log"
293 fi
294 else
295 cat "${GPGHOMEDIR}/gpgoutput.log"
296 fi
297 if [ -s "${GPGHOMEDIR}/gpgoutput.err" ]; then
298 cat >&2 "${GPGHOMEDIR}/gpgoutput.err"
299 fi
300}
301
302export_key_from_to() {
303 local FROM="$1"
304 local TO="$2"
305 shift 2
306 if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export "$@" > "$TO" 2> "${GPGHOMEDIR}/gpgoutput.log"; then
307 cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
308 false
309 else
310 chmod 0644 -- "$TO"
311 fi
5beb682d
DK
312}
313
f14cde2c
DK
314import_keyring_into_keyring() {
315 local FROM="${1:-${GPGHOMEDIR}/pubring.gpg}"
316 local TO="${2:-${GPGHOMEDIR}/pubring.gpg}"
317 shift 2
318 rm -f "${GPGHOMEDIR}/gpgoutput.log"
319 # the idea is simple: We take keys from one keyring and copy it to another
3a8776a3 320 # we do this with so many checks in between to ensure that WE control the
f14cde2c
DK
321 # creation, so we know that the (potentially) created $TO keyring is a
322 # simple keyring rather than a keybox as gpg2 would create it which in turn
323 # can't be read by gpgv.
324 # BEWARE: This is designed more in the way to work with the current
325 # callers, than to have a well defined it would be easy to add new callers to.
326 if [ ! -s "$TO" ]; then
327 if [ -s "$FROM" ]; then
328 if [ -z "$2" ]; then
2906182d
DK
329 local OPTS
330 if [ "${TO##*.}" = 'asc' ]; then
331 OPTS='--armor'
f14cde2c 332 fi
2906182d 333 export_key_from_to "$(dearmor_filename "$FROM")" "$TO" $OPTS ${1:+"$1"}
f14cde2c
DK
334 else
335 create_new_keyring "$TO"
336 fi
337 else
338 create_new_keyring "$TO"
339 fi
340 elif [ -s "$FROM" ]; then
341 local EXPORTLIMIT="$1"
342 if [ -n "$1$2" ]; then shift; fi
2906182d
DK
343 local DEARMORTO="$(dearmor_filename "$TO")"
344 if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export ${EXPORTLIMIT:+"$EXPORTLIMIT"} \
345 | aptkey_execute "$GPG_SH" --keyring "$DEARMORTO" --batch --import "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
f14cde2c
DK
346 cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
347 false
348 fi
2906182d
DK
349 if [ "$DEARMORTO" != "$TO" ]; then
350 export_key_from_to "$DEARMORTO" "${DEARMORTO}.asc" --armor
351 if ! cmp -s "$TO" "${DEARMORTO}.asc" 2>/dev/null; then
352 cp -a "$TO" "${TO}~"
353 mv -f "${DEARMORTO}.asc" "$TO"
354 fi
355 fi
0dae96a2
DK
356 fi
357}
358
2906182d
DK
359dearmor_keyring() {
360 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=831409#67
361 # The awk script is more complex through to skip surrounding garbage and
362 # to support multiple keys in one file (old gpgs generate version headers
363 # which get printed with the original and hence result in garbage input for base64
364 awk '/^-----BEGIN/{ x = 1; }
365/^$/{ if (x == 1) { x = 2; }; }
366/^[^=-]/{ if (x == 2) { print $0; }; }
367/^-----END/{ x = 0; }' | base64 -d
368}
369dearmor_filename() {
370 if [ "${1##*.}" = 'asc' ]; then
371 local trusted="${GPGHOMEDIR}/${1##*/}.gpg"
372 if [ -s "$1" ]; then
373 dearmor_keyring < "$1" > "$trusted"
374 fi
375 echo "$trusted"
376 elif [ "${1##*.}" = 'gpg' ]; then
377 echo "$1"
378 elif [ "$(head -n 1 "$1" 2>/dev/null)" = '-----BEGIN PGP PUBLIC KEY BLOCK-----' ]; then
379 local trusted="${GPGHOMEDIR}/${1##*/}.gpg"
380 dearmor_keyring < "$1" > "$trusted"
381 echo "$trusted"
382 else
383 echo "$1"
384 fi
385}
105503b4 386catfile() {
2906182d 387 cat "$(dearmor_filename "$1")" >> "$2"
105503b4
DK
388}
389
25f27319
DK
390merge_all_trusted_keyrings_into_pubring() {
391 # does the same as:
392 # foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
393 # but without using gpg, just cat and find
dd7758b9 394 local PUBRING="$(readlink -f "${GPGHOMEDIR}")/pubring.gpg"
105503b4
DK
395 rm -f "$PUBRING"
396 touch "$PUBRING"
397 foreach_keyring_do 'catfile' "$PUBRING"
25f27319
DK
398}
399
f14cde2c
DK
400import_keys_from_keyring() {
401 import_keyring_into_keyring "$1" "$2"
402}
403
0dae96a2 404merge_keys_into_keyrings() {
f14cde2c 405 import_keyring_into_keyring "$2" "$1" '' --import-options 'merge-only'
0dae96a2
DK
406}
407
408merge_back_changes() {
409 if [ -n "$FORCED_KEYRING" ]; then
410 # if the keyring was forced merge is already done
2906182d
DK
411 if [ "$FORCED_KEYRING" != "$TRUSTEDFILE" ]; then
412 mv -f "$FORCED_KEYRING" "${FORCED_KEYRING}~"
413 export_key_from_to "$TRUSTEDFILE" "$FORCED_KEYRING" --armor
414 fi
0dae96a2
DK
415 return
416 fi
417 if [ -s "${GPGHOMEDIR}/pubring.gpg" ]; then
418 # merge all updated keys
419 foreach_keyring_do 'merge_keys_into_keyrings' "${GPGHOMEDIR}/pubring.gpg"
420 fi
0b94a7bc 421 # look for keys which were added or removed
0dae96a2
DK
422 get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.orig.gpg" > "${GPGHOMEDIR}/pubring.orig.keylst"
423 get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.gpg" > "${GPGHOMEDIR}/pubring.keylst"
25f27319
DK
424 comm -3 "${GPGHOMEDIR}/pubring.keylst" "${GPGHOMEDIR}/pubring.orig.keylst" > "${GPGHOMEDIR}/pubring.diff"
425 # key isn't part of new keyring, so remove
426 cut -f 2 "${GPGHOMEDIR}/pubring.diff" | while read key; do
427 if [ -z "$key" ]; then continue; fi
428 foreach_keyring_do 'remove_key_from_keyring' "$key"
429 done
430 # key is only part of new keyring, so we need to import it
431 cut -f 1 "${GPGHOMEDIR}/pubring.diff" | while read key; do
432 if [ -z "$key" ]; then continue; fi
433 import_keyring_into_keyring '' "$TRUSTEDFILE" "$key"
0dae96a2 434 done
5beb682d
DK
435}
436
437setup_merged_keyring() {
b0d40854 438 if [ -n "$FORCED_KEYID" ]; then
25f27319 439 merge_all_trusted_keyrings_into_pubring
b0d40854
DK
440 FORCED_KEYRING="${GPGHOMEDIR}/forcedkeyid.gpg"
441 TRUSTEDFILE="${FORCED_KEYRING}"
fecfbf2e 442 echo "#!/bin/sh
bc8f83a5 443exec sh '($(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh"
fecfbf2e 444 GPG="${GPGHOMEDIR}/gpg.1.sh"
b0d40854 445 # ignore error as this "just" means we haven't found the forced keyid and the keyring will be empty
f14cde2c 446 import_keyring_into_keyring '' "$TRUSTEDFILE" "$FORCED_KEYID" || true
b0d40854 447 elif [ -z "$FORCED_KEYRING" ]; then
25f27319 448 merge_all_trusted_keyrings_into_pubring
0dae96a2
DK
449 if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then
450 cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
451 else
452 touch "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
0740a310 453 fi
fecfbf2e 454 echo "#!/bin/sh
bc8f83a5 455exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${GPGHOMEDIR}/pubring.gpg")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh"
fecfbf2e 456 GPG="${GPGHOMEDIR}/gpg.1.sh"
0dae96a2 457 else
2906182d 458 TRUSTEDFILE="$(dearmor_filename "$FORCED_KEYRING")"
0dae96a2 459 create_new_keyring "$TRUSTEDFILE"
fecfbf2e 460 echo "#!/bin/sh
bc8f83a5 461exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh"
fecfbf2e 462 GPG="${GPGHOMEDIR}/gpg.1.sh"
5beb682d
DK
463 fi
464}
465
0dae96a2
DK
466create_new_keyring() {
467 # gpg defaults to mode 0600 for new keyrings. Create one with 0644 instead.
2906182d
DK
468 if ! [ -e "$1" ]; then
469 if [ -w "$(dirname "$1")" ]; then
470 touch -- "$1"
471 chmod 0644 -- "$1"
0dae96a2
DK
472 fi
473 fi
474}
5beb682d 475
fecfbf2e
DK
476aptkey_execute() { sh "$@"; }
477
b3d44315 478usage() {
46e39c8e 479 echo "Usage: apt-key [--keyring file] [command] [arguments]"
b3d44315
MV
480 echo
481 echo "Manage apt's list of trusted keys"
482 echo
483 echo " apt-key add <file> - add the key contained in <file> ('-' for stdin)"
484 echo " apt-key del <keyid> - remove the key <keyid>"
bf6d5b42
OS
485 echo " apt-key export <keyid> - output the key <keyid>"
486 echo " apt-key exportall - output all trusted keys"
4a242c5b 487 echo " apt-key update - update keys using the keyring package"
848ecfa4 488 echo " apt-key net-update - update keys using the network"
b3d44315 489 echo " apt-key list - list keys"
a8cabc8f
LB
490 echo " apt-key finger - list fingerprints"
491 echo " apt-key adv - pass advanced options to gpg (download key)"
b3d44315 492 echo
46e39c8e 493 echo "If no specific keyring file is given the command applies to all keyring files."
b3d44315
MV
494}
495
3dc55197
DK
496while [ -n "$1" ]; do
497 case "$1" in
498 --keyring)
499 shift
500 TRUSTEDFILE="$1"
04937adc 501 FORCED_KEYRING="$1"
3dc55197 502 ;;
b0d40854
DK
503 --keyid)
504 shift
505 FORCED_KEYID="$1"
506 ;;
bd7fb5aa
DK
507 --secret-keyring)
508 shift
509 FORCED_SECRET_KEYRING="$1"
33a22672
DK
510 ;;
511 --readonly)
512 merge_back_changes() { true; }
fecfbf2e 513 create_new_keyring() { if [ ! -r "$FORCED_KEYRING" ]; then TRUSTEDFILE='/dev/null'; FORCED_KEYRING="$TRUSTEDFILE"; fi; }
bd7fb5aa 514 ;;
3dc55197
DK
515 --fakeroot)
516 requires_root() { true; }
3dc55197 517 ;;
3d0def05
DK
518 --quiet)
519 aptkey_echo() { true; }
3d0def05 520 ;;
c1642be5
DK
521 --debug1)
522 # some cmds like finger redirect stderr to /dev/null …
fecfbf2e 523 aptkey_execute() { echo 'EXEC:' "$@"; sh "$@"; }
c1642be5
DK
524 ;;
525 --debug2)
526 # … other more complicated ones pipe gpg into gpg.
fecfbf2e 527 aptkey_execute() { echo >&2 'EXEC:' "$@"; sh "$@"; }
c1642be5 528 ;;
3dc55197
DK
529 --*)
530 echo >&2 "Unknown option: $1"
531 usage
532 exit 1;;
533 *)
534 break;;
535 esac
33a22672 536 shift
3dc55197
DK
537done
538
539if [ -z "$TRUSTEDFILE" ]; then
540 TRUSTEDFILE="/etc/apt/trusted.gpg"
541 eval $(apt-config shell TRUSTEDFILE Apt::GPGV::TrustedKeyring)
542 eval $(apt-config shell TRUSTEDFILE Dir::Etc::Trusted/f)
46e39c8e 543fi
46e39c8e 544
b3d44315
MV
545command="$1"
546if [ -z "$command" ]; then
547 usage
548 exit 1
549fi
550shift
551
29c59095
DK
552find_gpgv_status_fd() {
553 while [ -n "$1" ]; do
554 if [ "$1" = '--status-fd' ]; then
555 shift
556 echo "$1"
557 break
558 fi
559 shift
560 done
561}
562GPGSTATUSFD="$(find_gpgv_status_fd "$@")"
563
8e438ede 564apt_warn() {
105503b4
DK
565 if [ -z "$GPGHOMEDIR" ]; then
566 echo >&2 'W:' "$@"
567 else
568 echo 'W:' "$@" > "${GPGHOMEDIR}/aptwarnings.log"
569 fi
29c59095
DK
570 if [ -n "$GPGSTATUSFD" ]; then
571 echo >&${GPGSTATUSFD} '[APTKEY:] WARNING' "$@"
572 fi
105503b4 573}
8e438ede
DK
574apt_error() {
575 if [ -z "$GPGHOMEDIR" ]; then
576 echo >&2 'E:' "$@"
577 else
578 echo 'E:' "$@" > "${GPGHOMEDIR}/aptwarnings.log"
579 fi
580 if [ -n "$GPGSTATUSFD" ]; then
581 echo >&${GPGSTATUSFD} '[APTKEY:] ERROR' "$@"
582 fi
583}
105503b4 584
4039798d
DK
585cleanup_gpg_home() {
586 if [ -z "$GPGHOMEDIR" ]; then return; fi
105503b4
DK
587 if [ -s "$GPGHOMEDIR/aptwarnings.log" ]; then
588 cat >&2 "$GPGHOMEDIR/aptwarnings.log"
589 fi
4039798d 590 if command_available 'gpgconf'; then
215598df 591 GNUPGHOME="${GPGHOMEDIR}" gpgconf --kill gpg-agent >/dev/null 2>&1 || true
4039798d
DK
592 fi
593 rm -rf "$GPGHOMEDIR"
594}
595
25f27319
DK
596create_gpg_home() {
597 # gpg needs (in different versions more or less) files to function correctly,
598 # so we give it its own homedir and generate some valid content for it later on
599 if [ -n "$TMPDIR" ]; then
600 # tmpdir is a directory and current user has rwx access to it
601 # same tests as in apt-pkg/contrib/fileutl.cc GetTempDir()
602 if [ ! -d "$TMPDIR" ] || [ ! -r "$TMPDIR" ] || [ ! -w "$TMPDIR" ] || [ ! -x "$TMPDIR" ]; then
603 unset TMPDIR
604 fi
605 fi
606 GPGHOMEDIR="$(mktemp -d)"
4039798d 607 CURRENTTRAP="${CURRENTTRAP} cleanup_gpg_home;"
25f27319 608 trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
4039798d 609 if [ -z "$GPGHOMEDIR" ]; then
8e438ede 610 apt_error "Could not create temporary gpg home directory in $TMPDIR (wrong permissions?)"
4039798d
DK
611 exit 28
612 fi
25f27319
DK
613 chmod 700 "$GPGHOMEDIR"
614}
615
616prepare_gpg_home() {
5f17b19f
DK
617 # crude detection if we are called from a maintainerscript where the
618 # package depends on gnupg or not. We accept recommends here as
619 # well as the script hopefully uses apt-key optionally then like e.g.
620 # debian-archive-keyring for (upgrade) cleanup did
08fcf962 621 if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ] && [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then
2e49f519 622 if ! dpkg-query --show --showformat '${Pre-Depends}${Depends}${Recommends}\n' "$DPKG_MAINTSCRIPT_PACKAGE" 2>/dev/null | grep -q gnupg; then
5f17b19f
DK
623 cat >&2 <<EOF
624Warning: The $DPKG_MAINTSCRIPT_NAME maintainerscript of the package $DPKG_MAINTSCRIPT_PACKAGE
625Warning: seems to use apt-key (provided by apt) without depending on gnupg or gnupg2.
626Warning: This will BREAK in the future and should be fixed by the package maintainer(s).
627Note: Check first if apt-key functionality is needed at all - it probably isn't!
628EOF
629 fi
630 fi
bc8f83a5 631 eval "$(apt-config shell GPG_EXE Apt::Key::gpgcommand)"
e23ee4c2 632 if [ -n "$GPG_EXE" ] && command_available "$GPG_EXE"; then
93d0d08c 633 true
e23ee4c2 634 elif command_available 'gpg'; then
93d0d08c 635 GPG_EXE="gpg"
e23ee4c2 636 elif command_available 'gpg2'; then
93d0d08c 637 GPG_EXE="gpg2"
19fdf93d
DK
638 elif command_available 'gpg1'; then
639 GPG_EXE="gpg1"
93d0d08c 640 else
8e438ede 641 apt_error 'gnupg, gnupg2 and gnupg1 do not seem to be installed, but one of them is required for this operation'
93d0d08c 642 exit 255
9fda3be1
DK
643 fi
644
25f27319
DK
645 create_gpg_home
646
05991180
DK
647 # create the trustdb with an (empty) dummy keyring
648 # older gpgs required it, newer gpgs even warn that it isn't needed,
649 # but require it nonetheless for some commands, so we just play safe
650 # here for the foreseeable future and create a dummy one
803491dc 651 touch "${GPGHOMEDIR}/empty.gpg"
fecfbf2e 652 if ! "$GPG_EXE" --ignore-time-conflict --no-options --no-default-keyring \
803491dc 653 --homedir "$GPGHOMEDIR" --quiet --check-trustdb --keyring "${GPGHOMEDIR}/empty.gpg" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
fecfbf2e
DK
654 cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
655 false
656 fi
803491dc
DK
657
658 # now tell gpg that it shouldn't try to maintain this trustdb file
fecfbf2e 659 echo "#!/bin/sh
bc8f83a5
DK
660exec '$(escape_shell "${GPG_EXE}")' --ignore-time-conflict --no-options --no-default-keyring \\
661--homedir '$(escape_shell "${GPGHOMEDIR}")' --no-auto-check-trustdb --trust-model always \"\$@\"" > "${GPGHOMEDIR}/gpg.0.sh"
fecfbf2e
DK
662 GPG_SH="${GPGHOMEDIR}/gpg.0.sh"
663 GPG="$GPG_SH"
05991180 664
803491dc 665 # We don't usually need a secret keyring, of course, but
bd7fb5aa
DK
666 # for advanced operations, we might really need a secret keyring after all
667 if [ -n "$FORCED_SECRET_KEYRING" ] && [ -r "$FORCED_SECRET_KEYRING" ]; then
803491dc
DK
668 if ! aptkey_execute "$GPG" -v --batch --import "$FORCED_SECRET_KEYRING" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
669 cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
670 false
671 fi
672 else
673 # and then, there are older versions of gpg which panic and implode
674 # if there isn't one available - and writeable for imports
675 # and even if not output is littered with the creation of a secring,
676 # so lets call import once to have it create what it wants in silence
677 echo -n | aptkey_execute "$GPG" --batch --import >/dev/null 2>&1 || true
bd7fb5aa 678 fi
25f27319
DK
679}
680
08fcf962
DK
681warn_on_script_usage() {
682 if [ -n "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then
683 return
684 fi
685 # (Maintainer) scripts should not be using apt-key
686 if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ]; then
687 echo >&2 "Warning: apt-key should not be used in scripts (called from $DPKG_MAINTSCRIPT_NAME maintainerscript of the package ${DPKG_MAINTSCRIPT_PACKAGE})"
688 elif [ ! -t 1 ]; then
689 echo >&2 "Warning: apt-key output should not be parsed (stdout is not a terminal)"
690 fi
691}
692
25f27319
DK
693if [ "$command" != 'help' ] && [ "$command" != 'verify' ]; then
694 prepare_gpg_home
b3d44315
MV
695fi
696
b3d44315
MV
697case "$command" in
698 add)
08fcf962 699 warn_on_script_usage
5beb682d
DK
700 requires_root
701 setup_merged_keyring
fecfbf2e 702 aptkey_execute "$GPG" --quiet --batch --import "$@"
0dae96a2 703 merge_back_changes
5beb682d 704 aptkey_echo "OK"
b3d44315
MV
705 ;;
706 del|rm|remove)
08fcf962 707 # no script warning here as removing 'add' usage needs 'del' for cleanup
5beb682d
DK
708 requires_root
709 foreach_keyring_do 'remove_key_from_keyring' "$@"
710 aptkey_echo "OK"
b3d44315 711 ;;
4a242c5b 712 update)
08fcf962 713 warn_on_script_usage
5beb682d
DK
714 requires_root
715 setup_merged_keyring
4a242c5b 716 update
0dae96a2 717 merge_back_changes
4a242c5b 718 ;;
848ecfa4 719 net-update)
5beb682d
DK
720 requires_root
721 setup_merged_keyring
848ecfa4 722 net_update
0dae96a2 723 merge_back_changes
848ecfa4 724 ;;
a5f9b45e 725 list|finger*)
08fcf962 726 warn_on_script_usage
2906182d 727 foreach_keyring_do 'list_keys_in_keyring' --fingerprint "$@"
5beb682d 728 ;;
4f51a496 729 export|exportall)
08fcf962 730 warn_on_script_usage
25f27319 731 merge_all_trusted_keyrings_into_pubring
fecfbf2e 732 aptkey_execute "$GPG_SH" --keyring "${GPGHOMEDIR}/pubring.gpg" --armor --export "$@"
5beb682d 733 ;;
b3d44315 734 adv*)
08fcf962 735 warn_on_script_usage
5beb682d 736 setup_merged_keyring
2906182d 737 aptkey_echo "Executing: $GPG" "$@"
fecfbf2e 738 aptkey_execute "$GPG" "$@"
0dae96a2 739 merge_back_changes
5beb682d 740 ;;
c46a36ad 741 verify)
f14cde2c
DK
742 GPGV=''
743 eval $(apt-config shell GPGV Apt::Key::gpgvcommand)
e23ee4c2
DK
744 if [ -n "$GPGV" ] && command_available "$GPGV"; then true;
745 elif command_available 'gpgv'; then GPGV='gpgv';
746 elif command_available 'gpgv2'; then GPGV='gpgv2';
19fdf93d 747 elif command_available 'gpgv1'; then GPGV='gpgv1';
25f27319 748 else
8e438ede 749 apt_error 'gpgv, gpgv2 or gpgv1 required for verification, but neither seems installed'
25f27319 750 exit 29
f14cde2c 751 fi
25f27319
DK
752 # for a forced keyid we need gpg --export, so full wrapping required
753 if [ -n "$FORCED_KEYID" ]; then
754 prepare_gpg_home
755 else
756 create_gpg_home
757 fi
758 setup_merged_keyring
759 if [ -n "$FORCED_KEYRING" ]; then
2906182d 760 "$GPGV" --homedir "${GPGHOMEDIR}" --keyring "$(dearmor_filename "${FORCED_KEYRING}")" --ignore-time-conflict "$@"
c46a36ad 761 else
fecfbf2e 762 "$GPGV" --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
c46a36ad
DK
763 fi
764 ;;
b3d44315
MV
765 help)
766 usage
767 ;;
768 *)
769 usage
770 exit 1
771 ;;
772esac