diff options
Diffstat (limited to 'cmdline/apt-key.in')
-rw-r--r-- | cmdline/apt-key.in | 128 |
1 files changed, 86 insertions, 42 deletions
diff --git a/cmdline/apt-key.in b/cmdline/apt-key.in index b15f71f6d..881f8a990 100644 --- a/cmdline/apt-key.in +++ b/cmdline/apt-key.in @@ -34,7 +34,8 @@ get_fingerprints_of_keyring() { elif [ "${fprline%%:*}" != 'fpr' ]; then continue; fi echo "$fprline" | cut -d':' -f 10 done - done + # order in the keyring shouldn't be important + done | sort } add_keys_with_verify_against_master_keyring() { @@ -167,8 +168,10 @@ remove_key_from_keyring() { local GPG="$GPG_CMD --keyring $KEYRINGFILE" for KEY in "$@"; do - # check if the key is in this keyring: the key id is in the 5 column at the end - if ! get_fingerprints_of_keyring "$KEYRINGFILE" | grep -iq "^[0-9A-F]*${KEY}$"; then + local FINGERPRINTS="${GPGHOMEDIR}/keyringfile.keylst" + get_fingerprints_of_keyring "$KEYRINGFILE" > "$FINGERPRINTS" + # check if the key is in this keyring + if ! grep -iq "^[0-9A-F]*${KEY}$" "$FINGERPRINTS"; then continue fi if [ ! -w "$KEYRINGFILE" ]; then @@ -176,7 +179,7 @@ remove_key_from_keyring() { continue fi # check if it is the only key in the keyring and if so remove the keyring altogether - if [ '1' = "$(get_fingerprints_of_keyring "$KEYRINGFILE" | wc -l)" ]; then + if [ '1' = "$(uniq "$FINGERPRINTS" | wc -l)" ]; then mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" # behave like gpg return fi @@ -188,7 +191,7 @@ remove_key_from_keyring() { cp -a "$REALTARGET" "$KEYRINGFILE" fi # delete the key from the keyring - $GPG --batch --delete-key --yes "$KEY" + $GPG --batch --delete-keys --yes "$KEY" if [ -n "$REALTARGET" ]; then # the real backup is the old link, not the copy we made mv -f "${KEYRINGFILE}.dpkg-tmp" "${KEYRINGFILE}~" @@ -268,6 +271,33 @@ import_keyring_into_keyring() { fi } +merge_all_trusted_keyrings_into_pubring() { + # does the same as: + # foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg" + # but without using gpg, just cat and find + local PUBRING="${GPGHOMEDIR}/pubring.gpg" + # if a --keyring was given, just use this one + if [ -n "$FORCED_KEYRING" ]; then + if [ -s "$FORCED_KEYRING" ]; then + cp --dereference "$FORCED_KEYRING" "$PUBRING" + fi + else + # otherwise all known keyrings are merged + local TRUSTEDPARTS="/etc/apt/trusted.gpg.d" + eval $(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d) + if [ -d "$TRUSTEDPARTS" ]; then + # ignore errors mostly for non-existing $TRUSTEDFILE + cat /dev/null "$TRUSTEDFILE" $(find -L "$TRUSTEDPARTS" -type f -name '*.gpg') > "$PUBRING" 2>/dev/null || true + elif [ -s "$TRUSTEDFILE" ]; then + cp --dereference "$TRUSTEDFILE" "$PUBRING" + fi + fi + + if [ ! -s "$PUBRING" ]; then + touch "$PUBRING" + fi +} + import_keys_from_keyring() { import_keyring_into_keyring "$1" "$2" } @@ -288,29 +318,29 @@ merge_back_changes() { # look for keys which were added or removed get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.orig.gpg" > "${GPGHOMEDIR}/pubring.orig.keylst" get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.gpg" > "${GPGHOMEDIR}/pubring.keylst" - sort "${GPGHOMEDIR}/pubring.keylst" "${GPGHOMEDIR}/pubring.orig.keylst" | uniq --unique | while read key; do - if grep -q "^${key}$" "${GPGHOMEDIR}/pubring.orig.keylst"; then - # key isn't part of new keyring, so remove - foreach_keyring_do 'remove_key_from_keyring' "$key" - elif grep -q "^${key}$" "${GPGHOMEDIR}/pubring.keylst"; then - # key is part of new keyring, so we need to import it - import_keyring_into_keyring '' "$TRUSTEDFILE" "$key" - else - echo >&2 "Errror: Key ${key} (dis)appeared out of nowhere" - fi + comm -3 "${GPGHOMEDIR}/pubring.keylst" "${GPGHOMEDIR}/pubring.orig.keylst" > "${GPGHOMEDIR}/pubring.diff" + # key isn't part of new keyring, so remove + cut -f 2 "${GPGHOMEDIR}/pubring.diff" | while read key; do + if [ -z "$key" ]; then continue; fi + foreach_keyring_do 'remove_key_from_keyring' "$key" + done + # key is only part of new keyring, so we need to import it + cut -f 1 "${GPGHOMEDIR}/pubring.diff" | while read key; do + if [ -z "$key" ]; then continue; fi + import_keyring_into_keyring '' "$TRUSTEDFILE" "$key" done } setup_merged_keyring() { if [ -n "$FORCED_KEYID" ]; then - foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg" + merge_all_trusted_keyrings_into_pubring FORCED_KEYRING="${GPGHOMEDIR}/forcedkeyid.gpg" TRUSTEDFILE="${FORCED_KEYRING}" GPG="$GPG --keyring $TRUSTEDFILE" # ignore error as this "just" means we haven't found the forced keyid and the keyring will be empty import_keyring_into_keyring '' "$TRUSTEDFILE" "$FORCED_KEYID" || true elif [ -z "$FORCED_KEYRING" ]; then - foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg" + merge_all_trusted_keyrings_into_pubring if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg" else @@ -407,7 +437,23 @@ if [ -z "$command" ]; then fi shift -if [ "$command" != "help" ]; then +create_gpg_home() { + # gpg needs (in different versions more or less) files to function correctly, + # so we give it its own homedir and generate some valid content for it later on + if [ -n "$TMPDIR" ]; then + # tmpdir is a directory and current user has rwx access to it + # same tests as in apt-pkg/contrib/fileutl.cc GetTempDir() + if [ ! -d "$TMPDIR" ] || [ ! -r "$TMPDIR" ] || [ ! -w "$TMPDIR" ] || [ ! -x "$TMPDIR" ]; then + unset TMPDIR + fi + fi + GPGHOMEDIR="$(mktemp -d)" + CURRENTTRAP="${CURRENTTRAP} rm -rf '${GPGHOMEDIR}';" + trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM + chmod 700 "$GPGHOMEDIR" +} + +prepare_gpg_home() { eval $(apt-config shell GPG_EXE Apt::Key::gpgcommand) if [ -n "$GPG_EXE" ] && which "$GPG_EXE" >/dev/null 2>&1; then @@ -418,7 +464,7 @@ if [ "$command" != "help" ]; then GPG_EXE="gpg2" else echo >&2 "Error: gnupg or gnupg2 do not seem to be installed," - echo >&2 "Error: but apt-key requires gnupg or gnupg2 for operation." + echo >&2 "Error: but apt-key requires gnupg or gnupg2 for this operation." echo >&2 exit 255 fi @@ -428,19 +474,8 @@ if [ "$command" != "help" ]; then fi GPG_CMD="$GPG_EXE --ignore-time-conflict --no-options --no-default-keyring" - # gpg needs (in different versions more or less) files to function correctly, - # so we give it its own homedir and generate some valid content for it - if [ -n "$TMPDIR" ]; then - # tmpdir is a directory and current user has rwx access to it - # same tests as in apt-pkg/contrib/fileutl.cc GetTempDir() - if [ ! -d "$TMPDIR" ] || [ ! -r "$TMPDIR" ] || [ ! -w "$TMPDIR" ] || [ ! -x "$TMPDIR" ]; then - unset TMPDIR - fi - fi - GPGHOMEDIR="$(mktemp -d)" - CURRENTTRAP="${CURRENTTRAP} rm -rf '${GPGHOMEDIR}';" - trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM - chmod 700 "$GPGHOMEDIR" + create_gpg_home + # We don't use a secret keyring, of course, but gpg panics and # implodes if there isn't one available - and writeable for imports SECRETKEYRING="${GPGHOMEDIR}/secring.gpg" @@ -466,6 +501,10 @@ if [ "$command" != "help" ]; then # anyhow, so nothing actually happens, but its three lines of output # nobody expects to see in apt-key context, so trigger it in silence echo -n | $GPG --batch --import >/dev/null 2>&1 || true +} + +if [ "$command" != 'help' ] && [ "$command" != 'verify' ]; then + prepare_gpg_home fi case "$command" in @@ -500,7 +539,7 @@ case "$command" in foreach_keyring_do 'run_cmd_on_keyring' --fingerprint "$@" ;; export|exportall) - foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg" + merge_all_trusted_keyrings_into_pubring $GPG_CMD --keyring "${GPGHOMEDIR}/pubring.gpg" --armor --export "$@" ;; adv*) @@ -510,21 +549,26 @@ case "$command" in merge_back_changes ;; verify) - setup_merged_keyring GPGV='' eval $(apt-config shell GPGV Apt::Key::gpgvcommand) - if [ -n "$GPGV" ] && ! which "$GPGV" >/dev/null 2>&1; then GPGV=''; + if [ -n "$GPGV" ] && which "$GPGV" >/dev/null 2>&1; then true; elif which gpgv >/dev/null 2>&1; then GPGV='gpgv'; elif which gpgv2 >/dev/null 2>&1; then GPGV='gpgv2'; + else + echo >&2 'ERROR: gpgv or gpgv2 required for verification' + exit 29 fi - if [ -n "$GPGV" ]; then - if [ -n "$FORCED_KEYRING" ]; then - $GPGV --homedir "${GPGHOMEDIR}" --keyring "${FORCED_KEYRING}" --ignore-time-conflict "$@" - else - $GPGV --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@" - fi + # for a forced keyid we need gpg --export, so full wrapping required + if [ -n "$FORCED_KEYID" ]; then + prepare_gpg_home + else + create_gpg_home + fi + setup_merged_keyring + if [ -n "$FORCED_KEYRING" ]; then + $GPGV --homedir "${GPGHOMEDIR}" --keyring "${FORCED_KEYRING}" --ignore-time-conflict "$@" else - $GPG --verify "$@" + $GPGV --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@" fi ;; help) |