diff options
Diffstat (limited to 'scripts/clean-keydir')
-rwxr-xr-x | scripts/clean-keydir | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/scripts/clean-keydir b/scripts/clean-keydir new file mode 100755 index 0000000..bd61432 --- /dev/null +++ b/scripts/clean-keydir @@ -0,0 +1,146 @@ +#!/bin/bash + +# Copyright (c) 2012 Jonathan McDowell <noodles@earth.li>, +# 2019 Daniel Kahn Gillmor <dkg@fifthhorseman.net> +# GNU GPL; v2 or later +# Given a key directory, prune, clean, or minimize the keys + +# "prune" just does basic cleanup on the file, without getting rid of +# any third-party signatures. + +set -e + +if [ -z "$1" ] || [ -z "$2" ]; then + cat >&2 <<EOF +Usage: $0 [prune|launder|clean|minimal] dir + prune: remove invalid parts + launder: invoke GnuPG's merge logic to trim the key + clean: prune and drop non-debian third-party certifications + minimal: prune and remove *all* third-party certifications +EOF + exit 1 +fi + +declare -a GPGOPTIONS=(--batch + --no-tty + --quiet + --no-options + --homedir=/dev/null + --trust-model=always + --fixed-list-mode + --with-colons + --export-options=no-export-attributes + ) + + +if [ "$1" == prune ]; then + GPGOPTIONS+=(--no-keyring + --import-options=import-export + ) +elif [ "$1" == launder ]; then + # we are going to do something very ugly... + # see https://dev.gnupg.org/T4421 + : pass +elif [ "$1" == clean ]; then + # we need to include all the known keys so that we keep the + # interlocking signatures + make + GPGOPTIONS+=(--no-default-keyring + --import-options=import-export,import-clean + --export-options=export-clean + --keyring "$(readlink -f output/keyrings/debian-keyring.gpg)" + --keyring "$(readlink -f output/keyrings/debian-nonupload.gpg)" + --keyring "$(readlink -f output/keyrings/debian-maintainers.gpg)" + --keyring "$(readlink -f output/keyrings/debian-role-keys.gpg)" + --keyring "$(readlink -f output/keyrings/emeritus-keyring.gpg)" + ) +elif [ "$1" == minimal ]; then + GPGOPTIONS+=(--no-keyring + --import-options=import-export,import-minimal + --export-options=export-minimal + ) +else + echo "Must specify prune, launder, clean or minimal; not $1" >&2 + exit 1 +fi + +if [ ! -d "$2" ]; then + printf '%s is not a directory' "$2" >&2 + exit 1 +fi + +# takes name of transferable public key file as $1, emits the laundered key to file named $2 +launder_tpk() { + local interim="$(mktemp -d interim.XXXXXXX)" + local success=false + local key="$1" + local output="$2" + mkdir -p -m 0700 "$interim/gpg" "$interim/split" + cat > "$interim/gpg/gpg.conf" <<EOF +batch +no-tty +quiet +no-options +trust-model always +fixed-list-mode +with-colons +export-options no-export-attributes +EOF + if gpg --homedir "$interim/gpg" --import-options=import-minimal --status-file "$interim/status" --import < "$key" && + fpr="$(awk '{ if ($1 == "[GNUPG:]" && $2 == "IMPORT_OK" && $3 == "1") { print $4 } }' < "$interim/status")" && + [ -n "$fpr" ] && + gpg --homedir "$interim/gpg" --export | (cd "$interim/split" && gpgsplit) && + gpg --homedir "$interim/gpg" --delete-key "$fpr"; then + local pub="$interim/split/000001-006.public_key" + local uid=$(ls "$interim/split/"*.user_id | head -n1) + local sig=$(printf '%s/split/%06d-002.sig' "$interim" $(( "$(echo "${uid##$interim/split/}" | sed -e 's_^0*__' -e 's_-.*$__')" + 1 )) ) + if [ -r "$pub" ] && [ -r "$uid" ] && [ -r "$sig" ]; then + if cat "$pub" "$uid" "$sig" | gpg --homedir "$interim/gpg" --import && + gpg --homedir "$interim/gpg" --import < "$key" && + gpg --homedir "$interim/gpg" --output "$output" --export "$fpr"; then + success=true + else + printf 'Merging failed for %s (fpr: %s)\n' "$key" "$fpr" >&2 + fi + else + printf 'Could not find minimal TPK for %s (fpr: %s)\n' "$key" "$fpr" >&2 + fi + else + printf 'failed to do initial import of %s\n' "$key" >&2 + fi + rm -rf "$interim" + [ $success = true ] +} + +cd "$2" +for key in 0x*; do + success=false + if [ "$1" == launder ]; then + if launder_tpk "$key" "$key.new"; then + success=true + fi + else + if gpg "${GPGOPTIONS[@]}" --output "$key.new" --import "$key"; then + success=true + fi + fi + if [ $success = true ] && [ -s $key.new ]; then + OLDSIZE=$(stat -c "%s" "$key") + NEWSIZE=$(stat -c "%s" "$key.new") + if [ $OLDSIZE -gt $NEWSIZE ]; then + echo "Cleaning $key [$OLDSIZE] -> [$NEWSIZE]" + mv "$key.new" "$key" + elif [ $OLDSIZE -eq $NEWSIZE ] && ! cmp --quiet "$key" "$key.new" ; then + printf "Packets were reordered in $key" + if [ "$1" == launder ]; then + echo " (but ignoring while doing launder: https://dev.gnupg.org/T4422)" + else + mv "$key.new" "$key" + echo + fi + fi + fi + [ -e "$key.new" ] && rm "$key.new" +done + +exit 0 |