From 2f9775ed580656214e62e7916ee896776fabc9e7 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Wed, 4 Dec 2024 11:12:34 +0100 Subject: gpgv: Drop references to apt-key --- apt-pkg/contrib/gpgv.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apt-pkg/contrib/gpgv.cc b/apt-pkg/contrib/gpgv.cc index 879e7635a..ce0bba0d1 100644 --- a/apt-pkg/contrib/gpgv.cc +++ b/apt-pkg/contrib/gpgv.cc @@ -232,7 +232,7 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG, FileFd keyFd(k, FileFd::ReadOnly); if (not keyFd.IsOpen()) { - apt_warning(std::cerr, statusfd, fd, "The key(s) in the keyring %s are ignored as the file is not readable by user executing apt-key.\n", k.c_str()); + apt_warning(std::cerr, statusfd, fd, "The key(s) in the keyring %s are ignored as the file is not readable by user executing gpgv.\n", k.c_str()); } else if (APT::String::Endswith(k, ".asc")) { @@ -512,14 +512,14 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG, { if (errno == EINTR) continue; - apt_error(std::cerr, statusfd, fd, _("Waited for %s but it wasn't there"), "apt-key"); + apt_error(std::cerr, statusfd, fd, _("Waited for %s but it wasn't there"), gpgv.c_str()); local_exit(EINTERNAL); } // check if it exit'ed normally … if (not WIFEXITED(Status)) { - apt_error(std::cerr, statusfd, fd, _("Sub-process %s exited unexpectedly"), "apt-key"); + apt_error(std::cerr, statusfd, fd, _("Sub-process %s exited unexpectedly"), gpgv.c_str()); local_exit(EINTERNAL); } @@ -527,7 +527,7 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG, if (WEXITSTATUS(Status) != 0) { // we forward the statuscode, so don't generate a message on the fd in this case - apt_error(std::cerr, -1, fd, _("Sub-process %s returned an error code (%u)"), "apt-key", WEXITSTATUS(Status)); + apt_error(std::cerr, -1, fd, _("Sub-process %s returned an error code (%u)"), gpgv.c_str(), WEXITSTATUS(Status)); local_exit(WEXITSTATUS(Status)); } -- cgit v1.2.3-70-g09d2 From 4b1665fc9df8b42832681adf88985ac762aedf9f Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 23 Nov 2024 20:36:28 +0100 Subject: Stop installing apt-key, make it a test suite helper --- cmdline/CMakeLists.txt | 11 - cmdline/apt-key.in | 840 --------------------- debian/apt.install | 1 - test/integration/framework | 2 +- .../test-apt-key-used-in-maintainerscript | 43 -- test/interactive-helper/CMakeLists.txt | 8 + test/interactive-helper/apt-key.in | 840 +++++++++++++++++++++ 7 files changed, 849 insertions(+), 896 deletions(-) delete mode 100644 cmdline/apt-key.in delete mode 100755 test/integration/test-apt-key-used-in-maintainerscript create mode 100644 test/interactive-helper/apt-key.in diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt index ef6f8b35b..4c5e85af7 100644 --- a/cmdline/CMakeLists.txt +++ b/cmdline/CMakeLists.txt @@ -11,14 +11,6 @@ add_executable(apt-extracttemplates apt-extracttemplates.cc) add_executable(apt-internal-solver apt-internal-solver.cc) add_executable(apt-dump-solver apt-dump-solver.cc) add_executable(apt-internal-planner apt-internal-planner.cc) -add_vendor_file(OUTPUT apt-key - INPUT apt-key.in - MODE 755 - VARIABLES keyring-filename - keyring-removed-filename - keyring-master-filename - keyring-uri keyring-package) - # Link the executables against the libraries target_link_libraries(apt apt-pkg apt-private) @@ -54,6 +46,3 @@ install(TARGETS apt-internal-planner RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXEC add_links(${CMAKE_INSTALL_LIBEXECDIR}/apt/planners ../solvers/dump planners/dump) add_links(${CMAKE_INSTALL_LIBEXECDIR}/apt/solvers apt solver3) - -# Install the not-to-be-compiled programs -INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/apt-key DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/cmdline/apt-key.in b/cmdline/apt-key.in deleted file mode 100644 index dd1c52ab0..000000000 --- a/cmdline/apt-key.in +++ /dev/null @@ -1,840 +0,0 @@ -#!/bin/sh - -set -e -unset GREP_OPTIONS GPGHOMEDIR CURRENTTRAP -export IFS="$(printf "\n\b")" - -MASTER_KEYRING='&keyring-master-filename;' -eval "$(apt-config shell MASTER_KEYRING APT::Key::MasterKeyring)" -ARCHIVE_KEYRING='&keyring-filename;' -eval "$(apt-config shell ARCHIVE_KEYRING APT::Key::ArchiveKeyring)" -REMOVED_KEYS='&keyring-removed-filename;' -eval "$(apt-config shell REMOVED_KEYS APT::Key::RemovedKeys)" -ARCHIVE_KEYRING_URI='&keyring-uri;' -eval "$(apt-config shell ARCHIVE_KEYRING_URI APT::Key::ArchiveKeyringURI)" - -aptkey_echo() { echo "$@"; } - -find_gpgv_status_fd() { - while [ -n "$1" ]; do - if [ "$1" = '--status-fd' ]; then - shift - echo "$1" - break - fi - shift - done -} -GPGSTATUSFD="$(find_gpgv_status_fd "$@")" - -apt_warn() { - if [ -z "$GPGHOMEDIR" ]; then - echo >&2 'W:' "$@" - else - echo 'W:' "$@" > "${GPGHOMEDIR}/aptwarnings.log" - fi - if [ -n "$GPGSTATUSFD" ]; then - echo >&${GPGSTATUSFD} '[APTKEY:] WARNING' "$@" - fi -} -apt_error() { - if [ -z "$GPGHOMEDIR" ]; then - echo >&2 'E:' "$@" - else - echo 'E:' "$@" > "${GPGHOMEDIR}/aptwarnings.log" - fi - if [ -n "$GPGSTATUSFD" ]; then - echo >&${GPGSTATUSFD} '[APTKEY:] ERROR' "$@" - fi -} - -cleanup_gpg_home() { - if [ -z "$GPGHOMEDIR" ]; then return; fi - if [ -s "$GPGHOMEDIR/aptwarnings.log" ]; then - cat >&2 "$GPGHOMEDIR/aptwarnings.log" - fi - if command_available 'gpgconf'; then - GNUPGHOME="${GPGHOMEDIR}" gpgconf --kill all >/dev/null 2>&1 || true - fi - rm -rf "$GPGHOMEDIR" -} - -# 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 -create_gpg_home() { - # for cases in which we want to cache a homedir due to expensive setup - if [ -n "$GPGHOMEDIR" ]; then - return - fi - 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 --directory --tmpdir 'apt-key-gpghome.XXXXXXXXXX')" - CURRENTTRAP="${CURRENTTRAP} cleanup_gpg_home;" - trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM - if [ -z "$GPGHOMEDIR" ]; then - apt_error "Could not create temporary gpg home directory in $TMPDIR (wrong permissions?)" - exit 28 - fi - chmod 700 "$GPGHOMEDIR" -} - -requires_root() { - if [ "$(id -u)" -ne 0 ]; then - apt_error "This command can only be used by root." - exit 1 - fi -} - -command_available() { - if [ -x "$1" ]; then return 0; fi - command -v "$1" >/dev/null # required by policy, see #747320 -} - -escape_shell() { - echo "$@" | sed -e "s#'#'\"'\"'#g" -} - -get_fingerprints_of_keyring() { - aptkey_execute "$GPG_SH" --keyring "$1" --with-colons --fingerprint | while read publine; do - # search for a public key - if [ "${publine%%:*}" != 'pub' ]; then continue; fi - # search for the associated fingerprint (should be the very next line) - while read fprline; do - if [ "${fprline%%:*}" = 'sub' ]; then break; # should never happen - elif [ "${fprline%%:*}" != 'fpr' ]; then continue; fi - echo "$fprline" | cut -d':' -f 10 - done - # order in the keyring shouldn't be important - done | sort -} - -add_keys_with_verify_against_master_keyring() { - ADD_KEYRING="$1" - MASTER="$2" - - if [ ! -f "$ADD_KEYRING" ]; then - apt_error "Keyring '$ADD_KEYRING' to be added not found" - return - fi - if [ ! -f "$MASTER" ]; then - apt_error "Master-Keyring '$MASTER' not found" - return - fi - - # when adding new keys, make sure that the archive-master-keyring - # is honored. so: - # all keys that are exported must have a valid signature - # from a key in the $distro-master-keyring - add_keys="$(get_fingerprints_of_keyring "$ADD_KEYRING")" - all_add_keys="$(aptkey_execute "$GPG_SH" --keyring "$ADD_KEYRING" --with-colons --list-keys | grep ^[ps]ub | cut -d: -f5)" - master_keys="$(aptkey_execute "$GPG_SH" --keyring "$MASTER" --with-colons --list-keys | grep ^pub | cut -d: -f5)" - - # ensure there are no colisions LP: #857472 - for all_add_key in $all_add_keys; do - for master_key in $master_keys; do - if [ "$all_add_key" = "$master_key" ]; then - echo >&2 "Keyid collision for '$all_add_key' detected, operation aborted" - return 1 - fi - done - done - - for add_key in $add_keys; do - # export the add keyring one-by-one - local TMP_KEYRING="${GPGHOMEDIR}/tmp-keyring.gpg" - aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --output "$TMP_KEYRING" --export "$add_key" - if ! aptkey_execute "$GPG_SH" --batch --yes --keyring "$TMP_KEYRING" --import "$MASTER" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then - cat >&2 "${GPGHOMEDIR}/gpgoutput.log" - false - fi - # check if signed with the master key and only add in this case - ADDED=0 - for master_key in $master_keys; do - if aptkey_execute "$GPG_SH" --keyring "$TMP_KEYRING" --check-sigs --with-colons "$add_key" \ - | grep '^sig:!:' | cut -d: -f5 | grep -q "$master_key"; then - aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --export "$add_key" \ - | aptkey_execute "$GPG" --batch --yes --import - ADDED=1 - fi - done - if [ $ADDED = 0 ]; then - echo >&2 "Key '$add_key' not added. It is not signed with a master key" - fi - rm -f "${TMP_KEYRING}" - done -} - -# update the current archive signing keyring from a network URI -# the archive-keyring keys needs to be signed with the master key -# (otherwise it does not make sense from a security POV) -net_update() { - local APT_DIR='/' - eval $(apt-config shell APT_DIR Dir) - - # Disabled for now as code is insecure (LP: #1013639 (and 857472, 1013128)) - APT_KEY_NET_UPDATE_ENABLED="" - eval $(apt-config shell APT_KEY_NET_UPDATE_ENABLED APT::Key::Net-Update-Enabled) - if [ -z "$APT_KEY_NET_UPDATE_ENABLED" ]; then - exit 1 - fi - - if [ -z "$ARCHIVE_KEYRING_URI" ]; then - apt_error 'Your distribution is not supported in net-update as no uri for the archive-keyring is set' - exit 1 - fi - # in theory we would need to depend on wget for this, but this feature - # isn't usable in debian anyway as we have no keyring uri nor a master key - if ! command_available 'wget'; then - apt_error 'wget is required for a network-based update, but it is not installed' - exit 1 - fi - if [ ! -d "${APT_DIR}/var/lib/apt/keyrings" ]; then - mkdir -p "${APT_DIR}/var/lib/apt/keyrings" - fi - keyring="${APT_DIR}/var/lib/apt/keyrings/$(basename "$ARCHIVE_KEYRING_URI")" - old_mtime=0 - if [ -e $keyring ]; then - old_mtime=$(stat -c %Y "$keyring") - fi - (cd "${APT_DIR}/var/lib/apt/keyrings"; wget --timeout=90 -q -N "$ARCHIVE_KEYRING_URI") - if [ ! -e "$keyring" ]; then - return - fi - new_mtime=$(stat -c %Y "$keyring") - if [ $new_mtime -ne $old_mtime ]; then - aptkey_echo "Checking for new archive signing keys now" - add_keys_with_verify_against_master_keyring "$keyring" "$MASTER_KEYRING" - fi -} - -update() { - if [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then - echo >&2 "Warning: 'apt-key update' is deprecated and should not be used anymore!" - if [ -z "$ARCHIVE_KEYRING" ]; then - echo >&2 "Note: In your distribution this command is a no-op and can therefore be removed safely." - exit 0 - fi - fi - if [ ! -f "$ARCHIVE_KEYRING" ]; then - apt_error "Can't find the archive-keyring (Is the &keyring-package; package installed?)" - exit 1 - fi - - # add new keys from the package; - - # we do not use add_keys_with_verify_against_master_keyring here, - # because "update" is run on regular package updates. A - # attacker might as well replace the master-archive-keyring file - # in the package and add his own keys. so this check wouldn't - # add any security. we *need* this check on net-update though - import_keyring_into_keyring "$ARCHIVE_KEYRING" '' && cat "${GPGHOMEDIR}/gpgoutput.log" - - if [ -r "$REMOVED_KEYS" ]; then - # remove no-longer supported/used keys - get_fingerprints_of_keyring "$(dearmor_filename "$REMOVED_KEYS")" | while read key; do - foreach_keyring_do 'remove_key_from_keyring' "$key" - done - else - apt_warn "Removed keys keyring '$REMOVED_KEYS' missing or not readable" - fi -} - -remove_key_from_keyring() { - local KEYRINGFILE="$1" - shift - # non-existent keyrings have by definition no keys - if [ ! -e "$KEYRINGFILE" ]; then - return - fi - - local FINGERPRINTS="${GPGHOMEDIR}/keyringfile.keylst" - local DEARMOR="$(dearmor_filename "$KEYRINGFILE")" - get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS" - - for KEY in "$@"; do - # strip leading 0x, if present: - KEY="$(echo "${KEY#0x}" | tr -d ' ')" - - # check if the key is in this keyring - if ! grep -iq "^[0-9A-F]*${KEY}$" "$FINGERPRINTS"; then - continue - fi - if [ ! -w "$KEYRINGFILE" ]; then - apt_warn "Key ${KEY} is in keyring ${KEYRINGFILE}, but can't be removed as it is read only." - continue - fi - # check if it is the only key in the keyring and if so remove the keyring altogether - if [ '1' = "$(uniq "$FINGERPRINTS" | wc -l)" ]; then - mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" # behave like gpg - return - fi - # we can't just modify pointed to files as these might be in /usr or something - local REALTARGET - if [ -L "$DEARMOR" ]; then - REALTARGET="$(readlink -f "$DEARMOR")" - mv -f "$DEARMOR" "${DEARMOR}.dpkg-tmp" - cp -a "$REALTARGET" "$DEARMOR" - fi - # delete the key from the keyring - aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --batch --delete-keys --yes "$KEY" - if [ -n "$REALTARGET" ]; then - # the real backup is the old link, not the copy we made - mv -f "${DEARMOR}.dpkg-tmp" "${DEARMOR}~" - fi - if [ "$DEARMOR" != "$KEYRINGFILE" ]; then - mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" - create_new_keyring "$KEYRINGFILE" - aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --armor --export > "$KEYRINGFILE" - fi - get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS" - done -} - -accessible_file_exists() { - if ! test -s "$1"; then - return 1 - fi - if test -r "$1"; then - return 0 - fi - apt_warn "The key(s) in the keyring $1 are ignored as the file is not readable by user '$USER' executing apt-key." - return 1 -} - -is_supported_keyring() { - # empty files are always supported - if ! test -s "$1"; then - return 0 - fi - local FILEEXT="${1##*.}" - if [ "$FILEEXT" = 'gpg' -o "$FILEEXT" = 'pub' ]; then - # 0x98, 0x99 and 0xC6 via octal as hex isn't supported by dashs printf - if printf '\231' | cmp -s -n 1 - "$1"; then - true - elif printf '\230' | cmp -s -n 1 - "$1"; then - true - elif printf '\306' | cmp -s -n 1 - "$1"; then - true - else - apt_warn "The key(s) in the keyring $1 are ignored as the file has an unsupported filetype." - return 1 - fi - elif [ "$FILEEXT" = 'asc' ]; then - true #dearmor_filename will deal with them - else - # most callers ignore unsupported extensions silently - apt_warn "The key(s) in the keyring $1 are ignored as the file has an unsupported filename extension." - return 1 - fi - return 0 -} - -foreach_keyring_do() { - local ACTION="$1" - shift - # if a --keyring was given, just work on this one - if [ -n "$FORCED_KEYRING" ]; then - $ACTION "$FORCED_KEYRING" "$@" - else - # otherwise all known keyrings are up for inspection - if accessible_file_exists "$TRUSTEDFILE" && is_supported_keyring "$TRUSTEDFILE"; then - $ACTION "$TRUSTEDFILE" "$@" - fi - local TRUSTEDPARTS="/etc/apt/trusted.gpg.d" - eval "$(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d)" - if [ -d "$TRUSTEDPARTS" ]; then - TRUSTEDPARTS="$(readlink -f "$TRUSTEDPARTS")" - local TRUSTEDPARTSLIST="$(cd /; find "$TRUSTEDPARTS" -mindepth 1 -maxdepth 1 \( -name '*.gpg' -o -name '*.asc' \))" - for trusted in $(echo "$TRUSTEDPARTSLIST" | sort); do - if accessible_file_exists "$trusted" && is_supported_keyring "$trusted"; then - $ACTION "$trusted" "$@" - fi - done - fi - fi -} - -list_keys_in_keyring() { - local KEYRINGFILE="$1" - shift - # fingerprint and co will fail if key isn't in this keyring - aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$KEYRINGFILE")" "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2> "${GPGHOMEDIR}/gpgoutput.err" || true - if [ ! -s "${GPGHOMEDIR}/gpgoutput.log" ]; then - return - fi - # we fake gpg header here to refer to the real asc file rather than a temp file - if [ "${KEYRINGFILE##*.}" = 'asc' ]; then - if expr match "$(sed -n '2p' "${GPGHOMEDIR}/gpgoutput.log")" '^-\+$' >/dev/null 2>&1; then - echo "$KEYRINGFILE" - echo "$KEYRINGFILE" | sed 's#[^-]#-#g' - sed '1,2d' "${GPGHOMEDIR}/gpgoutput.log" || true - else - cat "${GPGHOMEDIR}/gpgoutput.log" - fi - else - cat "${GPGHOMEDIR}/gpgoutput.log" - fi - if [ -s "${GPGHOMEDIR}/gpgoutput.err" ]; then - cat >&2 "${GPGHOMEDIR}/gpgoutput.err" - fi -} - -export_key_from_to() { - local FROM="$1" - local TO="$2" - shift 2 - if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export "$@" > "$TO" 2> "${GPGHOMEDIR}/gpgoutput.log"; then - cat >&2 "${GPGHOMEDIR}/gpgoutput.log" - false - else - chmod 0644 -- "$TO" - fi -} - -import_keyring_into_keyring() { - local FROM="${1:-${GPGHOMEDIR}/pubring.gpg}" - local TO="${2:-${GPGHOMEDIR}/pubring.gpg}" - shift 2 - rm -f "${GPGHOMEDIR}/gpgoutput.log" - # the idea is simple: We take keys from one keyring and copy it to another - # we do this with so many checks in between to ensure that WE control the - # creation, so we know that the (potentially) created $TO keyring is a - # simple keyring rather than a keybox as gpg2 would create it which in turn - # can't be read by gpgv. - # BEWARE: This is designed more in the way to work with the current - # callers, than to have a well defined it would be easy to add new callers to. - if [ ! -s "$TO" ]; then - if [ -s "$FROM" ]; then - if [ -z "$2" ]; then - local OPTS - if [ "${TO##*.}" = 'asc' ]; then - OPTS='--armor' - fi - export_key_from_to "$(dearmor_filename "$FROM")" "$TO" $OPTS ${1:+"$1"} - else - create_new_keyring "$TO" - fi - else - create_new_keyring "$TO" - fi - elif [ -s "$FROM" ]; then - local EXPORTLIMIT="$1" - if [ -n "$1$2" ]; then shift; fi - local DEARMORTO="$(dearmor_filename "$TO")" - if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export ${EXPORTLIMIT:+"$EXPORTLIMIT"} \ - | aptkey_execute "$GPG_SH" --keyring "$DEARMORTO" --batch --import "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then - cat >&2 "${GPGHOMEDIR}/gpgoutput.log" - false - fi - if [ "$DEARMORTO" != "$TO" ]; then - export_key_from_to "$DEARMORTO" "${DEARMORTO}.asc" --armor - if ! cmp -s "$TO" "${DEARMORTO}.asc" 2>/dev/null; then - cp -a "$TO" "${TO}~" - mv -f "${DEARMORTO}.asc" "$TO" - fi - fi - fi -} - -dearmor_keyring() { - # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=831409#67 - # The awk script is more complex through to skip surrounding garbage and - # to support multiple keys in one file (old gpgs generate version headers - # which get printed with the original and hence result in garbage input for base64 - awk '{ gsub(/\r/,"") } -/^-----BEGIN/{ x = 1; } -/^$/{ if (x == 1) { x = 2; }; } -/^[^=-]/{ if (x == 2) { print $0; }; } -/^-----END/{ x = 0; }' | base64 -d -} -dearmor_filename() { - if [ "${1##*.}" = 'asc' ]; then - local trusted="${GPGHOMEDIR}/${1##*/}.gpg" - if [ -s "$1" ]; then - dearmor_keyring < "$1" > "$trusted" - fi - echo "$trusted" - elif [ "${1##*.}" = 'gpg' ]; then - echo "$1" - elif [ "$(head -n 1 "$1" 2>/dev/null)" = '-----BEGIN PGP PUBLIC KEY BLOCK-----' ]; then - local trusted="${GPGHOMEDIR}/${1##*/}.gpg" - dearmor_keyring < "$1" > "$trusted" - echo "$trusted" - else - echo "$1" - fi -} -catfile() { - if accessible_file_exists "$1" && is_supported_keyring "$1"; then - cat "$(dearmor_filename "$1")" >> "$2" - 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="$(readlink -f "${GPGHOMEDIR}")/pubring.gpg" - rm -f "$PUBRING" - touch "$PUBRING" - foreach_keyring_do 'catfile' "$PUBRING" -} - -import_keys_from_keyring() { - import_keyring_into_keyring "$1" "$2" -} - -merge_keys_into_keyrings() { - import_keyring_into_keyring "$2" "$1" '' --import-options 'merge-only' -} - -merge_back_changes() { - if [ -n "$FORCED_KEYRING" ]; then - # if the keyring was forced merge is already done - if [ "$FORCED_KEYRING" != "$TRUSTEDFILE" ]; then - mv -f "$FORCED_KEYRING" "${FORCED_KEYRING}~" - export_key_from_to "$TRUSTEDFILE" "$FORCED_KEYRING" --armor - fi - return - fi - if [ -s "${GPGHOMEDIR}/pubring.gpg" ]; then - # merge all updated keys - foreach_keyring_do 'merge_keys_into_keyrings' "${GPGHOMEDIR}/pubring.gpg" - fi - # 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" - 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 - merge_all_trusted_keyrings_into_pubring - FORCED_KEYRING="${GPGHOMEDIR}/forcedkeyid.gpg" - TRUSTEDFILE="${FORCED_KEYRING}" - echo "#!/bin/sh -exec sh '($(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh" - GPG="${GPGHOMEDIR}/gpg.1.sh" - # 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 - merge_all_trusted_keyrings_into_pubring - if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then - cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg" - else - touch "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg" - fi - echo "#!/bin/sh -exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${GPGHOMEDIR}/pubring.gpg")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh" - GPG="${GPGHOMEDIR}/gpg.1.sh" - else - TRUSTEDFILE="$(dearmor_filename "$FORCED_KEYRING")" - create_new_keyring "$TRUSTEDFILE" - echo "#!/bin/sh -exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh" - GPG="${GPGHOMEDIR}/gpg.1.sh" - fi -} - -create_new_keyring() { - # gpg defaults to mode 0600 for new keyrings. Create one with 0644 instead. - if ! [ -e "$1" ]; then - if [ -w "$(dirname "$1")" ]; then - touch -- "$1" - chmod 0644 -- "$1" - fi - fi -} - -aptkey_execute() { sh "$@"; } - -usage() { - echo "Usage: apt-key [--keyring file] [command] [arguments]" - echo - echo "Manage apt's list of trusted keys" - echo - echo " apt-key add - add the key contained in ('-' for stdin)" - echo " apt-key del - remove the key " - echo " apt-key export - output the key " - echo " apt-key exportall - output all trusted keys" - echo " apt-key update - update keys using the keyring package" - echo " apt-key net-update - update keys using the network" - echo " apt-key list - list keys" - echo " apt-key finger - list fingerprints" - echo " apt-key adv - pass advanced options to gpg (download key)" - echo - echo "If no specific keyring file is given the command applies to all keyring files." -} - -while [ -n "$1" ]; do - case "$1" in - --keyring) - shift - if [ -z "$FORCED_KEYRING" -o "$FORCED_KEYRING" = '/dev/null' ]; then - TRUSTEDFILE="$1" - FORCED_KEYRING="$1" - elif [ "$TRUSTEDFILE" = "$FORCED_KEYRING" ]; then - create_gpg_home - FORCED_KEYRING="${GPGHOMEDIR}/mergedkeyrings.gpg" - echo -n '' > "$FORCED_KEYRING" - chmod 0644 -- "$FORCED_KEYRING" - catfile "$TRUSTEDFILE" "$FORCED_KEYRING" - catfile "$1" "$FORCED_KEYRING" - else - catfile "$1" "$FORCED_KEYRING" - fi - ;; - --keyid) - shift - if [ -n "$FORCED_KEYID" ]; then - apt_error 'Specifying --keyid multiple times is not supported' - exit 1 - fi - FORCED_KEYID="$1" - ;; - --secret-keyring) - shift - FORCED_SECRET_KEYRING="$1" - ;; - --readonly) - merge_back_changes() { true; } - create_new_keyring() { if [ ! -r "$FORCED_KEYRING" ]; then TRUSTEDFILE='/dev/null'; FORCED_KEYRING="$TRUSTEDFILE"; fi; } - ;; - --fakeroot) - requires_root() { true; } - ;; - --quiet) - aptkey_echo() { true; } - ;; - --debug1) - # some cmds like finger redirect stderr to /dev/null … - aptkey_execute() { echo 'EXEC:' "$@"; sh "$@"; } - ;; - --debug2) - # … other more complicated ones pipe gpg into gpg. - aptkey_execute() { echo >&2 'EXEC:' "$@"; sh "$@"; } - ;; - --homedir) - # force usage of a specific homedir instead of creating a temporary - shift - GPGHOMEDIR="$1" - ;; - --*) - echo >&2 "Unknown option: $1" - usage - exit 1;; - *) - break;; - esac - shift -done - -if [ -z "$TRUSTEDFILE" ]; then - TRUSTEDFILE="/etc/apt/trusted.gpg" - eval $(apt-config shell TRUSTEDFILE Apt::GPGV::TrustedKeyring) - eval $(apt-config shell TRUSTEDFILE Dir::Etc::Trusted/f) - if [ "$APT_KEY_NO_LEGACY_KEYRING" ]; then - TRUSTEDFILE="/dev/null" - fi -fi - -command="$1" -if [ -z "$command" ]; then - usage - exit 1 -fi -shift - -prepare_gpg_home() { - # crude detection if we are called from a maintainerscript where the - # package depends on gnupg or not. We accept recommends here as - # well as the script hopefully uses apt-key optionally then like e.g. - # debian-archive-keyring for (upgrade) cleanup did - if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ] && [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then - if ! dpkg-query --show --showformat '${Pre-Depends}${Depends}${Recommends}\n' "$DPKG_MAINTSCRIPT_PACKAGE" 2>/dev/null | grep -E -q 'gpg|gnupg'; then - cat >&2 < "${GPGHOMEDIR}/gpg.0.sh" - GPG_SH="${GPGHOMEDIR}/gpg.0.sh" - GPG="$GPG_SH" - - # create the trustdb with an (empty) dummy keyring - # older gpgs required it, newer gpgs even warn that it isn't needed, - # but require it nonetheless for some commands, so we just play safe - # here for the foreseeable future and create a dummy one - touch "${GPGHOMEDIR}/empty.gpg" - if ! "$GPG_EXE" --ignore-time-conflict --no-options --no-default-keyring \ - --homedir "$GPGHOMEDIR" --quiet --check-trustdb --keyring "${GPGHOMEDIR}/empty.gpg" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then - cat >&2 "${GPGHOMEDIR}/gpgoutput.log" - false - fi - - # We don't usually need a secret keyring, of course, but - # for advanced operations, we might really need a secret keyring after all - if [ -n "$FORCED_SECRET_KEYRING" ] && [ -r "$FORCED_SECRET_KEYRING" ]; then - if ! aptkey_execute "$GPG" -v --batch --import "$FORCED_SECRET_KEYRING" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then - # already imported keys cause gpg1 to fail for some reason… ignore this error - if ! grep -q 'already in secret keyring' "${GPGHOMEDIR}/gpgoutput.log"; then - cat >&2 "${GPGHOMEDIR}/gpgoutput.log" - false - fi - fi - else - # and then, there are older versions of gpg which panic and implode - # if there isn't one available - and writeable for imports - # and even if not output is littered with the creation of a secring, - # so lets call import once to have it create what it wants in silence - echo -n | aptkey_execute "$GPG" --batch --import >/dev/null 2>&1 || true - fi -} - -warn_on_script_usage() { - if [ -n "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then - return - fi - # (Maintainer) scripts should not be using apt-key - if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ]; then - echo >&2 "Warning: apt-key should not be used in scripts (called from $DPKG_MAINTSCRIPT_NAME maintainerscript of the package ${DPKG_MAINTSCRIPT_PACKAGE})" - fi - - echo >&2 "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))." -} - -warn_outside_maintscript() { - # In del, we want to warn in interactive use, but not inside maintainer - # scripts, so as to give people a chance to migrate keyrings. - # - # FIXME: We should always warn starting in 2022. - if [ -z "$DPKG_MAINTSCRIPT_PACKAGE" ]; then - echo >&2 "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))." - fi -} - -if [ "$command" != 'help' ] && [ "$command" != 'verify' ]; then - prepare_gpg_home -fi - -case "$command" in - add) - warn_on_script_usage - requires_root - setup_merged_keyring - aptkey_execute "$GPG" --quiet --batch --import "$@" - merge_back_changes - aptkey_echo "OK" - ;; - del|rm|remove) - # no script warning here as removing 'add' usage needs 'del' for cleanup - warn_outside_maintscript - requires_root - foreach_keyring_do 'remove_key_from_keyring' "$@" - aptkey_echo "OK" - ;; - update) - warn_on_script_usage - requires_root - setup_merged_keyring - update - merge_back_changes - ;; - net-update) - warn_on_script_usage - requires_root - setup_merged_keyring - net_update - merge_back_changes - ;; - list|finger*) - warn_on_script_usage - foreach_keyring_do 'list_keys_in_keyring' --fingerprint "$@" - ;; - export|exportall) - warn_on_script_usage - merge_all_trusted_keyrings_into_pubring - aptkey_execute "$GPG_SH" --keyring "${GPGHOMEDIR}/pubring.gpg" --armor --export "$@" - ;; - adv*) - warn_on_script_usage - setup_merged_keyring - aptkey_echo "Executing: $GPG" "$@" - aptkey_execute "$GPG" "$@" - merge_back_changes - ;; - verify) - GPGV='' - ASSERT_PUBKEY_ALGO='' - eval $(apt-config shell GPGV Apt::Key::gpgvcommand ASSERT_PUBKEY_ALGO Apt::Key::assert-pubkey-algo) - if [ -n "$GPGV" ] && command_available "$GPGV"; then true; - elif command_available 'gpgv-sq'; then GPGV='gpgv-sq'; - elif command_available 'gpgv'; then GPGV='gpgv'; - elif command_available 'gpgv2'; then GPGV='gpgv2'; - elif command_available 'gpgv1'; then GPGV='gpgv1'; - else - apt_error 'gpgv, gpgv2 or gpgv1 required for verification, but neither seems installed' - exit 29 - fi - GPGV_ARGS="" - if [ "$ASSERT_PUBKEY_ALGO" ] && $GPGV --dump-options | grep -q -- --assert-pubkey-algo; then - GPGV_ARGS="--assert-pubkey-algo=$ASSERT_PUBKEY_ALGO" - 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" $GPGV_ARGS --homedir "${GPGHOMEDIR}" --keyring "$(dearmor_filename "${FORCED_KEYRING}")" --ignore-time-conflict "$@" - else - "$GPGV" $GPGV_ARGS --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@" - fi - ;; - help) - usage - ;; - *) - usage - exit 1 - ;; -esac diff --git a/debian/apt.install b/debian/apt.install index 07bc737fd..eb465d498 100644 --- a/debian/apt.install +++ b/debian/apt.install @@ -7,7 +7,6 @@ usr/bin/apt-cache usr/bin/apt-cdrom usr/bin/apt-config usr/bin/apt-get -usr/bin/apt-key usr/bin/apt-mark usr/lib/*/libapt-private.so* usr/lib/apt/apt-helper diff --git a/test/integration/framework b/test/integration/framework index a64488f0e..6d4d0b07f 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -192,7 +192,7 @@ aptcache() { runapt apt-cache "$@"; } aptcdrom() { runapt apt-cdrom "$@"; } aptget() { runapt apt-get "$@"; } aptftparchive() { runapt "${APTFTPARCHIVEBINDIR}/apt-ftparchive" "$@"; } -aptkey() { runapt apt-key "$@"; } +aptkey() { runapt "${APTTESTHELPERSBINDIR}/apt-key" "$@"; } aptmark() { runapt apt-mark "$@"; } aptsortpkgs() { runapt apt-sortpkgs "$@"; } apt() { runapt apt "$@"; } diff --git a/test/integration/test-apt-key-used-in-maintainerscript b/test/integration/test-apt-key-used-in-maintainerscript deleted file mode 100755 index b5ed3279f..000000000 --- a/test/integration/test-apt-key-used-in-maintainerscript +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh -set -e - -TESTDIR="$(readlink -f "$(dirname "$0")")" -. "$TESTDIR/framework" - -setupenvironment -unset APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE -configarchitecture 'native' -configdpkgnoopchroot - -buildingpkg() { - local PKG="$1" - shift - setupsimplenativepackage "$PKG" 'native' '1' 'unstable' "$@" - BUILDDIR="incoming/${PKG}-1" - echo '#!/bin/sh -apt-key list >/dev/null' > "${BUILDDIR}/debian/postinst" - buildpackage "$BUILDDIR" 'unstable' 'main' 'native' - rm -rf "$BUILDDIR" -} -buildingpkg 'aptkeyuser-nodepends' 'Depends: unrelated' -buildingpkg 'aptkeyuser-depends' 'Depends: gnupg' - -insertinstalledpackage 'unrelated' 'native' '1' -insertinstalledpackage 'gnupg' 'native' '1' -testdpkgnotinstalled 'aptkeyuser-depends' 'aptkeyuser-nodepends' - -testsuccess apt install ./incoming/aptkeyuser-depends_*.changes -y -cp rootdir/tmp/testsuccess.output apt.output -testdpkginstalled 'aptkeyuser-depends' -testfailure grep '^Warning: This will BREAK' apt.output -testsuccess grep '^Warning: apt-key' apt.output - -testsuccess apt install --with-source ./incoming/aptkeyuser-nodepends_*.changes aptkeyuser-nodepends -y -cp rootdir/tmp/testsuccess.output apt.output -testdpkginstalled 'aptkeyuser-nodepends' -testsuccess grep '^Warning: This will BREAK' apt.output -testsuccess grep '^Warning: apt-key' apt.output - -testsuccess aptkey list -cp rootdir/tmp/testsuccess.output aptkey.list -testsuccess grep '^Warning: apt-key' aptkey.list diff --git a/test/interactive-helper/CMakeLists.txt b/test/interactive-helper/CMakeLists.txt index bfca6c8f6..0e0b57bcd 100644 --- a/test/interactive-helper/CMakeLists.txt +++ b/test/interactive-helper/CMakeLists.txt @@ -31,3 +31,11 @@ target_include_directories(longest-dependency-chain PRIVATE ${APTPRIVATE_INCLUDE add_library(noprofile SHARED libnoprofile.c) target_link_libraries(noprofile ${CMAKE_DL_LIBS}) + +add_vendor_file(OUTPUT apt-key + INPUT apt-key.in + MODE 755 + VARIABLES keyring-filename + keyring-removed-filename + keyring-master-filename + keyring-uri keyring-package) diff --git a/test/interactive-helper/apt-key.in b/test/interactive-helper/apt-key.in new file mode 100644 index 000000000..dd1c52ab0 --- /dev/null +++ b/test/interactive-helper/apt-key.in @@ -0,0 +1,840 @@ +#!/bin/sh + +set -e +unset GREP_OPTIONS GPGHOMEDIR CURRENTTRAP +export IFS="$(printf "\n\b")" + +MASTER_KEYRING='&keyring-master-filename;' +eval "$(apt-config shell MASTER_KEYRING APT::Key::MasterKeyring)" +ARCHIVE_KEYRING='&keyring-filename;' +eval "$(apt-config shell ARCHIVE_KEYRING APT::Key::ArchiveKeyring)" +REMOVED_KEYS='&keyring-removed-filename;' +eval "$(apt-config shell REMOVED_KEYS APT::Key::RemovedKeys)" +ARCHIVE_KEYRING_URI='&keyring-uri;' +eval "$(apt-config shell ARCHIVE_KEYRING_URI APT::Key::ArchiveKeyringURI)" + +aptkey_echo() { echo "$@"; } + +find_gpgv_status_fd() { + while [ -n "$1" ]; do + if [ "$1" = '--status-fd' ]; then + shift + echo "$1" + break + fi + shift + done +} +GPGSTATUSFD="$(find_gpgv_status_fd "$@")" + +apt_warn() { + if [ -z "$GPGHOMEDIR" ]; then + echo >&2 'W:' "$@" + else + echo 'W:' "$@" > "${GPGHOMEDIR}/aptwarnings.log" + fi + if [ -n "$GPGSTATUSFD" ]; then + echo >&${GPGSTATUSFD} '[APTKEY:] WARNING' "$@" + fi +} +apt_error() { + if [ -z "$GPGHOMEDIR" ]; then + echo >&2 'E:' "$@" + else + echo 'E:' "$@" > "${GPGHOMEDIR}/aptwarnings.log" + fi + if [ -n "$GPGSTATUSFD" ]; then + echo >&${GPGSTATUSFD} '[APTKEY:] ERROR' "$@" + fi +} + +cleanup_gpg_home() { + if [ -z "$GPGHOMEDIR" ]; then return; fi + if [ -s "$GPGHOMEDIR/aptwarnings.log" ]; then + cat >&2 "$GPGHOMEDIR/aptwarnings.log" + fi + if command_available 'gpgconf'; then + GNUPGHOME="${GPGHOMEDIR}" gpgconf --kill all >/dev/null 2>&1 || true + fi + rm -rf "$GPGHOMEDIR" +} + +# 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 +create_gpg_home() { + # for cases in which we want to cache a homedir due to expensive setup + if [ -n "$GPGHOMEDIR" ]; then + return + fi + 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 --directory --tmpdir 'apt-key-gpghome.XXXXXXXXXX')" + CURRENTTRAP="${CURRENTTRAP} cleanup_gpg_home;" + trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM + if [ -z "$GPGHOMEDIR" ]; then + apt_error "Could not create temporary gpg home directory in $TMPDIR (wrong permissions?)" + exit 28 + fi + chmod 700 "$GPGHOMEDIR" +} + +requires_root() { + if [ "$(id -u)" -ne 0 ]; then + apt_error "This command can only be used by root." + exit 1 + fi +} + +command_available() { + if [ -x "$1" ]; then return 0; fi + command -v "$1" >/dev/null # required by policy, see #747320 +} + +escape_shell() { + echo "$@" | sed -e "s#'#'\"'\"'#g" +} + +get_fingerprints_of_keyring() { + aptkey_execute "$GPG_SH" --keyring "$1" --with-colons --fingerprint | while read publine; do + # search for a public key + if [ "${publine%%:*}" != 'pub' ]; then continue; fi + # search for the associated fingerprint (should be the very next line) + while read fprline; do + if [ "${fprline%%:*}" = 'sub' ]; then break; # should never happen + elif [ "${fprline%%:*}" != 'fpr' ]; then continue; fi + echo "$fprline" | cut -d':' -f 10 + done + # order in the keyring shouldn't be important + done | sort +} + +add_keys_with_verify_against_master_keyring() { + ADD_KEYRING="$1" + MASTER="$2" + + if [ ! -f "$ADD_KEYRING" ]; then + apt_error "Keyring '$ADD_KEYRING' to be added not found" + return + fi + if [ ! -f "$MASTER" ]; then + apt_error "Master-Keyring '$MASTER' not found" + return + fi + + # when adding new keys, make sure that the archive-master-keyring + # is honored. so: + # all keys that are exported must have a valid signature + # from a key in the $distro-master-keyring + add_keys="$(get_fingerprints_of_keyring "$ADD_KEYRING")" + all_add_keys="$(aptkey_execute "$GPG_SH" --keyring "$ADD_KEYRING" --with-colons --list-keys | grep ^[ps]ub | cut -d: -f5)" + master_keys="$(aptkey_execute "$GPG_SH" --keyring "$MASTER" --with-colons --list-keys | grep ^pub | cut -d: -f5)" + + # ensure there are no colisions LP: #857472 + for all_add_key in $all_add_keys; do + for master_key in $master_keys; do + if [ "$all_add_key" = "$master_key" ]; then + echo >&2 "Keyid collision for '$all_add_key' detected, operation aborted" + return 1 + fi + done + done + + for add_key in $add_keys; do + # export the add keyring one-by-one + local TMP_KEYRING="${GPGHOMEDIR}/tmp-keyring.gpg" + aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --output "$TMP_KEYRING" --export "$add_key" + if ! aptkey_execute "$GPG_SH" --batch --yes --keyring "$TMP_KEYRING" --import "$MASTER" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then + cat >&2 "${GPGHOMEDIR}/gpgoutput.log" + false + fi + # check if signed with the master key and only add in this case + ADDED=0 + for master_key in $master_keys; do + if aptkey_execute "$GPG_SH" --keyring "$TMP_KEYRING" --check-sigs --with-colons "$add_key" \ + | grep '^sig:!:' | cut -d: -f5 | grep -q "$master_key"; then + aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --export "$add_key" \ + | aptkey_execute "$GPG" --batch --yes --import + ADDED=1 + fi + done + if [ $ADDED = 0 ]; then + echo >&2 "Key '$add_key' not added. It is not signed with a master key" + fi + rm -f "${TMP_KEYRING}" + done +} + +# update the current archive signing keyring from a network URI +# the archive-keyring keys needs to be signed with the master key +# (otherwise it does not make sense from a security POV) +net_update() { + local APT_DIR='/' + eval $(apt-config shell APT_DIR Dir) + + # Disabled for now as code is insecure (LP: #1013639 (and 857472, 1013128)) + APT_KEY_NET_UPDATE_ENABLED="" + eval $(apt-config shell APT_KEY_NET_UPDATE_ENABLED APT::Key::Net-Update-Enabled) + if [ -z "$APT_KEY_NET_UPDATE_ENABLED" ]; then + exit 1 + fi + + if [ -z "$ARCHIVE_KEYRING_URI" ]; then + apt_error 'Your distribution is not supported in net-update as no uri for the archive-keyring is set' + exit 1 + fi + # in theory we would need to depend on wget for this, but this feature + # isn't usable in debian anyway as we have no keyring uri nor a master key + if ! command_available 'wget'; then + apt_error 'wget is required for a network-based update, but it is not installed' + exit 1 + fi + if [ ! -d "${APT_DIR}/var/lib/apt/keyrings" ]; then + mkdir -p "${APT_DIR}/var/lib/apt/keyrings" + fi + keyring="${APT_DIR}/var/lib/apt/keyrings/$(basename "$ARCHIVE_KEYRING_URI")" + old_mtime=0 + if [ -e $keyring ]; then + old_mtime=$(stat -c %Y "$keyring") + fi + (cd "${APT_DIR}/var/lib/apt/keyrings"; wget --timeout=90 -q -N "$ARCHIVE_KEYRING_URI") + if [ ! -e "$keyring" ]; then + return + fi + new_mtime=$(stat -c %Y "$keyring") + if [ $new_mtime -ne $old_mtime ]; then + aptkey_echo "Checking for new archive signing keys now" + add_keys_with_verify_against_master_keyring "$keyring" "$MASTER_KEYRING" + fi +} + +update() { + if [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then + echo >&2 "Warning: 'apt-key update' is deprecated and should not be used anymore!" + if [ -z "$ARCHIVE_KEYRING" ]; then + echo >&2 "Note: In your distribution this command is a no-op and can therefore be removed safely." + exit 0 + fi + fi + if [ ! -f "$ARCHIVE_KEYRING" ]; then + apt_error "Can't find the archive-keyring (Is the &keyring-package; package installed?)" + exit 1 + fi + + # add new keys from the package; + + # we do not use add_keys_with_verify_against_master_keyring here, + # because "update" is run on regular package updates. A + # attacker might as well replace the master-archive-keyring file + # in the package and add his own keys. so this check wouldn't + # add any security. we *need* this check on net-update though + import_keyring_into_keyring "$ARCHIVE_KEYRING" '' && cat "${GPGHOMEDIR}/gpgoutput.log" + + if [ -r "$REMOVED_KEYS" ]; then + # remove no-longer supported/used keys + get_fingerprints_of_keyring "$(dearmor_filename "$REMOVED_KEYS")" | while read key; do + foreach_keyring_do 'remove_key_from_keyring' "$key" + done + else + apt_warn "Removed keys keyring '$REMOVED_KEYS' missing or not readable" + fi +} + +remove_key_from_keyring() { + local KEYRINGFILE="$1" + shift + # non-existent keyrings have by definition no keys + if [ ! -e "$KEYRINGFILE" ]; then + return + fi + + local FINGERPRINTS="${GPGHOMEDIR}/keyringfile.keylst" + local DEARMOR="$(dearmor_filename "$KEYRINGFILE")" + get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS" + + for KEY in "$@"; do + # strip leading 0x, if present: + KEY="$(echo "${KEY#0x}" | tr -d ' ')" + + # check if the key is in this keyring + if ! grep -iq "^[0-9A-F]*${KEY}$" "$FINGERPRINTS"; then + continue + fi + if [ ! -w "$KEYRINGFILE" ]; then + apt_warn "Key ${KEY} is in keyring ${KEYRINGFILE}, but can't be removed as it is read only." + continue + fi + # check if it is the only key in the keyring and if so remove the keyring altogether + if [ '1' = "$(uniq "$FINGERPRINTS" | wc -l)" ]; then + mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" # behave like gpg + return + fi + # we can't just modify pointed to files as these might be in /usr or something + local REALTARGET + if [ -L "$DEARMOR" ]; then + REALTARGET="$(readlink -f "$DEARMOR")" + mv -f "$DEARMOR" "${DEARMOR}.dpkg-tmp" + cp -a "$REALTARGET" "$DEARMOR" + fi + # delete the key from the keyring + aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --batch --delete-keys --yes "$KEY" + if [ -n "$REALTARGET" ]; then + # the real backup is the old link, not the copy we made + mv -f "${DEARMOR}.dpkg-tmp" "${DEARMOR}~" + fi + if [ "$DEARMOR" != "$KEYRINGFILE" ]; then + mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" + create_new_keyring "$KEYRINGFILE" + aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --armor --export > "$KEYRINGFILE" + fi + get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS" + done +} + +accessible_file_exists() { + if ! test -s "$1"; then + return 1 + fi + if test -r "$1"; then + return 0 + fi + apt_warn "The key(s) in the keyring $1 are ignored as the file is not readable by user '$USER' executing apt-key." + return 1 +} + +is_supported_keyring() { + # empty files are always supported + if ! test -s "$1"; then + return 0 + fi + local FILEEXT="${1##*.}" + if [ "$FILEEXT" = 'gpg' -o "$FILEEXT" = 'pub' ]; then + # 0x98, 0x99 and 0xC6 via octal as hex isn't supported by dashs printf + if printf '\231' | cmp -s -n 1 - "$1"; then + true + elif printf '\230' | cmp -s -n 1 - "$1"; then + true + elif printf '\306' | cmp -s -n 1 - "$1"; then + true + else + apt_warn "The key(s) in the keyring $1 are ignored as the file has an unsupported filetype." + return 1 + fi + elif [ "$FILEEXT" = 'asc' ]; then + true #dearmor_filename will deal with them + else + # most callers ignore unsupported extensions silently + apt_warn "The key(s) in the keyring $1 are ignored as the file has an unsupported filename extension." + return 1 + fi + return 0 +} + +foreach_keyring_do() { + local ACTION="$1" + shift + # if a --keyring was given, just work on this one + if [ -n "$FORCED_KEYRING" ]; then + $ACTION "$FORCED_KEYRING" "$@" + else + # otherwise all known keyrings are up for inspection + if accessible_file_exists "$TRUSTEDFILE" && is_supported_keyring "$TRUSTEDFILE"; then + $ACTION "$TRUSTEDFILE" "$@" + fi + local TRUSTEDPARTS="/etc/apt/trusted.gpg.d" + eval "$(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d)" + if [ -d "$TRUSTEDPARTS" ]; then + TRUSTEDPARTS="$(readlink -f "$TRUSTEDPARTS")" + local TRUSTEDPARTSLIST="$(cd /; find "$TRUSTEDPARTS" -mindepth 1 -maxdepth 1 \( -name '*.gpg' -o -name '*.asc' \))" + for trusted in $(echo "$TRUSTEDPARTSLIST" | sort); do + if accessible_file_exists "$trusted" && is_supported_keyring "$trusted"; then + $ACTION "$trusted" "$@" + fi + done + fi + fi +} + +list_keys_in_keyring() { + local KEYRINGFILE="$1" + shift + # fingerprint and co will fail if key isn't in this keyring + aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$KEYRINGFILE")" "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2> "${GPGHOMEDIR}/gpgoutput.err" || true + if [ ! -s "${GPGHOMEDIR}/gpgoutput.log" ]; then + return + fi + # we fake gpg header here to refer to the real asc file rather than a temp file + if [ "${KEYRINGFILE##*.}" = 'asc' ]; then + if expr match "$(sed -n '2p' "${GPGHOMEDIR}/gpgoutput.log")" '^-\+$' >/dev/null 2>&1; then + echo "$KEYRINGFILE" + echo "$KEYRINGFILE" | sed 's#[^-]#-#g' + sed '1,2d' "${GPGHOMEDIR}/gpgoutput.log" || true + else + cat "${GPGHOMEDIR}/gpgoutput.log" + fi + else + cat "${GPGHOMEDIR}/gpgoutput.log" + fi + if [ -s "${GPGHOMEDIR}/gpgoutput.err" ]; then + cat >&2 "${GPGHOMEDIR}/gpgoutput.err" + fi +} + +export_key_from_to() { + local FROM="$1" + local TO="$2" + shift 2 + if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export "$@" > "$TO" 2> "${GPGHOMEDIR}/gpgoutput.log"; then + cat >&2 "${GPGHOMEDIR}/gpgoutput.log" + false + else + chmod 0644 -- "$TO" + fi +} + +import_keyring_into_keyring() { + local FROM="${1:-${GPGHOMEDIR}/pubring.gpg}" + local TO="${2:-${GPGHOMEDIR}/pubring.gpg}" + shift 2 + rm -f "${GPGHOMEDIR}/gpgoutput.log" + # the idea is simple: We take keys from one keyring and copy it to another + # we do this with so many checks in between to ensure that WE control the + # creation, so we know that the (potentially) created $TO keyring is a + # simple keyring rather than a keybox as gpg2 would create it which in turn + # can't be read by gpgv. + # BEWARE: This is designed more in the way to work with the current + # callers, than to have a well defined it would be easy to add new callers to. + if [ ! -s "$TO" ]; then + if [ -s "$FROM" ]; then + if [ -z "$2" ]; then + local OPTS + if [ "${TO##*.}" = 'asc' ]; then + OPTS='--armor' + fi + export_key_from_to "$(dearmor_filename "$FROM")" "$TO" $OPTS ${1:+"$1"} + else + create_new_keyring "$TO" + fi + else + create_new_keyring "$TO" + fi + elif [ -s "$FROM" ]; then + local EXPORTLIMIT="$1" + if [ -n "$1$2" ]; then shift; fi + local DEARMORTO="$(dearmor_filename "$TO")" + if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export ${EXPORTLIMIT:+"$EXPORTLIMIT"} \ + | aptkey_execute "$GPG_SH" --keyring "$DEARMORTO" --batch --import "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then + cat >&2 "${GPGHOMEDIR}/gpgoutput.log" + false + fi + if [ "$DEARMORTO" != "$TO" ]; then + export_key_from_to "$DEARMORTO" "${DEARMORTO}.asc" --armor + if ! cmp -s "$TO" "${DEARMORTO}.asc" 2>/dev/null; then + cp -a "$TO" "${TO}~" + mv -f "${DEARMORTO}.asc" "$TO" + fi + fi + fi +} + +dearmor_keyring() { + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=831409#67 + # The awk script is more complex through to skip surrounding garbage and + # to support multiple keys in one file (old gpgs generate version headers + # which get printed with the original and hence result in garbage input for base64 + awk '{ gsub(/\r/,"") } +/^-----BEGIN/{ x = 1; } +/^$/{ if (x == 1) { x = 2; }; } +/^[^=-]/{ if (x == 2) { print $0; }; } +/^-----END/{ x = 0; }' | base64 -d +} +dearmor_filename() { + if [ "${1##*.}" = 'asc' ]; then + local trusted="${GPGHOMEDIR}/${1##*/}.gpg" + if [ -s "$1" ]; then + dearmor_keyring < "$1" > "$trusted" + fi + echo "$trusted" + elif [ "${1##*.}" = 'gpg' ]; then + echo "$1" + elif [ "$(head -n 1 "$1" 2>/dev/null)" = '-----BEGIN PGP PUBLIC KEY BLOCK-----' ]; then + local trusted="${GPGHOMEDIR}/${1##*/}.gpg" + dearmor_keyring < "$1" > "$trusted" + echo "$trusted" + else + echo "$1" + fi +} +catfile() { + if accessible_file_exists "$1" && is_supported_keyring "$1"; then + cat "$(dearmor_filename "$1")" >> "$2" + 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="$(readlink -f "${GPGHOMEDIR}")/pubring.gpg" + rm -f "$PUBRING" + touch "$PUBRING" + foreach_keyring_do 'catfile' "$PUBRING" +} + +import_keys_from_keyring() { + import_keyring_into_keyring "$1" "$2" +} + +merge_keys_into_keyrings() { + import_keyring_into_keyring "$2" "$1" '' --import-options 'merge-only' +} + +merge_back_changes() { + if [ -n "$FORCED_KEYRING" ]; then + # if the keyring was forced merge is already done + if [ "$FORCED_KEYRING" != "$TRUSTEDFILE" ]; then + mv -f "$FORCED_KEYRING" "${FORCED_KEYRING}~" + export_key_from_to "$TRUSTEDFILE" "$FORCED_KEYRING" --armor + fi + return + fi + if [ -s "${GPGHOMEDIR}/pubring.gpg" ]; then + # merge all updated keys + foreach_keyring_do 'merge_keys_into_keyrings' "${GPGHOMEDIR}/pubring.gpg" + fi + # 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" + 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 + merge_all_trusted_keyrings_into_pubring + FORCED_KEYRING="${GPGHOMEDIR}/forcedkeyid.gpg" + TRUSTEDFILE="${FORCED_KEYRING}" + echo "#!/bin/sh +exec sh '($(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh" + GPG="${GPGHOMEDIR}/gpg.1.sh" + # 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 + merge_all_trusted_keyrings_into_pubring + if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then + cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg" + else + touch "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg" + fi + echo "#!/bin/sh +exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${GPGHOMEDIR}/pubring.gpg")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh" + GPG="${GPGHOMEDIR}/gpg.1.sh" + else + TRUSTEDFILE="$(dearmor_filename "$FORCED_KEYRING")" + create_new_keyring "$TRUSTEDFILE" + echo "#!/bin/sh +exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh" + GPG="${GPGHOMEDIR}/gpg.1.sh" + fi +} + +create_new_keyring() { + # gpg defaults to mode 0600 for new keyrings. Create one with 0644 instead. + if ! [ -e "$1" ]; then + if [ -w "$(dirname "$1")" ]; then + touch -- "$1" + chmod 0644 -- "$1" + fi + fi +} + +aptkey_execute() { sh "$@"; } + +usage() { + echo "Usage: apt-key [--keyring file] [command] [arguments]" + echo + echo "Manage apt's list of trusted keys" + echo + echo " apt-key add - add the key contained in ('-' for stdin)" + echo " apt-key del - remove the key " + echo " apt-key export - output the key " + echo " apt-key exportall - output all trusted keys" + echo " apt-key update - update keys using the keyring package" + echo " apt-key net-update - update keys using the network" + echo " apt-key list - list keys" + echo " apt-key finger - list fingerprints" + echo " apt-key adv - pass advanced options to gpg (download key)" + echo + echo "If no specific keyring file is given the command applies to all keyring files." +} + +while [ -n "$1" ]; do + case "$1" in + --keyring) + shift + if [ -z "$FORCED_KEYRING" -o "$FORCED_KEYRING" = '/dev/null' ]; then + TRUSTEDFILE="$1" + FORCED_KEYRING="$1" + elif [ "$TRUSTEDFILE" = "$FORCED_KEYRING" ]; then + create_gpg_home + FORCED_KEYRING="${GPGHOMEDIR}/mergedkeyrings.gpg" + echo -n '' > "$FORCED_KEYRING" + chmod 0644 -- "$FORCED_KEYRING" + catfile "$TRUSTEDFILE" "$FORCED_KEYRING" + catfile "$1" "$FORCED_KEYRING" + else + catfile "$1" "$FORCED_KEYRING" + fi + ;; + --keyid) + shift + if [ -n "$FORCED_KEYID" ]; then + apt_error 'Specifying --keyid multiple times is not supported' + exit 1 + fi + FORCED_KEYID="$1" + ;; + --secret-keyring) + shift + FORCED_SECRET_KEYRING="$1" + ;; + --readonly) + merge_back_changes() { true; } + create_new_keyring() { if [ ! -r "$FORCED_KEYRING" ]; then TRUSTEDFILE='/dev/null'; FORCED_KEYRING="$TRUSTEDFILE"; fi; } + ;; + --fakeroot) + requires_root() { true; } + ;; + --quiet) + aptkey_echo() { true; } + ;; + --debug1) + # some cmds like finger redirect stderr to /dev/null … + aptkey_execute() { echo 'EXEC:' "$@"; sh "$@"; } + ;; + --debug2) + # … other more complicated ones pipe gpg into gpg. + aptkey_execute() { echo >&2 'EXEC:' "$@"; sh "$@"; } + ;; + --homedir) + # force usage of a specific homedir instead of creating a temporary + shift + GPGHOMEDIR="$1" + ;; + --*) + echo >&2 "Unknown option: $1" + usage + exit 1;; + *) + break;; + esac + shift +done + +if [ -z "$TRUSTEDFILE" ]; then + TRUSTEDFILE="/etc/apt/trusted.gpg" + eval $(apt-config shell TRUSTEDFILE Apt::GPGV::TrustedKeyring) + eval $(apt-config shell TRUSTEDFILE Dir::Etc::Trusted/f) + if [ "$APT_KEY_NO_LEGACY_KEYRING" ]; then + TRUSTEDFILE="/dev/null" + fi +fi + +command="$1" +if [ -z "$command" ]; then + usage + exit 1 +fi +shift + +prepare_gpg_home() { + # crude detection if we are called from a maintainerscript where the + # package depends on gnupg or not. We accept recommends here as + # well as the script hopefully uses apt-key optionally then like e.g. + # debian-archive-keyring for (upgrade) cleanup did + if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ] && [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then + if ! dpkg-query --show --showformat '${Pre-Depends}${Depends}${Recommends}\n' "$DPKG_MAINTSCRIPT_PACKAGE" 2>/dev/null | grep -E -q 'gpg|gnupg'; then + cat >&2 < "${GPGHOMEDIR}/gpg.0.sh" + GPG_SH="${GPGHOMEDIR}/gpg.0.sh" + GPG="$GPG_SH" + + # create the trustdb with an (empty) dummy keyring + # older gpgs required it, newer gpgs even warn that it isn't needed, + # but require it nonetheless for some commands, so we just play safe + # here for the foreseeable future and create a dummy one + touch "${GPGHOMEDIR}/empty.gpg" + if ! "$GPG_EXE" --ignore-time-conflict --no-options --no-default-keyring \ + --homedir "$GPGHOMEDIR" --quiet --check-trustdb --keyring "${GPGHOMEDIR}/empty.gpg" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then + cat >&2 "${GPGHOMEDIR}/gpgoutput.log" + false + fi + + # We don't usually need a secret keyring, of course, but + # for advanced operations, we might really need a secret keyring after all + if [ -n "$FORCED_SECRET_KEYRING" ] && [ -r "$FORCED_SECRET_KEYRING" ]; then + if ! aptkey_execute "$GPG" -v --batch --import "$FORCED_SECRET_KEYRING" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then + # already imported keys cause gpg1 to fail for some reason… ignore this error + if ! grep -q 'already in secret keyring' "${GPGHOMEDIR}/gpgoutput.log"; then + cat >&2 "${GPGHOMEDIR}/gpgoutput.log" + false + fi + fi + else + # and then, there are older versions of gpg which panic and implode + # if there isn't one available - and writeable for imports + # and even if not output is littered with the creation of a secring, + # so lets call import once to have it create what it wants in silence + echo -n | aptkey_execute "$GPG" --batch --import >/dev/null 2>&1 || true + fi +} + +warn_on_script_usage() { + if [ -n "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then + return + fi + # (Maintainer) scripts should not be using apt-key + if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ]; then + echo >&2 "Warning: apt-key should not be used in scripts (called from $DPKG_MAINTSCRIPT_NAME maintainerscript of the package ${DPKG_MAINTSCRIPT_PACKAGE})" + fi + + echo >&2 "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))." +} + +warn_outside_maintscript() { + # In del, we want to warn in interactive use, but not inside maintainer + # scripts, so as to give people a chance to migrate keyrings. + # + # FIXME: We should always warn starting in 2022. + if [ -z "$DPKG_MAINTSCRIPT_PACKAGE" ]; then + echo >&2 "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))." + fi +} + +if [ "$command" != 'help' ] && [ "$command" != 'verify' ]; then + prepare_gpg_home +fi + +case "$command" in + add) + warn_on_script_usage + requires_root + setup_merged_keyring + aptkey_execute "$GPG" --quiet --batch --import "$@" + merge_back_changes + aptkey_echo "OK" + ;; + del|rm|remove) + # no script warning here as removing 'add' usage needs 'del' for cleanup + warn_outside_maintscript + requires_root + foreach_keyring_do 'remove_key_from_keyring' "$@" + aptkey_echo "OK" + ;; + update) + warn_on_script_usage + requires_root + setup_merged_keyring + update + merge_back_changes + ;; + net-update) + warn_on_script_usage + requires_root + setup_merged_keyring + net_update + merge_back_changes + ;; + list|finger*) + warn_on_script_usage + foreach_keyring_do 'list_keys_in_keyring' --fingerprint "$@" + ;; + export|exportall) + warn_on_script_usage + merge_all_trusted_keyrings_into_pubring + aptkey_execute "$GPG_SH" --keyring "${GPGHOMEDIR}/pubring.gpg" --armor --export "$@" + ;; + adv*) + warn_on_script_usage + setup_merged_keyring + aptkey_echo "Executing: $GPG" "$@" + aptkey_execute "$GPG" "$@" + merge_back_changes + ;; + verify) + GPGV='' + ASSERT_PUBKEY_ALGO='' + eval $(apt-config shell GPGV Apt::Key::gpgvcommand ASSERT_PUBKEY_ALGO Apt::Key::assert-pubkey-algo) + if [ -n "$GPGV" ] && command_available "$GPGV"; then true; + elif command_available 'gpgv-sq'; then GPGV='gpgv-sq'; + elif command_available 'gpgv'; then GPGV='gpgv'; + elif command_available 'gpgv2'; then GPGV='gpgv2'; + elif command_available 'gpgv1'; then GPGV='gpgv1'; + else + apt_error 'gpgv, gpgv2 or gpgv1 required for verification, but neither seems installed' + exit 29 + fi + GPGV_ARGS="" + if [ "$ASSERT_PUBKEY_ALGO" ] && $GPGV --dump-options | grep -q -- --assert-pubkey-algo; then + GPGV_ARGS="--assert-pubkey-algo=$ASSERT_PUBKEY_ALGO" + 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" $GPGV_ARGS --homedir "${GPGHOMEDIR}" --keyring "$(dearmor_filename "${FORCED_KEYRING}")" --ignore-time-conflict "$@" + else + "$GPGV" $GPGV_ARGS --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@" + fi + ;; + help) + usage + ;; + *) + usage + exit 1 + ;; +esac -- cgit v1.2.3-70-g09d2 From f7f4a4cb5c99f2bfe7a2398c406048c92c9c93b2 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 23 Nov 2024 21:02:41 +0100 Subject: Remove the apt-key manual page and add documentation to apt-secure We move the user configuration section to the top of the manual page as that is going to be what most are interested in and rewrite it to cover all the modern ways to configure keys in a succinct way. --- debian/apt.manpages | 2 - doc/CMakeLists.txt | 1 - doc/apt-key.8.xml | 246 ------------------------------------------------- doc/apt-secure.8.xml | 56 ++++++----- doc/po4a.conf | 1 - doc/sources.list.5.xml | 4 +- 6 files changed, 37 insertions(+), 273 deletions(-) delete mode 100644 doc/apt-key.8.xml diff --git a/debian/apt.manpages b/debian/apt.manpages index 80dbabd44..582c145ba 100644 --- a/debian/apt.manpages +++ b/debian/apt.manpages @@ -2,7 +2,6 @@ usr/share/man/*/*/apt-cache.* usr/share/man/*/*/apt-cdrom.* usr/share/man/*/*/apt-config.* usr/share/man/*/*/apt-get.* -usr/share/man/*/*/apt-key.* usr/share/man/*/*/apt-mark.* usr/share/man/*/*/apt-secure.* usr/share/man/*/*/apt-patterns.* @@ -15,7 +14,6 @@ usr/share/man/*/apt-cache.* usr/share/man/*/apt-cdrom.* usr/share/man/*/apt-config.* usr/share/man/*/apt-get.* -usr/share/man/*/apt-key.* usr/share/man/*/apt-mark.* usr/share/man/*/apt-secure.* usr/share/man/*/apt-patterns.* diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 493c36c08..2df0ce721 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -84,7 +84,6 @@ add_docbook(apt-man MANPAGE ALL apt-extracttemplates.1.xml apt-ftparchive.1.xml apt-get.8.xml - apt-key.8.xml apt-mark.8.xml apt_preferences.5.xml apt-patterns.7.xml diff --git a/doc/apt-key.8.xml b/doc/apt-key.8.xml deleted file mode 100644 index c6c2d192e..000000000 --- a/doc/apt-key.8.xml +++ /dev/null @@ -1,246 +0,0 @@ - - %aptent; - %aptverbatiment; - %aptvendor; -]> - - - - &apt-author.jgunthorpe; - &apt-author.team; - &apt-email; - &apt-product; - - 2024-02-20T00:00:00Z - - - - apt-key - 8 - APT - - - - - apt-key - Deprecated APT key management utility - - - &synopsis-command-apt-key; - - Description - - apt-key is used to manage the list of keys used - by apt to authenticate packages. Packages which have been - authenticated using these keys will be considered trusted. - - - Use of apt-key is deprecated, except for the use of - apt-key del in maintainer scripts to remove existing - keys from the main keyring. - If such usage of apt-key is desired the additional - installation of the GNU Privacy Guard suite (packaged in - gnupg) is required. - - - apt-key(8) will last be available in Debian 12 and Ubuntu 24.04. - - - -Supported keyring files -apt-key supports only the binary OpenPGP format (also known as "GPG key - public ring") in files with the "gpg" extension, not - the keybox database format introduced in newer &gpg; versions as default - for keyring files. Binary keyring files intended to be used with any apt - version should therefore always be created with gpg --export. - -Alternatively, if all systems which should be using the created keyring - have at least apt version >= 1.4 installed, you can use the ASCII armored - format with the "asc" extension instead which can be - created with gpg --armor --export. - - - -Commands - - (deprecated) - - - Add a new key to the list of trusted keys. - The key is read from the filename given with the parameter - &synopsis-param-filename; or if the filename is - - from standard input. - - - It is critical that keys added manually via apt-key are - verified to belong to the owner of the repositories they claim to be for - otherwise the &apt-secure; infrastructure is completely undermined. - - - Note: Instead of using this command a keyring - should be placed directly in the /etc/apt/trusted.gpg.d/ - directory with a descriptive name and either "gpg" or - "asc" as file extension. - - - - - (mostly deprecated) - - - - Remove a key from the list of trusted keys. - - - - - - - (deprecated) - - - - Output the key &synopsis-param-keyid; to standard output. - - - - - - - (deprecated) - - - - Output all trusted keys to standard output. - - - - - - - , (deprecated) - - - - List trusted keys with fingerprints. - - - - - - - (deprecated) - - - Pass advanced options to gpg. With adv --recv-key you - can e.g. download key from keyservers directly into the trusted set of - keys. Note that there are no checks performed, so it is - easy to completely undermine the &apt-secure; infrastructure if used without - care. - - - - - - (deprecated) - - - Update the local keyring with the archive keyring and remove from - the local keyring the archive keys which are no longer valid. - The archive keyring is shipped in the archive-keyring package of your - distribution, e.g. the &keyring-package; package in &keyring-distro;. - - - Note that a distribution does not need to and in fact should not use - this command any longer and instead ship keyring files in the - /etc/apt/trusted.gpg.d/ directory directly as this - avoids a dependency on gnupg and it is easier to manage - keys by simply adding and removing files for maintainers and users alike. - - - - - (deprecated) - - - - Perform an update working similarly to the update command above, - but get the archive keyring from a URI instead and validate it against a master key. - - This requires an installed &wget; and an APT build configured to have - a server to fetch from and a master keyring to validate. - - APT in Debian does not support this command, relying on - update instead, but Ubuntu's APT does. - - - - - - - - - Options -Note that options need to be defined before the commands described in the previous section. - - (deprecated) - With this option it is possible to specify a particular keyring - file the command should operate on. The default is that a command is executed - on the trusted.gpg file as well as on all parts in the - trusted.gpg.d directory, though trusted.gpg - is the primary keyring which means that e.g. new keys are added to this one. - - - - - - Deprecation - - Except for using apt-key del in maintainer scripts, the use of apt-key is deprecated. This section shows how to replace existing use of apt-key. - -If your existing use of apt-key add looks like this: -wget -qO- https://myrepo.example/myrepo.asc | sudo apt-key add - -Then you can directly replace this with (though note the recommendation below): -wget -qO- https://myrepo.example/myrepo.asc | sudo tee /etc/apt/trusted.gpg.d/myrepo.asc -Make sure to use the "asc" extension for ASCII armored -keys and the "gpg" extension for the binary OpenPGP -format (also known as "GPG key public ring"). The binary OpenPGP format works -for all apt versions, while the ASCII armored format works for apt version >= -1.4. -Recommended: Instead of placing keys into the /etc/apt/trusted.gpg.d -directory, you can place them anywhere on your filesystem by using the -Signed-By option in your sources.list and -pointing to the filename of the key. See &sources-list; for details. -Since APT 2.4, /etc/apt/keyrings is provided as the recommended -location for keys not managed by packages. -When using a deb822-style sources.list, and with apt version >= 2.4, the -Signed-By option can also be used to include the full ASCII -armored keyring directly in the sources.list without an -additional file. - - - - - - Files - - - &file-trustedgpg; - - - - - -See Also - -&apt-get;, &apt-secure; - - - - &manbugs; - &manauthor; - - - diff --git a/doc/apt-secure.8.xml b/doc/apt-secure.8.xml index 7e40a2b1c..0549011e4 100644 --- a/doc/apt-secure.8.xml +++ b/doc/apt-secure.8.xml @@ -60,6 +60,40 @@ and &synaptic; support this authentication feature, so this manpage uses APT to refer to them all for simplicity only. + + +User Configuration + + Keys should usually be included inside their corresponding .sources + by embedding the ASCII-armored key in the Signed-By option. + To do so, replace the empty line with a dot, and then indent all lines by two spaces. + See &sources-list; for more information. + + + + Alternatively, keys may be placed in /etc/apt/keyrings for local keys, + or /usr/share/keyrings for keys managed by packages, and then referenced + by Signed-By: /etc/apt/keyrings/example-archive-keyring.asc option in a .sources + file or using deb [signed-by=/etc/apt/keyrings/example-archive-keyring.asc] ... in the legacy + .list format. This may be useful for APT versions prior to 2.4, which do not + support embedded keys. ASCII-armored keys must use an extension of .asc, and + unarmored keys an extension of .gpg. + + + + To generate keys suitable for use in APT using GnuPG, you will need to use the + gpg --export-options export-minimal [--armor] --export command. + Earlier solutions involving --keyring file --import no longer work + with recent GnuPG versions as they use a new internal format ("GPG keybox database"). + + + + Note that a default installation already contains all keys to securely + acquire packages from the default repositories, so managing keys + is only needed if third-party repositories are added. + The extrepo package can be used to manage several + external repositories with ease. + Unsigned Repositories @@ -180,26 +214,6 @@ -User Configuration - - apt-key is the program that manages the list of keys used - by APT to trust repositories. It can be used to add or remove keys as well - as list the trusted keys. Limiting which key(s) are able to sign which archive - is possible via the in &sources-list;. - - Note that a default installation already contains all keys to securely - acquire packages from the default repositories, so fiddling with - apt-key is only needed if third-party repositories are - added. - - In order to add a new key you need to first download it - (you should make sure you are using a trusted communication channel - when retrieving it), add it with apt-key and - then run apt-get update so that apt can download - and verify the InRelease or Release.gpg - files from the archives you have configured. - - Repository Configuration @@ -243,7 +257,7 @@ See Also -&apt-conf;, &apt-get;, &sources-list;, &apt-key;, &apt-ftparchive;, +&apt-conf;, &apt-get;, &sources-list;, &apt-ftparchive;, &debsign;, &debsig-verify;, &gpg; diff --git a/doc/po4a.conf b/doc/po4a.conf index 0798eac68..3cf4d5ea8 100644 --- a/doc/po4a.conf +++ b/doc/po4a.conf @@ -15,7 +15,6 @@ [type: manpage] apt.8.xml $lang:$lang/apt.$lang.8.xml add_$lang:xml.add [type: manpage] apt-get.8.xml $lang:$lang/apt-get.$lang.8.xml add_$lang:xml.add [type: manpage] apt-cache.8.xml $lang:$lang/apt-cache.$lang.8.xml add_$lang:xml.add -[type: manpage] apt-key.8.xml $lang:$lang/apt-key.$lang.8.xml add_$lang:xml.add [type: manpage] apt-mark.8.xml $lang:$lang/apt-mark.$lang.8.xml add_$lang:xml.add [type: manpage] apt-secure.8.xml $lang:$lang/apt-secure.$lang.8.xml add_$lang:xml.add [type: manpage] apt-cdrom.8.xml $lang:$lang/apt-cdrom.$lang.8.xml add_$lang:xml.add diff --git a/doc/sources.list.5.xml b/doc/sources.list.5.xml index 4fa7a245b..a463d4333 100644 --- a/doc/sources.list.5.xml +++ b/doc/sources.list.5.xml @@ -304,8 +304,8 @@ deb-src [ option1=value1 option2=value2 ] uri suite [component1] [component2] [. and /etc/apt/keyrings for keyrings managed by the system operator. If no keyring files are specified the default is the trusted.gpg keyring and - all keyrings in the trusted.gpg.d/ directory - (see apt-key fingerprint). If no fingerprint is + all keyrings in the trusted.gpg.d/ directory. + If no fingerprint is specified all keys in the keyrings are selected. A fingerprint will accept also all signatures by a subkey of this key, if this isn't desired an exclamation mark (!) can be appended to -- cgit v1.2.3-70-g09d2 From 2bb8a2c71c12063e52220c3a3839e063f11a319d Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 7 Dec 2024 15:07:35 +0100 Subject: methods/gpgv: Remove apt-key mentions --- methods/gpgv.cc | 6 +++--- test/integration/test-method-gpgv-legacy-keyring | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/methods/gpgv.cc b/methods/gpgv.cc index 947b585da..e5554b1e2 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -481,9 +481,9 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, else if (WEXITSTATUS(status) == 1) return _("At least one invalid signature was encountered."); else if (WEXITSTATUS(status) == 111) - return _("Could not execute 'apt-key' to verify signature (is gnupg installed?)"); + return _("Could not execute 'gpgv' to verify signature (is gnupg installed?)"); else - return _("Unknown error executing apt-key"); + return _("Unknown error executing gpgv"); } string GPGVMethod::VerifyGetSignersWithLegacy(const char *file, const char *outfile, vector const &keyFpts, @@ -518,7 +518,7 @@ string GPGVMethod::VerifyGetSignersWithLegacy(const char *file, const char *outf { std::string warning; strprintf(warning, - _("Key is stored in legacy trusted.gpg keyring (%s), see the DEPRECATION section in apt-key(8) for details."), + _("Key is stored in legacy trusted.gpg keyring (%s). Use Signed-By instead. See the USER CONFIGURATION section in apt-secure(8) for details."), legacyKeyFiles[0].c_str()); Warning(std::move(warning)); Signers = std::move(legacySigners); diff --git a/test/integration/test-method-gpgv-legacy-keyring b/test/integration/test-method-gpgv-legacy-keyring index 3c3e4536c..8afa5671b 100755 --- a/test/integration/test-method-gpgv-legacy-keyring +++ b/test/integration/test-method-gpgv-legacy-keyring @@ -24,7 +24,7 @@ rm rootdir/etc/apt/trusted.gpg.d/*.gpg testwarningequal "Get:1 file:${TMPWORKINGDIRECTORY}/aptarchive unstable InRelease [1420 B] Get:1 file:${TMPWORKINGDIRECTORY}/aptarchive unstable InRelease [1420 B] Reading package lists... -W: file:${TMPWORKINGDIRECTORY}/aptarchive/dists/unstable/InRelease: Key is stored in legacy trusted.gpg keyring (${TMPWORKINGDIRECTORY}/rootdir/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details." aptget update -q +W: file:${TMPWORKINGDIRECTORY}/aptarchive/dists/unstable/InRelease: Key is stored in legacy trusted.gpg keyring (${TMPWORKINGDIRECTORY}/rootdir/etc/apt/trusted.gpg). Use Signed-By instead. See the USER CONFIGURATION section in apt-secure(8) for details." aptget update -q # 2.4.0 regression: If the InRelease file was signed with two keys, fallback to trusted.gpg did not # work: It ran the fallback, but then ignored the result, as keys were still missing. @@ -32,4 +32,4 @@ signreleasefiles 'Joe Sixpack,Marvin Paranoid' testwarningequal "Get:1 file:${TMPWORKINGDIRECTORY}/aptarchive unstable InRelease [1867 B] Get:1 file:${TMPWORKINGDIRECTORY}/aptarchive unstable InRelease [1867 B] Reading package lists... -W: file:${TMPWORKINGDIRECTORY}/aptarchive/dists/unstable/InRelease: Key is stored in legacy trusted.gpg keyring (${TMPWORKINGDIRECTORY}/rootdir/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details." aptget update -q -omsg=with-two-signatures +W: file:${TMPWORKINGDIRECTORY}/aptarchive/dists/unstable/InRelease: Key is stored in legacy trusted.gpg keyring (${TMPWORKINGDIRECTORY}/rootdir/etc/apt/trusted.gpg). Use Signed-By instead. See the USER CONFIGURATION section in apt-secure(8) for details." aptget update -q -omsg=with-two-signatures -- cgit v1.2.3-70-g09d2