1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 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.pgp)"
--keyring "$(readlink -f output/keyrings/debian-nonupload.pgp)"
--keyring "$(readlink -f output/keyrings/debian-maintainers.pgp)"
--keyring "$(readlink -f output/keyrings/debian-role-keys.pgp)"
--keyring "$(readlink -f output/keyrings/emeritus-keyring.pgp)"
)
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
|