From patchwork Wed Jun 8 17:32:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonas Witschel X-Patchwork-Id: 2065 Return-Path: Delivered-To: patchwork@archlinux.org Received: from mail.archlinux.org [2a01:4f9:c010:3052::1] by patchwork.archlinux.org with IMAP (fetchmail-6.4.30) for (single-drop); Wed, 08 Jun 2022 17:32:51 +0000 (UTC) Received: from mail.archlinux.org by mail.archlinux.org with LMTP id YAqkN0LdoGI1NwEAK+/4rw (envelope-from ) for ; Wed, 08 Jun 2022 17:32:50 +0000 Received: from lists.archlinux.org (lists.archlinux.org [IPv6:2a01:4f9:c010:9eb4::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mail.archlinux.org (Postfix) with ESMTPS id 8DE4810B4850; Wed, 8 Jun 2022 17:32:49 +0000 (UTC) Received: from lists.archlinux.org (localhost [IPv6:::1]) by lists.archlinux.org (Postfix) with ESMTP id 535061099B99; Wed, 8 Jun 2022 17:32:49 +0000 (UTC) X-Original-To: pacman-dev@lists.archlinux.org Delivered-To: pacman-dev@lists.archlinux.org Received: from mail.archlinux.org (mail.archlinux.org [IPv6:2a01:4f9:c010:3052::1]) by lists.archlinux.org (Postfix) with ESMTPS id D99691099B89 for ; Wed, 8 Jun 2022 17:32:46 +0000 (UTC) From: Jonas Witschel DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=archlinux.org; s=dkim-rsa; t=1654709566; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=G7oRXKJSk7YmHuL4bu9ASPaKMvNELH32SgqNu6pvSw4=; b=v3oPSzjVkDMTxgdL+yCMD5aVA62iVXFL9tAMRGUl+S/xPi55bgrkh4lxp5fpi0KjYeLIU6 pQ/qhBiQG3kN2TH0BuQuBJKL1YD8FPcdC2TQ/JkGvpR3pqxlaYHK9m03TK4CotcL7uAgoM PDVvrHIjx0UmFpiMI9ojh52ljaOaWbu2T6rCr46gR+198st1CmXhCNPGdpMGIp03JVSCfj AdfYd2mF+rCNbhLAC1XQAJDvUK87oOxqzDxDKc/qTx/X3HB86oN7qo5d7q/Mus92hosfPX 97X3njhx2EI1+Qp35YpW5kBJZ7Cha5PNdqJDfLeg2YMkm2t/yLmFkZoCIwiUbaLof2+baC GwEWQX9luSi2fatSw2UtGn9tug/JkOiuqMUCl0wMJJzbl6ti9Cxn+48D3s9lfFChNX0o6N 7YwgQkxrY2sLb6p9TwJZgOVGBYb4c7g0+XV/GUP/4YQFzhOkLGwIbQUBauGUz/oHly1RwP otbhhVFkJYGSZmM2q+rYxdcMi684Vh2oSGgBFoLLasO8niUzFRaVdSVURFYiCuknny6lh0 vmMlYC0qsYvYazhMaiccpWVQccgIMOjDHNWI/Tlcm0sqTycEOXIoDkUDyVwP5pSFBlW8cV ay51WtwhFh6FXKSDw+Fkn8j/YcTcbA0LJpcvGm7X9qVFQsw7p52rg= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=archlinux.org; s=dkim-ed25519; t=1654709566; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=G7oRXKJSk7YmHuL4bu9ASPaKMvNELH32SgqNu6pvSw4=; b=kvjqmg3t/9hOJsJoUlri5vt3dOqtvOOm3bIPtf8SL1eMlhUrfRV3w2gb9VYCh8qsh9VzO8 JWlvBCMOZQOV2iBg== To: pacman-dev@lists.archlinux.org Cc: Jonas Witschel Subject: [PATCH] libmakepkg/integrity: handle PGP signature files containing multiple signatures Date: Wed, 8 Jun 2022 19:32:27 +0200 Message-Id: <20220608173227.170944-1-diabonas@archlinux.org> X-Mailer: git-send-email 2.36.1 MIME-Version: 1.0 X-BeenThere: pacman-dev@lists.archlinux.org X-Mailman-Version: 2.1.39 Precedence: list List-Id: Discussion list for pacman development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: pacman-dev-bounces@lists.archlinux.org Sender: "pacman-dev" Authentication-Results: mail.archlinux.org; dkim=pass header.d=archlinux.org header.s=dkim-rsa header.b=v3oPSzjV; dkim=pass header.d=archlinux.org header.s=dkim-ed25519 header.b=kvjqmg3t; spf=pass (mail.archlinux.org: domain of pacman-dev-bounces@lists.archlinux.org designates 2a01:4f9:c010:9eb4::1 as permitted sender) smtp.mailfrom=pacman-dev-bounces@lists.archlinux.org; dmarc=pass (policy=none) header.from=archlinux.org X-Rspamd-Server: mail.archlinux.org X-Spamd-Result: default: False [-2.61 / 15.00]; DWL_DNSWL_MED(-2.00)[archlinux.org:dkim]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; RCVD_DKIM_ARC_DNSWL_MED(-0.50)[]; DMARC_POLICY_ALLOW(-0.50)[archlinux.org,none]; RCVD_IN_DNSWL_MED(-0.40)[2a01:4f9:c010:3052::1:received,2a01:4f9:c010:9eb4::1:from]; MAILLIST(-0.20)[mailman]; R_SPF_ALLOW(-0.20)[+ip6:2a01:4f9:c010:9eb4::1:c]; R_DKIM_ALLOW(-0.20)[archlinux.org:s=dkim-rsa,archlinux.org:s=dkim-ed25519]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; MIME_TRACE(0.00)[0:+]; ASN(0.00)[asn:24940, ipnet:2a01:4f9::/32, country:DE]; RCVD_TLS_LAST(0.00)[]; ARC_NA(0.00)[]; RCVD_COUNT_THREE(0.00)[3]; DKIM_TRACE(0.00)[archlinux.org:+]; FROM_HAS_DN(0.00)[]; NEURAL_HAM(-0.00)[-1.000]; RCPT_COUNT_TWO(0.00)[2]; TO_DN_SOME(0.00)[]; FROM_NEQ_ENVFROM(0.00)[diabonas@archlinux.org,pacman-dev-bounces@lists.archlinux.org]; PREVIOUSLY_DELIVERED(0.00)[pacman-dev@lists.archlinux.org]; FORGED_SENDER_MAILLIST(0.00)[] X-Rspamd-Queue-Id: 8DE4810B4850 PGP signature files can contain multiple signatures from different keys. In this case, verification is considered to be successful if *all* signatures are valid, compare e.g. the behaviour of the gpgv verification tool. parse_gpg_statusfile() is currently not equipped to handle the gpg output for such signature files: it assumes that the file contains only one signature and therefore overwrites the verification results of previous signatures, effectively only considering the last signature in the file. Therefore the logic needs to be refined so that $success and $trusted are only true if all signatures are valid and trusted, respectively. We also need to store the verification results per key (in order to present more specific error messages) and all the validated fingerprints (in order to check whether they are in validpgpkeys). The signatures for the source tarballs of recent GnuPG releases, e.g. version 2.2.35, are examples of such signature files. Signed-off-by: Jonas Witschel --- .../integrity/verify_signature.sh.in | 115 ++++++++++-------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/scripts/libmakepkg/integrity/verify_signature.sh.in b/scripts/libmakepkg/integrity/verify_signature.sh.in index ad5bb66d..4aa14d94 100644 --- a/scripts/libmakepkg/integrity/verify_signature.sh.in +++ b/scripts/libmakepkg/integrity/verify_signature.sh.in @@ -26,6 +26,18 @@ LIBRARY=${LIBRARY:-'@libmakepkgdir@'} source "$LIBRARY/util/message.sh" source "$LIBRARY/util/pkgbuild.sh" +# Returns the elements of the first array that are not found in the second +# array as the third array. All arrays are passed by name. +set_difference() { + local -n first=$1 + local -n second=$2 + local -n result=$3 + result=() + for element in "${first[@]}"; do + ! in_array "$element" "${second[@]}" && result+=("$element") + done +} + check_pgpsigs() { (( SKIPPGPCHECK )) && return 0 ! source_has_signatures && return 0 @@ -56,48 +68,53 @@ check_pgpsigs() { fi # these variables are assigned values in parse_gpg_statusfile - success=0 - status= - pubkey= - fingerprint= + success= + declare -A status + fingerprints=() trusted= parse_gpg_statusfile "$statusfile" if (( ! $success )); then printf '%s' "$(gettext "FAILED")" >&2 - case "$status" in - "missingkey") - printf ' (%s)' "$(gettext "unknown public key") $pubkey" >&2 - ;; - "revokedkey") - printf " ($(gettext "public key %s has been revoked"))" "$pubkey" >&2 - ;; - "bad") - printf ' (%s)' "$(gettext "bad signature from public key") $pubkey" >&2 - ;; - "error") - printf ' (%s)' "$(gettext "error during signature verification")" >&2 - ;; - esac + for pubkey in "${!status[@]}"; do + case "${status["$pubkey"]}" in + "missingkey") + printf ' (%s)' "$(gettext "unknown public key") $pubkey" >&2 + ;; + "revokedkey") + printf " ($(gettext "public key %s has been revoked"))" "$pubkey" >&2 + ;; + "bad") + printf ' (%s)' "$(gettext "bad signature from public key") $pubkey" >&2 + ;; + "error") + printf ' (%s)' "$(gettext "error during signature verification")" >&2 + ;; + esac + done errors=1 else + local invalid_keys + set_difference fingerprints validpgpkeys invalid_keys if (( ${#validpgpkeys[@]} == 0 && !trusted )); then - printf "%s ($(gettext "the public key %s is not trusted"))" $(gettext "FAILED") "$fingerprint" >&2 + printf "%s ($(gettext "the public key %s is not trusted"))" $(gettext "FAILED") "${fingerprints[*]}" >&2 errors=1 - elif (( ${#validpgpkeys[@]} > 0 )) && ! in_array "$fingerprint" "${validpgpkeys[@]}"; then - printf "%s (%s %s)" "$(gettext "FAILED")" "$(gettext "invalid public key")" "$fingerprint" >&2 + elif (( ${#validpgpkeys[@]} > 0 )) && (( ${#invalid_keys[@]} > 0 )); then + printf "%s (%s %s)" "$(gettext "FAILED")" "$(gettext "invalid public key")" "${invalid_keys[*]}" >&2 errors=1 else printf '%s' "$(gettext "Passed")" >&2 - case "$status" in - "expired") - printf ' (%s)' "$(gettext "WARNING:") $(gettext "the signature has expired.")" >&2 - warnings=1 - ;; - "expiredkey") - printf ' (%s)' "$(gettext "WARNING:") $(gettext "the key has expired.")" >&2 - warnings=1 - ;; - esac + for pubkey in "${!status[@]}"; do + case "${status["$pubkey"]}" in + "expired") + printf " (%s $(gettext "the signature by key %s has expired."))" "$(gettext "WARNING:")" "$pubkey" >&2 + warnings=1 + ;; + "expiredkey") + printf " (%s $(gettext "the key %s has expired."))" "$(gettext "WARNING:")" "$pubkey" >&2 + warnings=1 + ;; + esac + done fi fi printf '\n' >&2 @@ -201,59 +218,57 @@ verify_git_signature() { parse_gpg_statusfile() { local type arg1 arg6 arg10 + success=-1 + trusted=-1 while read -r _ type arg1 _ _ _ _ arg6 _ _ _ arg10 _; do case "$type" in GOODSIG) - pubkey=$arg1 - success=1 - status="good" + success=$((success!=0)) + status["$arg1"]="good" ;; EXPSIG) - pubkey=$arg1 - success=1 - status="expired" + success=$((success!=0)) + status["$arg1"]="expired" ;; EXPKEYSIG) - pubkey=$arg1 - success=1 - status="expiredkey" + success=$((success!=0)) + status["$arg1"]="expiredkey" ;; REVKEYSIG) - pubkey=$arg1 success=0 - status="revokedkey" + status["$arg1"]="revokedkey" ;; BADSIG) - pubkey=$arg1 success=0 - status="bad" + status["$arg1"]="bad" ;; ERRSIG) - pubkey=$arg1 success=0 if [[ $arg6 == 9 ]]; then - status="missingkey" + status["$arg1"]="missingkey" else - status="error" + status["$arg1"]="error" fi ;; VALIDSIG) if [[ $arg10 ]]; then # If the file was signed with a subkey, arg10 contains # the fingerprint of the primary key - fingerprint=$arg10 + fingerprints+=("$arg10") else - fingerprint=$arg1 + fingerprints+=("$arg1") fi ;; TRUST_UNDEFINED|TRUST_NEVER) trusted=0 ;; TRUST_MARGINAL|TRUST_FULLY|TRUST_ULTIMATE) - trusted=1 + trusted=$((trusted!=0)) ;; esac done < "$1" + success=$((success>0)) + trusted=$((trusted>0)) } source_has_signatures() {