[pacman-dev] [PATCHv2 1/3] makepkg: Use read to parse status file during signature verification.
Instead of invoking grep multiple times, parse the status file once. This refactoring also changes the behvaiour when signature verification fails due to a missing public key: It is now an error instead of a warning. --- scripts/makepkg.sh.in | 92 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index e230c15..5386516 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1244,13 +1244,56 @@ check_checksums() { fi } +parse_gpg_statusfile() { + local gnupg type arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 rest + + while read -r gnupg type arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 rest; do + case "$type" in + GOODSIG) + pubkey=$arg1 + success=1 + status="good" + ;; + EXPSIG) + pubkey=$arg1 + success=1 + status="expired" + ;; + EXPKEYSIG) + pubkey=$arg1 + success=1 + status="expiredkey" + ;; + REVKEYSIG) + pubkey=$arg1 + success=0 + status="revokedkey" + ;; + BADSIG) + pubkey=$arg1 + success=0 + status="bad" + ;; + ERRSIG) + pubkey=$arg1 + success=0 + if [[ $arg6 == 9 ]]; then + status="missingkey" + else + status="error" + fi + ;; + esac + done < "$1" +} + check_pgpsigs() { (( SKIPPGPCHECK )) && return 0 ! source_has_signatures && return 0 msg "$(gettext "Verifying source file signatures with %s...")" "gpg" - local file pubkey ext decompress found + local file ext decompress found pubkey success status local warning=0 local errors=0 local statusfile=$(mktemp) @@ -1292,31 +1335,42 @@ check_pgpsigs() { "") decompress="cat" ;; esac - if ! $decompress < "$sourcefile" | gpg --quiet --batch --status-file "$statusfile" --verify "$file" - 2> /dev/null; then + $decompress < "$sourcefile" | gpg --quiet --batch --status-file "$statusfile" --verify "$file" - 2> /dev/null + success=0 + status= + pubkey= + parse_gpg_statusfile "$statusfile" + if (( ! $success )); then printf '%s' "$(gettext "FAILED")" >&2 - if ! pubkey=$(awk '/NO_PUBKEY/ { print $3; exit 1; }' "$statusfile"); then - printf ' (%s)' "$(gettext "unknown public key") $pubkey" >&2 - warnings=1 - else - errors=1 - fi - printf '\n' >&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 + errors=1 else - if grep -q "REVKEYSIG" "$statusfile"; then - printf '%s (%s)' "$(gettext "FAILED")" "$(gettext "the key has been revoked.")" >&2 - errors=1 - else - printf '%s' "$(gettext "Passed")" >&2 - if grep -q "EXPSIG" "$statusfile"; then + printf '%s' "$(gettext "Passed")" >&2 + case "$status" in + "expired") printf ' (%s)' "$(gettext "WARNING:") $(gettext "the signature has expired.")" >&2 warnings=1 - elif grep -q "EXPKEYSIG" "$statusfile"; then + ;; + "expiredkey") printf ' (%s)' "$(gettext "WARNING:") $(gettext "the key has expired.")" >&2 warnings=1 - fi - fi - printf '\n' >&2 + ;; + esac fi + printf '\n' >&2 done rm -f "$statusfile" -- 1.9.0
--- scripts/makepkg.sh.in | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index 5386516..d0e4fb5 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1283,6 +1283,12 @@ parse_gpg_statusfile() { status="error" fi ;; + TRUST_UNDEFINED|TRUST_NEVER) + trusted=0 + ;; + TRUST_MARGINAL|TRUST_FULLY|TRUST_ULTIMATE) + trusted=1 + ;; esac done < "$1" } @@ -1293,7 +1299,7 @@ check_pgpsigs() { msg "$(gettext "Verifying source file signatures with %s...")" "gpg" - local file ext decompress found pubkey success status + local file ext decompress found pubkey success status trusted local warning=0 local errors=0 local statusfile=$(mktemp) @@ -1339,6 +1345,7 @@ check_pgpsigs() { success=0 status= pubkey= + trusted= parse_gpg_statusfile "$statusfile" if (( ! $success )); then printf '%s' "$(gettext "FAILED")" >&2 @@ -1358,17 +1365,22 @@ check_pgpsigs() { esac 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 + if (( ! $trusted )); then + printf "%s ($(gettext "the public key %s is not trusted"))" $(gettext "FAILED") "$pubkey" >&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 + fi fi printf '\n' >&2 done -- 1.9.0
If validpgpkeys is set in the PKGBUILD, signature checking fails if the fingerprint of the key used to create the signature is not listed in the array. The key's trust value is ignored. --- doc/PKGBUILD.5.txt | 7 +++++++ scripts/makepkg.sh.in | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/doc/PKGBUILD.5.txt b/doc/PKGBUILD.5.txt index 50d8347..7a1e924 100644 --- a/doc/PKGBUILD.5.txt +++ b/doc/PKGBUILD.5.txt @@ -128,6 +128,13 @@ Files in the source array with extensions `.sig`, `.sign` or, `.asc` are recognized by makepkg as PGP signatures and will be automatically used to verify the integrity of the corresponding source file. +*validpgpkeys (array)*:: + An array of PGP fingerprints. If this array is non-empty, makepkg will + only accept signatures from the keys listed here and will ignore the + trust values from the keyring. ++ +Fingerprints must be uppercase and must not contain whitespace characters. + *noextract (array)*:: An array of file names corresponding to those from the source array. Files listed here will not be extracted with the rest of the source files. This diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index d0e4fb5..d24a2cd 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1283,6 +1283,13 @@ parse_gpg_statusfile() { status="error" fi ;; + VALIDSIG) + if [[ $arg10 ]]; then + fingerprint=$arg10 + else + fingerprint=$arg1 + fi + ;; TRUST_UNDEFINED|TRUST_NEVER) trusted=0 ;; @@ -1299,7 +1306,7 @@ check_pgpsigs() { msg "$(gettext "Verifying source file signatures with %s...")" "gpg" - local file ext decompress found pubkey success status trusted + local file ext decompress found pubkey success status fingerprint trusted local warning=0 local errors=0 local statusfile=$(mktemp) @@ -1345,6 +1352,7 @@ check_pgpsigs() { success=0 status= pubkey= + fingerprint= trusted= parse_gpg_statusfile "$statusfile" if (( ! $success )); then @@ -1365,9 +1373,12 @@ check_pgpsigs() { esac errors=1 else - if (( ! $trusted )); then + if (( ${#validpgpkeys[@]} == 0 && ! $trusted )); then printf "%s ($(gettext "the public key %s is not trusted"))" $(gettext "FAILED") "$pubkey" >&2 errors=1 + elif (( ${#validpgpkeys[@]} > 0 )) && ! in_array "$fingerprint" "${validpgpkeys[@]}"; then + printf "%s (%s $pubkey)" "$(gettext "FAILED")" "$(gettext "invalid public key")" + errors=1 else printf '%s' "$(gettext "Passed")" >&2 case "$status" in @@ -2875,7 +2886,7 @@ fi unset pkgname pkgbase pkgver pkgrel epoch pkgdesc url license groups provides unset md5sums replaces depends conflicts backup source install changelog build -unset makedepends optdepends options noextract +unset makedepends optdepends options noextract validpgpkeys BUILDFILE=${BUILDFILE:-$BUILDSCRIPT} if [[ ! -f $BUILDFILE ]]; then -- 1.9.0
On 09/03/14 05:22, Thomas Bächler wrote:
If validpgpkeys is set in the PKGBUILD, signature checking fails if the fingerprint of the key used to create the signature is not listed in the array.
The key's trust value is ignored. --- doc/PKGBUILD.5.txt | 7 +++++++ scripts/makepkg.sh.in | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/doc/PKGBUILD.5.txt b/doc/PKGBUILD.5.txt index 50d8347..7a1e924 100644 --- a/doc/PKGBUILD.5.txt +++ b/doc/PKGBUILD.5.txt @@ -128,6 +128,13 @@ Files in the source array with extensions `.sig`, `.sign` or, `.asc` are recognized by makepkg as PGP signatures and will be automatically used to verify the integrity of the corresponding source file.
+*validpgpkeys (array)*:: + An array of PGP fingerprints. If this array is non-empty, makepkg will + only accept signatures from the keys listed here and will ignore the + trust values from the keyring. ++ +Fingerprints must be uppercase and must not contain whitespace characters. + *noextract (array)*:: An array of file names corresponding to those from the source array. Files listed here will not be extracted with the rest of the source files. This diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index d0e4fb5..d24a2cd 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1283,6 +1283,13 @@ parse_gpg_statusfile() { status="error" fi ;; + VALIDSIG) + if [[ $arg10 ]]; then + fingerprint=$arg10 + else + fingerprint=$arg1 + fi + ;;
And here goes $arg10... But on every file I tested, $arg1 was always the fingerprint. How can $arg10 be different? Allan
TRUST_UNDEFINED|TRUST_NEVER) trusted=0 ;; @@ -1299,7 +1306,7 @@ check_pgpsigs() {
msg "$(gettext "Verifying source file signatures with %s...")" "gpg"
- local file ext decompress found pubkey success status trusted + local file ext decompress found pubkey success status fingerprint trusted local warning=0 local errors=0 local statusfile=$(mktemp) @@ -1345,6 +1352,7 @@ check_pgpsigs() { success=0 status= pubkey= + fingerprint= trusted= parse_gpg_statusfile "$statusfile" if (( ! $success )); then @@ -1365,9 +1373,12 @@ check_pgpsigs() { esac errors=1 else - if (( ! $trusted )); then + if (( ${#validpgpkeys[@]} == 0 && ! $trusted )); then printf "%s ($(gettext "the public key %s is not trusted"))" $(gettext "FAILED") "$pubkey" >&2 errors=1 + elif (( ${#validpgpkeys[@]} > 0 )) && ! in_array "$fingerprint" "${validpgpkeys[@]}"; then + printf "%s (%s $pubkey)" "$(gettext "FAILED")" "$(gettext "invalid public key")" + errors=1 else printf '%s' "$(gettext "Passed")" >&2 case "$status" in @@ -2875,7 +2886,7 @@ fi
unset pkgname pkgbase pkgver pkgrel epoch pkgdesc url license groups provides unset md5sums replaces depends conflicts backup source install changelog build -unset makedepends optdepends options noextract +unset makedepends optdepends options noextract validpgpkeys
BUILDFILE=${BUILDFILE:-$BUILDSCRIPT} if [[ ! -f $BUILDFILE ]]; then
Am 04.05.2014 08:50, schrieb Allan McRae:
On 09/03/14 05:22, Thomas Bächler wrote:
diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index d0e4fb5..d24a2cd 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1283,6 +1283,13 @@ parse_gpg_statusfile() { status="error" fi ;; + VALIDSIG) + if [[ $arg10 ]]; then + fingerprint=$arg10 + else + fingerprint=$arg1 + fi + ;;
And here goes $arg10... But on every file I tested, $arg1 was always the fingerprint. How can $arg10 be different?
Allan
VALIDSIG <fingerprint in hex> <sig_creation_date> <sig-timestamp> <expire-timestamp> <sig-version> <reserved> <pubkey-algo> <hash-algo> <sig-class> [ <primary-key-fpr> ] The signature with the keyid is good. This is the same as GOODSIG but has the fingerprint as the argument. Both status lines are emitted for a good signature. [...] PRIMARY-KEY-FPR is the fingerprint of the primary key or identical to the first argument. This is useful to get back to the primary key without running gpg again for this purpose. (doc/DETAILS from the gnupg source tarball) I'll add a bit to the documentation and a comment.
On 09/03/14 05:22, Thomas Bächler wrote:
Instead of invoking grep multiple times, parse the status file once.
This refactoring also changes the behvaiour when signature verification fails due to a missing public key: It is now an error instead of a warning. --- scripts/makepkg.sh.in | 92 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 19 deletions(-)
diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index e230c15..5386516 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1244,13 +1244,56 @@ check_checksums() { fi }
+parse_gpg_statusfile() { + local gnupg type arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 rest + + while read -r gnupg type arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 rest; do
It looks like we can clean this up a lot. I.e. while read -r _ type key _ _ _ _ missing _; do
+ case "$type" in + GOODSIG) + pubkey=$arg1 + success=1 + status="good" + ;; + EXPSIG) + pubkey=$arg1 + success=1 + status="expired" + ;; + EXPKEYSIG) + pubkey=$arg1 + success=1 + status="expiredkey" + ;; + REVKEYSIG) + pubkey=$arg1 + success=0 + status="revokedkey" + ;; + BADSIG) + pubkey=$arg1 + success=0 + status="bad" + ;; + ERRSIG) + pubkey=$arg1 + success=0 + if [[ $arg6 == 9 ]]; then + status="missingkey" + else + status="error" + fi + ;; + esac + done < "$1" +} + check_pgpsigs() { (( SKIPPGPCHECK )) && return 0 ! source_has_signatures && return 0
msg "$(gettext "Verifying source file signatures with %s...")" "gpg"
- local file pubkey ext decompress found + local file ext decompress found pubkey success status local warning=0 local errors=0 local statusfile=$(mktemp) @@ -1292,31 +1335,42 @@ check_pgpsigs() { "") decompress="cat" ;; esac
- if ! $decompress < "$sourcefile" | gpg --quiet --batch --status-file "$statusfile" --verify "$file" - 2> /dev/null; then + $decompress < "$sourcefile" | gpg --quiet --batch --status-file "$statusfile" --verify "$file" - 2> /dev/null + success=0 + status= + pubkey= + parse_gpg_statusfile "$statusfile" + if (( ! $success )); then printf '%s' "$(gettext "FAILED")" >&2 - if ! pubkey=$(awk '/NO_PUBKEY/ { print $3; exit 1; }' "$statusfile"); then - printf ' (%s)' "$(gettext "unknown public key") $pubkey" >&2 - warnings=1 - else - errors=1 - fi - printf '\n' >&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 + errors=1 else - if grep -q "REVKEYSIG" "$statusfile"; then - printf '%s (%s)' "$(gettext "FAILED")" "$(gettext "the key has been revoked.")" >&2 - errors=1 - else - printf '%s' "$(gettext "Passed")" >&2 - if grep -q "EXPSIG" "$statusfile"; then + printf '%s' "$(gettext "Passed")" >&2 + case "$status" in + "expired") printf ' (%s)' "$(gettext "WARNING:") $(gettext "the signature has expired.")" >&2 warnings=1 - elif grep -q "EXPKEYSIG" "$statusfile"; then + ;; + "expiredkey") printf ' (%s)' "$(gettext "WARNING:") $(gettext "the key has expired.")" >&2 warnings=1 - fi - fi - printf '\n' >&2 + ;; + esac fi + printf '\n' >&2 done
rm -f "$statusfile"
participants (2)
-
Allan McRae
-
Thomas Bächler